pax_global_header00006660000000000000000000000064126556515400014523gustar00rootroot0000000000000052 comment=545dc854fdcb77fd2318a2230572e7a4913c4d37 openbsc-0.15.0/000077500000000000000000000000001265565154000132375ustar00rootroot00000000000000openbsc-0.15.0/.gitignore000066400000000000000000000000151265565154000152230ustar00rootroot00000000000000debian/*.log openbsc-0.15.0/debian/000077500000000000000000000000001265565154000144615ustar00rootroot00000000000000openbsc-0.15.0/debian/autoreconf000066400000000000000000000000101265565154000165400ustar00rootroot00000000000000openbsc openbsc-0.15.0/debian/changelog000066400000000000000000000033661265565154000163430ustar00rootroot00000000000000openbsc (0.14.0) UNRELEASED; urgency=low * New upstream tag and additional patches. -- Holger Hans Peter Freyther Sat, 14 Mar 2015 20:33:25 +0100 openbsc (0.12.0+git26-7) unstable; urgency=low * 64bit fix for the MGCP rewriting -- Holger Hans Peter Freyther Wed, 07 Nov 2012 11:39:34 +0100 openbsc (0.12.0+git26-6) precise; urgency=low * Added init script for osmocom-sgsn. -- Eric Butler Fri, 24 Aug 2012 21:04:32 -0700 openbsc (0.12.0+git26-5) precise; urgency=low * Don't enable MNCC sock by default. * Automatically create important directories. * Fix init script 'stop' command. -- Eric Butler Fri, 24 Aug 2012 20:56:33 -0700 openbsc (0.12.0+git26-4) precise; urgency=low * Specify HLR path and enable RTP proxy. -- Eric Butler Mon, 20 Aug 2012 00:21:07 -0700 openbsc (0.12.0+git26-3) precise; urgency=low * Fix init script. -- Eric Butler Sun, 19 Aug 2012 16:05:44 -0700 openbsc (0.12.0+git26-2) precise; urgency=low * Fix libdbi package dependency. -- Eric Butler Wed, 15 Aug 2012 00:35:37 -0700 openbsc (0.12.0+git26-1) precise; urgency=low * Fix version issue. -- Eric Butler Tue, 14 Aug 2012 21:00:51 -0700 openbsc (0.12.0+git26) precise; urgency=low * Updated ubuntu package. -- Eric Butler Tue, 14 Aug 2012 17:36:51 -0700 openbsc (0.9.13.115.eb113-1) natty; urgency=low * New upstream release -- Harald Welte Wed, 11 May 2011 18:41:24 +0000 openbsc (0.9.4-1) unstable; urgency=low * Initial release -- Harald Welte Tue, 24 Aug 2010 13:34:24 +0200 openbsc-0.15.0/debian/compat000066400000000000000000000000021265565154000156570ustar00rootroot000000000000007 openbsc-0.15.0/debian/control000066400000000000000000000065351265565154000160750ustar00rootroot00000000000000Source: openbsc Section: net Priority: optional Maintainer: Harald Welte Build-Depends: debhelper (>= 7.0.0~), autotools-dev, pkg-config, libgtp0-dev, libosmocore-dev, libosmo-sccp-dev, libdbi0-dev, dh-autoreconf, libosmo-abis-dev, libosmo-netif-dev, libdbd-sqlite3, libpcap-dev, libssl-dev, libc-ares-dev, libsmpp34-dev Standards-Version: 3.8.4 Homepage: http://openbsc.osmocom.org/ Vcs-Git: git://bs11-abis.gnumonks.org/openbsc.git Vcs-Browser: http://openbsc.osmocom.org/trac/browser Package: osmocom-bsc Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Description: GSM Base Station Controller; BSC-only version of OpenBSC. Needs a real MSC! Classical BSC which requires MSC to operate. Package: osmocom-nitb Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, libdbd-sqlite3 Description: GSM Network-in-a-Box, implements BSC, MSC, SMSC, HLR, VLR All the GSM network components bundled together. Package: osmocom-ipaccess-utils Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Description: Command line utilities for ip.access nanoBTS Utilities specific for ip.access unit. Package: osmocom-bs11-utils Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Description: Command line utilities for Siemens BS-11 BTS Utilities specific for BS-11 unit. Package: osmocom-sgsn Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Description: Osmocom Serving GPRS Support Node SGSN implementation. Package: osmocom-gbproxy Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Description: Osmocom GPRS Gb Interface Proxy Proxy for Gb interface. Package: osmocom-bsc-nat Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Description: Osmocom Base Station Controller Network Address Translation Network address translation for BSC. Package: osmocom-bsc-dbg Architecture: any Section: debug Priority: extra Depends: osmocom-bsc (= ${binary:Version}), ${misc:Depends} Description: Debug symbols for the OpenBSC BSC Make debugging possible Package: osmocom-nitb-dbg Architecture: any Section: debug Priority: extra Depends: osmocom-nitb (= ${binary:Version}), ${misc:Depends} Description: Debug symbols for the OpenBSC NITB Make debugging possible Package: osmocom-ipaccess-utils-dbg Architecture: any Section: debug Priority: extra Depends: osmocom-ipaccess-utils (= ${binary:Version}), ${misc:Depends} Description: Debug symbols for the OpenBSC ip.access utils Make debugging possible Package: osmocom-bs11-utils-dbg Architecture: any Section: debug Priority: extra Depends: osmocom-bs11-utils (= ${binary:Version}), ${misc:Depends} Description: Debug symbols for the OpenBSC BS11 utils Make debugging possible Package: osmocom-sgsn-dbg Architecture: any Section: debug Priority: extra Depends: osmocom-sgsn (= ${binary:Version}), ${misc:Depends} Description: Debug symbols for the OpenBSC Serving GPRS Support Node Make debugging possible Package: osmocom-gbproxy-dbg Architecture: any Section: debug Priority: extra Depends: osmocom-gbproxy (= ${binary:Version}), ${misc:Depends} Description: Debug symbols for the OpenBSC GPRS GBProxy Make debugging possible Package: osmocom-bsc-nat-dbg Architecture: any Section: debug Priority: extra Depends: osmocom-bsc-nat (= ${binary:Version}), ${misc:Depends} Description: Debug symbols for the OpenBSC Network Address Translation Make debugging possible openbsc-0.15.0/debian/copyright000066400000000000000000000033251265565154000164170ustar00rootroot00000000000000This work was packaged for Debian by: Harald Welte on Tue, 24 Aug 2010 10:55:04 +0200 It was downloaded from: git://bs11-abis.gnumonks.org/openbsc.git Upstream Authors: Harald Welte Dieter Spaar Holger Hans Peter Freyther Sylvain Munaut Daniel Willmann Jan Luebbe Mike Haben Andreas Eversberg Copyright: Copyright (C) 2008-2010 Harald Welte Copyright (C) 2008-2009 Dieter Spaar Copyright (C) 2008-2010 Holger Hans Peter Freyther Copyright (C) 2009-2010 Sylvain Munaut Copyright (C) 2009-2010 On-Waves Copyright (C) 2008 Daniel Willmann Copyright (C) 2008 Jan Luebbe Copyright (C) 2009 by Mike Haben Copyright (C) 2009 Andreas Eversberg License: GNU General Public License, Version 2 or later The Debian packaging is: Copyright (C) 2010 Harald Welte # Please chose a license for your packaging work. If the program you package # uses a mainstream license, using the same license is the safest choice. # Please avoid to pick license terms that are more restrictive than the # packaged work, as it may make Debian's contributions unacceptable upstream. # If you just want it to be GPL version 3, leave the following lines in. and is licensed under the GPL version 3, see "/usr/share/common-licenses/GPL-3". openbsc-0.15.0/debian/docs000066400000000000000000000000171265565154000153320ustar00rootroot00000000000000openbsc/README openbsc-0.15.0/debian/osmocom-bs11-utils.install000066400000000000000000000000471265565154000214300ustar00rootroot00000000000000/usr/bin/bs11_config /usr/bin/isdnsync openbsc-0.15.0/debian/osmocom-bsc-nat.init000077500000000000000000000103471265565154000203570ustar00rootroot00000000000000#!/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 # 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 : openbsc-0.15.0/debian/osmocom-bsc-nat.install000066400000000000000000000000261265565154000210500ustar00rootroot00000000000000/usr/bin/osmo-bsc_nat openbsc-0.15.0/debian/osmocom-bsc.examples000066400000000000000000000000431265565154000204370ustar00rootroot00000000000000openbsc/doc/examples/osmo-bsc_mgcp openbsc-0.15.0/debian/osmocom-bsc.install000066400000000000000000000000511265565154000202660ustar00rootroot00000000000000/usr/bin/osmo-bsc_mgcp /usr/bin/osmo-bsc openbsc-0.15.0/debian/osmocom-gbproxy.init000077500000000000000000000100301265565154000205070ustar00rootroot00000000000000#!/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 # 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 : openbsc-0.15.0/debian/osmocom-gbproxy.install000066400000000000000000000000261265565154000212130ustar00rootroot00000000000000/usr/bin/osmo-gbproxy openbsc-0.15.0/debian/osmocom-ipaccess-utils.install000066400000000000000000000001101265565154000224430ustar00rootroot00000000000000/usr/bin/ipaccess-config /usr/bin/ipaccess-find /usr/bin/ipaccess-proxy openbsc-0.15.0/debian/osmocom-nitb.default000066400000000000000000000002351265565154000204350ustar00rootroot00000000000000CONFIG_FILE="/etc/osmocom/osmo-nitb.cfg" HLR_FILE="/var/lib/osmocom/hlr.sqlite3" DAEMON_ARGS="-P" # Uncomment if using LCR+Asterisk # DAEMON_ARGS="-m -P" openbsc-0.15.0/debian/osmocom-nitb.dirs000066400000000000000000000000571265565154000177540ustar00rootroot00000000000000/etc/osmocom /var/log/osmocom /var/lib/osmocom openbsc-0.15.0/debian/osmocom-nitb.examples000066400000000000000000000000371265565154000206270ustar00rootroot00000000000000openbsc/doc/examples/osmo-nitb openbsc-0.15.0/debian/osmocom-nitb.init000077500000000000000000000102721265565154000177610ustar00rootroot00000000000000#!/bin/sh ### BEGIN INIT INFO # Provides: osmo-nitb # 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 # PATH should only include /usr/* if it runs after the mountnfs.sh script PATH=/sbin:/usr/sbin:/bin:/usr/bin NAME=osmo-nitb # Introduce the short server's name here DESC="Osmocom GSM Network-in-a-Box" # Introduce a short description here DAEMON=/usr/bin/osmo-nitb # Introduce the server's location here SCRIPTNAME=/etc/init.d/osmocom-nitb # Exit if the package is not installed [ -x $DAEMON ] || exit 0 # Read configuration variable file if it is present [ -r /etc/default/osmocom-nitb ] && . /etc/default/osmocom-nitb # 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 -l $HLR_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 : openbsc-0.15.0/debian/osmocom-nitb.install000066400000000000000000000000231265565154000204520ustar00rootroot00000000000000/usr/bin/osmo-nitb openbsc-0.15.0/debian/osmocom-sgsn.default000066400000000000000000000000521265565154000204500ustar00rootroot00000000000000CONFIG_FILE="/etc/osmocom/osmo-sgsn.cfg" openbsc-0.15.0/debian/osmocom-sgsn.examples000066400000000000000000000000371265565154000206450ustar00rootroot00000000000000openbsc/doc/examples/osmo-sgsn openbsc-0.15.0/debian/osmocom-sgsn.init000077500000000000000000000100171265565154000177740ustar00rootroot00000000000000#!/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 # 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 : openbsc-0.15.0/debian/osmocom-sgsn.install000066400000000000000000000000231265565154000204700ustar00rootroot00000000000000/usr/bin/osmo-sgsn openbsc-0.15.0/debian/rules000077500000000000000000000030651265565154000155450ustar00rootroot00000000000000#!/usr/bin/make -f # -*- makefile -*- # Sample debian/rules that uses debhelper. # # This file was originally written by Joey Hess and Craig Small. # As a special exception, when this file is copied by dh-make into a # dh-make output file, you may use that output file without restriction. # This special exception was added by Craig Small in version 0.37 of dh-make. # # Modified to make a template file for a multi-binary package with separated # build-arch and build-indep targets by Bill Allombert 2001 # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 DEBIAN := $(shell dpkg-parsechangelog | grep ^Version: | cut -d' ' -f2) DEBVERS := $(shell echo '$(DEBIAN)' | cut -d- -f1) VERSION := $(shell echo '$(DEBVERS)' | sed -e 's/[+-].*//' -e 's/~//g') export DEB_BUILD_HARDENING=1 %: dh --sourcedirectory=openbsc --with autoreconf $@ # This is needed for debian stable (squeeze) override_dh_autoreconf: cd openbsc && autoreconf --install --force override_dh_strip: dh_strip -posmocom-bsc --dbg-package=osmocom-bsc-dbg dh_strip -posmocom-nitb --dbg-package=osmocom-nitb-dbg dh_strip -posmocom-ipaccess-utils --dbg-package=osmocom-ipaccess-utils-dbg dh_strip -posmocom-bs11-utils --dbg-package=osmocom-bs11-utils-dbg dh_strip -posmocom-sgsn --dbg-package=osmocom-sgsn-dbg dh_strip -posmocom-gbproxy --dbg-package=osmocom-gbproxy-dbg dh_strip -posmocom-bsc-nat --dbg-package=osmocom-bsc-nat-dbg override_dh_auto_configure: echo $(VERSION) > openbsc/.tarball-version dh_auto_configure --sourcedirectory=openbsc -- --enable-nat --enable-osmo-bsc --enable-smpp openbsc-0.15.0/debian/source/000077500000000000000000000000001265565154000157615ustar00rootroot00000000000000openbsc-0.15.0/debian/source/format000066400000000000000000000000151265565154000171700ustar00rootroot000000000000003.0 (native) openbsc-0.15.0/hlrsync/000077500000000000000000000000001265565154000147215ustar00rootroot00000000000000openbsc-0.15.0/hlrsync/hlrsync.py000077500000000000000000000067271265565154000167740ustar00rootroot00000000000000#!/usr/bin/python2.5 from __future__ import with_statement from pysqlite2 import dbapi2 as sqlite3 import sys hlr = sqlite3.connect(sys.argv[1]) web = sqlite3.connect(sys.argv[2]) # switch to autocommit hlr.isolation_level = None web.isolation_level = None hlr.row_factory = sqlite3.Row web.row_factory = sqlite3.Row with hlr: hlr_subscrs = hlr.execute(""" SELECT * FROM Subscriber """).fetchall() hlr_tokens = hlr.execute(""" SELECT * FROM AuthToken """).fetchall() with web: web_tokens = web.execute(""" SELECT * FROM reg_tokens """).fetchall() web_sms = web.execute(""" SELECT * FROM sms_queue """).fetchall() # index by subscr id hlr_subscrs_by_id = {} hlr_subscrs_by_ext = {} hlr_tokens_by_subscr_id = {} for x in hlr_subscrs: hlr_subscrs_by_id[x['id']] = x hlr_subscrs_by_ext[x['extension']] = x del hlr_subscrs for x in hlr_tokens: hlr_tokens_by_subscr_id[x['subscriber_id']] = x del hlr_tokens web_tokens_by_subscr_id = {} for x in web_tokens: web_tokens_by_subscr_id[x['subscriber_id']] = x del web_tokens # remove leftover web_tokens and correct inconsistent fields with web: for x in web_tokens_by_subscr_id.values(): subscr = hlr_subscrs_by_id.get(x['subscriber_id'], None) if subscr is None: web.execute(""" DELETE FROM reg_tokens WHERE subscriber_id = ? """, (x['subscriber_id'],)) del web_tokens_by_subscr_id[x['subscriber_id']] continue if str(x['imsi']) != str(subscr['imsi']) or \ x['extension'] != subscr['extension'] or \ x['tmsi'] != subscr['tmsi'] or \ x['lac'] != subscr['lac']: web.execute(""" UPDATE reg_tokens SET imsi = ?, extension = ?, tmsi = ?, lac = ? WHERE subscriber_id = ? """, (str(subscr['imsi']), subscr['extension'], subscr['tmsi'], subscr['lac'], x['subscriber_id'])) # add missing web_tokens with web: for x in hlr_tokens_by_subscr_id.values(): subscr = hlr_subscrs_by_id.get(x['subscriber_id'], None) if subscr is None: hlr.execute(""" DELETE FROM AuthToken WHERE subscriber_id = ? """, (x['subscriber_id'],)) del hlr_tokens_by_subscr_id[x['subscriber_id']] continue webtoken = web_tokens_by_subscr_id.get(x['subscriber_id'], None) if webtoken is None: web.execute(""" INSERT INTO reg_tokens (subscriber_id, extension, reg_completed, name, email, lac, imsi, token, tmsi) VALUES (?, ?, 0, ?, '', ?, ?, ?, ?) """, (x['subscriber_id'], subscr['extension'], subscr['name'], subscr['lac'], str(subscr['imsi']), x['token'], subscr['tmsi'])) # authorize subscribers with hlr: for x in web_tokens_by_subscr_id.values(): subscr = hlr_subscrs_by_id.get(x['subscriber_id'], None) if x['reg_completed'] and not subscr['authorized']: hlr.execute(""" UPDATE Subscriber SET authorized = 1 WHERE id = ? """, (x['subscriber_id'],)) # Sync SMS from web to hlr with hlr: for sms in web_sms: subscr = hlr_subscrs_by_ext.get(sms['receiver_ext']) if subscr is None: print '%s not found' % sms['receiver_ext'] continue hlr.execute(""" INSERT INTO SMS (created, sender_id, receiver_id, reply_path_req, status_rep_req, protocol_id, data_coding_scheme, ud_hdr_ind, text) VALUES (?, 1, ?, 0, 0, 0, 0, 0, ?) """, (sms['created'], subscr['id'], sms['text'])) with web: for sms in web_sms: web.execute(""" DELETE FROM sms_queue WHERE id = ? """, (sms['id'],)) hlr.close() web.close() openbsc-0.15.0/linux-kernel/000077500000000000000000000000001265565154000156545ustar00rootroot00000000000000openbsc-0.15.0/linux-kernel/linux-2.6.27.4-misdn-abis.diff000066400000000000000000000100531265565154000225640ustar00rootroot00000000000000diff -Nru --exclude-from /sunbeam/home/laforge/scripts/dontdiff linux-2.6.27.4-clean/drivers/isdn/mISDN/layer2.c linux-2.6.27.4/drivers/isdn/mISDN/layer2.c --- linux-2.6.27.4-clean/drivers/isdn/mISDN/layer2.c 2008-10-26 00:05:07.000000000 +0200 +++ linux-2.6.27.4/drivers/isdn/mISDN/layer2.c 2008-12-23 16:16:29.000000000 +0100 @@ -94,8 +94,10 @@ struct layer2 *l2 = fi->userdata; va_list va; +#if 0 if (!(*debug & DEBUG_L2_FSM)) return; +#endif va_start(va, fmt); printk(KERN_DEBUG "l2 (tei %d): ", l2->tei); vprintk(fmt, va); @@ -882,6 +884,8 @@ l2->va = 0; l2->vr = 0; l2->sow = 0; + l2->sapi = skb->data[0] >> 2; + set_channel_address(&l2->ch, l2->sapi, l2->tei); clear_exception(l2); send_uframe(l2, NULL, UA | get_PollFlag(l2, skb), RSP); mISDN_FsmChangeState(fi, ST_L2_7); @@ -898,6 +902,7 @@ struct layer2 *l2 = fi->userdata; struct sk_buff *skb = arg; + printk(KERN_DEBUG "l2_send_UA()\n"); send_uframe(l2, skb, UA | get_PollFlag(l2, skb), RSP); } @@ -931,6 +936,8 @@ l2->va = 0; l2->vr = 0; l2->sow = 0; + l2->sapi = skb->data[0] >> 2; + set_channel_address(&l2->ch, l2->sapi, l2->tei); mISDN_FsmChangeState(fi, ST_L2_7); stop_t200(l2, 3); mISDN_FsmRestartTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 3); @@ -982,6 +989,8 @@ } else if (l2->vs != l2->va) { skb_queue_purge(&l2->i_queue); pr = DL_ESTABLISH_IND; + //l2->sapi = skb->data[0] >> 2; + //set_channel_address(&l2->ch, l2->sapi, l2->tei); } stop_t200(l2, 5); l2->vr = 0; @@ -1841,11 +1850,14 @@ u_int l; int c = 0; + printk(KERN_DEBUG "ph_data_indication 0x%x 0x%x 0x%x\n", datap[0], datap[1], datap[2]); + l = l2addrsize(l2); if (skb->len <= l) { mISDN_FsmEvent(&l2->l2m, EV_L2_FRAME_ERROR, (void *) 'N'); return ret; } +#if 0 if (test_bit(FLG_LAPD, &l2->flag)) { /* Maybe not needed */ psapi = *datap++; ptei = *datap++; @@ -1875,6 +1887,7 @@ return 0; } } else +#endif datap += l; if (!(*datap & 1)) { /* I-Frame */ c = iframe_error(l2, skb); @@ -1890,6 +1903,7 @@ ret = mISDN_FsmEvent(&l2->l2m, EV_L2_UI, skb); } else if (IsSABME(datap, l2)) { c = unnum_error(l2, skb, CMD); + printk(KERN_DEBUG "IsSABME() returned true, unnum_error=%d\n", c); if (!c) ret = mISDN_FsmEvent(&l2->l2m, EV_L2_SABME, skb); } else if (IsUA(datap)) { @@ -2087,7 +2101,7 @@ test_and_set_bit(FLG_LAPD, &l2->flag); test_and_set_bit(FLG_LAPD_NET, &l2->flag); test_and_set_bit(FLG_MOD128, &l2->flag); - l2->sapi = 0; + l2->sapi = 62; l2->maxlen = MAX_DFRAME_LEN; if (test_bit(OPTION_L2_PMX, &options)) l2->window = 7; diff -Nru --exclude-from /sunbeam/home/laforge/scripts/dontdiff linux-2.6.27.4-clean/drivers/isdn/mISDN/tei.c linux-2.6.27.4/drivers/isdn/mISDN/tei.c --- linux-2.6.27.4-clean/drivers/isdn/mISDN/tei.c 2008-10-26 00:05:07.000000000 +0200 +++ linux-2.6.27.4/drivers/isdn/mISDN/tei.c 2008-12-23 16:32:59.000000000 +0100 @@ -830,18 +830,29 @@ int tei, ri; struct layer2 *l2; + printk(KERN_DEBUG "new tei request: tei=%d\n", dp[3] >> 1); + ri = dp[0] << 8; ri += dp[1]; - if (!mgr->up) - goto denied; - tei = get_free_tei(mgr); - if (tei < 0) { - printk(KERN_WARNING "%s:No free tei\n", __func__); + if (!mgr->up) { + printk(KERN_DEBUG "mgr->up == NULL\n"); goto denied; } + if (dp[3] != 0xff) { + /* This is a TEI request according to 3GPP TS 08.56 6.1.11.2 */ + tei = dp[3] >> 1; + } else { + tei = get_free_tei(mgr); + if (tei < 0) { + printk(KERN_WARNING "%s:No free tei\n", __func__); + goto denied; + } + } l2 = create_new_tei(mgr, tei); - if (!l2) + if (!l2) { + printk(KERN_DEBUG "create_new_tei == NULL\n"); goto denied; + } else mISDN_FsmEvent(&l2->tm->tei_m, EV_ASSIGN_REQ, dp); return; @@ -1159,12 +1170,14 @@ return -ENOTCONN; if (skb->len != 3) return -ENOTCONN; +#if 0 if (skb->data[0] != 0) /* only SAPI 0 command */ return -ENOTCONN; +#endif if (!(skb->data[1] & 1)) /* invalid EA1 */ return -EINVAL; - tei = skb->data[1] >> 0; + tei = skb->data[1] >> 1; if (tei > 63) /* not a fixed tei */ return -ENOTCONN; if ((skb->data[2] & ~0x10) != SABME) openbsc-0.15.0/linux-kernel/linux-2.6.30-hfcmulti-multibts.patch000066400000000000000000000411361265565154000242370ustar00rootroot00000000000000This experimental patch splits one E1 card into three virtual cards, TS 1,2,3,4,5 is card 0 TS 6,7,8,9,10 is card 1 TS 11,12,13,14 is card 2 This allows you to run one L2 TEI handler on each of the virtual cards, which is required if you want to run multiple BTS on a single E1 link. Thanks to Andreas Eversberg for this patch. diff --git a/drivers/isdn/hardware/mISDN/hfc_multi.h b/drivers/isdn/hardware/mISDN/hfc_multi.h index 0c77386..02dd4a1 100644 --- a/drivers/isdn/hardware/mISDN/hfc_multi.h +++ b/drivers/isdn/hardware/mISDN/hfc_multi.h @@ -209,14 +209,17 @@ struct hfc_multi { u_long ledstate; /* save last state of leds */ int opticalsupport; /* has the e1 board */ /* an optical Interface */ - int dslot; /* channel # of d-channel (E1) default 16 */ + + u_int bmask[32]; /* bitmask of bchannels for port */ + u_char dnum[32]; /* array of used dchannel numbers for port */ + u_char created[32]; /* what port is created */ + u_int activity[32]; /* if there is any action on this */ + /* port (will be cleared after */ + /* showing led-states) */ u_long wdcount; /* every 500 ms we need to */ /* send the watchdog a signal */ u_char wdbyte; /* watchdog toggle byte */ - u_int activity[8]; /* if there is any action on this */ - /* port (will be cleared after */ - /* showing led-states) */ int e1_state; /* keep track of last state */ int e1_getclock; /* if sync is retrieved from interface */ int syncronized; /* keep track of existing sync interface */ @@ -233,7 +236,6 @@ struct hfc_multi { * the bch->channel is equvalent to the hfc-channel */ struct hfc_chan chan[32]; - u_char created[8]; /* what port is created */ signed char slot_owner[256]; /* owner channel of slot */ }; diff --git a/drivers/isdn/hardware/mISDN/hfcmulti.c b/drivers/isdn/hardware/mISDN/hfcmulti.c index e1dab30..4fe2d27 100644 --- a/drivers/isdn/hardware/mISDN/hfcmulti.c +++ b/drivers/isdn/hardware/mISDN/hfcmulti.c @@ -1619,8 +1619,8 @@ hfcmulti_leds(struct hfc_multi *hc) * left red: frame sync, but no L1 * right green: L2 active */ - if (hc->chan[hc->dslot].sync != 2) { /* no frame sync */ - if (hc->chan[hc->dslot].dch->dev.D.protocol + if (hc->chan[hc->dnum[0]].sync != 2) { /* no frame sync */ + if (hc->chan[hc->dnum[0]].dch->dev.D.protocol != ISDN_P_NT_E1) { led[0] = 1; led[1] = 1; @@ -2428,55 +2428,56 @@ handle_timer_irq(struct hfc_multi *hc) } } if (hc->ctype == HFC_TYPE_E1 && hc->created[0]) { - dch = hc->chan[hc->dslot].dch; - if (test_bit(HFC_CFG_REPORT_LOS, &hc->chan[hc->dslot].cfg)) { +#warning todo: put interface parameters to hc + dch = hc->chan[hc->dnum[0]].dch; + if (test_bit(HFC_CFG_REPORT_LOS, &hc->chan[hc->dnum[0]].cfg)) { /* LOS */ temp = HFC_inb_nodebug(hc, R_SYNC_STA) & V_SIG_LOS; - if (!temp && hc->chan[hc->dslot].los) + if (!temp && hc->chan[hc->dnum[0]].los) signal_state_up(dch, L1_SIGNAL_LOS_ON, "LOS detected"); - if (temp && !hc->chan[hc->dslot].los) + if (temp && !hc->chan[hc->dnum[0]].los) signal_state_up(dch, L1_SIGNAL_LOS_OFF, "LOS gone"); - hc->chan[hc->dslot].los = temp; + hc->chan[hc->dnum[0]].los = temp; } - if (test_bit(HFC_CFG_REPORT_AIS, &hc->chan[hc->dslot].cfg)) { + if (test_bit(HFC_CFG_REPORT_AIS, &hc->chan[hc->dnum[0]].cfg)) { /* AIS */ temp = HFC_inb_nodebug(hc, R_SYNC_STA) & V_AIS; - if (!temp && hc->chan[hc->dslot].ais) + if (!temp && hc->chan[hc->dnum[0]].ais) signal_state_up(dch, L1_SIGNAL_AIS_ON, "AIS detected"); - if (temp && !hc->chan[hc->dslot].ais) + if (temp && !hc->chan[hc->dnum[0]].ais) signal_state_up(dch, L1_SIGNAL_AIS_OFF, "AIS gone"); - hc->chan[hc->dslot].ais = temp; + hc->chan[hc->dnum[0]].ais = temp; } - if (test_bit(HFC_CFG_REPORT_SLIP, &hc->chan[hc->dslot].cfg)) { + if (test_bit(HFC_CFG_REPORT_SLIP, &hc->chan[hc->dnum[0]].cfg)) { /* SLIP */ temp = HFC_inb_nodebug(hc, R_SLIP) & V_FOSLIP_RX; - if (!temp && hc->chan[hc->dslot].slip_rx) + if (!temp && hc->chan[hc->dnum[0]].slip_rx) signal_state_up(dch, L1_SIGNAL_SLIP_RX, " bit SLIP detected RX"); - hc->chan[hc->dslot].slip_rx = temp; + hc->chan[hc->dnum[0]].slip_rx = temp; temp = HFC_inb_nodebug(hc, R_SLIP) & V_FOSLIP_TX; - if (!temp && hc->chan[hc->dslot].slip_tx) + if (!temp && hc->chan[hc->dnum[0]].slip_tx) signal_state_up(dch, L1_SIGNAL_SLIP_TX, " bit SLIP detected TX"); - hc->chan[hc->dslot].slip_tx = temp; + hc->chan[hc->dnum[0]].slip_tx = temp; } - if (test_bit(HFC_CFG_REPORT_RDI, &hc->chan[hc->dslot].cfg)) { + if (test_bit(HFC_CFG_REPORT_RDI, &hc->chan[hc->dnum[0]].cfg)) { /* RDI */ temp = HFC_inb_nodebug(hc, R_RX_SL0_0) & V_A; - if (!temp && hc->chan[hc->dslot].rdi) + if (!temp && hc->chan[hc->dnum[0]].rdi) signal_state_up(dch, L1_SIGNAL_RDI_ON, "RDI detected"); - if (temp && !hc->chan[hc->dslot].rdi) + if (temp && !hc->chan[hc->dnum[0]].rdi) signal_state_up(dch, L1_SIGNAL_RDI_OFF, "RDI gone"); - hc->chan[hc->dslot].rdi = temp; + hc->chan[hc->dnum[0]].rdi = temp; } temp = HFC_inb_nodebug(hc, R_JATT_DIR); - switch (hc->chan[hc->dslot].sync) { + switch (hc->chan[hc->dnum[0]].sync) { case 0: if ((temp & 0x60) == 0x60) { if (debug & DEBUG_HFCMULTI_SYNC) @@ -2485,10 +2486,10 @@ handle_timer_irq(struct hfc_multi *hc) "in clock sync\n", __func__, hc->id); HFC_outb(hc, R_RX_OFF, - hc->chan[hc->dslot].jitter | V_RX_INIT); + hc->chan[hc->dnum[0]].jitter | V_RX_INIT); HFC_outb(hc, R_TX_OFF, - hc->chan[hc->dslot].jitter | V_RX_INIT); - hc->chan[hc->dslot].sync = 1; + hc->chan[hc->dnum[0]].jitter | V_RX_INIT); + hc->chan[hc->dnum[0]].sync = 1; goto check_framesync; } break; @@ -2499,7 +2500,7 @@ handle_timer_irq(struct hfc_multi *hc) "%s: (id=%d) E1 " "lost clock sync\n", __func__, hc->id); - hc->chan[hc->dslot].sync = 0; + hc->chan[hc->dnum[0]].sync = 0; break; } check_framesync: @@ -2510,7 +2511,7 @@ check_framesync: "%s: (id=%d) E1 " "now in frame sync\n", __func__, hc->id); - hc->chan[hc->dslot].sync = 2; + hc->chan[hc->dnum[0]].sync = 2; } break; case 2: @@ -2520,7 +2521,7 @@ check_framesync: "%s: (id=%d) E1 lost " "clock & frame sync\n", __func__, hc->id); - hc->chan[hc->dslot].sync = 0; + hc->chan[hc->dnum[0]].sync = 0; break; } temp = HFC_inb_nodebug(hc, R_SYNC_STA); @@ -2530,7 +2531,7 @@ check_framesync: "%s: (id=%d) E1 " "lost frame sync\n", __func__, hc->id); - hc->chan[hc->dslot].sync = 1; + hc->chan[hc->dnum[0]].sync = 1; } break; } @@ -2746,7 +2747,8 @@ hfcmulti_interrupt(int intno, void *dev_id) if (r_irq_misc & V_STA_IRQ) { if (hc->ctype == HFC_TYPE_E1) { /* state machine */ - dch = hc->chan[hc->dslot].dch; +#warning todo + dch = hc->chan[hc->dnum[0]].dch; e1_syncsta = HFC_inb_nodebug(hc, R_SYNC_STA); if (test_bit(HFC_CHIP_PLXSD, &hc->chip) && hc->e1_getclock) { @@ -2768,7 +2770,15 @@ hfcmulti_interrupt(int intno, void *dev_id) } dch->state = HFC_inb_nodebug(hc, R_E1_RD_STA) & 0x7; +#warning todo hack!!! broadcast state change!!! + dch = hc->chan[hc->dnum[0]].dch; schedule_event(dch, FLG_PHCHANGE); + dch = hc->chan[hc->dnum[1]].dch; + dch->state = HFC_inb_nodebug(hc, R_E1_RD_STA) + & 0x7; + schedule_event(dch, FLG_PHCHANGE); + + if (debug & DEBUG_HFCMULTI_STATE) printk(KERN_DEBUG "%s: E1 (id=%d) newstate %x\n", @@ -3851,31 +3861,35 @@ hfcmulti_initmode(struct dchannel *dch) if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: entered\n", __func__); + i = dch->slot; + pt = hc->chan[i].port; if (hc->ctype == HFC_TYPE_E1) { - hc->chan[hc->dslot].slot_tx = -1; - hc->chan[hc->dslot].slot_rx = -1; - hc->chan[hc->dslot].conf = -1; - if (hc->dslot) { - mode_hfcmulti(hc, hc->dslot, dch->dev.D.protocol, + /* E1 */ +#warning todo: don''t do it if dnum == 0 + hc->chan[hc->dnum[pt]].slot_tx = -1; + hc->chan[hc->dnum[pt]].slot_rx = -1; + hc->chan[hc->dnum[pt]].conf = -1; + if (hc->dnum[pt]) { + mode_hfcmulti(hc, dch->slot, dch->dev.D.protocol, -1, 0, -1, 0); dch->timer.function = (void *) hfcmulti_dbusy_timer; dch->timer.data = (long) dch; init_timer(&dch->timer); } for (i = 1; i <= 31; i++) { - if (i == hc->dslot) + if (!((1 << i) & hc->bmask[pt])) /* skip unused channel */ continue; hc->chan[i].slot_tx = -1; hc->chan[i].slot_rx = -1; hc->chan[i].conf = -1; mode_hfcmulti(hc, i, ISDN_P_NONE, -1, 0, -1, 0); } - /* E1 */ - if (test_bit(HFC_CFG_REPORT_LOS, &hc->chan[hc->dslot].cfg)) { +#warning todo (global) + if (test_bit(HFC_CFG_REPORT_LOS, &hc->chan[hc->dnum[pt]].cfg)) { HFC_outb(hc, R_LOS0, 255); /* 2 ms */ HFC_outb(hc, R_LOS1, 255); /* 512 ms */ } - if (test_bit(HFC_CFG_OPTICAL, &hc->chan[hc->dslot].cfg)) { + if (test_bit(HFC_CFG_OPTICAL, &hc->chan[hc->dnum[pt]].cfg)) { HFC_outb(hc, R_RX0, 0); hc->hw.r_tx0 = 0 | V_OUT_EN; } else { @@ -3888,12 +3902,12 @@ hfcmulti_initmode(struct dchannel *dch) HFC_outb(hc, R_TX_FR0, 0x00); HFC_outb(hc, R_TX_FR1, 0xf8); - if (test_bit(HFC_CFG_CRC4, &hc->chan[hc->dslot].cfg)) + if (test_bit(HFC_CFG_CRC4, &hc->chan[hc->dnum[pt]].cfg)) HFC_outb(hc, R_TX_FR2, V_TX_MF | V_TX_E | V_NEG_E); HFC_outb(hc, R_RX_FR0, V_AUTO_RESYNC | V_AUTO_RECO | 0); - if (test_bit(HFC_CFG_CRC4, &hc->chan[hc->dslot].cfg)) + if (test_bit(HFC_CFG_CRC4, &hc->chan[hc->dnum[pt]].cfg)) HFC_outb(hc, R_RX_FR1, V_RX_MF | V_RX_MF_SYNC); if (dch->dev.D.protocol == ISDN_P_NT_E1) { @@ -3957,7 +3971,7 @@ hfcmulti_initmode(struct dchannel *dch) plxsd_checksync(hc, 0); } } else { - i = dch->slot; + /* ST */ hc->chan[i].slot_tx = -1; hc->chan[i].slot_rx = -1; hc->chan[i].conf = -1; @@ -3973,8 +3987,6 @@ hfcmulti_initmode(struct dchannel *dch) hc->chan[i - 1].slot_rx = -1; hc->chan[i - 1].conf = -1; mode_hfcmulti(hc, i - 1, ISDN_P_NONE, -1, 0, -1, 0); - /* ST */ - pt = hc->chan[i].port; /* select interface */ HFC_outb(hc, R_ST_SEL, pt); /* undocumented: delay after R_ST_SEL */ @@ -4557,6 +4569,8 @@ release_port(struct hfc_multi *hc, struct dchannel *dch) } /* free channels */ for (i = 0; i <= 31; i++) { + if (!((1 << i) & hc->bmask[pt])) /* skip unused channel */ + continue; if (hc->chan[i].bch) { if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG @@ -4680,12 +4694,13 @@ release_card(struct hfc_multi *hc) } static int -init_e1_port(struct hfc_multi *hc, struct hm_map *m) +init_e1_port(struct hfc_multi *hc, struct hm_map *m, int pt) { struct dchannel *dch; struct bchannel *bch; int ch, ret = 0; char name[MISDN_MAX_IDLEN]; + int bcount = 0; dch = kzalloc(sizeof(struct dchannel), GFP_KERNEL); if (!dch) @@ -4698,13 +4713,12 @@ init_e1_port(struct hfc_multi *hc, struct hm_map *m) (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)); dch->dev.D.send = handle_dmsg; dch->dev.D.ctrl = hfcm_dctrl; - dch->dev.nrbchan = (hc->dslot) ? 30 : 31; - dch->slot = hc->dslot; - hc->chan[hc->dslot].dch = dch; - hc->chan[hc->dslot].port = 0; - hc->chan[hc->dslot].nt_timer = -1; + dch->slot = hc->dnum[pt]; + hc->chan[hc->dnum[pt]].dch = dch; + hc->chan[hc->dnum[pt]].port = pt; + hc->chan[hc->dnum[pt]].nt_timer = -1; for (ch = 1; ch <= 31; ch++) { - if (ch == hc->dslot) /* skip dchannel */ + if (!((1 << ch) & hc->bmask[pt])) /* skip unused channel */ continue; bch = kzalloc(sizeof(struct bchannel), GFP_KERNEL); if (!bch) { @@ -4733,7 +4747,10 @@ init_e1_port(struct hfc_multi *hc, struct hm_map *m) hc->chan[ch].bch = bch; hc->chan[ch].port = 0; set_channelmap(bch->nr, dch->dev.channelmap); + bcount++; } + dch->dev.nrbchan = bcount; +#warning todo: must be set globally, and must be a seperate function /* set optical line type */ if (port[Port_cnt] & 0x001) { if (!m->opticalsupport) { @@ -4749,7 +4766,7 @@ init_e1_port(struct hfc_multi *hc, struct hm_map *m) __func__, HFC_cnt + 1, 1); test_and_set_bit(HFC_CFG_OPTICAL, - &hc->chan[hc->dslot].cfg); + &hc->chan[hc->dnum[pt]].cfg); } } /* set LOS report */ @@ -4759,7 +4776,7 @@ init_e1_port(struct hfc_multi *hc, struct hm_map *m) "LOS report: card(%d) port(%d)\n", __func__, HFC_cnt + 1, 1); test_and_set_bit(HFC_CFG_REPORT_LOS, - &hc->chan[hc->dslot].cfg); + &hc->chan[hc->dnum[pt]].cfg); } /* set AIS report */ if (port[Port_cnt] & 0x008) { @@ -4768,7 +4785,7 @@ init_e1_port(struct hfc_multi *hc, struct hm_map *m) "AIS report: card(%d) port(%d)\n", __func__, HFC_cnt + 1, 1); test_and_set_bit(HFC_CFG_REPORT_AIS, - &hc->chan[hc->dslot].cfg); + &hc->chan[hc->dnum[pt]].cfg); } /* set SLIP report */ if (port[Port_cnt] & 0x010) { @@ -4778,7 +4795,7 @@ init_e1_port(struct hfc_multi *hc, struct hm_map *m) "card(%d) port(%d)\n", __func__, HFC_cnt + 1, 1); test_and_set_bit(HFC_CFG_REPORT_SLIP, - &hc->chan[hc->dslot].cfg); + &hc->chan[hc->dnum[pt]].cfg); } /* set RDI report */ if (port[Port_cnt] & 0x020) { @@ -4788,7 +4805,7 @@ init_e1_port(struct hfc_multi *hc, struct hm_map *m) "card(%d) port(%d)\n", __func__, HFC_cnt + 1, 1); test_and_set_bit(HFC_CFG_REPORT_RDI, - &hc->chan[hc->dslot].cfg); + &hc->chan[hc->dnum[pt]].cfg); } /* set CRC-4 Mode */ if (!(port[Port_cnt] & 0x100)) { @@ -4797,7 +4814,7 @@ init_e1_port(struct hfc_multi *hc, struct hm_map *m) " card(%d) port(%d)\n", __func__, HFC_cnt + 1, 1); test_and_set_bit(HFC_CFG_CRC4, - &hc->chan[hc->dslot].cfg); + &hc->chan[hc->dnum[pt]].cfg); } else { if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: PORT turn off CRC4" @@ -4829,20 +4846,23 @@ init_e1_port(struct hfc_multi *hc, struct hm_map *m) } /* set elastic jitter buffer */ if (port[Port_cnt] & 0x3000) { - hc->chan[hc->dslot].jitter = (port[Port_cnt]>>12) & 0x3; + hc->chan[hc->dnum[pt]].jitter = (port[Port_cnt]>>12) & 0x3; if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: PORT set elastic " "buffer to %d: card(%d) port(%d)\n", - __func__, hc->chan[hc->dslot].jitter, + __func__, hc->chan[hc->dnum[pt]].jitter, HFC_cnt + 1, 1); } else - hc->chan[hc->dslot].jitter = 2; /* default */ - snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-e1.%d", HFC_cnt + 1); + hc->chan[hc->dnum[pt]].jitter = 2; /* default */ + if (hc->ports > 1) + snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-e1.%d-%d", HFC_cnt + 1, pt+1); + else + snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-e1.%d", HFC_cnt + 1); ret = mISDN_register_device(&dch->dev, &hc->pci_dev->dev, name); if (ret) goto free_chan; - hc->created[0] = 1; + hc->created[pt] = 1; return ret; free_chan: release_port(hc, dch); @@ -5009,18 +5029,30 @@ hfcmulti_init(struct hm_map *m, struct pci_dev *pdev, hc->id = HFC_cnt; hc->pcm = pcm[HFC_cnt]; hc->io_mode = iomode[HFC_cnt]; +#warning todo: rework module parameters for customizing e1 fragments.... yea, let''s call it: fragments if (dslot[HFC_cnt] < 0 && hc->ctype == HFC_TYPE_E1) { - hc->dslot = 0; + hc->dnum[0] = 0; printk(KERN_INFO "HFC-E1 card has disabled D-channel, but " "31 B-channels\n"); } if (dslot[HFC_cnt] > 0 && dslot[HFC_cnt] < 32 && hc->ctype == HFC_TYPE_E1) { - hc->dslot = dslot[HFC_cnt]; + hc->dnum[0] = dslot[HFC_cnt]; printk(KERN_INFO "HFC-E1 card has alternating D-channel on " "time slot %d\n", dslot[HFC_cnt]); } else - hc->dslot = 16; + hc->dnum[0] = 16; + +#warning todo HACK!!! just a small map of two "fragments" + if (hc->ctype == HFC_TYPE_E1) { + hc->dnum[0] = 1; + hc->bmask[0] = 0x0000003c; + hc->dnum[1] = 6; + hc->bmask[1] = 0x00000780; + hc->dnum[2] = 11; + hc->bmask[2] = 0x00007800; + hc->ports = 3; + } /* set chip specific features */ hc->masterclk = -1; @@ -5103,7 +5135,7 @@ hfcmulti_init(struct hm_map *m, struct pci_dev *pdev, goto free_card; } if (hc->ctype == HFC_TYPE_E1) - ret_err = init_e1_port(hc, m); + ret_err = init_e1_port(hc, m, pt); else ret_err = init_multi_port(hc, pt); if (debug & DEBUG_HFCMULTI_INIT) @@ -5115,10 +5147,14 @@ hfcmulti_init(struct hm_map *m, struct pci_dev *pdev, if (ret_err) { while (pt) { /* release already registered ports */ pt--; - release_port(hc, hc->chan[(pt << 2) + 2].dch); + if (hc->ctype == HFC_TYPE_E1) + release_port(hc, hc->chan[hc->dnum[pt]].dch); + else + release_port(hc, hc->chan[(pt << 2) + 2].dch); } goto free_card; } +#warning todo: count it right, add additional "fragment" counter... Port_cnt++; } openbsc-0.15.0/openbsc/000077500000000000000000000000001265565154000146705ustar00rootroot00000000000000openbsc-0.15.0/openbsc/.gitignore000066400000000000000000000025371265565154000166670ustar00rootroot00000000000000*.o *.lo *.a .deps Makefile Makefile.in bscconfig.h bscconfig.h.in openbsc.pc src/osmo-nitb/osmo-nitb src/osmo-bsc_mgcp/osmo-bsc_mgcp src/osmo-bsc/osmo-bsc src/utils/meas_vis src/utils/osmo-meas-pcap2db src/utils/osmo-meas-udp2db src/utils/smpp_mirror *.*~ *.sw? .libs *.pyc *.gcda *.gcno #configure aclocal.m4 autom4te.cache/ config.log config.status config.guess config.sub configure compile depcomp install-sh missing stamp-h1 libtool ltmain.sh # git-version-gen magic .tarball-version .version # apps and app data hlr.sqlite3 src/utils/bs11_config src/ipaccess/ipaccess-config src/ipaccess/ipaccess-find src/ipaccess/ipaccess-firmware src/ipaccess/ipaccess-proxy src/utils/isdnsync src/nat/bsc_nat src/gprs/osmo-sgsn src/gprs/osmo-gbproxy src/osmo-bsc_nat/osmo-bsc_nat #tests tests/testsuite.dir tests/bsc-nat/bsc_nat_test tests/bsc-nat-trie/bsc_nat_trie_test tests/channel/channel_test tests/db/db_test tests/debug/debug_test tests/gsm0408/gsm0408_test tests/mgcp/mgcp_test tests/sccp/sccp_test tests/sms/sms_test tests/timer/timer_test tests/gprs/gprs_test tests/gbproxy/gbproxy_test tests/abis/abis_test tests/si/si_test tests/smpp/smpp_test tests/bsc/bsc_test tests/trau/trau_test tests/mgcp/mgcp_transcoding_test tests/sgsn/sgsn_test tests/subscr/subscr_test tests/atconfig tests/atlocal tests/package.m4 tests/testsuite tests/testsuite.log src/openbsc.cfg* openbsc-0.15.0/openbsc/AUTHORS000066400000000000000000000004251265565154000157410ustar00rootroot00000000000000Harald Welte Holger Freyther Jan Luebbe Stefan Schmidt Daniel Willmann Andreas Eversberg Sylvain Munaut <246tnt@gmail.com> openbsc-0.15.0/openbsc/COPYING000066400000000000000000001033301265565154000157230ustar00rootroot00000000000000 GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU Affero General Public License is a free, copyleft license for software and other kinds of works, specifically designed to ensure cooperation with the community in the case of network server software. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, our General Public Licenses are intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. Developers that use our General Public Licenses protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License which gives you legal permission to copy, distribute and/or modify the software. A secondary benefit of defending all users' freedom is that improvements made in alternate versions of the program, if they receive widespread use, become available for other developers to incorporate. Many developers of free software are heartened and encouraged by the resulting cooperation. However, in the case of software used on network servers, this result may fail to come about. The GNU General Public License permits making a modified version and letting the public access it on a server without ever releasing its source code to the public. The GNU Affero General Public License is designed specifically to ensure that, in such cases, the modified source code becomes available to the community. It requires the operator of a network server to provide the source code of the modified version running there to the users of that server. Therefore, public use of a modified version, on a publicly accessible server, gives the public access to the source code of the modified version. An older license, called the Affero General Public License and published by Affero, was designed to accomplish similar goals. This is a different license, not a version of the Affero GPL, but Affero has released a new version of the Affero GPL which permits relicensing under this license. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU Affero General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Remote Network Interaction; Use with the GNU General Public License. Notwithstanding any other provision of this License, if you modify the Program, your modified version must prominently offer all users interacting with it remotely through a computer network (if your version supports such interaction) an opportunity to receive the Corresponding Source of your version by providing access to the Corresponding Source from a network server at no charge, through some standard or customary means of facilitating copying of software. This Corresponding Source shall include the Corresponding Source for any work covered by version 3 of the GNU General Public License that is incorporated pursuant to the following paragraph. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the work with which it is combined will remain governed by version 3 of the GNU General Public License. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU Affero General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU Affero General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU Affero General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU Affero General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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 . Also add information on how to contact you by electronic and paper mail. If your software can interact with users remotely through a computer network, you should also make sure that it provides a way for users to get its source. For example, if your program is a web application, its interface could display a "Source" link that leads users to an archive of the code. There are many ways you could offer source, and different solutions will be better for different programs; see section 13 for the specific requirements. You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU AGPL, see . openbsc-0.15.0/openbsc/Makefile.am000066400000000000000000000006261265565154000167300ustar00rootroot00000000000000AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6 AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include SUBDIRS = doc include src tests pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = openbsc.pc BUILT_SOURCES = $(top_srcdir)/.version EXTRA_DIST = git-version-gen osmoappdesc.py .version $(top_srcdir)/.version: echo $(VERSION) > $@-t && mv $@-t $@ dist-hook: echo $(VERSION) > $(distdir)/.tarball-version openbsc-0.15.0/openbsc/README000066400000000000000000000017451265565154000155570ustar00rootroot00000000000000About OpenBSC ============= OpenBSC is a minimalistic implementation of the GSM Network, with particular emphasis on the functionality typically provided by the BSC, MSC, HLR, VLR and SMSC. Its currently supported interfaces towards the BTS are: * Classic A-bis over E1 using a mISDN based E1 interface. In other words, you can connect existing GSM Base Transceiver Station (BTS) through E1 to OpenBSC. So far, we have only tested the Siemens BS-11 Test reports with other BTS are much appreciated! * A-bis over IP as used by the ip.access nanoBTS product family You can find the project documentation at http://openbsc.gnumonks.org/ This project is still in its early days, and there are lots of areas where it doesn't behave as per GSM spec. Harald Welte libosmocore =========== Please note that as of March 2010, OpenBSC has a dependency to a library called "libosmocore". You can obtain that library from git://git.osmocom.org/libosmocore.git openbsc-0.15.0/openbsc/configure.ac000066400000000000000000000162101265565154000171560ustar00rootroot00000000000000dnl Process this file with autoconf to produce a configure script AC_INIT([openbsc], m4_esyscmd([./git-version-gen .tarball-version]), [openbsc@lists.osmocom.org]) AM_INIT_AUTOMAKE([dist-bzip2]) AC_CONFIG_TESTDIR(tests) dnl kernel style compile messages m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) dnl checks for programs AC_PROG_MAKE_SET AC_PROG_CC AC_PROG_INSTALL AC_PROG_RANLIB dnl checks for libraries AC_SEARCH_LIBS([dlopen], [dl dld], [LIBRARY_DL="$LIBS";LIBS=""]) AC_SUBST(LIBRARY_DL) PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.6.4) PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.3.0) PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl) PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.7.0) 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(LIBCRYPTO, libcrypto >= 0.9.5) # 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 nitb? AC_ARG_ENABLE([smpp], [AS_HELP_STRING([--enable-smpp], [Build the SMPP interface])], [osmo_ac_build_smpp="$enableval"],[osmo_ac_build_smpp="no"]) if test "$osmo_ac_build_smpp" = "yes" ; then PKG_CHECK_MODULES(LIBSMPP34, libsmpp34 >= 1.10) AC_DEFINE(BUILD_SMPP, 1, [Define if we want to build SMPP]) fi AM_CONDITIONAL(BUILD_SMPP, test "x$osmo_ac_build_smpp" = "xyes") AC_SUBST(osmo_ac_build_smpp) # Enable/disable transcoding within osmo-bsc_mgcp? AC_ARG_ENABLE([mgcp-transcoding], [AS_HELP_STRING([--enable-mgcp-transcoding], [Build the MGCP gateway with internal transcoding enabled.])], [osmo_ac_mgcp_transcoding="$enableval"],[osmo_ac_mgcp_transcoding="no"]) AC_ARG_WITH([g729], [AS_HELP_STRING([--with-g729], [Enable G.729 encoding/decoding.])], [osmo_ac_with_g729="$withval"],[osmo_ac_with_g729="no"]) if test "$osmo_ac_mgcp_transcoding" = "yes" ; then AC_SEARCH_LIBS([gsm_create], [gsm], [LIBRARY_GSM="$LIBS";LIBS=""]) AC_SUBST(LIBRARY_GSM) if test "$osmo_ac_with_g729" = "yes" ; then PKG_CHECK_MODULES(LIBBCG729, libbcg729 >= 0.1, [AC_DEFINE([HAVE_BCG729], [1], [Use bgc729 decoder/encoder])]) fi AC_DEFINE(BUILD_MGCP_TRANSCODING, 1, [Define if we want to build the MGCP gateway with transcoding support]) fi AM_CONDITIONAL(BUILD_MGCP_TRANSCODING, test "x$osmo_ac_mgcp_transcoding" = "xyes") AC_SUBST(osmo_ac_mgcp_transcoding) found_libgtp=yes PKG_CHECK_MODULES(LIBGTP, libgtp, , 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) dnl checks for header files AC_HEADER_STDC AC_CHECK_HEADERS(dahdi/user.h,,AC_MSG_WARN(DAHDI input driver will not be built)) AC_CHECK_HEADERS(dbi/dbd.h,,AC_MSG_ERROR(DBI library is not installed)) AC_CHECK_HEADERS(pcap/pcap.h,,AC_MSG_ERROR(PCAP library is not installed)) found_cdk=yes AC_CHECK_HEADERS(cdk/cdk.h,,found_cdk=no) AM_CONDITIONAL(HAVE_LIBCDK, test "$found_cdk" = yes) found_sqlite3=yes PKG_CHECK_MODULES(SQLITE3, sqlite3, ,found_sqlite3=no) AM_CONDITIONAL(HAVE_SQLITE3, test "$found_sqlite3" = yes) AC_SUBST(found_sqlite3) dnl Checks for typedefs, structures and compiler characteristics # The following test is taken from WebKit's webkit.m4 saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -fvisibility=hidden " AC_MSG_CHECKING([if ${CC} supports -fvisibility=hidden]) AC_COMPILE_IFELSE([AC_LANG_SOURCE([char foo;])], [ AC_MSG_RESULT([yes]) SYMBOL_VISIBILITY="-fvisibility=hidden"], AC_MSG_RESULT([no])) CFLAGS="$saved_CFLAGS" AC_SUBST(SYMBOL_VISIBILITY) # Coverage build taken from WebKit's configure.in AC_MSG_CHECKING([whether to enable code coverage support]) AC_ARG_ENABLE(coverage, AC_HELP_STRING([--enable-coverage], [enable code coverage support [default=no]]), [],[enable_coverage="no"]) AC_MSG_RESULT([$enable_coverage]) if test "$enable_coverage" = "yes"; then COVERAGE_CFLAGS="-ftest-coverage -fprofile-arcs" COVERAGE_LDFLAGS="-ftest-coverage -fprofile-arcs" AC_SUBST([COVERAGE_CFLAGS]) AC_SUBST([COVERAGE_LDFLAGS]) fi AC_DEFUN([CHECK_TM_INCLUDES_TM_GMTOFF], [ AC_CACHE_CHECK( [whether struct tm has tm_gmtoff member], osmo_cv_tm_includes_tm_gmtoff, [AC_LINK_IFELSE([ AC_LANG_PROGRAM([ #include ], [ time_t t = time(NULL); struct tm* lt = localtime(&t); int off = lt->tm_gmtoff; ]) ], osmo_cv_tm_includes_tm_gmtoff=yes, osmo_cv_tm_includes_tm_gmtoff=no )] ) if test "x$osmo_cv_tm_includes_tm_gmtoff" = xyes; then AC_DEFINE(HAVE_TM_GMTOFF_IN_TM, 1, [Define if struct tm has tm_gmtoff member.]) fi ]) CHECK_TM_INCLUDES_TM_GMTOFF AC_ARG_ENABLE([vty_tests], AC_HELP_STRING([--enable-vty-tests], [Include the VTY/CTRL tests in make check (deprecated) [default=no]]), [enable_ext_tests="$enableval"],[enable_ext_tests="no"]) AC_ARG_ENABLE([external_tests], AC_HELP_STRING([--enable-external-tests], [Include the VTY/CTRL tests in make check [default=no]]), [enable_ext_tests="$enableval"],[enable_ext_tests="no"]) if test "x$enable_ext_tests" = "xyes" ; then AM_PATH_PYTHON AC_CHECK_PROG(OSMOTESTEXT_CHECK,osmotestvty.py,yes) if test "x$OSMOTESTEXT_CHECK" != "xyes" ; then AC_MSG_ERROR([Please install osmocom-python to run the VTY/CTRL tests.]) fi fi AC_MSG_CHECKING([whether to enable VTY/CTRL tests]) AC_MSG_RESULT([$enable_ext_tests]) AM_CONDITIONAL(ENABLE_EXT_TESTS, test "x$enable_ext_tests" = "xyes") dnl Generate the output AM_CONFIG_HEADER(bscconfig.h) AC_OUTPUT( openbsc.pc include/openbsc/Makefile include/Makefile src/Makefile src/libtrau/Makefile src/libbsc/Makefile src/libmsc/Makefile src/libmgcp/Makefile src/libcommon/Makefile src/libfilter/Makefile src/osmo-nitb/Makefile src/osmo-bsc/Makefile src/osmo-bsc_nat/Makefile src/osmo-bsc_mgcp/Makefile src/ipaccess/Makefile src/utils/Makefile src/gprs/Makefile tests/Makefile tests/atlocal tests/gsm0408/Makefile tests/db/Makefile tests/channel/Makefile tests/bsc/Makefile tests/bsc-nat/Makefile tests/bsc-nat-trie/Makefile tests/mgcp/Makefile tests/gprs/Makefile tests/gbproxy/Makefile tests/abis/Makefile tests/smpp/Makefile tests/trau/Makefile tests/sgsn/Makefile tests/subscr/Makefile doc/Makefile doc/examples/Makefile Makefile) openbsc-0.15.0/openbsc/contrib/000077500000000000000000000000001265565154000163305ustar00rootroot00000000000000openbsc-0.15.0/openbsc/contrib/a-link/000077500000000000000000000000001265565154000175035ustar00rootroot00000000000000openbsc-0.15.0/openbsc/contrib/a-link/sccp-split-by-con.lua000066400000000000000000000106711265565154000234610ustar00rootroot00000000000000-- Split trace based on SCCP Source -- There are still bugs to find... bugs bugs bugs... hmm do local function init_listener() print("CREATED LISTENER") local tap = Listener.new("ip", "sccp && (ip.src == 172.16.1.81 || ip.dst == 172.16.1.81)") local sccp_type_field = Field.new("sccp.message_type") local sccp_src_field = Field.new("sccp.slr") local sccp_dst_field = Field.new("sccp.dlr") local msg_type_field = Field.new("gsm_a.dtap_msg_mm_type") local lu_rej_field = Field.new("gsm_a.dtap.rej_cause") local ip_src_field = Field.new("ip.src") local ip_dst_field = Field.new("ip.dst") -- local bssmap_msgtype_field = Field.new("gsm_a.bssmap_msgtype") -- assignment failure 0x03 -- -- local dtap_cause_field = Field.new("gsm_a_dtap.cause") local dtap_cc_field = Field.new("gsm_a.dtap_msg_cc_type") local connections = {} function check_failure(con) check_lu_reject(con) check_disconnect(con) check_failures(con) end -- cipher mode reject function check_failures(con) local msgtype = bssmap_msgtype_field() if not msgtype then return end msgtype = tonumber(msgtype) if msgtype == 89 then print("Cipher mode reject") con[4] = true elseif msgtype == 0x03 then print("Assignment failure") con[4] = true elseif msgtype == 0x22 then print("Clear Request... RF failure?") con[4] = true end end -- check if a DISCONNECT is normal function check_disconnect(con) local msg_type = dtap_cc_field() if not msg_type then return end if tonumber(msg_type) ~= 0x25 then return end local cause = dtap_cause_field() if not cause then return end cause = tonumber(cause) if cause ~= 0x10 then print("DISCONNECT != Normal") con[4] = true end end -- check if we have a LU Reject function check_lu_reject(con) local msg_type = msg_type_field() if not msg_type then return end msg_type = tonumber(tostring(msg_type)) if msg_type == 0x04 then print("LU REJECT with " .. tostring(lu_rej_field())) con[4] = true end end function tap.packet(pinfo,tvb,ip) local ip_src = tostring(ip_src_field()) local ip_dst = tostring(ip_dst_field()) local sccp_type = tonumber(tostring(sccp_type_field())) local sccp_src = sccp_src_field() local sccp_dst = sccp_dst_field() local con if sccp_type == 0x01 then elseif sccp_type == 0x2 then local src = string.format("%s-%s", ip_src, tostring(sccp_src)) local dst = string.format("%s-%s", ip_dst, tostring(sccp_dst)) local datestring = os.date("%Y%m%d%H%M%S") local pcap_name = string.format("alink_trace_%s-%s_%s.pcap", src, dst, datestring) local dumper = Dumper.new_for_current(pcap_name) local con = { ip_src, tostring(sccp_src), tostring(sccp_dst), false, dumper, pcap_name } dumper:dump_current() connections[src] = con connections[dst] = con elseif sccp_type == 0x4 then -- close a connection... remove it from the list local src = string.format("%s-%s", ip_src, tostring(sccp_src)) local dst = string.format("%s-%s", ip_dst, tostring(sccp_dst)) local con = connections[src] if not con then return end con[5]:dump_current() con[5]:flush() -- this causes a crash on unpacted wireshark con[5]:close() -- the connection had a failure if con[4] == true then local datestring = os.date("%Y%m%d%H%M%S") local new_name = string.format("alink_failure_%s_%s-%s.pcap", datestring, con[2], con[3]) os.rename(con[6], new_name) else os.remove(con[6]) end -- clear the old connection connections[src] = nil connections[dst] = nil elseif sccp_type == 0x5 then -- not handled yet... we should verify stuff here... local dst = string.format("%s-%s", ip_dst, tostring(sccp_dst)) local con = connections[dst] if not con then return end con[5]:dump_current() elseif sccp_type == 0x6 then local dst = string.format("%s-%s", ip_dst, tostring(sccp_dst)) local con = connections[dst] if not con then print("DON'T KNOW THIS CONNECTION for " .. ip_dst) return end con[5]:dump_current() check_failure(con) end end function tap.draw() print("DRAW") end function tap.reset() print("RESET") end end init_listener() end openbsc-0.15.0/openbsc/contrib/bsc-test/000077500000000000000000000000001265565154000200545ustar00rootroot00000000000000openbsc-0.15.0/openbsc/contrib/bsc-test/README000066400000000000000000000001031265565154000207260ustar00rootroot00000000000000Some crazy scripts call testing... and MSC link failure simulation openbsc-0.15.0/openbsc/contrib/bsc-test/all_dial000066400000000000000000000001341265565154000215360ustar00rootroot00000000000000ABORT BUSY ABORT 'NO CARRIER' ABORT 'OK' '' AT SAY "Dialing a number\n" 'OK' ATD05660066; openbsc-0.15.0/openbsc/contrib/bsc-test/dial.sh000077500000000000000000000002551265565154000213260ustar00rootroot00000000000000#!/bin/sh # Evil dial script.. while true; do chat -v -f all_dial < /dev/ttyACM0 > /dev/ttyACM0 sleep 5s chat -v -f hangup < /dev/ttyACM0 > /dev/ttyACM0 sleep 2s done openbsc-0.15.0/openbsc/contrib/bsc-test/drop-oml.sh000077500000000000000000000001211265565154000221360ustar00rootroot00000000000000#!/bin/sh sleep 3 echo "enable" sleep 1 echo "drop bts connection 0 oml" sleep 1 openbsc-0.15.0/openbsc/contrib/bsc-test/drop.sh000077500000000000000000000001721265565154000213570ustar00rootroot00000000000000#!/bin/sh while true; do echo "Going to drop the OML connection" ./drop-oml.sh | telnet 127.0.0.1 4242 sleep 58m done openbsc-0.15.0/openbsc/contrib/bsc-test/hangup000066400000000000000000000000741265565154000212620ustar00rootroot00000000000000TIMEOUT 10 '' ^Z SAY "Waiting for hangup confirm\n" '' ATH; openbsc-0.15.0/openbsc/contrib/bsc-test/msc.sh000077500000000000000000000001571265565154000212000ustar00rootroot00000000000000#!/bin/sh while true; do echo "Kill the osmo-bsc" /usr/bin/kill -s SIGUSR2 `pidof osmo-bsc` sleep 58s done openbsc-0.15.0/openbsc/contrib/bsc_control.py000077500000000000000000000056371265565154000212270ustar00rootroot00000000000000#!/usr/bin/python import sys,os from optparse import OptionParser import socket import struct verbose = False def prefix_ipa_ctrl_header(data): return struct.pack(">HBB", len(data)+1, 0xee, 0) + data def remove_ipa_ctrl_header(data): if (len(data) < 4): raise BaseException("Answer too short!") (plen, ipa_proto, osmo_proto) = struct.unpack(">HBB", data[:4]) if (plen + 3 > len(data)): print "Warning: Wrong payload length (expected %i, got %i)" % (plen, len(data) - 3) if (ipa_proto != 0xee or osmo_proto != 0): raise BaseException("Wrong protocol in answer!") return data[4:plen+3], data[plen+3:] def connect(host, port): if verbose: print "Connecting to host %s:%i" % (host, port) sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sck.setblocking(1) sck.connect((host, port)) return sck def send(sck, data): if verbose: print "Sending \"%s\"" %(data) data = prefix_ipa_ctrl_header(data) sck.send(data) def do_set(var, value, id, sck): setmsg = "SET %s %s %s" %(options.id, var, value) send(sck, setmsg) def do_get(var, id, sck): getmsg = "GET %s %s" %(options.id, var) send(sck, getmsg) parser = OptionParser("Usage: %prog [options] var [value]") parser.add_option("-d", "--host", dest="host", help="connect to HOST", metavar="HOST") parser.add_option("-p", "--port", dest="port", type="int", help="use PORT", metavar="PORT", default=4249) parser.add_option("-g", "--get", action="store_true", dest="cmd_get", help="perform GET operation") parser.add_option("-s", "--set", action="store_true", dest="cmd_set", help="perform SET operation") parser.add_option("-i", "--id", dest="id", default="1", help="set id manually", metavar="ID") parser.add_option("-v", "--verbose", action="store_true", dest="verbose", help="be verbose", default=False) parser.add_option("-m", "--monitor", action="store_true", dest="monitor", help="monitor the connection for traps", default=False) (options, args) = parser.parse_args() verbose = options.verbose if options.cmd_set and options.cmd_get: parser.error("Get and set options are mutually exclusive!") if not (options.cmd_get or options.cmd_set or options.monitor): parser.error("One of -m, -g, or -s must be set") if not (options.host): parser.error("Destination host and port required!") sock = connect(options.host, options.port) if options.cmd_set: if len(args) < 2: parser.error("Set requires var and value arguments") do_set(args[0], ' '.join(args[1:]), options.id, sock) if options.cmd_get: if len(args) != 1: parser.error("Get requires the var argument") do_get(args[0], options.id, sock) data = sock.recv(1024) while (len(data)>0): (answer, data) = remove_ipa_ctrl_header(data) print "Got message:", answer if options.monitor: while (True): data = sock.recv(1024) if len(data) == 0: print "Connection is gone." break while (len(data)>0): (answer, data) = remove_ipa_ctrl_header(data) print "Got message:", answer sock.close() openbsc-0.15.0/openbsc/contrib/bt.py000077500000000000000000000011741265565154000173150ustar00rootroot00000000000000#!/usr/bin/env python import os f = open("unbalanced") lines = [] for line in f: lines.append(line) filenames = {} output = [] for line in lines: if "[0x" in line: start = line.find("[") end = line.find("]") addr = line[start+1:end] try: file = filenames[addr] except KeyError: r = os.popen("addr2line -fs -e ./bsc_hack %s" % addr) all = r.read().replace("\n", ",") file = all filenames[addr] = file line = line.replace(addr, file) output.append(line) g = open("unbalanced.2", "w") g.write("".join(output)) openbsc-0.15.0/openbsc/contrib/convert_to_enum.py000077500000000000000000000015031265565154000221120ustar00rootroot00000000000000#!/usr/bin/env python # # Convert ETSI documents to an enum # import re, sys def convert(string): string = string.strip().replace(" ", "").rjust(8, "0") var = 0 offset = 7 for char in string: assert offset >= 0 var = var | (int(char) << offset) offset = offset - 1 return var def string(name): name = name.replace(" ", "_") name = name.replace('"', "") name = name.replace('/', '_') name = name.replace('(', '_') name = name.replace(')', '_') return "%s_%s" % (sys.argv[2], name.upper()) file = open(sys.argv[1]) for line in file: m = re.match(r"[ \t]*(?P[01 ]+)[ ]+(?P[a-zA-Z /0-9()]+)", line[:-1]) if m: print "\t%s\t\t= %d," % (string(m.groupdict()["name"]), convert(m.groupdict()["value"])) else: print line[:-1] openbsc-0.15.0/openbsc/contrib/gprs/000077500000000000000000000000001265565154000173035ustar00rootroot00000000000000openbsc-0.15.0/openbsc/contrib/gprs/gb-proxy-unblock-bug.py000077500000000000000000000026201265565154000236350ustar00rootroot00000000000000#!/usr/bin/env python """ demonstrate a unblock bug on the GB Proxy.. """ bts_ns_reset = "\x02\x00\x81\x01\x01\x82\x1f\xe7\x04\x82\x1f\xe7" ns_reset_ack = "\x03\x01\x82\x1f\xe7\x04\x82\x1f\xe7" bts_ns_unblock = "\x06" ns_unblock_ack = "\x07" bts_bvc_reset_0 = "\x00\x00\x00\x00\x22\x04\x82\x00\x00\x07\x81\x03\x3b\x81\x02" ns_bvc_reset_0_ack = "\x00\x00\x00\x00\x23\x04\x82\x00\x00" bts_bvc_reset_8167 = "\x00\x00\x00\x00\x22\x04\x82\x1f\xe7\x07\x81\x08\x08\x88\x72\xf4\x80\x10\x1c\x00\x9c\x40" import socket socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) socket.bind(("0.0.0.0", 0)) socket.setblocking(1) import sys port = int(sys.argv[1]) print "Sending data to port: %d" % port def send_and_receive(packet): socket.sendto(packet, ("127.0.0.1", port)) try: data, addr = socket.recvfrom(4096) except socket.error, e: print "ERROR", e import sys sys.exit(0) return data #send stuff once to_send = [ (bts_ns_reset, ns_reset_ack, "reset ack"), (bts_ns_unblock, ns_unblock_ack, "unblock ack"), (bts_bvc_reset_0, ns_bvc_reset_0_ack, "BVCI=0 reset ack"), ] for (out, inp, type) in to_send: res = send_and_receive(out) if res != inp: print "Failed to get the %s" % type sys.exit(-1) import time time.sleep(3) res = send_and_receive(bts_bvc_reset_8167) print "Sent all messages... check wireshark for the last response" openbsc-0.15.0/openbsc/contrib/gprs/gprs-bssgp-histogram.lua000066400000000000000000000036241265565154000240750ustar00rootroot00000000000000-- Simple LUA script to print the size of BSSGP messages over their type... do local ip_bucket = {} local pdu_types = {} pdu_types[ 6] = "PAGING" pdu_types[11] = "SUSPEND" pdu_types[12] = "SUSPEND-ACK" pdu_types[32] = "BVC-BLOCK" pdu_types[33] = "BVC-BLOCK-ACK" pdu_types[34] = "BVC-RESET" pdu_types[35] = "BVC-RESET-ACK" pdu_types[36] = "UNBLOCK" pdu_types[37] = "UNBLOCK-ACK" pdu_types[38] = "FLOW-CONTROL-BVC" pdu_types[39] = "FLOW-CONTROL-BVC-ACK" pdu_types[40] = "FLOW-CONTROL-MS" pdu_types[41] = "FLOW-CONTROL-MS-ACK" pdu_types[44] = "LLC-DISCARDED" local function init_listener() -- handle the port as NS over IP local udp_port_table = DissectorTable.get("udp.port") local gprs_ns_dis = Dissector.get("gprs_ns") udp_port_table:add(23000,gprs_ns_dis) -- bssgp filters local bssgp_pdu_get = Field.new("bssgp.pdu_type") local udp_length_get = Field.new("udp.length") local tap = Listener.new("ip", "udp.port == 23000") function tap.packet(pinfo,tvb,ip) local pdu = bssgp_pdu_get() local len = udp_length_get() -- only handle bssgp, but we also want the IP frame if not pdu then return end pdu = tostring(pdu) if tonumber(pdu) == 0 or tonumber(pdu) == 1 then return end local ip_src = tostring(ip.ip_src) local bssgp_histo = ip_bucket[ip_src] if not bssgp_histo then bssgp_histo = {} ip_bucket[ip_src] = bssgp_histo end local key = pdu local bucket = bssgp_histo[key] if not bucket then bucket = {} bssgp_histo[key] = bucket end table.insert(bucket, tostring(len)) print("IP: " .. ip_src .. " PDU: " .. pdu_types[tonumber(pdu)] .. " Length: " .. tostring(len)) end function tap.draw() -- well... this will not be called... -- for ip,bssgp_histo in pairs(dumpers) do -- print("IP " .. ip) -- end end function tap.reset() -- well... this will not be called... end end init_listener() end openbsc-0.15.0/openbsc/contrib/gprs/gprs-buffer-count.lua000066400000000000000000000037421265565154000233640ustar00rootroot00000000000000-- I count the buffer space needed for LLC PDUs in the worse case and print it do local function init_listener() -- handle the port as NS over IP local udp_port_table = DissectorTable.get("udp.port") local gprs_ns_dis = Dissector.get("gprs_ns") udp_port_table:add(23000,gprs_ns_dis) -- bssgp filters local bssgp_pdu_get = Field.new("bssgp.pdu_type") local bssgp_delay_get = Field.new("bssgp.delay_val") local llcgprs_get = Field.new("llcgprs") local pdus = nil print("START...") local tap = Listener.new("ip", "udp.port == 23000 && bssgp.pdu_type == 0") function tap.packet(pinfo,tvb,ip) local pdu = bssgp_pdu_get() local len = llcgprs_get().len local delay = bssgp_delay_get() -- only handle bssgp, but we also want the IP frame if not pdu then return end if tonumber(tostring(delay)) == 65535 then pdus = { next = pdus, len = len, expires = -1 } else local off = tonumber(tostring(delay)) / 100.0 pdus = { next = pdus, len = len, expires = pinfo.rel_ts + off } end local now_time = tonumber(tostring(pinfo.rel_ts)) local now_size = 0 local l = pdus local prev = nil local count = 0 while l do if now_time < l.expires or l.expires == -1 then now_size = now_size + l.len prev = l l = l.next count = count + 1 else -- delete things if prev == nil then pdus = nil l = nil else prev.next = l.next l = l.next end end end -- print("TOTAL: " .. now_time .. " PDU_SIZE: " .. now_size) print(now_time .. " " .. now_size / 1024.0 .. " " .. count) -- print("NOW: " .. tostring(pinfo.rel_ts) .. " Delay: " .. tostring(delay) .. " Length: " .. tostring(len)) end function tap.draw() -- well... this will not be called... -- for ip,bssgp_histo in pairs(dumpers) do -- print("IP " .. ip) -- end print("END") end function tap.reset() -- well... this will not be called... end end init_listener() end openbsc-0.15.0/openbsc/contrib/gprs/gprs-split-trace-by-tlli.lua000066400000000000000000000025071265565154000245640ustar00rootroot00000000000000-- Create a file named by_ip/''ip_addess''.cap with all ip traffic of each ip host. (works for tshark only) -- Dump files are created for both source and destination hosts do local dir = "by_tlli" local dumpers = {} local function init_listener() local udp_port_table = DissectorTable.get("udp.port") local gprs_ns_dis = Dissector.get("gprs_ns") udp_port_table:add(23000,gprs_ns_dis) local field_tlli = Field.new("bssgp.tlli") local tap = Listener.new("ip", "udp.port == 23000") -- we will be called once for every IP Header. -- If there's more than one IP header in a given packet we'll dump the packet once per every header function tap.packet(pinfo,tvb,ip) local tlli = field_tlli() if not tlli then return end local tlli_str = tostring(tlli) tlli_dmp = dumpers[tlli_str] if not tlli_dmp then local tlli_hex = string.format("0x%x", tonumber(tlli_str)) print("Creating dump for TLLI " .. tlli_hex) tlli_dmp = Dumper.new_for_current(dir .. "/" .. tlli_hex .. ".pcap") dumpers[tlli_str] = tlli_dmp end tlli_dmp:dump_current() tlli_dmp:flush() end function tap.draw() for tlli,dumper in pairs(dumpers) do dumper:flush() end end function tap.reset() for tlli,dumper in pairs(dumpers) do dumper:close() end dumpers = {} end end init_listener() end openbsc-0.15.0/openbsc/contrib/gprs/gprs-verify-nu.lua000066400000000000000000000030501265565154000227010ustar00rootroot00000000000000-- This script verifies that the N(U) is increasing... -- do local nu_state_src = {} local function init_listener() -- handle the port as NS over IP local udp_port_table = DissectorTable.get("udp.port") local gprs_ns_dis = Dissector.get("gprs_ns") udp_port_table:add(23000,gprs_ns_dis) -- we want to look here... local llc_sapi_get = Field.new("llcgprs.sapib") local llc_nu_get = Field.new("llcgprs.nu") local bssgp_tlli_get = Field.new("bssgp.tlli") local tap = Listener.new("ip", "udp.port == 23000") function tap.packet(pinfo,tvb,ip) local llc_sapi = llc_sapi_get() local llc_nu = llc_nu_get() local bssgp_tlli = bssgp_tlli_get() if not llc_sapi or not llc_nu or not bssgp_tlli then return end local ip_src = tostring(ip.ip_src) local bssgp_tlli = tostring(bssgp_tlli) local llc_nu = tostring(llc_nu) local llc_sapi = tostring(llc_sapi) local src_key = ip_src .. "-" .. bssgp_tlli .. "-" .. llc_sapi local last_nu = nu_state_src[src_key] if not last_nu then -- print("Establishing mapping for " .. src_key) nu_state_src[src_key] = llc_nu return end local function tohex(number) return string.format("0x%x", tonumber(number)) end nu_state_src[src_key] = llc_nu if tonumber(last_nu) + 1 ~= tonumber(llc_nu) then print("JUMP in N(U) on TLLI " .. tohex(bssgp_tlli) .. " and SAPI: " .. llc_sapi .. " src: " .. ip_src) print("\t last: " .. last_nu .. " now: " .. llc_nu) end end function tap.draw() end function tap.reset() end end init_listener() end openbsc-0.15.0/openbsc/contrib/hlr-remove-old.sql000066400000000000000000000011551265565154000217070ustar00rootroot00000000000000-- Remove old data from the database DELETE FROM Subscriber WHERE id != 1 AND datetime('now', '-10 days') > updated AND authorized != 1; DELETE FROM Equipment WHERE datetime('now', '-10 days') > updated; DELETE FROM EquipmentWatch WHERE datetime('now', '-10 days') > updated; DELETE FROM SMS WHERE datetime('now', '-10 days') > created; DELETE FROM VLR WHERE datetime('now', '-10 days') > updated; DELETE FROM ApduBlobs WHERE datetime('now', '-10 days') > created; DELETE FROM Counters WHERE datetime('now', '-10 days') > timestamp; DELETE FROM RateCounters WHERE datetime('now', '-10 days') > timestamp; VACUUM; openbsc-0.15.0/openbsc/contrib/mgcp_server.py000077500000000000000000000042721265565154000212260ustar00rootroot00000000000000#!/usr/bin/env python # Simple server for mgcp... send audit, receive response.. import socket, time MGCP_GATEWAY_PORT = 2427 MGCP_CALLAGENT_PORT = 2727 rsip_resp = """200 321321332\r\n""" audit_packet = """AUEP %d 13@mgw MGCP 1.0\r\n""" crcx_packet = """CRCX %d 14@mgw MGCP 1.0\r\nC: 4a84ad5d25f\r\nL: p:20, a:GSM-EFR, nt:IN\r\nM: recvonly\r\n""" dlcx_packet = """DLCX %d 14@mgw MGCP 1.0\r\nC: 4a84ad5d25f\r\nI: %d\r\n""" mdcx_packet = """MDCX %d 14@mgw MGCP 1.0\r\nC: 4a84ad5d25f\r\nI: %d\r\nL: p:20, a:GSM-EFR, nt:IN\r\nM: recvonly\r\n\r\nv=0\r\no=- 258696477 0 IN IP4 172.16.1.107\r\ns=-\r\nc=IN IP4 172.16.1.107\r\nt=0 0\r\nm=audio 6666 RTP/AVP 127\r\na=rtpmap:127 GSM-EFR/8000/1\r\na=ptime:20\r\na=recvonly\r\nm=image 4402 udptl t38\r\na=T38FaxVersion:0\r\na=T38MaxBitRate:14400\r\n""" def hexdump(src, length=8): """Recipe is from http://code.activestate.com/recipes/142812/""" result = [] digits = 4 if isinstance(src, unicode) else 2 for i in xrange(0, len(src), length): s = src[i:i+length] hexa = b' '.join(["%0*X" % (digits, ord(x)) for x in s]) text = b''.join([x if 0x20 <= ord(x) < 0x7F else b'.' for x in s]) result.append( b"%04X %-*s %s" % (i, length*(digits + 1), hexa, text) ) return b'\n'.join(result) server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) server_socket.bind(("127.0.0.1", MGCP_CALLAGENT_PORT)) server_socket.setblocking(1) last_ci = 1 def send_and_receive(packet): global last_ci server_socket.sendto(packet, ("127.0.0.1", MGCP_GATEWAY_PORT)) try: data, addr = server_socket.recvfrom(4096) # attempt to store the CI of the response list = data.split("\n") for item in list: if item.startswith("I: "): last_ci = int(item[3:]) print hexdump(data), addr except socket.error, e: print e pass def generate_tid(): import random return random.randint(0, 65123) while True: send_and_receive(audit_packet % generate_tid()) send_and_receive(crcx_packet % generate_tid() ) send_and_receive(mdcx_packet % (generate_tid(), last_ci)) send_and_receive(dlcx_packet % (generate_tid(), last_ci)) time.sleep(3) openbsc-0.15.0/openbsc/contrib/nat/000077500000000000000000000000001265565154000171125ustar00rootroot00000000000000openbsc-0.15.0/openbsc/contrib/nat/test_regexp.c000066400000000000000000000011101265565154000216000ustar00rootroot00000000000000/* make test_regexp */ #include #include #include int main(int argc, char **argv) { regex_t reg; regmatch_t matches[2]; if (argc != 4) { printf("Invoke with: test_regexp REGEXP REPLACE NR\n"); return -1; } if (regcomp(®, argv[1], REG_EXTENDED) != 0) { fprintf(stderr, "Regexp '%s' is not valid.\n", argv[1]); return -1; } if (regexec(®, argv[3], 2, matches, 0) == 0 && matches[1].rm_eo != -1) printf("New Number: %s%s\n", argv[2], &argv[3][matches[1].rm_so]); else printf("No match.\n"); regfree(®); return 0; } openbsc-0.15.0/openbsc/contrib/rtp/000077500000000000000000000000001265565154000171355ustar00rootroot00000000000000openbsc-0.15.0/openbsc/contrib/rtp/gen_rtp_header.erl000077500000000000000000000330501265565154000226130ustar00rootroot00000000000000#!/usr/bin/env escript %% -*- erlang -*- %%! -smp disable -module(gen_rtp_header). % -mode(compile). -define(VERSION, "0.1"). -export([main/1]). -record(rtp_packet, { version = 2, padding = 0, marker = 0, payload_type = 0, seqno = 0, timestamp = 0, ssrc = 0, csrcs = [], extension = <<>>, payload = <<>>, realtime }). main(Args) -> DefaultOpts = [{format, state}, {ssrc, 16#11223344}, {rate, 8000}, {pt, 98}], {PosArgs, Opts} = getopts_checked(Args, DefaultOpts), log(debug, fun (Dev) -> io:format(Dev, "Initial options:~n", []), dump_opts(Dev, Opts), io:format(Dev, "~s: ~p~n", ["Args", PosArgs]) end, [], Opts), main(PosArgs, Opts). main([First | RemArgs], Opts) -> try F = list_to_integer(First), Format = proplists:get_value(format, Opts, state), PayloadData = proplists:get_value(payload, Opts, undef), InFile = proplists:get_value(file, Opts, undef), Payload = case {PayloadData, InFile} of {undef, undef} -> % use default value #rtp_packet{}#rtp_packet.payload; {P, undef} -> P; {_, File} -> log(info, "Loading file '~s'~n", [File], Opts), {ok, InDev} = file:open(File, [read]), DS = [ Pl#rtp_packet.payload || {_T, Pl} <- read_packets(InDev, Opts)], file:close(InDev), log(debug, "File '~s' closed, ~w packets read.~n", [File, length(DS)], Opts), DS end, Dev = standard_io, write_packet_pre(Dev, Format), do_groups(Dev, Payload, F, RemArgs, Opts), write_packet_post(Dev, Format), 0 catch _:_ -> log(debug, "~p~n", [hd(erlang:get_stacktrace())], Opts), usage(), halt(1) end ; main(_, _Opts) -> usage(), halt(1). %%% group (count + offset) handling %%% do_groups(_Dev, _Pl, _F, [], _Opts) -> ok; do_groups(Dev, Pl, F, [L], Opts) -> do_groups(Dev, Pl, F, [L, 0], Opts); do_groups(Dev, Pl, First, [L, O | Args], Opts) -> Ssrc = proplists:get_value(ssrc, Opts, #rtp_packet.ssrc), PT = proplists:get_value(pt, Opts, #rtp_packet.payload_type), Len = list_to_num(L), Offs = list_to_num(O), log(info, "Starting group: Ssrc=~.16B, PT=~B, First=~B, Len=~B, Offs=~B~n", [Ssrc, PT, First, Len, Offs], Opts), Pkg = #rtp_packet{ssrc = Ssrc, payload_type = PT}, Pl2 = write_packets(Dev, Pl, Pkg, First, Len, Offs, Opts), {Args2, Opts2} = getopts_checked(Args, Opts), log(debug, fun (Io) -> io:format(Io, "Changed options:~n", []), dump_opts(Io, Opts2 -- Opts) end, [], Opts), do_groups(Dev, Pl2, First+Len, Args2, Opts2). %%% error handling helpers %%% getopts_checked(Args, Opts) -> try getopts(Args, Opts) catch C:R -> log(error, "~s~n", [explain_error(C, R, erlang:get_stacktrace(), Opts)], Opts), usage(), halt(1) end. explain_error(error, badarg, [{erlang,list_to_integer,[S,B]} | _ ], _Opts) -> io_lib:format("Invalid number '~s' (base ~B)", [S, B]); explain_error(error, badarg, [{erlang,list_to_integer,[S]} | _ ], _Opts) -> io_lib:format("Invalid decimal number '~s'", [S]); explain_error(C, R, [Hd | _ ], _Opts) -> io_lib:format("~p, ~p:~p", [Hd, C, R]); explain_error(_, _, [], _Opts) -> "". %%% usage and options %%% myname() -> filename:basename(escript:script_name()). usage(Text) -> io:format(standard_error, "~s: ~s~n", [myname(), Text]), usage(). usage() -> io:format(standard_error, "Usage: ~s [Options] Start Count1 Offs1 [[Options] Count2 Offs2 ...]~n", [myname()]). show_version() -> io:format(standard_io, "~s ~s~n", [myname(), ?VERSION]). show_help() -> io:format(standard_io, "Usage: ~s [Options] Start Count1 Offs1 [[Options] Count2 Offs2 ...]~n~n" ++ "Options:~n" ++ " -h, --help this text~n" ++ " --version show version info~n" ++ " -i, --file=FILE reads payload from file (state format by default)~n" ++ " -f, --frame-size=N read payload as binary frames of size N instead~n" ++ " -p, --payload=HEX set constant payload~n" ++ " --verbose=N set verbosity~n" ++ " -v increase verbosity~n" ++ " --format=state use state format for output (default)~n" ++ " -C, --format=c use simple C lines for output~n" ++ " --format=carray use a C array for output~n" ++ " -s, --ssrc=SSRC set the SSRC~n" ++ " -t, --type=N set the payload type~n" ++ " -r, --rate=N set the RTP rate [8000]~n" ++ " -D, --duration=N set the packet duration in RTP time units [160]~n" ++ " -d, --delay=FLOAT add offset to playout timestamp~n" ++ "~n" ++ "Arguments:~n" ++ " Start initial packet (sequence) number~n" ++ " Count number of packets~n" ++ " Offs timestamp offset (in RTP units)~n" ++ "", [myname()]). getopts([ "--file=" ++ File | R], Opts) -> getopts(R, [{file, File} | Opts]); getopts([ "-i" ++ T | R], Opts) -> getopts_alias_arg("--file", T, R, Opts); getopts([ "--frame-size=" ++ N | R], Opts) -> Size = list_to_integer(N), getopts(R, [{frame_size, Size}, {in_format, bin} | Opts]); getopts([ "-f" ++ T | R], Opts) -> getopts_alias_arg("--frame-size", T, R, Opts); getopts([ "--duration=" ++ N | R], Opts) -> Duration = list_to_integer(N), getopts(R, [{duration, Duration} | Opts]); getopts([ "-D" ++ T | R], Opts) -> getopts_alias_arg("--duration", T, R, Opts); getopts([ "--rate=" ++ N | R], Opts) -> Rate = list_to_integer(N), getopts(R, [{rate, Rate} | Opts]); getopts([ "-r" ++ T | R], Opts) -> getopts_alias_arg("--rate", T, R, Opts); getopts([ "--version" | _], _Opts) -> show_version(), halt(0); getopts([ "--help" | _], _Opts) -> show_help(), halt(0); getopts([ "-h" ++ T | R], Opts) -> getopts_alias_no_arg("--help", T, R, Opts); getopts([ "--verbose=" ++ V | R], Opts) -> Verbose = list_to_integer(V), getopts(R, [{verbose, Verbose} | Opts]); getopts([ "-v" ++ T | R], Opts) -> Verbose = proplists:get_value(verbose, Opts, 0), getopts_short_no_arg(T, R, [ {verbose, Verbose+1} | Opts]); getopts([ "--format=state" | R], Opts) -> getopts(R, [{format, state} | Opts]); getopts([ "--format=c" | R], Opts) -> getopts(R, [{format, c} | Opts]); getopts([ "-C" ++ T | R], Opts) -> getopts_alias_no_arg("--format=c", T, R, Opts); getopts([ "--format=carray" | R], Opts) -> getopts(R, [{format, carray} | Opts]); getopts([ "--payload=" ++ Hex | R], Opts) -> getopts(R, [{payload, hex_to_bin(Hex)} | Opts]); getopts([ "--ssrc=" ++ Num | R], Opts) -> getopts(R, [{ssrc, list_to_num(Num)} | Opts]); getopts([ "-s" ++ T | R], Opts) -> getopts_alias_arg("--ssrc", T, R, Opts); getopts([ "--type=" ++ Num | R], Opts) -> getopts(R, [{pt, list_to_num(Num)} | Opts]); getopts([ "-t" ++ T | R], Opts) -> getopts_alias_arg("--type", T, R, Opts); getopts([ "--delay=" ++ Num | R], Opts) -> getopts(R, [{delay, list_to_float(Num)} | Opts]); getopts([ "-d" ++ T | R], Opts) -> getopts_alias_arg("--delay", T, R, Opts); % parsing helpers getopts([ "--" | R], Opts) -> {R, normalize_opts(Opts)}; getopts([ O = "--" ++ _ | _], _Opts) -> usage("Invalid option: " ++ O), halt(1); getopts([ [ $-, C | _] | _], _Opts) when C < $0; C > $9 -> usage("Invalid option: -" ++ [C]), halt(1); getopts(R, Opts) -> {R, normalize_opts(Opts)}. getopts_short_no_arg([], R, Opts) -> getopts(R, Opts); getopts_short_no_arg(T, R, Opts) -> getopts([ "-" ++ T | R], Opts). getopts_alias_no_arg(A, [], R, Opts) -> getopts([A | R], Opts); getopts_alias_no_arg(A, T, R, Opts) -> getopts([A, "-" ++ T | R], Opts). getopts_alias_arg(A, [], [T | R], Opts) -> getopts([A ++ "=" ++ T | R], Opts); getopts_alias_arg(A, T, R, Opts) -> getopts([A ++ "=" ++ T | R], Opts). normalize_opts(Opts) -> [ proplists:lookup(E, Opts) || E <- proplists:get_keys(Opts) ]. %%% conversions %%% bin_to_hex(Bin) -> [hd(integer_to_list(N,16)) || <> <= Bin]. hex_to_bin(Hex) -> << <<(list_to_integer([Nib],16)):4>> || Nib <- Hex>>. list_to_num("-" ++ Str) -> -list_to_num(Str); list_to_num("0x" ++ Str) -> list_to_integer(Str, 16); list_to_num("0b" ++ Str) -> list_to_integer(Str, 2); list_to_num(Str = [ $0 | _ ]) -> list_to_integer(Str, 8); list_to_num(Str) -> list_to_integer(Str, 10). %%% dumping data %%% dump_opts(Dev, Opts) -> dump_opts2(Dev, Opts, proplists:get_keys(Opts)). dump_opts2(Dev, Opts, [OptName | R]) -> io:format(Dev, " ~-10s: ~p~n", [OptName, proplists:get_value(OptName, Opts)]), dump_opts2(Dev, Opts, R); dump_opts2(_Dev, _Opts, []) -> ok. %%% logging %%% log(L, Fmt, Args, Opts) when is_list(Opts) -> log(L, Fmt, Args, proplists:get_value(verbose, Opts, 0), Opts). log(debug, Fmt, Args, V, Opts) when V > 2 -> log2("DEBUG", Fmt, Args, Opts); log(info, Fmt, Args, V, Opts) when V > 1 -> log2("INFO", Fmt, Args, Opts); log(notice, Fmt, Args, V, Opts) when V > 0 -> log2("NOTICE", Fmt, Args, Opts); log(warn, Fmt, Args, _V, Opts) -> log2("WARNING", Fmt, Args, Opts); log(error, Fmt, Args, _V, Opts) -> log2("ERROR", Fmt, Args, Opts); log(Lvl, Fmt, Args, V, Opts) when V >= Lvl -> log2("", Fmt, Args, Opts); log(_, _, _, _i, _) -> ok. log2(Type, Fmt, Args, _Opts) when is_list(Fmt) -> io:format(standard_error, "~s: " ++ Fmt, [Type | Args]); log2("", Fmt, Args, _Opts) when is_list(Fmt) -> io:format(standard_error, Fmt, Args); log2(_Type, Fun, _Args, _Opts) when is_function(Fun, 1) -> Fun(standard_error). %%% RTP packets %%% make_rtp_packet(P = #rtp_packet{version = 2}) -> << (P#rtp_packet.version):2, 0:1, % P 0:1, % X 0:4, % CC (P#rtp_packet.marker):1, (P#rtp_packet.payload_type):7, (P#rtp_packet.seqno):16, (P#rtp_packet.timestamp):32, (P#rtp_packet.ssrc):32, (P#rtp_packet.payload)/bytes >>. parse_rtp_packet( << 2:2, % Version 2 0:1, % P (not supported yet) 0:1, % X (not supported yet) 0:4, % CC (not supported yet) M:1, PT:7, SeqNo: 16, TS:32, Ssrc:32, Payload/bytes >>) -> #rtp_packet{ version = 0, marker = M, payload_type = PT, seqno = SeqNo, timestamp = TS, ssrc = Ssrc, payload = Payload}. %%% payload generation %%% next_payload(F) when is_function(F) -> {F(), F}; next_payload({F, D}) when is_function(F) -> {P, D2} = F(D), {P, {F, D2}}; next_payload([P | R]) -> {P, R}; next_payload([]) -> undef; next_payload(Bin = <<_/bytes>>) -> {Bin, Bin}. %%% real writing work %%% write_packets(_Dev, DS, _P, _F, 0, _O, _Opts) -> DS; write_packets(Dev, DataSource, P = #rtp_packet{}, F, L, O, Opts) -> Format = proplists:get_value(format, Opts, state), Ptime = proplists:get_value(duration, Opts, 160), Delay = proplists:get_value(delay, Opts, 0), Rate = proplists:get_value(rate, Opts, 8000), case next_payload(DataSource) of {Payload, DataSource2} -> write_packet(Dev, Ptime * F / Rate + Delay, P#rtp_packet{seqno = F, timestamp = F*Ptime+O, payload = Payload}, Format), write_packets(Dev, DataSource2, P, F+1, L-1, O, Opts); Other -> Other end. write_packet(Dev, Time, P = #rtp_packet{}, Format) -> Bin = make_rtp_packet(P), write_packet_line(Dev, Time, P, Bin, Format). write_packet_pre(Dev, carray) -> io:format(Dev, "struct {float t; int len; char *data;} packets[] = {~n", []); write_packet_pre(_Dev, _) -> ok. write_packet_post(Dev, carray) -> io:format(Dev, "};~n", []); write_packet_post(_Dev, _) -> ok. write_packet_line(Dev, Time, _P, Bin, state) -> io:format(Dev, "~f ~s~n", [Time, bin_to_hex(Bin)]); write_packet_line(Dev, Time, #rtp_packet{seqno = N, timestamp = TS}, Bin, c) -> ByteList = [ [ $0, $x | integer_to_list(Byte, 16) ] || <> <= Bin ], ByteStr = string:join(ByteList, ", "), io:format(Dev, "/* time=~f, SeqNo=~B, TS=~B */ {~s}~n", [Time, N, TS, ByteStr]); write_packet_line(Dev, Time, #rtp_packet{seqno = N, timestamp = TS}, Bin, carray) -> io:format(Dev, " /* RTP: SeqNo=~B, TS=~B */~n", [N, TS]), io:format(Dev, " {~f, ~B, \"", [Time, size(Bin)]), [ io:format(Dev, "\\x~2.16.0B", [Byte]) || <> <= Bin ], io:format(Dev, "\"},~n", []). %%% real reading work %%% read_packets(Dev, Opts) -> Format = proplists:get_value(in_format, Opts, state), read_packets(Dev, Opts, Format). read_packets(Dev, Opts, Format) -> case read_packet(Dev, Opts, Format) of eof -> []; Tuple -> [Tuple | read_packets(Dev, Opts, Format)] end. read_packet(Dev, Opts, bin) -> Size = proplists:get_value(frame_size, Opts), case file:read(Dev, Size) of {ok, Data} -> {0, #rtp_packet{payload = iolist_to_binary(Data)}}; eof -> eof end; read_packet(Dev, _Opts, Format) -> case read_packet_line(Dev, Format) of {Time, Bin} -> {Time, parse_rtp_packet(Bin)}; eof -> eof end. read_packet_line(Dev, state) -> case io:fread(Dev, "", "~f ~s") of {ok, [Time, Hex]} -> {Time, hex_to_bin(Hex)}; eof -> eof end. openbsc-0.15.0/openbsc/contrib/rtp/rtp_replay.st000066400000000000000000000011241265565154000216640ustar00rootroot00000000000000" Simple UDP replay from the state files " PackageLoader fileInPackage: #Sockets. FileStream fileIn: 'rtp_replay_shared.st'. Eval [ | replay file host dport | file := Smalltalk arguments at: 1 ifAbsent: [ 'rtpstream.state' ]. host := Smalltalk arguments at: 2 ifAbsent: [ '127.0.0.1' ]. dport := (Smalltalk arguments at: 3 ifAbsent: [ '4000' ]) asInteger. sport := (Smalltalk arguments at: 4 ifAbsent: [ '0' ]) asInteger. replay := RTPReplay on: file fromPort: sport. Transcript nextPutAll: 'Going to stream now'; nl. replay streamAudio: host port: dport. ] openbsc-0.15.0/openbsc/contrib/rtp/rtp_replay_shared.st000066400000000000000000000062011265565154000232130ustar00rootroot00000000000000" Simple UDP replay from the state files " PackageLoader fileInPackage: #Sockets. Object subclass: SDPUtils [ "Look into using PetitParser." SDPUtils class >> findPort: aSDP [ aSDP linesDo: [:line | (line startsWith: 'm=audio ') ifTrue: [ | stream | stream := line readStream skip: 'm=audio ' size; yourself. ^ Number readFrom: stream. ] ]. ^ self error: 'Not found'. ] SDPUtils class >> findHost: aSDP [ aSDP linesDo: [:line | (line startsWith: 'c=IN IP4 ') ifTrue: [ | stream | ^ stream := line readStream skip: 'c=IN IP4 ' size; upToEnd. ] ]. ^ self error: 'Not found'. ] ] Object subclass: RTPReplay [ | filename socket | RTPReplay class >> on: aFile [ ^ self new initialize; file: aFile; yourself ] RTPReplay class >> on: aFile fromPort: aPort [ ^ self new initialize: aPort; file: aFile; yourself ] initialize [ self initialize: 0. ] initialize: aPort [ socket := Sockets.DatagramSocket local: '0.0.0.0' port: aPort. ] file: aFile [ filename := aFile ] localPort [ ^ socket port ] streamAudio: aHost port: aPort [ | file last_time last_image udp_send dest | last_time := nil. last_image := nil. file := FileStream open: filename mode: #read. "Send the payload" dest := Sockets.SocketAddress byName: aHost. udp_send := [:payload | | datagram | datagram := Sockets.Datagram data: payload contents address: dest port: aPort. socket nextPut: datagram ]. [file atEnd] whileFalse: [ | lineStream time data now_image | lineStream := file nextLine readStream. "Read the time, skip the blank, parse the data" time := Number readFrom: lineStream. lineStream skip: 1. data := WriteStream on: (ByteArray new: 30). [lineStream atEnd] whileFalse: [ | hex | hex := lineStream next: 2. data nextPut: (Number readFrom: hex readStream radix: 16). ]. last_time isNil ifTrue: [ "First time, send it right now" last_time := time. last_image := Time millisecondClockValue. udp_send value: data. ] ifFalse: [ | wait_image new_image_time | "How long to wait?" wait_image := last_image + ((time - last_time) * 1000). [ wait_image > Time millisecondClockValue ] whileTrue: [Processor yield]. udp_send value: data. last_time := time. last_image := wait_image. ] ] ] ] openbsc-0.15.0/openbsc/contrib/rtp/rtp_replay_sip.st000066400000000000000000000041121265565154000225370ustar00rootroot00000000000000""" Create a SIP connection and then stream... """ PackageLoader fileInPackage: #OsmoSIP. "Load for the replay code" FileStream fileIn: 'rtp_replay_shared.st'. Osmo.SIPCall subclass: StreamCall [ | sem stream | createCall: aSDP [ | sdp | stream := RTPReplay on: 'rtp_ssrc6976010.240.240.1_to_10.240.240.50.state'. sdp := aSDP % {stream localPort}. ^ super createCall: sdp. ] sem: aSemaphore [ sem := aSemaphore ] sessionNew [ | host port | Transcript nextPutAll: 'The call has started'; nl. Transcript nextPutAll: sdp_result; nl. host := SDPUtils findHost: sdp_result. port := SDPUtils findPort: sdp_result. [ stream streamAudio: host port: port. Transcript nextPutAll: 'Streaming has finished.'; nl. ] fork. ] sessionFailed [ sem signal ] sessionEnd [ sem signal ] ] Eval [ | transport agent call sem sdp_fr sdp_amr | sdp_fr := (WriteStream on: String new) nextPutAll: 'v=0'; cr; nl; nextPutAll: 'o=twinkle 1739517580 1043400482 IN IP4 127.0.0.1'; cr; nl; nextPutAll: 's=-'; cr; nl; nextPutAll: 'c=IN IP4 127.0.0.1'; cr; nl; nextPutAll: 't=0 0'; cr; nl; nextPutAll: 'm=audio %1 RTP/AVP 0 101'; cr; nl; nextPutAll: 'a=rtpmap:0 PCMU/8000'; cr; nl; nextPutAll: 'a=rtpmap:101 telephone-event/8000'; cr; nl; nextPutAll: 'a=fmtp:101 0-15'; cr; nl; nextPutAll: 'a=ptime:20'; cr; nl; contents. sem := Semaphore new. transport := Osmo.SIPUdpTransport startOn: '0.0.0.0' port: 5066. agent := Osmo.SIPUserAgent createOn: transport. transport start. call := (StreamCall fromUser: 'sip:1000@sip.zecke.osmocom.org' host: '127.0.0.1' port: 5060 to: 'sip:123456@127.0.0.1' on: agent) sem: sem; yourself. call createCall: sdp_fr. "Wait for the stream to have ended" sem wait. (Delay forSeconds: 4) wait. ] openbsc-0.15.0/openbsc/contrib/rtp/timestamp_rtp.lua000066400000000000000000000012031265565154000225240ustar00rootroot00000000000000print("Ni hao") do local tap = Listener.new("ip", "rtp") local rtp_ssrc = Field.new("rtp.ssrc") local frame_time = Field.new("frame.time_relative") local rtp = Field.new("rtp") function tap.packet(pinfo, tvb, ip) local ip_src, ip_dst = tostring(ip.ip_src), tostring(ip.ip_dst) local rtp_data = rtp() local filename = "rtp_ssrc" .. rtp_ssrc() "_src_" .. ip_src .. "_to_" .. ip_dst .. ".state" local f = io.open(filename, "a") f:write(tostring(frame_time()) .. " ") f:write(tostring(rtp_data.value)) f:write("\n") f:close() end function tap.draw() print("DRAW") end function tap.reset() print("RESET") end end openbsc-0.15.0/openbsc/contrib/sms/000077500000000000000000000000001265565154000171325ustar00rootroot00000000000000openbsc-0.15.0/openbsc/contrib/sms/fill-hlr.st000066400000000000000000000045721265565154000212230ustar00rootroot00000000000000"I create output for some simple SQL statements for the HLR db" Eval [ "Create tables if they don't exist" Transcript show: 'CREATE TABLE SMS ( id INTEGER PRIMARY KEY AUTOINCREMENT, created TIMESTAMP NOT NULL, sent TIMESTAMP, sender_id INTEGER NOT NULL, receiver_id INTEGER NOT NULL, deliver_attempts INTEGER NOT NULL DEFAULT 0, valid_until TIMESTAMP, reply_path_req INTEGER NOT NULL, status_rep_req INTEGER NOT NULL, protocol_id INTEGER NOT NULL, data_coding_scheme INTEGER NOT NULL, ud_hdr_ind INTEGER NOT NULL, dest_addr TEXT, user_data BLOB, header BLOB, text TEXT);'; nl; show: 'CREATE TABLE Subscriber ( id INTEGER PRIMARY KEY AUTOINCREMENT, created TIMESTAMP NOT NULL, updated TIMESTAMP NOT NULL, imsi NUMERIC UNIQUE NOT NULL, name TEXT, extension TEXT UNIQUE, authorized INTEGER NOT NULL DEFAULT 0, tmsi TEXT UNIQUE, lac INTEGER NOT NULL DEFAULT 0);'; nl. "Create some dummy subscribers" num_sub := 1000. num_sms := 30. lac := 1. Transcript show: 'BEGIN;'; nl. 1 to: num_sub do: [:each | Transcript show: 'INSERT INTO Subscriber (imsi, created, updated, authorized, lac, extension) VALUES (%1, datetime(''now''), datetime(''now''), 1, %2, %3);' % {(274090000000000 + each). lac. each}; nl. ]. 1 to: num_sms do: [:sms | 1 to: num_sub do: [:sub | Transcript show: 'INSERT INTO SMS (created, sender_id, receiver_id, valid_until, reply_path_req, status_rep_req, protocol_id, data_coding_scheme, ud_hdr_ind, dest_addr, text) VALUES (datetime(''now''), 1, %1, ''2222-2-2'', 0, 0, 0, 0, 0, ''123456'', ''abc'');' % {sub}; nl. ] ]. Transcript show: 'COMMIT;'; nl. ] openbsc-0.15.0/openbsc/contrib/sms/hlr-query.st000066400000000000000000000005041265565154000214310ustar00rootroot00000000000000"Query for one SMS" Eval [ 1 to: 100 do: [:each | Transcript show: 'SELECT SMS.* FROM SMS JOIN Subscriber ON SMS.receiver_id = Subscriber.id WHERE SMS.id >= 1 AND SMS.sent IS NULL AND Subscriber.lac > 0 ORDER BY SMS.id LIMIT 1;'; nl. ]. ] openbsc-0.15.0/openbsc/contrib/sms/sqlite-probe.tap.d000066400000000000000000000002321265565154000224650ustar00rootroot00000000000000probe process("/usr/lib/libsqlite3.so.0.8.6").function("sqlite3_get_table") { a = user_string($zSql); printf("sqlite3_get_table called '%s'\n", a); } openbsc-0.15.0/openbsc/contrib/systemd/000077500000000000000000000000001265565154000200205ustar00rootroot00000000000000openbsc-0.15.0/openbsc/contrib/systemd/osmo-bsc-mgcp.service000066400000000000000000000002771265565154000240560ustar00rootroot00000000000000[Unit] Description=OpenBSC MGCP [Service] Type=simple Restart=always ExecStart=/usr/bin/osmo-bsc_mgcp -s -c /etc/osmocom/osmo-bsc-mgcp.cfg RestartSec=2 [Install] WantedBy=multi-user.target openbsc-0.15.0/openbsc/contrib/systemd/osmo-bsc.service000066400000000000000000000003201265565154000231170ustar00rootroot00000000000000[Unit] Description=OpenBSC BSC Wants=osmo-bsc-mgcp.service [Service] Type=simple Restart=always ExecStart=/usr/bin/osmo-bsc -c /etc/osmocom/osmo-bsc.cfg -s RestartSec=2 [Install] WantedBy=multi-user.target openbsc-0.15.0/openbsc/contrib/systemd/osmo-nitb.service000066400000000000000000000003571265565154000233160ustar00rootroot00000000000000[Unit] Description=OpenBSC Network In the Box (NITB) [Service] Type=simple Restart=always ExecStart=/usr/bin/osmo-nitb -s -C -c /etc/osmocom/osmo-nitb.cfg -l /var/lib/osmocom/hlr.sqlite3 RestartSec=2 [Install] WantedBy=multi-user.target openbsc-0.15.0/openbsc/contrib/systemd/osmo-sgsn.service000066400000000000000000000002641265565154000233310ustar00rootroot00000000000000[Unit] Description=OpenBSC SGSN [Service] Type=simple Restart=always ExecStart=/usr/bin/osmo-sgsn -c /etc/osmocom/osmo-sgsn.cfg RestartSec=2 [Install] WantedBy=multi-user.target openbsc-0.15.0/openbsc/contrib/testconv/000077500000000000000000000000001265565154000201755ustar00rootroot00000000000000openbsc-0.15.0/openbsc/contrib/testconv/Makefile000066400000000000000000000007371265565154000216440ustar00rootroot00000000000000 OBJS = testconv_main.o CC = gcc CFLAGS = -O0 -ggdb -Wall LDFLAGS = CPPFLAGS = -I../.. -I../../include $(shell pkg-config --cflags libosmocore) $(shell pkg-config --cflags libbcg729) LIBS = ../../src/libmgcp/libmgcp.a ../../src/libcommon/libcommon.a $(shell pkg-config --libs libosmocore) $(shell pkg-config --libs libbcg729) -lgsm -lrt testconv: $(OBJS) $(CC) -o $@ $^ $(LDFLAGS) $(LIBS) testconv_main.o: testconv_main.c $(OBJS): $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $< openbsc-0.15.0/openbsc/contrib/testconv/testconv_main.c000066400000000000000000000056711265565154000232230ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include "bscconfig.h" #ifndef BUILD_MGCP_TRANSCODING #error "Requires MGCP transcoding enabled (see --enable-mgcp-transcoding)" #endif #include "openbsc/mgcp_transcode.h" static int audio_name_to_type(const char *name) { if (!strcasecmp(name, "gsm")) return 3; #ifdef HAVE_BCG729 else if (!strcasecmp(name, "g729")) return 18; #endif else if (!strcasecmp(name, "pcma")) return 8; else if (!strcasecmp(name, "l16")) return 11; return -1; } int mgcp_get_trans_frame_size(void *state_, int nsamples, int dst); int main(int argc, char **argv) { char buf[4096] = {0x80, 0}; int cc, rc; struct mgcp_rtp_end *dst_end; struct mgcp_rtp_end *src_end; struct mgcp_trunk_config tcfg = {{0}}; struct mgcp_endpoint endp = {0}; struct mgcp_process_rtp_state *state; int in_size; int in_samples = 160; int out_samples = 0; uint32_t ts = 0; uint16_t seq = 0; osmo_init_logging(&log_info); tcfg.endpoints = &endp; tcfg.number_endpoints = 1; endp.tcfg = &tcfg; mgcp_initialize_endp(&endp); dst_end = &endp.bts_end; src_end = &endp.net_end; if (argc <= 2) errx(1, "Usage: {gsm|g729|pcma|l16} {gsm|g729|pcma|l16} [SPP]"); if ((src_end->codec.payload_type = audio_name_to_type(argv[1])) == -1) errx(1, "invalid input format '%s'", argv[1]); if ((dst_end->codec.payload_type = audio_name_to_type(argv[2])) == -1) errx(1, "invalid output format '%s'", argv[2]); if (argc > 3) out_samples = atoi(argv[3]); if (out_samples) { dst_end->codec.frame_duration_den = dst_end->codec.rate; dst_end->codec.frame_duration_num = out_samples; dst_end->frames_per_packet = 1; } rc = mgcp_transcoding_setup(&endp, dst_end, src_end); if (rc < 0) errx(1, "setup failed: %s", strerror(-rc)); state = dst_end->rtp_process_data; OSMO_ASSERT(state != NULL); in_size = mgcp_transcoding_get_frame_size(state, in_samples, 0); OSMO_ASSERT(sizeof(buf) >= in_size + 12); buf[1] = src_end->codec.payload_type; *(uint16_t*)(buf+2) = htons(1); *(uint32_t*)(buf+4) = htonl(0); *(uint32_t*)(buf+8) = htonl(0xaabbccdd); while ((cc = read(0, buf + 12, in_size))) { int cont; int len; if (cc != in_size) err(1, "read"); *(uint16_t*)(buf+2) = htonl(seq); *(uint32_t*)(buf+4) = htonl(ts); seq += 1; ts += in_samples; cc += 12; /* include RTP header */ len = cc; do { cont = mgcp_transcoding_process_rtp(&endp, dst_end, buf, &len, sizeof(buf)); if (cont == -EAGAIN) { fprintf(stderr, "Got EAGAIN\n"); break; } if (cont < 0) errx(1, "processing failed: %s", strerror(-cont)); len -= 12; /* ignore RTP header */ if (write(1, buf + 12, len) != len) err(1, "write"); len = cont; } while (len > 0); } return 0; } openbsc-0.15.0/openbsc/doc/000077500000000000000000000000001265565154000154355ustar00rootroot00000000000000openbsc-0.15.0/openbsc/doc/BS11-OML.txt000066400000000000000000000014121265565154000172670ustar00rootroot00000000000000The 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. openbsc-0.15.0/openbsc/doc/Makefile.am000066400000000000000000000000231265565154000174640ustar00rootroot00000000000000SUBDIRS = examples openbsc-0.15.0/openbsc/doc/call-routing.txt000066400000000000000000000016341265565154000206020ustar00rootroot00000000000000Call routing in OpenBSC Flow of events: # MO call initiated by MS, CHANNEL RQD, IMMEDIATE ASSIGN # MS sends CC SETUP message, we assume already on TCH/H FACCH # OpenBSC does a subscriber lookup based on the target extension * If a subscriber is found: # send CALL PROCEEDING message to MO # page the MT subscriber and ask itI to ask for TCH/H # once paging completes, we have the TCH/H for the MT end # send SETUP to MT # receive CALL CONFIRMED from MT # set-up the TRAU mux mapping between the E1 subslots for both TCH/H # receive ALERTING from MT, route ALERTING to MO # receive CONNECT from MT, confirm to MT with CONNECT_ACK # send a CONNECT message to MO, receive CONNECT_ACK from MO * If subscriber is not found: # send RELEASE COMPLETE with apropriate cause to MO (1: unalloacated 3: no route) Thoughts about RR/MM: * we allocate RR/MM entities on demand, when we need them openbsc-0.15.0/openbsc/doc/channel_release.txt000066400000000000000000000062341265565154000213130ustar00rootroot00000000000000 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() openbsc-0.15.0/openbsc/doc/control-interface.txt000066400000000000000000000011711265565154000216140ustar00rootroot00000000000000The protocol for the control interface is wrapped inside the ip.access header with the IPAC_PROTO_OSMO protocol ID (0xee). Inside the ip.access header is a struct ipaccess_head_ext with protocol ID 0x00 which indicates the control interface. After that the actual protocol is text based: * Getting the value of a variable -> GET <- GET_REPLY or ERROR * Setting the value of a variable -> SET <- SET_REPLY or ERROR * A value changes which triggers a trap <- TRAP needs to be unique within a connection. '0' is not allowed openbsc-0.15.0/openbsc/doc/e1-data-model.txt000066400000000000000000000103361265565154000205130ustar00rootroot00000000000000E1 related data model This data model describes the physical relationship of the individual parts in the network, it is not the logical/protocol side of the GSM network. A BTS is connected to the BSC by some physical link. It could be an actual E1 link, but it could also be abis-over-IP with a mixture of TCP and RTP/UDP. To further complicate the fact, multiple BTS can share one such pysical link. On a single E1 line, we can easily accomodate up to three BTS with two TRX each. Thus, it is best for OpenBSC to have some kind of abstraction layer. The BSC's view of a BTS connected to it. We call this 'bts_link'. A bts_link can be * all the TCP and UDP streams of a Abis-over-IP BTS * a set of E1 timeslots for OML, RSL and TRAU connections on a E1 link * a serial line exclusively used for OML messages (T-Link) A bts_link can be registered with the OpenBSC core at runtime. struct trx_link { struct gsm_bts_trx *trx; }; struct bts_link { struct gsm_bts *bts; struct trx_link trx_links[NUM_TRX]; }; Interface from stack to input core: ====================================================================== int abis_rsl_sendmsg(struct msgb *msg); send a message through a RSL link to the TRX specified by the caller in msg->trx. int abis_rsl_rcvmsg(struct msgb *msg); receive a message from a RSL link from the TRX specified by the caller in msg->trx. int abis_nm_sendmsg(struct msgb *msg); send a message through a OML link to the BTS specified by the caller in msg->trx->bts. The caller can just use bts->c0 to get the first TRX in a BTS. (OML messages are not really sent to a TRX but to the BTS) int abis_nm_rcvmsg(struct msgb *msg); receive a message from a OML link from the BTS specified by the caller in msg->trx->bts. The caller can just use bts->c0 to get the first TRX in a BTS. int abis_link_event(int event, void *data); signal some event (such as layer 1 connect/disconnect) from the input core to the stack. int subch_demux_in(mx, const uint8_t *data, int len); receive 'len' bytes from a given E1 timeslot (TRAU frames) int subchan_mux_out(mx, uint8_t *data, int len); obtain 'len' bytes of output data to be sent on E1 timeslot Intrface by Input Core for Input Plugins ====================================================================== int btslink_register_plugin(); Configuration for the E1 input module ====================================================================== BTS BTS number number of TRX OML link E1 line number timeslot number [subslot number] SAPI TEI for each TRX RSL link E1 line number timeslot number [subslot number] SAPI TEI for each TS E1 line number timeslot number subslot number E1 input module data model ====================================================================== enum e1inp_sign_type { E1INP_SIGN_NONE, E1INP_SIGN_OML, E1INP_SIGN_RSL, }; struct e1inp_sign_link { /* list of signalling links */ struct llist_head list; enum e1inp_sign_type type; /* trx for msg->trx of received msgs */ struct gsm_bts_trx *trx; /* msgb queue of to-be-transmitted msgs */ struct llist_head tx_list; /* SAPI and TEI on the E1 TS */ uint8_t sapi; uint8_t tei; } enum e1inp_ts_type { E1INP_TS_TYPE_NONE, E1INP_TS_TYPE_SIGN, E1INP_TS_TYPE_TRAU, }; /* A timeslot in the E1 interface */ struct e1inp_ts { enum e1inp_ts_type type; struct e1inp_line *line; union { struct { struct llist_head sign_links; } sign; struct { /* subchannel demuxer for frames from E1 */ struct subch_demux demux; /* subchannel muxer for frames to E1 */ struct subch_mux mux; } trau; }; union { struct { /* mISDN driver has one fd for each ts */ struct osmo_fd; } misdn; } driver; }; struct e1inp_line { unsigned int num; char *name; struct e1inp_ts ts[NR_E1_TS]; char *e1inp_driver; void *driver_data; }; /* Call from the Stack: configuration of this TS has changed */ int e1inp_update_ts(struct e1inp_ts *ts); /* Receive a packet from the E1 driver */ int e1inp_rx_ts(struct e1inp_ts *ts, struct msgb *msg, uint8_t tei, uint8_t sapi); /* Send a packet, callback function in the driver */ int e1driver_tx_ts(struct e1inp_ts *ts, struct msgb *msg) struct e1inp_driver { const char *name; int (*want_write)(struct e1inp_ts *ts); }; openbsc-0.15.0/openbsc/doc/examples/000077500000000000000000000000001265565154000172535ustar00rootroot00000000000000openbsc-0.15.0/openbsc/doc/examples/Makefile.am000066400000000000000000000010211265565154000213010ustar00rootroot00000000000000 CFG_FILES = find $(srcdir) -name '*.cfg*' | sed -e 's,^$(srcdir),,' dist-hook: for f in $$($(CFG_FILES)); do \ j="$(distdir)/$$f" && \ mkdir -p "$$(dirname $$j)" && \ $(INSTALL_DATA) $(srcdir)/$$f $$j; \ done install-data-hook: for f in $$($(CFG_FILES)); do \ j="$(DESTDIR)$(docdir)/examples/$$f" && \ mkdir -p "$$(dirname $$j)" && \ $(INSTALL_DATA) $(srcdir)/$$f $$j; \ done uninstall-hook: @$(PRE_UNINSTALL) for f in $$($(CFG_FILES)); do \ j="$(DESTDIR)$(docdir)/examples/$$f" && \ $(RM) $$j; \ done openbsc-0.15.0/openbsc/doc/examples/osmo-bsc/000077500000000000000000000000001265565154000207755ustar00rootroot00000000000000openbsc-0.15.0/openbsc/doc/examples/osmo-bsc/osmo-bsc.cfg000066400000000000000000000041751265565154000232070ustar00rootroot00000000000000! ! 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 timer t3101 10 timer t3103 0 timer t3105 0 timer t3107 0 timer t3109 0 timer t3111 0 timer t3113 60 timer t3115 0 timer t3117 0 timer t3119 0 timer t3122 0 timer t3141 0 dtx-used 0 subscriber-keep-in-ram 0 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 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 msc 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 openbsc-0.15.0/openbsc/doc/examples/osmo-bsc_mgcp/000077500000000000000000000000001265565154000220035ustar00rootroot00000000000000openbsc-0.15.0/openbsc/doc/examples/osmo-bsc_mgcp/mgcp.cfg000066400000000000000000000004551265565154000234160ustar00rootroot00000000000000! ! MGCP configuration hand edited ! ! password foo ! line vty no login ! mgcp ! local ip 213.167.134.14 bts ip 172.16.252.43 bind ip 127.0.0.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 loop 1 openbsc-0.15.0/openbsc/doc/examples/osmo-bsc_nat/000077500000000000000000000000001265565154000216375ustar00rootroot00000000000000openbsc-0.15.0/openbsc/doc/examples/osmo-bsc_nat/black-list.cfg000066400000000000000000000000251265565154000243420ustar00rootroot00000000000000678012512671923:6:6: openbsc-0.15.0/openbsc/doc/examples/osmo-bsc_nat/osmo-bsc_nat.cfg000066400000000000000000000030251265565154000247040ustar00rootroot00000000000000! ! 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 everything 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 everything logging level ns info logging level bssgp everything logging level llc everything logging level sndcp everything logging level nat notice logging level ctrl notice logging level smpp everything 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 access-list bla imsi-allow ^11$ bsc 0 token bla location_area_code 1234 description bsc max-endpoints 32 paging forbidden 0 openbsc-0.15.0/openbsc/doc/examples/osmo-gbproxy/000077500000000000000000000000001265565154000217205ustar00rootroot00000000000000openbsc-0.15.0/openbsc/doc/examples/osmo-gbproxy/osmo-gbproxy-legacy.cfg000066400000000000000000000017541265565154000263170ustar00rootroot00000000000000! ! OsmoGbProxy (UNKNOWN) configuration saved from vty !! ! log stderr logging filter all 1 logging color 1 logging timestamp 0 logging level all everything 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 openbsc-0.15.0/openbsc/doc/examples/osmo-gbproxy/osmo-gbproxy.cfg000066400000000000000000000010251265565154000250440ustar00rootroot00000000000000! ! 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 openbsc-0.15.0/openbsc/doc/examples/osmo-nitb/000077500000000000000000000000001265565154000211625ustar00rootroot00000000000000openbsc-0.15.0/openbsc/doc/examples/osmo-nitb/bs11/000077500000000000000000000000001265565154000217305ustar00rootroot00000000000000openbsc-0.15.0/openbsc/doc/examples/osmo-nitb/bs11/openbsc-1bts-2trx-hopping.cfg000066400000000000000000000073301265565154000272530ustar00rootroot00000000000000! ! OpenBSC (0.9.0.845-57c4) configuration saved from vty !! password foo ! line vty no login ! e1_input e1_line 0 driver misdn network network country code 1 mobile network code 1 short name OpenBSC long name OpenBSC auth policy closed location updating reject cause 13 encryption a5 0 neci 1 rrlp mode none mm info 0 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 timer t3101 10 timer t3103 0 timer t3105 0 timer t3107 0 timer t3109 4 timer t3111 0 timer t3113 60 timer t3115 0 timer t3117 0 timer t3119 0 timer t3141 0 bts 0 type bs11 band GSM900 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 descending rach tx integer 9 rach max transmission 7 oml e1 line 0 timeslot 1 sub-slot full oml e1 tei 25 gprs mode none trx 0 rf_locked 0 arfcn 121 nominal power 24 max_power_red 12 rsl e1 line 0 timeslot 1 sub-slot full rsl e1 tei 1 timeslot 0 phys_chan_config CCCH hopping enabled 0 e1 line 0 timeslot 1 sub-slot full timeslot 1 phys_chan_config TCH/F hopping enabled 0 e1 line 0 timeslot 2 sub-slot 1 timeslot 2 phys_chan_config TCH/F hopping enabled 0 e1 line 0 timeslot 2 sub-slot 2 timeslot 3 phys_chan_config TCH/F hopping enabled 0 e1 line 0 timeslot 2 sub-slot 3 timeslot 4 phys_chan_config TCH/F hopping enabled 0 e1 line 0 timeslot 3 sub-slot 0 timeslot 5 phys_chan_config TCH/F hopping enabled 0 e1 line 0 timeslot 3 sub-slot 1 timeslot 6 phys_chan_config TCH/F hopping enabled 0 e1 line 0 timeslot 3 sub-slot 2 timeslot 7 phys_chan_config TCH/F hopping enabled 0 e1 line 0 timeslot 3 sub-slot 3 trx 1 rf_locked 0 arfcn 119 nominal power 24 max_power_red 12 rsl e1 line 0 timeslot 1 sub-slot full rsl e1 tei 2 timeslot 0 phys_chan_config SDCCH8 hopping enabled 1 hopping sequence-number 0 hopping maio 0 hopping arfcn add 117 hopping arfcn add 119 timeslot 1 phys_chan_config TCH/F hopping enabled 1 hopping sequence-number 0 hopping maio 0 hopping arfcn add 117 hopping arfcn add 119 e1 line 0 timeslot 4 sub-slot 1 timeslot 2 phys_chan_config TCH/F hopping enabled 1 hopping sequence-number 0 hopping maio 0 hopping arfcn add 117 hopping arfcn add 119 e1 line 0 timeslot 4 sub-slot 2 timeslot 3 phys_chan_config TCH/F hopping enabled 1 hopping sequence-number 0 hopping maio 0 hopping arfcn add 117 hopping arfcn add 119 e1 line 0 timeslot 4 sub-slot 3 timeslot 4 phys_chan_config TCH/F hopping enabled 1 hopping sequence-number 0 hopping maio 0 hopping arfcn add 117 hopping arfcn add 119 e1 line 0 timeslot 5 sub-slot 0 timeslot 5 phys_chan_config TCH/F hopping enabled 1 hopping sequence-number 0 hopping maio 0 hopping arfcn add 117 hopping arfcn add 119 e1 line 0 timeslot 5 sub-slot 1 timeslot 6 phys_chan_config TCH/F hopping enabled 1 hopping sequence-number 0 hopping maio 0 hopping arfcn add 117 hopping arfcn add 119 e1 line 0 timeslot 5 sub-slot 2 timeslot 7 phys_chan_config TCH/F hopping enabled 1 hopping sequence-number 0 hopping maio 0 hopping arfcn add 117 hopping arfcn add 119 e1 line 0 timeslot 5 sub-slot 3 openbsc-0.15.0/openbsc/doc/examples/osmo-nitb/bs11/openbsc-1bts-2trx.cfg000066400000000000000000000035701265565154000256130ustar00rootroot00000000000000! ! OpenBSC configuration saved from vty ! ! password foo ! line vty no login ! e1_input e1_line 0 driver misdn network network country code 1 mobile network code 1 short name OpenBSC long name OpenBSC timer t3101 10 timer t3113 60 bts 0 type bs11 band GSM900 cell_identity 1 location_area_code 1 training_sequence_code 7 base_station_id_code 63 oml e1 line 0 timeslot 1 sub-slot full oml e1 tei 25 trx 0 arfcn 121 max_power_red 0 rsl e1 line 0 timeslot 1 sub-slot full rsl e1 tei 1 timeslot 0 phys_chan_config CCCH+SDCCH4 e1 line 0 timeslot 1 sub-slot full timeslot 1 phys_chan_config SDCCH8 e1 line 0 timeslot 2 sub-slot 1 timeslot 2 phys_chan_config TCH/F e1 line 0 timeslot 2 sub-slot 2 timeslot 3 phys_chan_config TCH/F e1 line 0 timeslot 2 sub-slot 3 timeslot 4 phys_chan_config TCH/F e1 line 0 timeslot 3 sub-slot 0 timeslot 5 phys_chan_config TCH/F e1 line 0 timeslot 3 sub-slot 1 timeslot 6 phys_chan_config TCH/F e1 line 0 timeslot 3 sub-slot 2 timeslot 7 phys_chan_config TCH/F e1 line 0 timeslot 3 sub-slot 3 trx 1 arfcn 123 max_power_red 0 rsl e1 line 0 timeslot 1 sub-slot full rsl e1 tei 2 timeslot 0 phys_chan_config TCH/F e1 line 0 timeslot 4 sub-slot 0 timeslot 1 phys_chan_config TCH/F e1 line 0 timeslot 4 sub-slot 1 timeslot 2 phys_chan_config TCH/F e1 line 0 timeslot 4 sub-slot 2 timeslot 3 phys_chan_config TCH/F e1 line 0 timeslot 4 sub-slot 3 timeslot 4 phys_chan_config TCH/F e1 line 0 timeslot 5 sub-slot 0 timeslot 5 phys_chan_config TCH/F e1 line 0 timeslot 5 sub-slot 1 timeslot 6 phys_chan_config TCH/F e1 line 0 timeslot 5 sub-slot 2 timeslot 7 phys_chan_config TCH/F e1 line 0 timeslot 5 sub-slot 3 openbsc-0.15.0/openbsc/doc/examples/osmo-nitb/bs11/openbsc-2bts-2trx.cfg000066400000000000000000000067131265565154000256160ustar00rootroot00000000000000! ! OpenBSC configuration saved from vty ! ! password foo ! line vty no login ! network network country code 1 mobile network code 1 short name OpenBSC long name OpenBSC timer t3101 10 timer t3113 60 bts 0 type bs11 band GSM900 cell_identity 1 location_area_code 1 training_sequence_code 7 base_station_id_code 63 oml e1 line 0 timeslot 1 sub-slot full oml e1 tei 25 trx 0 arfcn 121 max_power_red 0 rsl e1 line 0 timeslot 1 sub-slot full rsl e1 tei 1 timeslot 0 phys_chan_config CCCH+SDCCH4 e1 line 0 timeslot 1 sub-slot full timeslot 1 phys_chan_config TCH/F e1 line 0 timeslot 2 sub-slot 1 timeslot 2 phys_chan_config TCH/F e1 line 0 timeslot 2 sub-slot 2 timeslot 3 phys_chan_config TCH/F e1 line 0 timeslot 2 sub-slot 3 timeslot 4 phys_chan_config TCH/F e1 line 0 timeslot 3 sub-slot 0 timeslot 5 phys_chan_config TCH/F e1 line 0 timeslot 3 sub-slot 1 timeslot 6 phys_chan_config TCH/F e1 line 0 timeslot 3 sub-slot 2 timeslot 7 phys_chan_config TCH/F e1 line 0 timeslot 3 sub-slot 3 trx 1 arfcn 123 max_power_red 0 rsl e1 line 0 timeslot 1 sub-slot full rsl e1 tei 2 timeslot 0 phys_chan_config TCH/F e1 line 0 timeslot 4 sub-slot 0 timeslot 1 phys_chan_config TCH/F e1 line 0 timeslot 4 sub-slot 1 timeslot 2 phys_chan_config TCH/F e1 line 0 timeslot 4 sub-slot 2 timeslot 3 phys_chan_config TCH/F e1 line 0 timeslot 4 sub-slot 3 timeslot 4 phys_chan_config TCH/F e1 line 0 timeslot 5 sub-slot 0 timeslot 5 phys_chan_config TCH/F e1 line 0 timeslot 5 sub-slot 1 timeslot 6 phys_chan_config TCH/F e1 line 0 timeslot 5 sub-slot 2 timeslot 7 phys_chan_config TCH/F e1 line 0 timeslot 5 sub-slot 3 bts 1 type bs11 band GSM900 location_area_code 2 training_sequence_code 7 base_station_id_code 63 oml e1 line 1 timeslot 6 sub-slot full oml e1 tei 25 trx 0 arfcn 122 max_power_red 0 rsl e1 line 1 timeslot 6 sub-slot full rsl e1 tei 1 timeslot 0 phys_chan_config CCCH+SDCCH4 e1 line 1 timeslot 7 sub-slot 0 timeslot 1 phys_chan_config SDCCH8 e1 line 1 timeslot 7 sub-slot 1 timeslot 2 phys_chan_config TCH/F e1 line 1 timeslot 7 sub-slot 2 timeslot 3 phys_chan_config TCH/F e1 line 1 timeslot 7 sub-slot 3 timeslot 4 phys_chan_config TCH/F e1 line 1 timeslot 8 sub-slot 0 timeslot 5 phys_chan_config TCH/F e1 line 1 timeslot 8 sub-slot 1 timeslot 6 phys_chan_config TCH/F e1 line 1 timeslot 8 sub-slot 2 timeslot 7 phys_chan_config TCH/F e1 line 1 timeslot 8 sub-slot 3 trx 1 arfcn 124 max_power_red 0 rsl e1 line 1 timeslot 6 sub-slot full rsl e1 tei 2 timeslot 0 phys_chan_config TCH/F e1 line 1 timeslot 9 sub-slot 0 timeslot 1 phys_chan_config TCH/F e1 line 1 timeslot 9 sub-slot 1 timeslot 2 phys_chan_config TCH/F e1 line 1 timeslot 9 sub-slot 2 timeslot 3 phys_chan_config TCH/F e1 line 1 timeslot 9 sub-slot 3 timeslot 4 phys_chan_config TCH/F e1 line 1 timeslot 10 sub-slot 0 timeslot 5 phys_chan_config TCH/F e1 line 1 timeslot 10 sub-slot 1 timeslot 6 phys_chan_config TCH/F e1 line 1 timeslot 10 sub-slot 2 timeslot 7 phys_chan_config TCH/F e1 line 1 timeslot 10 sub-slot 3 openbsc-0.15.0/openbsc/doc/examples/osmo-nitb/bs11/openbsc.cfg000066400000000000000000000022261265565154000240440ustar00rootroot00000000000000! ! OpenBSC configuration saved from vty ! ! password foo ! line vty no login ! e1_input e1_line 0 driver misdn network network country code 1 mobile network code 1 short name OpenBSC long name OpenBSC timer t3101 10 timer t3113 60 bts 0 type bs11 band GSM900 cell_identity 1 location_area_code 1 training_sequence_code 7 base_station_id_code 63 oml e1 line 0 timeslot 1 sub-slot full oml e1 tei 25 trx 0 arfcn 121 max_power_red 0 rsl e1 line 0 timeslot 1 sub-slot full rsl e1 tei 1 timeslot 0 phys_chan_config CCCH+SDCCH4 e1 line 0 timeslot 1 sub-slot full timeslot 1 phys_chan_config TCH/F e1 line 0 timeslot 2 sub-slot 1 timeslot 2 phys_chan_config TCH/F e1 line 0 timeslot 2 sub-slot 2 timeslot 3 phys_chan_config TCH/F e1 line 0 timeslot 2 sub-slot 3 timeslot 4 phys_chan_config TCH/F e1 line 0 timeslot 3 sub-slot 0 timeslot 5 phys_chan_config TCH/F e1 line 0 timeslot 3 sub-slot 1 timeslot 6 phys_chan_config TCH/F e1 line 0 timeslot 3 sub-slot 2 timeslot 7 phys_chan_config TCH/F e1 line 0 timeslot 3 sub-slot 3 openbsc-0.15.0/openbsc/doc/examples/osmo-nitb/nanobts/000077500000000000000000000000001265565154000226265ustar00rootroot00000000000000openbsc-0.15.0/openbsc/doc/examples/osmo-nitb/nanobts/openbsc-multitrx.cfg000066400000000000000000000036361265565154000266360ustar00rootroot00000000000000! ! OpenBSC 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 OpenBSC long name OpenBSC auth policy closed location updating reject cause 13 encryption a5 0 neci 1 rrlp mode none mm info 0 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 timer t3101 10 timer t3103 0 timer t3105 0 timer t3107 0 timer t3109 4 timer t3111 0 timer t3113 60 timer t3115 0 timer t3117 0 timer t3119 0 timer t3141 0 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 ip.access unit_id 1800 0 oml ip.access stream_id 255 line 0 gprs mode none trx 0 rf_locked 0 arfcn 871 nominal power 23 max_power_red 0 rsl e1 tei 0 timeslot 0 phys_chan_config CCCH+SDCCH4 timeslot 1 phys_chan_config SDCCH8 timeslot 2 phys_chan_config TCH/F timeslot 3 phys_chan_config TCH/F timeslot 4 phys_chan_config TCH/F timeslot 5 phys_chan_config TCH/F timeslot 6 phys_chan_config TCH/F timeslot 7 phys_chan_config TCH/F trx 1 rf_locked 0 arfcn 873 nominal power 23 max_power_red 0 rsl e1 tei 0 timeslot 0 phys_chan_config SDCCH8 timeslot 1 phys_chan_config TCH/F timeslot 2 phys_chan_config TCH/F timeslot 3 phys_chan_config TCH/F timeslot 4 phys_chan_config TCH/F timeslot 5 phys_chan_config TCH/F timeslot 6 phys_chan_config TCH/F timeslot 7 phys_chan_config TCH/F openbsc-0.15.0/openbsc/doc/examples/osmo-nitb/nanobts/openbsc.cfg000066400000000000000000000027531265565154000247470ustar00rootroot00000000000000! ! OpenBSC 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 OpenBSC long name OpenBSC auth policy closed location updating reject cause 13 encryption a5 0 neci 1 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 timer t3101 10 timer t3103 0 timer t3105 0 timer t3107 0 timer t3109 4 timer t3111 0 timer t3113 60 timer t3115 0 timer t3117 0 timer t3119 0 timer t3141 0 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 ip.access unit_id 1801 0 oml ip.access stream_id 255 line 0 gprs mode none trx 0 rf_locked 0 arfcn 514 nominal power 23 max_power_red 20 rsl e1 tei 0 timeslot 0 phys_chan_config CCCH+SDCCH4 timeslot 1 phys_chan_config SDCCH8 timeslot 2 phys_chan_config TCH/F timeslot 3 phys_chan_config TCH/F timeslot 4 phys_chan_config TCH/F timeslot 5 phys_chan_config TCH/F timeslot 6 phys_chan_config TCH/F timeslot 7 phys_chan_config TCH/F openbsc-0.15.0/openbsc/doc/examples/osmo-nitb/nokia/000077500000000000000000000000001265565154000222635ustar00rootroot00000000000000openbsc-0.15.0/openbsc/doc/examples/osmo-nitb/nokia/openbsc_nokia_3trx.cfg000066400000000000000000000051631265565154000265430ustar00rootroot00000000000000! ! OpenBSC configuration saved from vty ! ! password foo ! line vty no login ! e1_input e1_line 0 driver misdn network network country code 1 mobile network code 1 short name OpenBSC long name OpenBSC timer t3101 10 timer t3113 60 bts 0 type nokia_site band GSM1800 cell_identity 1 location_area_code 1 base_station_id_code 63 training_sequence_code 7 oml e1 line 0 timeslot 1 sub-slot full oml e1 tei 1 trx 0 arfcn 866 max_power_red 24 rsl e1 line 0 timeslot 2 sub-slot full rsl e1 tei 1 timeslot 0 phys_chan_config CCCH+SDCCH4 e1 line 0 timeslot 6 sub-slot full timeslot 1 phys_chan_config SDCCH8 e1 line 0 timeslot 6 sub-slot 1 timeslot 2 phys_chan_config TCH/F e1 line 0 timeslot 6 sub-slot 2 timeslot 3 phys_chan_config TCH/F e1 line 0 timeslot 6 sub-slot 3 timeslot 4 phys_chan_config TCH/F e1 line 0 timeslot 7 sub-slot 0 timeslot 5 phys_chan_config TCH/F e1 line 0 timeslot 7 sub-slot 1 timeslot 6 phys_chan_config TCH/F e1 line 0 timeslot 7 sub-slot 2 timeslot 7 phys_chan_config TCH/F e1 line 0 timeslot 7 sub-slot 3 trx 1 arfcn 870 max_power_red 24 rsl e1 line 0 timeslot 3 sub-slot full rsl e1 tei 2 timeslot 0 phys_chan_config TCH/F e1 line 0 timeslot 8 sub-slot 0 timeslot 1 phys_chan_config TCH/F e1 line 0 timeslot 8 sub-slot 1 timeslot 2 phys_chan_config TCH/F e1 line 0 timeslot 8 sub-slot 2 timeslot 3 phys_chan_config TCH/F e1 line 0 timeslot 8 sub-slot 3 timeslot 4 phys_chan_config TCH/F e1 line 0 timeslot 9 sub-slot 0 timeslot 5 phys_chan_config TCH/F e1 line 0 timeslot 9 sub-slot 1 timeslot 6 phys_chan_config TCH/F e1 line 0 timeslot 9 sub-slot 2 timeslot 7 phys_chan_config TCH/F e1 line 0 timeslot 9 sub-slot 3 trx 2 arfcn 874 max_power_red 24 rsl e1 line 0 timeslot 4 sub-slot full rsl e1 tei 3 timeslot 0 phys_chan_config TCH/F e1 line 0 timeslot 10 sub-slot 0 timeslot 1 phys_chan_config TCH/F e1 line 0 timeslot 10 sub-slot 1 timeslot 2 phys_chan_config TCH/F e1 line 0 timeslot 10 sub-slot 2 timeslot 3 phys_chan_config TCH/F e1 line 0 timeslot 10 sub-slot 3 timeslot 4 phys_chan_config TCH/F e1 line 0 timeslot 11 sub-slot 0 timeslot 5 phys_chan_config TCH/F e1 line 0 timeslot 11 sub-slot 1 timeslot 6 phys_chan_config TCH/F e1 line 0 timeslot 11 sub-slot 2 timeslot 7 phys_chan_config TCH/F e1 line 0 timeslot 11 sub-slot 3 openbsc-0.15.0/openbsc/doc/examples/osmo-nitb/rbs2308/000077500000000000000000000000001265565154000222655ustar00rootroot00000000000000openbsc-0.15.0/openbsc/doc/examples/osmo-nitb/rbs2308/openbsc.cfg000066400000000000000000000120721265565154000244010ustar00rootroot00000000000000! ! OpenBSC (0.9.11.308-62d46) configuration saved from vty !! password foo ! line vty no login ! network network country code 262 mobile network code 42 short name OpenBSC long name OpenBSC auth policy closed location updating reject cause 13 encryption a5 0 neci 0 paging any use tch 0 rrlp mode none mm info 0 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 timer t3101 10 timer t3103 0 timer t3105 0 timer t3107 0 timer t3109 4 timer t3111 0 timer t3113 60 timer t3115 0 timer t3117 0 timer t3119 0 timer t3122 0 timer t3141 0 dtx-used 0 subscriber-keep-in-ram 0 bts 0 type rbs2000 band GSM900 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 descending rach tx integer 9 rach max transmission 7 oml e1 line 0 timeslot 1 sub-slot full oml e1 tei 62 neighbor-list mode automatic gprs mode none is-connection-list add 4 512 12 is-connection-list add 16 524 12 is-connection-list add 28 536 12 is-connection-list add 40 548 12 trx 0 rf_locked 0 arfcn 55 nominal power 24 max_power_red 12 rsl e1 line 0 timeslot 1 sub-slot full rsl e1 tei 0 timeslot 0 phys_chan_config CCCH+SDCCH4 hopping enabled 0 e1 line 0 timeslot 1 sub-slot full timeslot 1 phys_chan_config TCH/F hopping enabled 0 e1 line 0 timeslot 2 sub-slot 1 timeslot 2 phys_chan_config TCH/F hopping enabled 0 e1 line 0 timeslot 2 sub-slot 2 timeslot 3 phys_chan_config TCH/F hopping enabled 0 e1 line 0 timeslot 2 sub-slot 3 timeslot 4 phys_chan_config TCH/F hopping enabled 0 e1 line 0 timeslot 3 sub-slot 0 timeslot 5 phys_chan_config TCH/F hopping enabled 0 e1 line 0 timeslot 3 sub-slot 1 timeslot 6 phys_chan_config TCH/F hopping enabled 0 e1 line 0 timeslot 3 sub-slot 2 timeslot 7 phys_chan_config TCH/F hopping enabled 0 e1 line 0 timeslot 3 sub-slot 3 trx 1 rf_locked 0 arfcn 57 nominal power 24 max_power_red 12 rsl e1 line 0 timeslot 4 sub-slot full rsl e1 tei 1 timeslot 0 phys_chan_config TCH/F hopping enabled 0 e1 line 0 timeslot 5 sub-slot 0 timeslot 1 phys_chan_config TCH/F hopping enabled 0 e1 line 0 timeslot 5 sub-slot 1 timeslot 2 phys_chan_config TCH/F hopping enabled 0 e1 line 0 timeslot 5 sub-slot 2 timeslot 3 phys_chan_config TCH/F hopping enabled 0 e1 line 0 timeslot 5 sub-slot 3 timeslot 4 phys_chan_config TCH/F hopping enabled 0 e1 line 0 timeslot 6 sub-slot 0 timeslot 5 phys_chan_config TCH/F hopping enabled 0 e1 line 0 timeslot 6 sub-slot 1 timeslot 6 phys_chan_config TCH/F hopping enabled 0 e1 line 0 timeslot 6 sub-slot 2 timeslot 7 phys_chan_config TCH/F hopping enabled 0 e1 line 0 timeslot 6 sub-slot 3 trx 2 rf_locked 0 arfcn 59 nominal power 24 max_power_red 12 rsl e1 line 0 timeslot 7 sub-slot full rsl e1 tei 2 timeslot 0 phys_chan_config TCH/F hopping enabled 0 e1 line 0 timeslot 8 sub-slot 0 timeslot 1 phys_chan_config TCH/F hopping enabled 0 e1 line 0 timeslot 8 sub-slot 1 timeslot 2 phys_chan_config TCH/F hopping enabled 0 e1 line 0 timeslot 8 sub-slot 2 timeslot 3 phys_chan_config TCH/F hopping enabled 0 e1 line 0 timeslot 8 sub-slot 3 timeslot 4 phys_chan_config TCH/F hopping enabled 0 e1 line 0 timeslot 9 sub-slot 0 timeslot 5 phys_chan_config TCH/F hopping enabled 0 e1 line 0 timeslot 9 sub-slot 1 timeslot 6 phys_chan_config TCH/F hopping enabled 0 e1 line 0 timeslot 9 sub-slot 2 timeslot 7 phys_chan_config TCH/F hopping enabled 0 e1 line 0 timeslot 9 sub-slot 3 trx 3 rf_locked 0 arfcn 61 nominal power 24 max_power_red 12 rsl e1 line 0 timeslot 10 sub-slot full rsl e1 tei 3 timeslot 0 phys_chan_config TCH/F hopping enabled 0 e1 line 0 timeslot 11 sub-slot 0 timeslot 1 phys_chan_config TCH/F hopping enabled 0 e1 line 0 timeslot 11 sub-slot 1 timeslot 2 phys_chan_config TCH/F hopping enabled 0 e1 line 0 timeslot 11 sub-slot 2 timeslot 3 phys_chan_config TCH/F hopping enabled 0 e1 line 0 timeslot 11 sub-slot 3 timeslot 4 phys_chan_config TCH/F hopping enabled 0 e1 line 0 timeslot 12 sub-slot 0 timeslot 5 phys_chan_config TCH/F hopping enabled 0 e1 line 0 timeslot 12 sub-slot 1 timeslot 6 phys_chan_config TCH/F hopping enabled 0 e1 line 0 timeslot 12 sub-slot 2 timeslot 7 phys_chan_config TCH/F hopping enabled 0 e1 line 0 timeslot 12 sub-slot 3 e1_input e1_line 0 driver dahdi openbsc-0.15.0/openbsc/doc/examples/osmo-sgsn/000077500000000000000000000000001265565154000212005ustar00rootroot00000000000000openbsc-0.15.0/openbsc/doc/examples/osmo-sgsn/osmo-sgsn.cfg000066400000000000000000000006461265565154000236140ustar00rootroot00000000000000! ! Osmocom SGSN configuration ! ! line vty no login ! sgsn gtp local-ip 127.0.0.1 ggsn 0 remote-ip 127.0.0.1 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 127.0.0.1 encapsulation udp local-port 23000 encapsulation framerelay-gre enabled 0 ! bssgp ! openbsc-0.15.0/openbsc/doc/gsm-hopping.txt000066400000000000000000000025441265565154000204330ustar00rootroot00000000000000according to GSM 05.02: general parameters from CCCH: * CA cell allocation of ARFCN's (System Information / BCCH) * FN: TDMA frame number (t1,t2,t3') in SCH specific parameters from channel assignment: * MA: mobile allocation, defines set of ARFCN's, up to 64 * MAIO: index * HSN: hopping sequence generator number (0..64) hopping sequence generation (6.2.3): uint8_t rntable[114] = { 48, 98, 63, 1, 36, 95, 78, 102, 94, 73, 0, 64, 25, 81, 76, 59, 124, 23, 104, 100, 101, 47, 118, 85, 18, 56, 96, 86, 54, 2, 80, 34, 127, 13, 6, 89, 57, 103, 12, 74, 55, 111, 75, 38, 109, 71, 112, 29, 11, 88, 87, 19, 3, 68, 110, 26, 33, 31, 8, 45, 82, 58, 40, 107, 32, 5, 106, 92, 62, 67, 77, 108, 122, 37, 60, 66, 121, 42, 51, 126, 117, 114, 4, 90, 43, 52, 53, 113, 120, 72, 16, 49, 7, 79, 119, 61, 22, 84, 9, 97, 125, 99, 17, 123 }; /* mai=0 represents lowest ARFCN in the MA */ uint8_t hopping_mai(uint8_t hsn, uint32_t fn, uint8_t maio, uint8_t t1, uint8_t t2, uint8_t t3_) { uint8_t mai; if (hsn == 0) /* cyclic hopping */ mai = (fn + maio) % n; else { uint32_t m, m_, t_, s; m = t2 + rntable[(hsn xor (t1 % 64)) + t3]; m_ = m % (2^NBIN); t_ = t3 % (2^NBIN); if (m_ < n then) s = m_; else s = (m_ + t_) % n; mai = (s + maio) % n; } return mai; } openbsc-0.15.0/openbsc/doc/handover.txt000066400000000000000000000052421265565154000200070ustar00rootroot00000000000000Ideas about a handover algorithm ====================================================================== This is mostly based on the results presented in Chapter 8 of "Performance Enhancements in a Frequency Hopping GSM Network" by Thomas Toftegaard Nielsen and Joeroen Wigard. === Reasons for performing handover === Section 2.1.1: Handover used in their CAPACITY simulation: 1) Interference Handover Average RXLEV is satisfactory high, but average RXQUAL too low indicates interference to the channel. Handover should be made. 2) Bad Quality Averaged RXQUAL is lower than a threshold 3) Low Level / Signal Strength Average RXLEV is lower than a threshold 4) Distance Handover MS is too far away from a cell (measured by TA) 5) Power budget / Better Cell RX Level of neighbor cell is at least "HO Margin dB" dB better than the current serving cell. === Ideal parameters for HO algorithm === Chapter 8, Section 2.2, Table 24: Window RXLEV averaging: 10 SACCH frames (no weighting) Window RXQUAL averaging: 1 SACCH frame (no averaging) Level Threashold: 1 of the last 1 AV-RXLEV values < -110dBm Quality Threshold: 3 of the last 4 AV-RXQUAL values >= 5 Interference Threshold: 1 of the last AV-RXLEV > -85 dBm & 3 of the last 4 AV-RXQUAL values >= 5 Power Budget: Level of neighbor cell > 3 dB better Power Budget Interval: Every 6 SACCH frames (6 seconds ?!?) Distance Handover: Disabled Evaluation rule 1: RXLEV of the candidate cell a tleast -104 dBm Evaluation rule 2: Level of candidate cell > 3dB better own cell Timer Successful HO: 5 SACCH frames Timer Unsuccessful HO: 1 SACCH frame In a non-frequency hopping case, RXQUAL threshold can be decreased to RXLEV >= 4 When frequency hopping is enabled, the following additional parameters should be introduced: * No intra-cell handover * Use a HO Margin of 2dB === Handover Channel Reservation === In loaded network, each cell should reserve some channels for handovers, rather than using all of them for new call establishment. This reduces the need to drop calls due to failing handovers, at the expense of failing new call attempts. === Dynamic HO Margin === The handover margin (hysteresis) should depend on the RXQUAL. Optimal results were achieved with the following settings: * RXQUAL <= 4: 9 dB * RXQUAL == 5: 6 dB * RXQUAL >= 6: 1 dB == Actual Handover on a protocol level == After the BSC has decided a handover shall be done, it has to # allocate a channel at the new BTS # allocate a handover reference # activate the channel on the BTS side using RSL CHANNEL ACTIVATION, indicating the HO reference # BTS responds with CHAN ACT ACK, including GSM frame number # BSC sends 04.08 HO CMD to MS using old BTS openbsc-0.15.0/openbsc/doc/ipa-sccp.txt000066400000000000000000000077671265565154000177160ustar00rootroot00000000000000 IPA SCCP message flow in the BSC February, 2013 Holger Hans Peter Freyther CONTENTS 1. SCCP inside the IPA header 2. Supported SCCP message types 3. Receiving SCCP messages 4. Sending SCCP messages 1. SCCP inside the IPA header Many Soft-MSCs implement something that is called SCCP/lite. This means that SCCP messages are transported inside a small multiplexing protocol over TCP/IP. This is an alternative to a full SIGTRAN implementation. The multiplexing protocol is the same as used with the sysmoBTS and the ip.access nanoBTS. It is a three byte header with two bytes for the length in network byte order and one byte for the type. The type to be used for SCCP is 0xFD. struct ipa_header { uint16_t length_in_network_order; uint8_t type; } __attribute__((packed)); 2. Supported SCCP message types To implement GSM 08.08 only a subset of SCCP messages need to be implemented. For transporting paging and reset messages SCCP UDT messages are used. For the connections with a Mobile Station (MS) a SCCP connection is opened. This means that the SCCP CR, SCCP CC, SCCP CREF, SCCP RLC, SCCP RLSD, SCCP DT1 and SCCP IT messages are supported. 3. Receiving SCCP UDT messages This is an illustration of the flow of messages. The IPA multiplexing protocol is used for various protocols. This means there is a central place where the multiplexing stream terminates. The stream is terminated in the osmo_bsc_msc.c file and the ipaccess_a_fd_cb method. For SCCP messages the SCCP dispatching sccp_system_incoming method is called. This function is implemented in the libosmo-sccp library. To receive UDT messages osmo_bsc_sccp.c:osmo_bsc_sccp_init is using the sccp_set_read function to register a callback for UDT messages. The callback is msc_sccp_read and it is calling bsc_handle_udt that is implemented in the osmo_bsc_bssap.c. This function will handle the GSM 08.08 BSSAP messages. Currently only the reset acknowledge and the paging messages are handled. The BSC currently does not accept incoming SCCP messages and is only opening SCCP connections to the MSC. When opening a connection the callbacks for state changes (connection confirmed, released, release complete) are set and a routine for handling incoming data. This registration is done in the osmo_bsc_sccp.c file and the bsc_create_new_connection method. The name of the callback is msc_outgoing_sccp_data and this will call bsc_handle_dt1 that is implemented in the osmo_bsc_bssap.c file. This will forward the messages to the right Mobile Station (MS). 4. Sending SCCP messages There are three parts to sending that will be explained below. The first part is to send an entire SCCP frame (which includes the GSM 08.08 data) to the MSC. This is done by first registering the low level sending. sccp_system_init is called with the function that is responsible for sending a message. The msc_sccp_write_ipa will call the msc_queue_write function with the data and the right MSC connection. Below the msc_queue_write the IPA header will be prepended to the msg and then send to the MSC. The BSC supports multiple different A-link connections, the decision to pick the right MSC is done in this method. It is either done via the SCCP connection or the ctx pointer. When the BSC is starting a BSS RESET message will be sent to the MSC. The reset is created in osmo_bsc_msc.c:initialize_if_needed and sccp_write is called with the GSM 08.08 data and the connection to use. The libosmo-sccp library will embed it into a SCCP UDT message and call the msc_sccp_write_ipa method. When a new SCCP connection is to be created the bsc_create_new_connection in the osmo_bsc_sccp.c file. The sccp_connection_socket method will create the context for a SCCP connection. The state and data callback will be used to be notified about data and changes. Once the connection is configured the bsc_open_connection will be called that will ask the libosmo-sccp library to create a SCCP CR message using the sccp_connection_connect method. For active connections the sccp_connection_write method will be called. openbsc-0.15.0/openbsc/doc/oml-interface.txt000066400000000000000000000015741265565154000207320ustar00rootroot00000000000000oml 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 openbsc-0.15.0/openbsc/doc/osmocom-authn-protocol.txt000066400000000000000000000201741265565154000226320ustar00rootroot00000000000000 Osmocom Authentication Protocol (OAP) 1. General The Osmocom Authentication Protocol employs mutual authentication to register a client with a server over an IPA connection. Milenage is used as the authentication algorithm, where client and server have a shared secret. For example, an SGSN, as OAP client, may use its SGSN ID to register with a MAP proxy, an OAP server. 1.1. Connection The protocol expects that a reliable, ordered, packet boundaries preserving connection is used (e.g. IPA over TCP). 1.2. Using IPA By default, the following identifiers should be used: - IPA protocol: 0xee (OSMO) - IPA OSMO protocol extension: 0x06 (OAP) 2. Procedures Ideal communication sequence: Client Server | | | Register (ID) | |----------------------------------->| | | | Challenge (RAND+AUTN) | |<-----------------------------------| | | | Challenge Result (XRES) | |----------------------------------->| | | | Register Result | |<-----------------------------------| Variation "test setup": Client Server | | | Register (ID) | |----------------------------------->| | | | Register Result | |<-----------------------------------| Variation "invalid sequence nr": Client Server | | | Register (ID) | |----------------------------------->| | | | Challenge (RAND+AUTN) | |<-----------------------------------| | | | Sync Request (AUTS) | |----------------------------------->| | | | Challenge (RAND'+AUTN') | |<-----------------------------------| | | | Challenge Result (XRES) | |----------------------------------->| | | | Register Result | |<-----------------------------------| 2.1. Register The client sends a REGISTER_REQ message containing an identifier number. 2.2. Challenge The OAP server (optionally) sends back a CHALLENGE_REQ, containing random bytes and a milenage authentication token generated from these random bytes, using a shared secret, to authenticate itself to the OAP client. The server may omit this challenge entirely, based on its configuration, and immediately reply with a Register Result response. If the client cannot be registered (e.g. id is invalid), the server sends a REGISTER_ERR response. 2.3. Challenge Result When the client has received a Challenge, it may verify the server's authenticity and validity of the sequence number (included in AUTN), and, if valid, reply with a CHALLENGE_RES message. This shall contain an XRES authentication token generated by milenage from the same random bytes received from the server and the same shared secet. If the client decides to cancel the registration (e.g. invalid AUTN), it shall not reply to the CHALLENGE_REQ; a CHALLENGE_ERR message may be sent, but is not mandatory. For example, the client may directly start with a new REGISTER_REQ message. 2.4. Sync Request When the client has received a Challenge but sees an invalid sequence number (embedded in AUTN, according to the milenage algorithm), the client may send a SYNC_REQ message containing an AUTS synchronisation token. 2.5. Sync Result If the server has received a valid Sync Request, it shall answer by directly sending another Challenge (see 2.2.). If an invalid Sync Request is received, the server shall reply with a REGISTER_ERR message. 2.6. Register Result The server sends a REGISTER_RES message to indicate that registration has been successful. If the server cannot register the client (e.g. invalid challenge response), it shall send a REGISTER_ERR message. 3. Message Format 3.1. General Every message is based on the following message format IEI Info Element Type Pres. Format Length Message type 4.2.1 M V 1 The receiver shall be able to receive IEs in any order. Unknown IEs shall be ignored. 3.2.1. Register Request Client -> Server IEI Info Element Type Pres. Format Length Message type 4.2.1 M V 1 30 Client ID big endian int (2 oct) M TLV 4 3.2.2. Register Error Server -> Client IEI Info Element Type Pres. Format Length Message type 4.2.1 M V 1 02 Cause GMM cause, M TLV 3 04.08: 10.5.5.14 3.2.6. Register Result Server -> Client IEI Info Element Type Pres. Format Length Message type 4.2.1 M V 1 3.2.3. Challenge Server -> Client IEI Info Element Type Pres. Format Length Message type 4.2.1 M V 1 20 RAND octet string (16) M TLV 18 23 AUTN octet string (16) M TLV 18 3.2.4. Challenge Error Client -> Server IEI Info Element Type Pres. Format Length Message type 4.2.1 M V 1 02 Cause GMM cause, M TLV 3 04.08: 10.5.5.14 3.2.5. Challenge Result Client -> Server IEI Info Element Type Pres. Format Length Message type 4.2.1 M V 1 21 XRES octet string (8) M TLV 10 3.2.3. Sync Request Client -> Server IEI Info Element Type Pres. Format Length Message type 4.2.1 M V 1 20 AUTS octet string (16) M TLV 18 3.2.4. Sync Error Server -> Client IEI Info Element Type Pres. Format Length Message type 4.2.1 M V 1 02 Cause GMM cause, M TLV 3 04.08: 10.5.5.14 4. Information Elements 4.1. General [...] 4.2.1. Message Type +---------------------------------------------------+ | 8 7 6 5 4 3 2 1 | | | | 0 0 0 0 0 1 0 0 - Register Request | | 0 0 0 0 0 1 0 1 - Register Error | | 0 0 0 0 0 1 1 0 - Register Result | | | | 0 0 0 0 1 0 0 0 - Challenge Request | | 0 0 0 0 1 0 0 1 - Challenge Error | | 0 0 0 0 1 0 1 0 - Challenge Result | | | | 0 0 0 0 1 1 0 0 - Sync Request | | 0 0 0 0 1 1 0 1 - Sync Error (not used) | | 0 0 0 0 1 1 1 0 - Sync Result (not used) | | | +---------------------------------------------------+ 4.2.2. IE Identifier (informational) These are the standard values for the IEI. +---------------------------------------------------------+ | IEI Info Element Type | | | | 0x02 Cause GMM cause, 04.08: 10.5.5.14 | | 0x20 RAND octet string | | 0x23 AUTN octet string | | 0x24 XRES octet string | | 0x25 AUTS octet string | | 0x30 Client ID big endian int (2 octets) | +---------------------------------------------------------+ 4.2.3. Client ID 8 7 6 5 4 3 2 1 +-----------------------------------------------------+ | | Client ID IEI | octet 1 +-----------------------------------------------------+ | Length of Client ID IE contents (2) | octet 2 +-----------------------------------------------------+ | Client ID number, most significant byte | octet 3 +-----------------------------------------------------+ | Client ID number, least significant byte | octet 4 +-----------------------------------------------------+ The Client ID number shall be interpreted as an unsigned 16bit integer, where 0 indicates an invalid / unset ID. openbsc-0.15.0/openbsc/doc/paging.txt000066400000000000000000000035551265565154000174530ustar00rootroot00000000000000 GSM Paging implementation in OpenBSC == Code structure == The code is implemented in the libbsc/paging.c file. The external interface is documented/specified in the include/openbsc/paging.h header file. The code is used by the NITB and BSC application. == Implementation == Paging can be initiated in two ways. The standard way is to page by LAC. Each BTS has its own list/queue of outstanding paging operation. When a subscriber is paged one "struct paging_request" per BTS will be allocated and added to the tail of the list. The BTS is supposed to be configured to not repeat the paging. A paging_request will remain in the queue until a paging response or at the expiry of the T3113. Every 500 milliseconds a RSL paging command is send to the BTS. The 500 milliseconds is a throttling to not crash the ip.access nanoBTS. Once one paging_request has been handled it will be put at the end of the queue/list and the available slots for the BTS will be decreased. The available slots will be updated based on the paging load information element of the CCCH Load indication. If no paging slots are considered to be available and no load indication is sent a timer is started. The current timeout is 500 milliseconds and at the expiry of the timer the available slots will be set to 20. OpenBSC has the " paging free <-1-1024>" configuration option. In case there are less free channels than required no paging request will be sent to the BTS. Instead it will be attempted to send the paging request at the next timeout (500 milliseconds). == Limitation == The paging throughput could be higher but this has lead to crashes on the ip.access nanoBTS in the past. == Configuration == === ip.access nanoBTS === The current CCCH Load indication threshold is 10% and the period is 1 second. The code can be found inside the src/libbsc/bts_ipaccess_nanobts.c inside the nanobts_attr_bts array. openbsc-0.15.0/openbsc/doc/sgsn-remote-protocol.txt000066400000000000000000000407701265565154000223100ustar00rootroot00000000000000 GPRS Subscriber Update Protocol 1. General This document describes the remote protocol that is used by the SGSN to update and manage the local subscriber list. The protocol and the messages are designed after the corresponding MAP messages (see GSM 09.02) with the following differences: - The encoding uses TLV structures instead of ASN.1 encodings - Segmentation is not used See the specification of the Gr interface (GSM 03.60). 2. Connection The protocol expects that a reliable, ordered, packet boundaries preserving connection is used (e.g. IPA over TCP). The remote peer is either a service that understands the protocol natively or a wrapper service that maps the messages to/from real MAP messages that can be used to directly communicate with an HLR. 2.1. Using IPA By default, the following identifiers should be used: - IPA protocol: 0xee (OSMO) - IPA OSMO protocol extension: 0x05 2. Procedures 2.1. Authentication management The SGSN sends a SEND_AUTHENTICATION_INFO_REQ message containing the MS's IMSI to the peer. On errors, especially if authentication info is not availabe for that IMSI, the peer returns a SEND_AUTHENTICATION_INFO_ERR message. Otherwise the peer returns a SEND_AUTHENTICATION_INFO_RES message. If this message contains at least one authentication tuple, the SGSN replaces all tuples that are assigned to the subscriber. If the message doesn't contain any tuple the SGSN may reject the Attach Request. (see GSM 09.02, 25.5.6) 2.2. Location Updating The SGSN sends a UPDATE_LOCATION_REQ to the peer. If the request is denied by the network, the peer returns an UPDATE_LOCATION_ERR message to the SGSN. Otherwise the peer returns an UPDATE_LOCATION_RES message containing all information fields that shall be inserted into the subscriber record. If the 'PDP info complete' information element is set in the message, the SGSN clears existing PDP information fields in the subscriber record first. (see GSM 09.02, 19.1.1.8) [...] 3. Message Format 3.1. General Every message is based on the following message format IEI Info Element Type Pres. Format Length Message type 4.2.1 M V 1 01 IMSI 4.2.9 M TLV 2-10 If a numeric range is indicated in the 'presence' column, multiple information elements with the same tag may be used in sequence. The information elements shall be sent in the given order. Nevertheless after the generic part the receiver shall be able to received them in any order. Unknown IE shall be ignored. 3.2.1. Send Authentication Info Request SGSN -> Network peer IEI Info Element Type Pres. Format Length Message type 4.2.1 M V 1 01 IMSI 4.2.9 M TLV 2-10 3.2.2. Send Authentication Info Error Network peer -> SGSN IEI Info Element Type Pres. Format Length Message type 4.2.1 M V 1 01 IMSI 4.2.9 M TLV 2-10 02 Cause GMM cause, M TLV 3 04.08: 10.5.5.14 3.2.3. Send Authentication Info Response Network peer -> SGSN IEI Info Element Type Pres. Format Length Message type 4.2.1 M V 1 01 IMSI 4.2.9 M TLV 2-10 03 Auth tuple 4.2.5 0-5 TLV 36 3.2.4. Update Location Request SGSN -> Network peer IEI Info Element Type Pres. Format Length Message type 4.2.1 M V 1 01 IMSI 4.2.9 M TLV 2-10 3.2.5. Update Location Error Network peer -> SGSN IEI Info Element Type Pres. Format Length Message type 4.2.1 M V 1 01 IMSI 4.2.9 M TLV 2-10 02 Cause GMM cause, M TLV 3 04.08: 10.5.5.14 3.2.6. Update Location Result Network peer -> SGSN IEI Info Element Type Pres. Format Length Message type 4.2.1 M V 1 01 IMSI 4.2.9 M TLV 2-10 08 MSISDN 4.2.10 O TLV 0-9 09 HLR Number 4.2.12 O TLV 0-9 04 PDP info complete 4.2.8 O TLV 2 05 PDP info 4.2.3 1-10 TLV If the PDP info complete IE is present, the old PDP info list shall be cleared. 3.2.7. Location Cancellation Request Network peer -> SGSN IEI Info Element Type Pres. Format Length Message type 4.2.1 M V 1 01 IMSI 4.2.9 M TLV 2-10 06 Cancellation type 4.2.6 M TLV 3 3.2.8. Location Cancellation Result SGSN -> Network peer IEI Info Element Type Pres. Format Length Message type 4.2.1 M V 1 01 IMSI 4.2.9 M TLV 2-10 3.2.9. Purge MS Request SGSN -> Network peer IEI Info Element Type Pres. Format Length Message type 4.2.1 M V 1 01 IMSI 4.2.9 M TLV 2-10 09 HLR Number 4.2.12 M TLV 0-9 3.2.10. Purge MS Error Network peer -> SGSN IEI Info Element Type Pres. Format Length Message type 4.2.1 M V 1 01 IMSI 4.2.9 M TLV 2-10 02 Cause GMM cause, M TLV 3 04.08: 10.5.5.14 3.2.11. Purge MS Result Network peer -> SGSN IEI Info Element Type Pres. Format Length Message type 4.2.1 M V 1 01 IMSI 4.2.9 M TLV 2-10 07 Freeze P-TMSI 4.2.8 O TLV 2 3.2.12. Insert Subscriber Data Request Network peer -> SGSN IEI Info Element Type Pres. Format Length Message type 4.2.1 M V 1 01 IMSI 4.2.9 M TLV 2-10 04 PDP info complete 4.2.8 O TLV 2 05 PDP info 4.2.3 0-10 TLV If the PDP info complete IE is present, the old PDP info list shall be cleared. 3.2.13. Insert Subscriber Data Error SGSN -> Network peer IEI Info Element Type Pres. Format Length Message type 4.2.1 M V 1 01 IMSI 4.2.9 M TLV 2-10 02 Cause GMM cause, M TLV 3 04.08: 10.5.5.14 3.2.14. Insert Subscriber Data Result SGSN -> Network peer IEI Info Element Type Pres. Format Length Message type 4.2.1 M V 1 01 IMSI 4.2.9 M TLV 2-10 3.2.15. Delete Subscriber Data Request Network peer -> SGSN IEI Info Element Type Pres. Format Length Message type 4.2.1 M V 1 01 IMSI 4.2.9 M TLV 2-10 10 PDP context id 4.2.3 0-10 TLV (no conditional IE) 3.2.16. Delete Subscriber Data Error SGSN -> Network peer IEI Info Element Type Pres. Format Length Message type 4.2.1 M V 1 01 IMSI 4.2.9 M TLV 2-10 02 Cause GMM cause, M TLV 3 04.08: 10.5.5.14 3.2.17. Delete Subscriber Data Result Network peer -> SGSN IEI Info Element Type Pres. Format Length Message type 4.2.1 M V 1 01 IMSI 4.2.9 M TLV 2-10 4. Information Elements 4.1. General [...] 4.2.1. Message Type +---------------------------------------------------+ | 8 7 6 5 4 3 2 1 | | | | 0 0 0 0 0 1 0 0 - Update Location Request | | 0 0 0 0 0 1 0 1 - Update Location Error | | 0 0 0 0 0 1 1 0 - Update Location Result | | | | 0 0 0 0 1 0 0 0 - Send Auth Info Request | | 0 0 0 0 1 0 0 1 - Send Auth Info Error | | 0 0 0 0 1 0 1 0 - Send Auth Info Result | | | | 0 0 0 0 1 1 0 0 - Purge MS Request | | 0 0 0 0 1 1 0 1 - Purge MS Error | | 0 0 0 0 1 1 1 0 - Purge MS Result | | | | 0 0 0 1 0 0 0 0 - Insert Subscr. Data Request | | 0 0 0 1 0 0 0 1 - Insert Subscr. Data Error | | 0 0 0 1 0 0 1 0 - Insert Subscr. Data Result | | | | 0 0 0 1 0 1 0 0 - Delete Subscr. Data Request | | 0 0 0 1 0 1 0 1 - Delete Subscr. Data Error | | 0 0 0 1 0 1 1 0 - Delete Subscr. Data Result | | | | 0 0 0 1 1 1 0 0 - Location Cancellation Request | | 0 0 0 1 1 1 0 1 - Location Cancellation Error | | 0 0 0 1 1 1 1 0 - Location Cancellation Result | | | +---------------------------------------------------+ 4.2.2. IP Address The value part is encoded like in the Packet data protocol address IE defined in GSM 04.08, 10.5.6.4. PDP type organization must be set to 'IETF allocated address'. 4.2.3. PDP Info This is a container for information elements describing a single PDP. IEI Info Element Type Pres. Format Length PDP Info IEI M V 1 Length of PDP Info IE length, no ext M V 1 10 PDP context id big endian int, 1-N C TLV 3 11 PDP type 4.2.4 C TLV 4 12 Access point name 04.08, 10.5.6.1 C TLV 3-102 13 Quality of Service 4.2.11 O TLV 1-20 The conditional IE are mandantory unless mentioned otherwise. 4.2.4. PDP Type The PDP type value consists of 2 octets that are encoded like octet 4-5 of the End User Address defined in GSM 09.60, 7.9.18. 8 7 6 5 4 3 2 1 +-----------------------------------------------------+ | | PDP type IEI | octet 1 +-----------------------------------------------------+ | Length of PDP type IE contents (2) | octet 2 +-----------------------------------------------------+ | Spare | PDP type org. | octet 3 +-----------------------------------------------------+ | PDP type number | octet 4 +-----------------------------------------------------+ The spare bits are left undefined. While 09.60 defines them as '1 1 1 1', there are MAP traces where these bits are set to '0 0 0 0'. So the receiver shall ignore these bits. Examples: IPv4: PDP type org: 1 (IETF), PDP type number: 0x21 IPv6: PDP type org: 1 (IETF), PDP type number: 0x57 4.2.5. Auth tuple This is a container for information elements describing a single authentication tuple. IEI Info Element Type Pres. Format Length Auth. Tuple IEI M V 1 Length of Ath Tuple IE length, no ext M V 1 20 RAND octet string (16) M TLV 18 21 SRES octet string (4) M TLV 6 22 Kc octet string (8) M TLV 10 4.2.6. Cancellation Type 8 7 6 5 4 3 2 1 +-----------------------------------------------------+ | | Cancellation type IEI | octet 1 +-----------------------------------------------------+ | Length of Cancellation Type IE contents (1) | octet 2 +-----------------------------------------------------+ | Cancellation type number | octet 4 +-----------------------------------------------------+ Where the cancellation type number is: +---------------------------------------------------+ | 8 7 6 5 4 3 2 1 | | | | 0 0 0 0 0 0 0 0 - Update Procedure | | 0 0 0 0 0 0 0 1 - Subscription Withdraw | +---------------------------------------------------+ 4.2.7. IE Identifier (informational) These are the standard values for the IEI. See the message definitions for the IEI that shall be used for the encoding. +---------------------------------------------------------+ | IEI Info Element Type | | | | 0x01 IMSI Mobile identity, 04.08: 10.5.1.4 | | 0x02 Cause GMM cause, 04.08: 10.5.5.14 | | 0x03 Auth tuple 4.2.5 | | 0x04 PDP info compl 4.2.8 | | 0x05 PDP info 4.2.3 | | 0x06 Cancel type 4.2.6 | | 0x07 Freeze P-TMSI 4.2.8 | | 0x08 MSISDN ISDN-AddressString/octet, 4.2.10 | | 0x09 HLR Number 4.2.12 | | 0x10 PDP context id big endian int | | 0x11 PDP type 4.2.4 | | 0x12 APN 04.08, 10.5.6.1 | | 0x13 QoS 4.2.11 | | 0x20 RAND octet string | | 0x21 SRES octet string | | 0x22 Kc octet string | +---------------------------------------------------------+ 4.2.8 Empty field This is used for flags, if and only if this IE is present, the flag is set. The semantics depend on the IEI and the context. 8 7 6 5 4 3 2 1 +-----------------------------------------------------+ | | IEI | octet 1 +-----------------------------------------------------+ | Length of IE contents (0) | octet 2 +-----------------------------------------------------+ 4.2.9. IMSI The IMSI is encoded like in octet 4-N of the Called Party BCD Number defined in GSM 04.08, 10.5.4.7. 8 7 6 5 4 3 2 1 +-----------------------------------------------------+ | | IMSI IEI | octet 1 +-----------------------------------------------------+ | Length of IMSI IE contents | octet 2 +-----------------------------------------------------+ | Number digit 2 | Number digit 1 | octet 3 +-----------------------------------------------------+ | Number digit 4 | Number digit 3 | octet 4 +-----------------------------------------------------+ : : : +-----------------------------------------------------+ | see note 1) | octet 2+(N+1)div2 +-----------------------------------------------------+ Note 1) Either '1 1 1 1 | Number digit N' (N odd) or 'Number digit N | Number digit N-1' (N even), where N is the number of digits. 4.2.10. ISDN-AddressString / MSISDN / Called Party BCD Number The MSISDN is encoded as an ISDN-AddressString in GSM 09.02 and Called Party BCD Number in GSM 04.08. It will be stored by the SGSN and then passed as is to the GGSN during the activation of the primary PDP Context. 8 7 6 5 4 3 2 1 +-----------------------------------------------------+ | | IEI | octet 1 +-----------------------------------------------------+ | Length of IE contents | octet 2 +-----------------------------------------------------+ | ext | Type of num | Numbering plan | octet 2 +-----------------------------------------------------+ | Number digit 2 | Number digit 1 | octet 3 +-----------------------------------------------------+ | Number digit 4 | Number digit 3 | octet 4 +-----------------------------------------------------+ : : : +-----------------------------------------------------+ 4.2.11 Quality of Service Subscribed Service This encodes the subscribed QoS of a subscriber. It will be used by the SGSN during the PDP Context activation. If the length of the QoS data is 3 (three) octets it is assumed that these are octets 3-5 of the TS 3GPP TS 24.008 Quality of Service Octets. If it is more than three then then it is assumed that the first octet is the Allocation/Retention Priority and the reset are encoded as octets 3-N of 24.008. 8 7 6 5 4 3 2 1 +-----------------------------------------------------+ | | IEI | octet 1 +-----------------------------------------------------+ | Length of IE contents | octet 2 +-----------------------------------------------------+ : : : +-----------------------------------------------------+ 4.2.12. HLR Number encoded as GSM 09.02 ISDN-AddressString The HLR Number is encoded as an ISDN-AddressString in GSM 09.02. It will be stored by the SGSN can be used by the CDR module to keep a record. 8 7 6 5 4 3 2 1 +-----------------------------------------------------+ | | IEI | octet 1 +-----------------------------------------------------+ | Length of IE contents | octet 2 +-----------------------------------------------------+ | ext | Type of num | Numbering plan | octet 2 +-----------------------------------------------------+ | Number digit 2 | Number digit 1 | octet 3 +-----------------------------------------------------+ | Number digit 4 | Number digit 3 | octet 4 +-----------------------------------------------------+ : : : +-----------------------------------------------------+ openbsc-0.15.0/openbsc/git-version-gen000077500000000000000000000125241265565154000176370ustar00rootroot00000000000000#!/bin/sh # Print a version string. scriptversion=2010-01-28.01 # Copyright (C) 2007-2010 Free Software Foundation, Inc. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # This script is derived from GIT-VERSION-GEN from GIT: http://git.or.cz/. # It may be run two ways: # - from a git repository in which the "git describe" command below # produces useful output (thus requiring at least one signed tag) # - from a non-git-repo directory containing a .tarball-version file, which # presumes this script is invoked like "./git-version-gen .tarball-version". # In order to use intra-version strings in your project, you will need two # separate generated version string files: # # .tarball-version - present only in a distribution tarball, and not in # a checked-out repository. Created with contents that were learned at # the last time autoconf was run, and used by git-version-gen. Must not # be present in either $(srcdir) or $(builddir) for git-version-gen to # give accurate answers during normal development with a checked out tree, # but must be present in a tarball when there is no version control system. # Therefore, it cannot be used in any dependencies. GNUmakefile has # hooks to force a reconfigure at distribution time to get the value # correct, without penalizing normal development with extra reconfigures. # # .version - present in a checked-out repository and in a distribution # tarball. Usable in dependencies, particularly for files that don't # want to depend on config.h but do want to track version changes. # Delete this file prior to any autoconf run where you want to rebuild # files to pick up a version string change; and leave it stale to # minimize rebuild time after unrelated changes to configure sources. # # It is probably wise to add these two files to .gitignore, so that you # don't accidentally commit either generated file. # # Use the following line in your configure.ac, so that $(VERSION) will # automatically be up-to-date each time configure is run (and note that # since configure.ac no longer includes a version string, Makefile rules # should not depend on configure.ac for version updates). # # AC_INIT([GNU project], # m4_esyscmd([build-aux/git-version-gen .tarball-version]), # [bug-project@example]) # # Then use the following lines in your Makefile.am, so that .version # will be present for dependencies, and so that .tarball-version will # exist in distribution tarballs. # # BUILT_SOURCES = $(top_srcdir)/.version # $(top_srcdir)/.version: # echo $(VERSION) > $@-t && mv $@-t $@ # dist-hook: # echo $(VERSION) > $(distdir)/.tarball-version case $# in 1) ;; *) echo 1>&2 "Usage: $0 \$srcdir/.tarball-version"; exit 1;; esac tarball_version_file=$1 nl=' ' # First see if there is a tarball-only version file. # then try "git describe", then default. if test -f $tarball_version_file then v=`cat $tarball_version_file` || exit 1 case $v in *$nl*) v= ;; # reject multi-line output [0-9]*) ;; *) v= ;; esac test -z "$v" \ && echo "$0: WARNING: $tarball_version_file seems to be damaged" 1>&2 fi if test -n "$v" then : # use $v elif test -d ./../.git \ && v=`git describe --abbrev=4 --match='v*' HEAD 2>/dev/null \ || git describe --abbrev=4 HEAD 2>/dev/null` \ && case $v in [0-9]*) ;; v[0-9]*) ;; *) (exit 1) ;; esac then # Is this a new git that lists number of commits since the last # tag or the previous older version that did not? # Newer: v6.10-77-g0f8faeb # Older: v6.10-g0f8faeb case $v in *-*-*) : git describe is okay three part flavor ;; *-*) : git describe is older two part flavor # Recreate the number of commits and rewrite such that the # result is the same as if we were using the newer version # of git describe. vtag=`echo "$v" | sed 's/-.*//'` numcommits=`git rev-list "$vtag"..HEAD | wc -l` v=`echo "$v" | sed "s/\(.*\)-\(.*\)/\1-$numcommits-\2/"`; ;; esac # Change the first '-' to a '.', so version-comparing tools work properly. # Remove the "g" in git describe's output string, to save a byte. v=`echo "$v" | sed 's/-/./;s/\(.*\)-g/\1-/'`; else v=UNKNOWN fi v=`echo "$v" |sed 's/^v//'` # Don't declare a version "dirty" merely because a time stamp has changed. git status > /dev/null 2>&1 dirty=`sh -c 'git diff-index --name-only HEAD' 2>/dev/null` || dirty= case "$dirty" in '') ;; *) # Append the suffix only if there isn't one already. case $v in *-dirty) ;; *) v="$v-dirty" ;; esac ;; esac # Omit the trailing newline, so that m4_esyscmd can use the result directly. echo "$v" | tr -d '\012' # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-end: "$" # End: openbsc-0.15.0/openbsc/include/000077500000000000000000000000001265565154000163135ustar00rootroot00000000000000openbsc-0.15.0/openbsc/include/Makefile.am000066400000000000000000000000771265565154000203530ustar00rootroot00000000000000SUBDIRS = openbsc noinst_HEADERS = mISDNif.h compat_af_isdn.h openbsc-0.15.0/openbsc/include/compat_af_isdn.h000066400000000000000000000010331265565154000214270ustar00rootroot00000000000000#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 openbsc-0.15.0/openbsc/include/mISDNif.h000066400000000000000000000232701265565154000177210ustar00rootroot00000000000000/* * * Author Karsten Keil * * Copyright 2008 by Karsten Keil * * 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 #ifdef linux #include #include #include #else #include #include #include #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 */ openbsc-0.15.0/openbsc/include/openbsc/000077500000000000000000000000001265565154000177445ustar00rootroot00000000000000openbsc-0.15.0/openbsc/include/openbsc/Makefile.am000066400000000000000000000020431265565154000217770ustar00rootroot00000000000000noinst_HEADERS = abis_nm.h abis_rsl.h db.h gsm_04_08.h gsm_data.h \ gsm_subscriber.h gsm_04_11.h debug.h signal.h \ misdn.h chan_alloc.h paging.h ctrl.h \ trau_mux.h rs232.h openbscdefines.h rtp_proxy.h \ bsc_rll.h mncc.h transaction.h ussd.h gsm_04_80.h \ silent_call.h mgcp.h meas_rep.h rest_octets.h \ system_information.h handover.h mgcp_internal.h \ vty.h socket.h e1_config.h trau_upqueue.h token_auth.h \ handover_decision.h rrlp.h \ crc24.h gprs_llc.h gprs_gmm.h \ gb_proxy.h gprs_sgsn.h gsm_04_08_gprs.h sgsn.h \ auth.h osmo_msc.h bsc_msc.h bsc_nat.h \ osmo_bsc_rf.h osmo_bsc.h network_listen.h bsc_nat_sccp.h \ osmo_msc_data.h osmo_bsc_grace.h sms_queue.h abis_om2000.h \ bss.h gsm_data_shared.h ipaccess.h mncc_int.h \ arfcn_range_encode.h nat_rewrite_trie.h bsc_nat_callstats.h \ osmux.h mgcp_transcode.h gprs_utils.h \ gprs_gb_parse.h smpp.h meas_feed.h gprs_gsup_messages.h \ gprs_gsup_client.h bsc_msg_filter.h openbsc_HEADERS = gsm_04_08.h meas_rep.h bsc_api.h openbscdir = $(includedir)/openbsc openbsc-0.15.0/openbsc/include/openbsc/abis_nm.h000066400000000000000000000166141265565154000215350ustar00rootroot00000000000000/* GSM Network Management messages on the A-bis interface * 3GPP TS 12.21 version 8.0.0 Release 1999 / ETSI TS 100 623 V8.0.0 */ /* (C) 2008-2009 by Harald Welte * 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 . * */ #ifndef _NM_H #define _NM_H #include #include #include struct cell_global_id { uint16_t mcc; uint16_t mnc; uint16_t lac; uint16_t ci; }; /* The BCCH info from an ip.access test, in host byte order * and already parsed... */ struct ipac_bcch_info { struct llist_head list; uint16_t info_type; uint8_t freq_qual; uint16_t arfcn; uint8_t rx_lev; uint8_t rx_qual; int16_t freq_err; uint16_t frame_offset; uint32_t frame_nr_offset; uint8_t bsic; struct cell_global_id cgi; uint8_t ba_list_si2[16]; uint8_t ba_list_si2bis[16]; uint8_t ba_list_si2ter[16]; uint8_t ca_list_si1[16]; }; /* PUBLIC */ struct msgb; struct abis_nm_cfg { /* callback for unidirectional reports */ int (*report_cb)(struct msgb *, struct abis_om_fom_hdr *); /* callback for software activate requests from BTS */ int (*sw_act_req)(struct msgb *); }; struct abis_nm_sw_descr { /* where does it start? how long is it? */ const uint8_t *start; size_t len; /* the parsed data */ const uint8_t *file_id; uint16_t file_id_len; const uint8_t *file_ver; uint16_t file_ver_len; }; extern int abis_nm_rcvmsg(struct msgb *msg); int abis_nm_tlv_parse(struct tlv_parsed *tp, struct gsm_bts *bts, const uint8_t *buf, int len); int abis_nm_rx(struct msgb *msg); int abis_nm_opstart(struct gsm_bts *bts, uint8_t obj_class, uint8_t i0, uint8_t i1, uint8_t i2); int abis_nm_chg_adm_state(struct gsm_bts *bts, uint8_t obj_class, uint8_t i0, uint8_t i1, uint8_t i2, enum abis_nm_adm_state adm_state); int abis_nm_establish_tei(struct gsm_bts *bts, uint8_t trx_nr, uint8_t e1_port, uint8_t e1_timeslot, uint8_t e1_subslot, uint8_t tei); int abis_nm_conn_terr_sign(struct gsm_bts_trx *trx, uint8_t e1_port, uint8_t e1_timeslot, uint8_t e1_subslot); int abis_nm_conn_terr_traf(struct gsm_bts_trx_ts *ts, uint8_t e1_port, uint8_t e1_timeslot, uint8_t e1_subslot); int abis_nm_get_attr(struct gsm_bts *bts, uint8_t obj_class, uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr, uint8_t *attr, uint8_t attr_len); int abis_nm_set_bts_attr(struct gsm_bts *bts, uint8_t *attr, int attr_len); int abis_nm_set_radio_attr(struct gsm_bts_trx *trx, uint8_t *attr, int attr_len); int abis_nm_set_channel_attr(struct gsm_bts_trx_ts *ts, uint8_t chan_comb); int abis_nm_sw_act_req_ack(struct gsm_bts *bts, uint8_t obj_class, uint8_t i1, uint8_t i2, uint8_t i3, int nack, uint8_t *attr, int att_len); int abis_nm_raw_msg(struct gsm_bts *bts, int len, uint8_t *msg); int abis_nm_event_reports(struct gsm_bts *bts, int on); int abis_nm_reset_resource(struct gsm_bts *bts); int abis_nm_software_load(struct gsm_bts *bts, int trx_nr, const char *fname, uint8_t win_size, int forced, gsm_cbfn *cbfn, void *cb_data); int abis_nm_software_load_status(struct gsm_bts *bts); int abis_nm_software_activate(struct gsm_bts *bts, const char *fname, gsm_cbfn *cbfn, void *cb_data); int abis_nm_conn_mdrop_link(struct gsm_bts *bts, uint8_t e1_port0, uint8_t ts0, uint8_t e1_port1, uint8_t ts1); int abis_nm_perform_test(struct gsm_bts *bts, uint8_t obj_class, uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr, uint8_t test_nr, uint8_t auton_report, struct msgb *msg); /* Siemens / BS-11 specific */ int abis_nm_bs11_reset_resource(struct gsm_bts *bts); int abis_nm_bs11_db_transmission(struct gsm_bts *bts, int begin); int abis_nm_bs11_create_object(struct gsm_bts *bts, enum abis_bs11_objtype type, uint8_t idx, uint8_t attr_len, const uint8_t *attr); int abis_nm_bs11_create_envaBTSE(struct gsm_bts *bts, uint8_t idx); int abis_nm_bs11_create_bport(struct gsm_bts *bts, uint8_t idx); int abis_nm_bs11_delete_object(struct gsm_bts *bts, enum abis_bs11_objtype type, uint8_t idx); int abis_nm_bs11_delete_bport(struct gsm_bts *bts, uint8_t idx); int abis_nm_bs11_conn_oml_tei(struct gsm_bts *bts, uint8_t e1_port, uint8_t e1_timeslot, uint8_t e1_subslot, uint8_t tei); int abis_nm_bs11_get_oml_tei_ts(struct gsm_bts *bts); int abis_nm_bs11_get_serno(struct gsm_bts *bts); int abis_nm_bs11_set_trx_power(struct gsm_bts_trx *trx, uint8_t level); int abis_nm_bs11_get_trx_power(struct gsm_bts_trx *trx); int abis_nm_bs11_logon(struct gsm_bts *bts, uint8_t level, const char *name, int on); int abis_nm_bs11_factory_logon(struct gsm_bts *bts, int on); int abis_nm_bs11_infield_logon(struct gsm_bts *bts, int on); int abis_nm_bs11_set_trx1_pw(struct gsm_bts *bts, const char *password); int abis_nm_bs11_set_pll_locked(struct gsm_bts *bts, int locked); int abis_nm_bs11_get_pll_mode(struct gsm_bts *bts); int abis_nm_bs11_set_pll(struct gsm_bts *bts, int value); int abis_nm_bs11_get_cclk(struct gsm_bts *bts); int abis_nm_bs11_get_state(struct gsm_bts *bts); int abis_nm_bs11_load_swl(struct gsm_bts *bts, const char *fname, uint8_t win_size, int forced, gsm_cbfn *cbfn); int abis_nm_bs11_set_ext_time(struct gsm_bts *bts); int abis_nm_bs11_get_bport_line_cfg(struct gsm_bts *bts, uint8_t bport); int abis_nm_bs11_set_bport_line_cfg(struct gsm_bts *bts, uint8_t bport, enum abis_bs11_line_cfg line_cfg); int abis_nm_bs11_bsc_disconnect(struct gsm_bts *bts, int reconnect); int abis_nm_bs11_restart(struct gsm_bts *bts); /* ip.access nanoBTS specific commands */ int abis_nm_ipaccess_msg(struct gsm_bts *bts, uint8_t msg_type, uint8_t obj_class, uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr, uint8_t *attr, int attr_len); int abis_nm_ipaccess_set_nvattr(struct gsm_bts_trx *trx, uint8_t *attr, int attr_len); int abis_nm_ipaccess_restart(struct gsm_bts_trx *trx); int abis_nm_ipaccess_set_attr(struct gsm_bts *bts, uint8_t obj_class, uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr, uint8_t *attr, uint8_t attr_len); int abis_nm_ipaccess_rsl_connect(struct gsm_bts_trx *trx, uint32_t ip, uint16_t port, uint8_t stream); void abis_nm_ipaccess_cgi(uint8_t *buf, struct gsm_bts *bts); int ipac_parse_bcch_info(struct ipac_bcch_info *binf, uint8_t *buf); const char *ipacc_testres_name(uint8_t res); /* Functions calling into other code parts */ int nm_is_running(struct gsm_nm_state *s); int abis_nm_vty_init(void); void abis_nm_clear_queue(struct gsm_bts *bts); int _abis_nm_sendmsg(struct msgb *msg); void abis_nm_queue_send_next(struct gsm_bts *bts); /* for bs11_config. */ int abis_nm_parse_sw_config(const uint8_t *data, const size_t len, struct abis_nm_sw_descr *res, const int res_len); int abis_nm_select_newest_sw(const struct abis_nm_sw_descr *sw, const size_t len); /* Helper functions for updating attributes */ int abis_nm_update_max_power_red(struct gsm_bts_trx *trx); #endif /* _NM_H */ openbsc-0.15.0/openbsc/include/openbsc/abis_om2000.h000066400000000000000000000062611265565154000220350ustar00rootroot00000000000000#ifndef OPENBSC_ABIS_OM2K_H #define OPENBSC_ABIS_OM2K_H /* Ericsson RBS 2xxx GSM O&M (OM2000) messages on the A-bis interface * implemented based on protocol trace analysis, no formal documentation */ /* (C) 2010-2011 by Harald Welte * * 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 . * */ enum abis_om2k_mo_cls { OM2K_MO_CLS_TRXC = 0x01, OM2K_MO_CLS_TS = 0x03, OM2K_MO_CLS_TF = 0x04, OM2K_MO_CLS_IS = 0x05, OM2K_MO_CLS_CON = 0x06, OM2K_MO_CLS_DP = 0x07, OM2K_MO_CLS_CF = 0x0a, OM2K_MO_CLS_TX = 0x0b, OM2K_MO_CLS_RX = 0x0c, }; enum om2k_mo_state { OM2K_MO_S_RESET = 0, OM2K_MO_S_STARTED, OM2K_MO_S_ENABLED, OM2K_MO_S_DISABLED, }; struct abis_om2k_mo { uint8_t class; uint8_t bts; uint8_t assoc_so; uint8_t inst; } __attribute__ ((packed)); /* on-wire format for IS conn group */ struct om2k_is_conn_grp { uint16_t icp1; uint16_t icp2; uint8_t cont_idx; } __attribute__ ((packed)); /* internal data formant for IS conn group */ struct is_conn_group { struct llist_head list; uint16_t icp1; uint16_t icp2; uint8_t ci; }; extern const struct abis_om2k_mo om2k_mo_cf; extern const struct abis_om2k_mo om2k_mo_is; extern const struct abis_om2k_mo om2k_mo_con; extern const struct abis_om2k_mo om2k_mo_tf; extern const struct value_string om2k_mo_class_short_vals[]; int abis_om2k_rcvmsg(struct msgb *msg); extern const struct abis_om2k_mo om2k_mo_cf; int abis_om2k_tx_reset_cmd(struct gsm_bts *bts, const struct abis_om2k_mo *mo); int abis_om2k_tx_start_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo); int abis_om2k_tx_status_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo); int abis_om2k_tx_connect_cmd(struct gsm_bts *bts, const struct abis_om2k_mo *mo); int abis_om2k_tx_disconnect_cmd(struct gsm_bts *bts, const struct abis_om2k_mo *mo); int abis_om2k_tx_enable_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo); int abis_om2k_tx_disable_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo); int abis_om2k_tx_test_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo); int abis_om2k_tx_op_info(struct gsm_bts *bts, const struct abis_om2k_mo *mo, uint8_t operational); int abis_om2k_tx_is_conf_req(struct gsm_bts *bts); int abis_om2k_tx_tf_conf_req(struct gsm_bts *bts); int abis_om2k_tx_rx_conf_req(struct gsm_bts_trx *trx); int abis_om2k_tx_tx_conf_req(struct gsm_bts_trx *trx); int abis_om2k_tx_ts_conf_req(struct gsm_bts_trx_ts *ts); int abis_om2k_vty_init(void); struct vty; void abis_om2k_config_write_bts(struct vty *vty, struct gsm_bts *bts); #endif /* OPENBCS_ABIS_OM2K_H */ openbsc-0.15.0/openbsc/include/openbsc/abis_rsl.h000066400000000000000000000076021265565154000217200ustar00rootroot00000000000000/* GSM Radio Signalling Link messages on the A-bis interface * 3GPP TS 08.58 version 8.6.0 Release 1999 / ETSI TS 100 596 V8.6.0 */ /* (C) 2008 by Harald Welte * 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 . * */ #ifndef _RSL_H #define _RSL_H #include #include struct gsm_bts; struct gsm_lchan; struct gsm_subscriber; struct gsm_bts_trx_ts; int rsl_bcch_info(struct gsm_bts_trx *trx, uint8_t type, const uint8_t *data, int len); int rsl_sacch_filling(struct gsm_bts_trx *trx, uint8_t type, const uint8_t *data, int len); int rsl_chan_activate(struct gsm_bts_trx *trx, uint8_t chan_nr, uint8_t act_type, struct rsl_ie_chan_mode *chan_mode, struct rsl_ie_chan_ident *chan_ident, uint8_t bs_power, uint8_t ms_power, uint8_t ta); int rsl_chan_activate_lchan(struct gsm_lchan *lchan, uint8_t act_type, uint8_t ho_ref); int rsl_chan_mode_modify_req(struct gsm_lchan *ts); int rsl_encryption_cmd(struct msgb *msg); int rsl_paging_cmd(struct gsm_bts *bts, uint8_t paging_group, uint8_t len, uint8_t *ms_ident, uint8_t chan_needed); int rsl_imm_assign_cmd(struct gsm_bts *bts, uint8_t len, uint8_t *val); int rsl_data_request(struct msgb *msg, uint8_t link_id); int rsl_establish_request(struct gsm_lchan *lchan, uint8_t link_id); int rsl_relase_request(struct gsm_lchan *lchan, uint8_t link_id); /* Siemens vendor-specific RSL extensions */ int rsl_siemens_mrpci(struct gsm_lchan *lchan, struct rsl_mrpci *mrpci); /* ip.access specfic RSL extensions */ int rsl_ipacc_crcx(struct gsm_lchan *lchan); int rsl_ipacc_mdcx(struct gsm_lchan *lchan, uint32_t ip, uint16_t port, uint8_t rtp_payload2); int rsl_ipacc_mdcx_to_rtpsock(struct gsm_lchan *lchan); int rsl_ipacc_pdch_activate(struct gsm_bts_trx_ts *ts, int act); int abis_rsl_rcvmsg(struct msgb *msg); uint64_t str_to_imsi(const char *imsi_str); int rsl_release_request(struct gsm_lchan *lchan, uint8_t link_id, enum rsl_rel_mode release_mode); int rsl_lchan_set_state(struct gsm_lchan *lchan, int); int rsl_lchan_mark_broken(struct gsm_lchan *lchan, const char *broken); /* to be provided by external code */ int rsl_deact_sacch(struct gsm_lchan *lchan); /* BCCH related code */ int rsl_ccch_conf_to_bs_cc_chans(int ccch_conf); int rsl_ccch_conf_to_bs_ccch_sdcch_comb(int ccch_conf); int rsl_sacch_info_modify(struct gsm_lchan *lchan, uint8_t type, const uint8_t *data, int len); int rsl_chan_bs_power_ctrl(struct gsm_lchan *lchan, unsigned int fpc, int db); int rsl_chan_ms_power_ctrl(struct gsm_lchan *lchan, unsigned int fpc, int dbm); /* SMSCB functionality */ int rsl_sms_cb_command(struct gsm_bts *bts, uint8_t chan_number, struct rsl_ie_cb_cmd_type cb_command, const uint8_t *data, int len); /* some Nokia specific stuff */ int rsl_nokia_si_begin(struct gsm_bts_trx *trx); int rsl_nokia_si_end(struct gsm_bts_trx *trx); /* required for Nokia BTS power control */ int rsl_bs_power_control(struct gsm_bts_trx *trx, uint8_t channel, uint8_t reduction); int rsl_release_sapis_from(struct gsm_lchan *lchan, int start, enum rsl_rel_mode release_mode); int rsl_start_t3109(struct gsm_lchan *lchan); int rsl_direct_rf_release(struct gsm_lchan *lchan); #endif /* RSL_MT_H */ openbsc-0.15.0/openbsc/include/openbsc/arfcn_range_encode.h000066400000000000000000000014771265565154000237100ustar00rootroot00000000000000#ifndef ARFCN_RANGE_ENCODE_H #define ARFCN_RANGE_ENCODE_H #include enum { ARFCN_RANGE_INVALID = -1, ARFCN_RANGE_128 = 127, ARFCN_RANGE_256 = 255, ARFCN_RANGE_512 = 511, ARFCN_RANGE_1024 = 1023, }; #define RANGE_ENC_MAX_ARFCNS 29 int range_enc_determine_range(const int *arfcns, int size, int *f0_out); int range_enc_arfcns(const int rng, const int *arfcns, int sze, int *out, int idx); int range_enc_find_index(const int rng, const int *arfcns, int size); int range_enc_filter_arfcns(int *arfcns, const int sze, const int f0, int *f0_included); int range_enc_range128(uint8_t *chan_list, int f0, int *w); int range_enc_range256(uint8_t *chan_list, int f0, int *w); int range_enc_range512(uint8_t *chan_list, int f0, int *w); int range_enc_range1024(uint8_t *chan_list, int f0, int f0_incl, int *w); #endif openbsc-0.15.0/openbsc/include/openbsc/auth.h000066400000000000000000000007421265565154000210610ustar00rootroot00000000000000#ifndef _AUTH_H #define _AUTH_H struct gsm_auth_tuple; struct gsm_subscriber; enum auth_action { AUTH_NOT_AVAIL = 0, /* No auth tuple available */ AUTH_DO_AUTH_THAN_CIPH = 1, /* Firsth authenticate, then cipher */ AUTH_DO_CIPH = 2, /* Only ciphering */ AUTH_DO_AUTH = 3, /* Only authentication, no ciphering */ }; int auth_get_tuple_for_subscr(struct gsm_auth_tuple *atuple, struct gsm_subscriber *subscr, int key_seq); #endif /* _AUTH_H */ openbsc-0.15.0/openbsc/include/openbsc/bsc_api.h000066400000000000000000000045241265565154000215220ustar00rootroot00000000000000/* GSM 08.08 like API for OpenBSC */ #ifndef OPENBSC_BSC_API_H #define OPENBSC_BSC_API_H #include "gsm_data.h" #define BSC_API_CONN_POL_ACCEPT 0 #define BSC_API_CONN_POL_REJECT 1 struct bsc_api { /*! \brief BTS->MSC: tell MSC a SAPI was not established */ void (*sapi_n_reject)(struct gsm_subscriber_connection *conn, int dlci); /*! \brief MS->MSC: Tell MSC that ciphering has been enabled */ void (*cipher_mode_compl)(struct gsm_subscriber_connection *conn, struct msgb *msg, uint8_t chosen_encr); /*! \brief MS->MSC: New MM context with L3 payload */ int (*compl_l3)(struct gsm_subscriber_connection *conn, struct msgb *msg, uint16_t chosen_channel); /*! \brief MS->BSC/MSC: Um L3 message */ void (*dtap)(struct gsm_subscriber_connection *conn, uint8_t link_id, struct msgb *msg); /*! \brief BSC->MSC: Assignment of lchan successful */ void (*assign_compl)(struct gsm_subscriber_connection *conn, uint8_t rr_cause, uint8_t chosen_channel, uint8_t encr_alg_id, uint8_t speech_mode); /*! \brief BSC->MSC: Assignment of lchan failed */ void (*assign_fail)(struct gsm_subscriber_connection *conn, uint8_t cause, uint8_t *rr_cause); /*! \brief BSC->MSC: RR conn has been cleared */ int (*clear_request)(struct gsm_subscriber_connection *conn, uint32_t cause); /*! \brief BSC->MSC: Classmark Update */ void (*classmark_chg)(struct gsm_subscriber_connection *conn, const uint8_t *cm2, uint8_t cm2_len, const uint8_t *cm3, uint8_t cm3_len); /** * Configure the multirate setting on this channel. If it is * not implemented AMR5.9 will be used. */ void (*mr_config)(struct gsm_subscriber_connection *conn, struct gsm_lchan *lchan, int full_rate); }; int bsc_api_init(struct gsm_network *network, struct bsc_api *api); int gsm0808_submit_dtap(struct gsm_subscriber_connection *conn, struct msgb *msg, int link_id, int allow_sacch); int gsm0808_assign_req(struct gsm_subscriber_connection *conn, int chan_mode, int full_rate); int gsm0808_cipher_mode(struct gsm_subscriber_connection *conn, int cipher, const uint8_t *key, int len, int include_imeisv); int gsm0808_page(struct gsm_bts *bts, unsigned int page_group, unsigned int mi_len, uint8_t *mi, int chan_type); int gsm0808_clear(struct gsm_subscriber_connection *conn); struct llist_head *bsc_api_sub_connections(struct gsm_network *net); #endif openbsc-0.15.0/openbsc/include/openbsc/bsc_msc.h000066400000000000000000000034041265565154000215270ustar00rootroot00000000000000/* Routines to talk to the MSC using the IPA Protocol */ /* * (C) 2010 by Holger Hans Peter Freyther * (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 . * */ #ifndef BSC_MSC_H #define BSC_MSC_H #include #include #include struct bsc_msc_dest { struct llist_head list; char *ip; int port; int dscp; }; struct bsc_msc_connection { struct osmo_wqueue write_queue; int is_connected; int is_authenticated; int first_contact; struct llist_head *dests; const char *name; void (*connection_loss) (struct bsc_msc_connection *); void (*connected) (struct bsc_msc_connection *); struct osmo_timer_list reconnect_timer; struct osmo_timer_list timeout_timer; struct msgb *pending_msg; }; struct bsc_msc_connection *bsc_msc_create(void *ctx, struct llist_head *dest); int bsc_msc_connect(struct bsc_msc_connection *); void bsc_msc_schedule_connect(struct bsc_msc_connection *); void bsc_msc_lost(struct bsc_msc_connection *); struct msgb *bsc_msc_id_get_resp(int fixed, const char *token, const uint8_t *res, int len); #endif openbsc-0.15.0/openbsc/include/openbsc/bsc_msg_filter.h000066400000000000000000000046611265565154000231060ustar00rootroot00000000000000#pragma once #include #include #include #include #include struct vty; struct gsm48_hdr; struct bsc_filter_reject_cause { int lu_reject_cause; int cm_reject_cause; }; struct bsc_filter_barr_entry { struct rb_node node; char *imsi; int cm_reject_cause; int lu_reject_cause; }; enum bsc_filter_acc_ctr { ACC_LIST_LOCAL_FILTER, ACC_LIST_GLOBAL_FILTER, }; struct bsc_msg_acc_lst { struct llist_head list; /* counter */ struct rate_ctr_group *stats; /* the name of the list */ const char *name; struct llist_head fltr_list; }; struct bsc_msg_acc_lst_entry { struct llist_head list; /* the filter */ char *imsi_allow; regex_t imsi_allow_re; char *imsi_deny; regex_t imsi_deny_re; /* reject reasons for the access lists */ int cm_reject_cause; int lu_reject_cause; }; enum { FLT_CON_TYPE_NONE, FLT_CON_TYPE_LU, FLT_CON_TYPE_CM_SERV_REQ, FLT_CON_TYPE_PAG_RESP, FLT_CON_TYPE_SSA, FLT_CON_TYPE_LOCAL_REJECT, FLT_CON_TYPE_OTHER, }; struct bsc_filter_state { char *imsi; int imsi_checked; int con_type; }; struct bsc_filter_request { void *ctx; struct rb_root *black_list; struct llist_head *access_lists; const char *local_lst_name; const char *global_lst_name; int bsc_nr; }; int bsc_filter_barr_adapt(void *ctx, struct rb_root *rbtree, const struct osmo_config_list *); int bsc_filter_barr_find(struct rb_root *root, const char *imsi, int *cm, int *lu); /** * Content filtering. */ int bsc_msg_filter_initial(struct gsm48_hdr *hdr, size_t size, struct bsc_filter_request *req, int *con_type, char **imsi, struct bsc_filter_reject_cause *cause); int bsc_msg_filter_data(struct gsm48_hdr *hdr, size_t size, struct bsc_filter_request *req, struct bsc_filter_state *state, struct bsc_filter_reject_cause *cause); /* IMSI allow/deny handling */ struct bsc_msg_acc_lst *bsc_msg_acc_lst_find(struct llist_head *lst, const char *name); struct bsc_msg_acc_lst *bsc_msg_acc_lst_get(void *ctx, struct llist_head *lst, const char *name); void bsc_msg_acc_lst_delete(struct bsc_msg_acc_lst *lst); struct bsc_msg_acc_lst_entry *bsc_msg_acc_lst_entry_create(struct bsc_msg_acc_lst *); int bsc_msg_acc_lst_check_allow(struct bsc_msg_acc_lst *lst, const char *imsi); void bsc_msg_lst_vty_init(void *ctx, struct llist_head *lst, int node); void bsc_msg_acc_lst_write(struct vty *vty, struct bsc_msg_acc_lst *lst); openbsc-0.15.0/openbsc/include/openbsc/bsc_nat.h000066400000000000000000000267731265565154000215450ustar00rootroot00000000000000/* * (C) 2010-2012 by Holger Hans Peter Freyther * (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 . * */ #ifndef BSC_NAT_H #define BSC_NAT_H #include "mgcp.h" #include "bsc_msg_filter.h" #include #include #include #include #include #include #include #include #include #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; /* 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; /* 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); 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, 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 openbsc-0.15.0/openbsc/include/openbsc/bsc_nat_callstats.h000066400000000000000000000026401265565154000236020ustar00rootroot00000000000000/* * (C) 2010-2012 by Holger Hans Peter Freyther * (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 . * */ #ifndef BSC_NAT_CALLSTATS_H #define BSC_NAT_CALLSTATS_H #include #include 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 openbsc-0.15.0/openbsc/include/openbsc/bsc_nat_sccp.h000066400000000000000000000051011265565154000225330ustar00rootroot00000000000000/* NAT utilities using SCCP types */ /* * (C) 2010 by Holger Hans Peter Freyther * (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 . * */ #ifndef BSC_NAT_SCCP_H #define BSC_NAT_SCCP_H #include "bsc_msg_filter.h" #include /* * 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 openbsc-0.15.0/openbsc/include/openbsc/bsc_rll.h000066400000000000000000000006761265565154000215460ustar00rootroot00000000000000#ifndef _BSC_RLL_H #define _BSC_RLL_H #include enum bsc_rllr_ind { BSC_RLLR_IND_EST_CONF, BSC_RLLR_IND_REL_IND, BSC_RLLR_IND_ERR_IND, BSC_RLLR_IND_TIMEOUT, }; int rll_establish(struct gsm_lchan *lchan, uint8_t link_id, void (*cb)(struct gsm_lchan *, uint8_t, void *, enum bsc_rllr_ind), void *data); void rll_indication(struct gsm_lchan *lchan, uint8_t link_id, uint8_t type); #endif /* _BSC_RLL_H */ openbsc-0.15.0/openbsc/include/openbsc/bss.h000066400000000000000000000010251265565154000207020ustar00rootroot00000000000000#ifndef _BSS_H_ #define _BSS_H_ struct gsm_network; struct msgb; /* start and stop network */ extern int bsc_bootstrap_network(int (*mncc_recv)(struct gsm_network *, struct msgb *), const char *cfg_file); extern int bsc_shutdown_net(struct gsm_network *net); /* register all supported BTS */ extern int bts_init(void); extern int bts_model_bs11_init(void); extern int bts_model_rbs2k_init(void); extern int bts_model_nanobts_init(void); extern int bts_model_nokia_site_init(void); extern int bts_model_sysmobts_init(void); #endif openbsc-0.15.0/openbsc/include/openbsc/chan_alloc.h000066400000000000000000000035271265565154000222070ustar00rootroot00000000000000/* Management functions to allocate/release struct gsm_lchan */ /* (C) 2008 by Harald Welte * (C) 2009 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 . * */ #ifndef _CHAN_ALLOC_H #define _CHAN_ALLOC_H #include "gsm_data.h" struct gsm_subscriber_connection; /* Find an allocated channel for a specified subscriber */ struct gsm_subscriber_connection *connection_for_subscr(struct gsm_subscriber *subscr); /* Allocate a logical channel (SDCCH, TCH, ...) */ struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type, int allow_bigger); /* Free a logical channel (SDCCH, TCH, ...) */ void lchan_free(struct gsm_lchan *lchan); void lchan_reset(struct gsm_lchan *lchan); /* Release the given lchan */ int lchan_release(struct gsm_lchan *lchan, int sacch_deact, enum rsl_rel_mode release_mode); struct load_counter { unsigned int total; unsigned int used; }; struct pchan_load { struct load_counter pchan[_GSM_PCHAN_MAX]; }; void bts_chan_load(struct pchan_load *cl, const struct gsm_bts *bts); void network_chan_load(struct pchan_load *pl, struct gsm_network *net); int trx_is_usable(struct gsm_bts_trx *trx); #endif /* _CHAN_ALLOC_H */ openbsc-0.15.0/openbsc/include/openbsc/crc24.h000066400000000000000000000002371265565154000210340ustar00rootroot00000000000000#ifndef _CRC24_H #define _CRC24_H #include #define INIT_CRC24 0xffffff uint32_t crc24_calc(uint32_t fcs, uint8_t *cp, unsigned int len); #endif openbsc-0.15.0/openbsc/include/openbsc/ctrl.h000066400000000000000000000001371265565154000210620ustar00rootroot00000000000000#pragma once struct ctrl_handle *bsc_controlif_setup(struct gsm_network *net, uint16_t port); openbsc-0.15.0/openbsc/include/openbsc/db.h000066400000000000000000000065201265565154000205050ustar00rootroot00000000000000/* (C) 2008 by Jan Luebbe * (C) 2009 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 . * */ #ifndef _DB_H #define _DB_H #include "gsm_subscriber.h" struct gsm_equipment; struct gsm_network; struct gsm_auth_info; struct gsm_auth_tuple; struct gsm_sms; struct gsm_subscriber; /* one time initialisation */ int db_init(const char *name); int db_prepare(void); int db_fini(void); /* subscriber management */ struct gsm_subscriber *db_create_subscriber(const char *imsi); struct gsm_subscriber *db_get_subscriber(enum gsm_subscriber_field field, const char *subscr); int db_sync_subscriber(struct gsm_subscriber *subscriber); int db_subscriber_expire(void *priv, void (*callback)(void *priv, long long unsigned int id)); int db_subscriber_alloc_tmsi(struct gsm_subscriber *subscriber); int db_subscriber_alloc_exten(struct gsm_subscriber *subscriber); int db_subscriber_alloc_token(struct gsm_subscriber *subscriber, uint32_t* token); int db_subscriber_assoc_imei(struct gsm_subscriber *subscriber, char *imei); int db_subscriber_delete(struct gsm_subscriber *subscriber); int db_sync_equipment(struct gsm_equipment *equip); int db_subscriber_update(struct gsm_subscriber *subscriber); int db_subscriber_list_active(void (*list_cb)(struct gsm_subscriber*,void*), void*); /* auth info */ int db_get_authinfo_for_subscr(struct gsm_auth_info *ainfo, struct gsm_subscriber *subscr); int db_sync_authinfo_for_subscr(struct gsm_auth_info *ainfo, struct gsm_subscriber *subscr); int db_get_lastauthtuple_for_subscr(struct gsm_auth_tuple *atuple, struct gsm_subscriber *subscr); int db_sync_lastauthtuple_for_subscr(struct gsm_auth_tuple *atuple, struct gsm_subscriber *subscr); /* SMS store-and-forward */ int db_sms_store(struct gsm_sms *sms); struct gsm_sms *db_sms_get(struct gsm_network *net, unsigned long long id); struct gsm_sms *db_sms_get_unsent(struct gsm_network *net, unsigned long long min_id); struct gsm_sms *db_sms_get_unsent_by_subscr(struct gsm_network *net, unsigned long long min_subscr_id, unsigned int failed); struct gsm_sms *db_sms_get_unsent_for_subscr(struct gsm_subscriber *subscr); int db_sms_mark_delivered(struct gsm_sms *sms); int db_sms_inc_deliver_attempts(struct gsm_sms *sms); /* APDU blob storage */ int db_apdu_blob_store(struct gsm_subscriber *subscr, uint8_t apdu_id_flags, uint8_t len, uint8_t *apdu); /* Statistics counter storage */ struct osmo_counter; int db_store_counter(struct osmo_counter *ctr); struct rate_ctr_group; int db_store_rate_ctr_group(struct rate_ctr_group *ctrg); #endif /* _DB_H */ openbsc-0.15.0/openbsc/include/openbsc/debug.h000066400000000000000000000020261265565154000212030ustar00rootroot00000000000000#ifndef _DEBUG_H #define _DEBUG_H #include #include #define DEBUG #include /* Debug Areas of the code */ enum { DRLL, DCC, DMM, DRR, DRSL, DNM, DMNCC, DPAG, DMEAS, DSCCP, DMSC, DMGCP, DHO, DDB, DREF, DGPRS, DNS, DBSSGP, DLLC, DSNDCP, DNAT, DCTRL, DSMPP, DFILTER, Debug_LastEntry, }; /* context */ #define BSC_CTX_LCHAN 0 #define BSC_CTX_SUBSCR 1 #define BSC_CTX_BTS 2 #define BSC_CTX_SCCP 3 /* target */ enum { //DEBUG_FILTER_ALL = 1 << 0, LOG_FILTER_IMSI = 1 << 1, LOG_FILTER_NSVC = 1 << 2, LOG_FILTER_BVC = 1 << 3, }; /* we don't need a header dependency for this... */ struct gprs_nsvc; struct bssgp_bvc_ctx; struct gsm_subscriber; void log_set_imsi_filter(struct log_target *target, struct gsm_subscriber *subscr); void log_set_nsvc_filter(struct log_target *target, struct gprs_nsvc *nsvc); void log_set_bvc_filter(struct log_target *target, struct bssgp_bvc_ctx *bctx); extern const struct log_info log_info; #endif /* _DEBUG_H */ openbsc-0.15.0/openbsc/include/openbsc/e1_config.h000066400000000000000000000003641265565154000217520ustar00rootroot00000000000000#ifndef _E1_CONFIG_H #define _E1_CONFIG_H #include int e1_reconfig_ts(struct gsm_bts_trx_ts *ts); int e1_reconfig_trx(struct gsm_bts_trx *trx); int e1_reconfig_bts(struct gsm_bts *bts); #endif /* _E1_CONFIG_H */ openbsc-0.15.0/openbsc/include/openbsc/gb_proxy.h000066400000000000000000000170221265565154000217500ustar00rootroot00000000000000#ifndef _GB_PROXY_H #define _GB_PROXY_H #include #include #include #include #include #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_TLLI_UNKNOWN, GBPROX_PEER_CTR_TLLI_CACHE_SIZE, }; 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]; /* Used to generate identifiers */ unsigned bss_ptmsi_state; unsigned sgsn_tlli_state; }; 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); void 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 openbsc-0.15.0/openbsc/include/openbsc/gprs_gb_parse.h000066400000000000000000000026501265565154000227350ustar00rootroot00000000000000#pragma once #include #include 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); openbsc-0.15.0/openbsc/include/openbsc/gprs_gmm.h000066400000000000000000000021171265565154000217310ustar00rootroot00000000000000#ifndef _GPRS_GMM_H #define _GPRS_GMM_H #include #include 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(struct msgb *msg, struct gprs_llc_llme *llme); int gsm0408_gprs_force_reattach(struct sgsn_mm_ctx *mmctx); int gsm0408_gprs_force_reattach_oldmsg(struct msgb *msg); 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); #endif /* _GPRS_GMM_H */ openbsc-0.15.0/openbsc/include/openbsc/gprs_gsup_client.h000066400000000000000000000031771265565154000234740ustar00rootroot00000000000000/* GPRS Subscriber Update Protocol client */ /* (C) 2014 by Sysmocom s.f.m.c. GmbH * All Rights Reserved * * Author: Jacob Erlbeck * * 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 . * */ #pragma once #include #define GPRS_GSUP_RECONNECT_INTERVAL 10 #define GPRS_GSUP_PING_INTERVAL 20 struct msgb; struct ipa_client_conn; struct gprs_gsup_client; /* Expects message in msg->l2h */ typedef int (*gprs_gsup_read_cb_t)(struct gprs_gsup_client *gsupc, struct msgb *msg); struct gprs_gsup_client { struct ipa_client_conn *link; gprs_gsup_read_cb_t read_cb; void *data; struct osmo_timer_list ping_timer; struct osmo_timer_list connect_timer; int is_connected; int got_ipa_pong; }; struct gprs_gsup_client *gprs_gsup_client_create(const char *ip_addr, unsigned int tcp_port, gprs_gsup_read_cb_t read_cb); void gprs_gsup_client_destroy(struct gprs_gsup_client *gsupc); int gprs_gsup_client_send(struct gprs_gsup_client *gsupc, struct msgb *msg); struct msgb *gprs_gsup_msgb_alloc(void); openbsc-0.15.0/openbsc/include/openbsc/gprs_gsup_messages.h000066400000000000000000000075611265565154000240260ustar00rootroot00000000000000/* GPRS Subscriber Update Protocol message encoder/decoder */ /* (C) 2014 by Sysmocom s.f.m.c. GmbH * All Rights Reserved * * Author: Jacob Erlbeck * * 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 . * */ #pragma once #include #include #include /* Needed for GSM_IMSI_LENGTH: */ #include #define GPRS_GSUP_MAX_NUM_PDP_INFO 10 /* GSM 09.02 limits this to 50 */ #define GPRS_GSUP_MAX_NUM_AUTH_INFO 5 #define GPRS_GSUP_MAX_MSISDN_LEN 9 #define GPRS_GSUP_PDP_TYPE_SIZE 2 enum gprs_gsup_iei { GPRS_GSUP_IMSI_IE = 0x01, GPRS_GSUP_CAUSE_IE = 0x02, GPRS_GSUP_AUTH_TUPLE_IE = 0x03, GPRS_GSUP_PDP_INFO_COMPL_IE = 0x04, GPRS_GSUP_PDP_INFO_IE = 0x05, GPRS_GSUP_CANCEL_TYPE_IE = 0x06, GPRS_GSUP_FREEZE_PTMSI_IE = 0x07, GPRS_GSUP_MSISDN_IE = 0x08, GPRS_GSUP_HLR_NUMBER_IE = 0x09, GPRS_GSUP_PDP_CONTEXT_ID_IE = 0x10, GPRS_GSUP_PDP_TYPE_IE = 0x11, GPRS_GSUP_ACCESS_POINT_NAME_IE = 0x12, GPRS_GSUP_PDP_QOS_IE = 0x13, GPRS_GSUP_RAND_IE = 0x20, GPRS_GSUP_SRES_IE = 0x21, GPRS_GSUP_KC_IE = 0x22 }; enum gprs_gsup_message_type { GPRS_GSUP_MSGT_UPDATE_LOCATION_REQUEST = 0b00000100, GPRS_GSUP_MSGT_UPDATE_LOCATION_ERROR = 0b00000101, GPRS_GSUP_MSGT_UPDATE_LOCATION_RESULT = 0b00000110, GPRS_GSUP_MSGT_SEND_AUTH_INFO_REQUEST = 0b00001000, GPRS_GSUP_MSGT_SEND_AUTH_INFO_ERROR = 0b00001001, GPRS_GSUP_MSGT_SEND_AUTH_INFO_RESULT = 0b00001010, GPRS_GSUP_MSGT_PURGE_MS_REQUEST = 0b00001100, GPRS_GSUP_MSGT_PURGE_MS_ERROR = 0b00001101, GPRS_GSUP_MSGT_PURGE_MS_RESULT = 0b00001110, GPRS_GSUP_MSGT_INSERT_DATA_REQUEST = 0b00010000, GPRS_GSUP_MSGT_INSERT_DATA_ERROR = 0b00010001, GPRS_GSUP_MSGT_INSERT_DATA_RESULT = 0b00010010, GPRS_GSUP_MSGT_DELETE_DATA_REQUEST = 0b00010100, GPRS_GSUP_MSGT_DELETE_DATA_ERROR = 0b00010101, GPRS_GSUP_MSGT_DELETE_DATA_RESULT = 0b00010110, GPRS_GSUP_MSGT_LOCATION_CANCEL_REQUEST = 0b00011100, GPRS_GSUP_MSGT_LOCATION_CANCEL_ERROR = 0b00011101, GPRS_GSUP_MSGT_LOCATION_CANCEL_RESULT = 0b00011110, }; #define GPRS_GSUP_IS_MSGT_REQUEST(msgt) (((msgt) & 0b00000011) == 0b00) #define GPRS_GSUP_IS_MSGT_ERROR(msgt) (((msgt) & 0b00000011) == 0b01) #define GPRS_GSUP_TO_MSGT_ERROR(msgt) (((msgt) & 0b11111100) | 0b01) enum gprs_gsup_cancel_type { GPRS_GSUP_CANCEL_TYPE_UPDATE = 1, /* on wire: 0 */ GPRS_GSUP_CANCEL_TYPE_WITHDRAW = 2, /* on wire: 1 */ }; struct gprs_gsup_pdp_info { unsigned int context_id; int have_info; uint16_t pdp_type; const uint8_t *apn_enc; size_t apn_enc_len; const uint8_t *qos_enc; size_t qos_enc_len; }; struct gprs_gsup_message { enum gprs_gsup_message_type message_type; char imsi[GSM_IMSI_LENGTH]; enum gsm48_gmm_cause cause; enum gprs_gsup_cancel_type cancel_type; int pdp_info_compl; int freeze_ptmsi; struct gsm_auth_tuple auth_tuples[GPRS_GSUP_MAX_NUM_AUTH_INFO]; size_t num_auth_tuples; struct gprs_gsup_pdp_info pdp_infos[GPRS_GSUP_MAX_NUM_PDP_INFO]; size_t num_pdp_infos; const uint8_t *msisdn_enc; size_t msisdn_enc_len; const uint8_t *hlr_enc; size_t hlr_enc_len; }; int gprs_gsup_decode(const uint8_t *data, size_t data_len, struct gprs_gsup_message *gsup_msg); void gprs_gsup_encode(struct msgb *msg, const struct gprs_gsup_message *gsup_msg); openbsc-0.15.0/openbsc/include/openbsc/gprs_llc.h000066400000000000000000000145641265565154000217340ustar00rootroot00000000000000#ifndef _GPRS_LLC_H #define _GPRS_LLC_H #include #include /* 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, }; /* 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[8]; /* over which BSSGP BTS ctx do we need to transmit */ uint16_t bvci; uint16_t nsei; struct gprs_llc_lle lle[NUM_SAPIS]; /* 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, void *mmctx); /* 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); /* 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, enum gprs_ciph_algo alg, const uint8_t *kc); 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 */ /* 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); 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 openbsc-0.15.0/openbsc/include/openbsc/gprs_sgsn.h000066400000000000000000000261661265565154000221350ustar00rootroot00000000000000#ifndef _GPRS_SGSN_H #define _GPRS_SGSN_H #include #include #include #include #include #include #define GSM_IMSI_LENGTH 17 #define GSM_IMEI_LENGTH 17 #define GSM_EXTENSION_LENGTH 15 #define GSM_APN_LENGTH 102 struct gprs_llc_lle; struct ctrl_handle; struct gsm_subscriber; enum gsm48_gsm_cause; /* TS 04.08 4.1.3.3 GMM mobility management states on the network side */ enum gprs_mm_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 */ }; 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_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; }; /* 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; char imsi[GSM_IMSI_LENGTH]; enum gprs_mm_state mm_state; uint32_t p_tmsi; uint32_t p_tmsi_old; /* old P-TMSI before new is confirmed */ uint32_t p_tmsi_sig; char imei[GSM_IMEI_LENGTH]; /* Opt: Software Version Numbber / TS 23.195 */ char msisdn[GSM_EXTENSION_LENGTH]; struct gprs_ra_id ra; uint16_t cell_id; uint32_t cell_id_age; uint16_t sac; /* Iu: Service Area Code */ uint32_t sac_age;/* Iu: Service Area Code age */ /* 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; struct { uint8_t len; uint8_t buf[50]; /* GSM 04.08 10.5.5.12a, extended in TS 24.008 */ } ms_radio_access_capa; struct { uint8_t len; uint8_t buf[8]; /* GSM 04.08 10.5.5.12, extended in TS 24.008 */ } ms_network_capa; uint16_t drx_parms; int mnrg; /* MS reported to HLR? */ int ngaf; /* MS reported to MSC/VLR? */ int ppf; /* paging for GPRS + non-GPRS? */ /* SMS Parameters */ int recovery; uint8_t radio_prio_sms; struct llist_head pdp_list; /* Additional bits not present in the GSM TS */ struct gprs_llc_llme *llme; uint32_t tlli; uint32_t tlli_new; uint16_t nsei; uint16_t bvci; 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 gsm_subscriber *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); /* Allocate a new SGSN MM context */ struct sgsn_mm_ctx *sgsn_mm_ctx_alloc(uint32_t tlli, const struct gprs_ra_id *raid); 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 tx_npdu_nr; uint32_t rx_npdu_nr; uint32_t tx_gtp_snd; uint32_t rx_gtp_snu; //uint32_t charging_id; int reordering_reqd; 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); /* Force re-attachment based on msgb meta data */ int sgsn_force_reattach_oldmsg(struct msgb *oldmsg); /* * ctrl interface related work */ struct gsm_network; struct ctrl_handle *sgsn_controlif_setup(struct gsm_network *, 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; }; 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; }; #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_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); void gprs_subscr_cleanup(struct gsm_subscriber *subscr); struct gsm_subscriber *gprs_subscr_get_or_create(const char *imsi); struct gsm_subscriber *gprs_subscr_get_or_create_by_mmctx( struct sgsn_mm_ctx *mmctx); struct gsm_subscriber *gprs_subscr_get_by_imsi(const char *imsi); void gprs_subscr_cancel(struct gsm_subscriber *subscr); void gprs_subscr_update(struct gsm_subscriber *subscr); void gprs_subscr_update_auth_info(struct gsm_subscriber *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); #endif /* _GPRS_SGSN_H */ openbsc-0.15.0/openbsc/include/openbsc/gprs_utils.h000066400000000000000000000041331265565154000223110ustar00rootroot00000000000000/* GPRS utility functions */ /* (C) 2010 by Harald Welte * (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 . * */ #pragma once #include #include struct msgb; 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); char *gprs_apn_to_str(char *out_str, const uint8_t *apn_enc, size_t rest_chars); 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_shift_v_fixed(uint8_t **data, size_t *data_len, size_t len, uint8_t **value); int gprs_match_tv_fixed(uint8_t **data, size_t *data_len, uint8_t tag, size_t len, uint8_t **value); int gprs_shift_tlv(uint8_t **data, size_t *data_len, uint8_t *tag, uint8_t **value, size_t *value_len); int gprs_match_tlv(uint8_t **data, size_t *data_len, uint8_t tag, uint8_t **value, size_t *value_len); int gprs_shift_lv(uint8_t **data, size_t *data_len, uint8_t **value, size_t *value_len); openbsc-0.15.0/openbsc/include/openbsc/gsm_04_08.h000066400000000000000000000074101265565154000215170ustar00rootroot00000000000000#ifndef _GSM_04_08_H #define _GSM_04_08_H #include #include #include #include struct msgb; struct gsm_bts; struct gsm_subscriber; struct gsm_network; struct gsm_trans; struct gsm_subscriber_connection; struct amr_multirate_conf; struct amr_mode; #define GSM48_ALLOC_SIZE 2048 #define GSM48_ALLOC_HEADROOM 256 static inline struct msgb *gsm48_msgb_alloc(void) { return msgb_alloc_headroom(GSM48_ALLOC_SIZE, GSM48_ALLOC_HEADROOM, "GSM 04.08"); } static inline int get_radio_link_timeout(struct gsm48_cell_options *cell_options) { return (cell_options->radio_link_timeout + 1) << 2; } static inline void set_radio_link_timeout(struct gsm48_cell_options *cell_options, int value) { if (value < 4) value = 4; if (value > 64) value = 64; cell_options->radio_link_timeout = (value >> 2) - 1; } /* config options controlling the behaviour of the lower leves */ void gsm0408_allow_everyone(int allow); void gsm0408_clear_request(struct gsm_subscriber_connection *conn, uint32_t cause); void gsm0408_clear_all_trans(struct gsm_network *net, int protocol); int gsm0408_dispatch(struct gsm_subscriber_connection *conn, struct msgb *msg); int gsm0408_rcvmsg(struct msgb *msg, uint8_t link_id); int gsm0408_new_conn(struct gsm_subscriber_connection *conn); enum gsm_chan_t get_ctype_by_chreq(struct gsm_network *bts, uint8_t ra); /* don't use "enum gsm_chreq_reason_t" to avoid circular dependency */ int get_reason_by_chreq(uint8_t ra, int neci); void gsm_net_update_ctype(struct gsm_network *net); int gsm48_tx_mm_info(struct gsm_subscriber_connection *conn); int gsm48_tx_mm_auth_req(struct gsm_subscriber_connection *conn, uint8_t *rand, int key_seq); int gsm48_tx_mm_auth_rej(struct gsm_subscriber_connection *conn); int gsm48_tx_mm_serv_ack(struct gsm_subscriber_connection *conn); int gsm48_tx_mm_serv_rej(struct gsm_subscriber_connection *conn, enum gsm48_reject_value value); int gsm48_send_rr_release(struct gsm_lchan *lchan); int gsm48_send_rr_ciph_mode(struct gsm_lchan *lchan, int want_imeisv); int gsm48_send_rr_app_info(struct gsm_subscriber_connection *conn, uint8_t apdu_id, uint8_t apdu_len, const uint8_t *apdu); int gsm48_send_rr_ass_cmd(struct gsm_lchan *dest_lchan, struct gsm_lchan *lchan, uint8_t power_class); int gsm48_send_ho_cmd(struct gsm_lchan *old_lchan, struct gsm_lchan *new_lchan, uint8_t power_command, uint8_t ho_ref); int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg); /* convert a ASCII phone number to call-control BCD */ int encode_bcd_number(uint8_t *bcd_lv, uint8_t max_len, int h_len, const char *input); int decode_bcd_number(char *output, int output_len, const uint8_t *bcd_lv, int h_len); int send_siemens_mrpci(struct gsm_lchan *lchan, uint8_t *classmark2_lv); int gsm48_extract_mi(uint8_t *classmark2, int length, char *mi_string, uint8_t *mi_type); int gsm48_paging_extract_mi(struct gsm48_pag_resp *pag, int length, char *mi_string, uint8_t *mi_type); int gsm48_handle_paging_resp(struct gsm_subscriber_connection *conn, struct msgb *msg, struct gsm_subscriber *subscr); int gsm48_lchan_modify(struct gsm_lchan *lchan, uint8_t lchan_mode); int gsm48_rx_rr_modif_ack(struct msgb *msg); int gsm48_parse_meas_rep(struct gsm_meas_rep *rep, struct msgb *msg); struct msgb *gsm48_create_mm_serv_rej(enum gsm48_reject_value value); struct msgb *gsm48_create_loc_upd_rej(uint8_t cause); void gsm48_lchan2chan_desc(struct gsm48_chan_desc *cd, const struct gsm_lchan *lchan); void release_security_operation(struct gsm_subscriber_connection *conn); void allocate_security_operation(struct gsm_subscriber_connection *conn); int gsm48_multirate_config(uint8_t *lv, struct amr_multirate_conf *mr, struct amr_mode *modes); #endif openbsc-0.15.0/openbsc/include/openbsc/gsm_04_08_gprs.h000066400000000000000000000261471265565154000225620ustar00rootroot00000000000000#ifndef _GSM48_GPRS_H #define _GSM48_GPRS_H #include #include /* Table 10.4 / 10.4a, GPRS Mobility Management (GMM) */ #define GSM48_MT_GMM_ATTACH_REQ 0x01 #define GSM48_MT_GMM_ATTACH_ACK 0x02 #define GSM48_MT_GMM_ATTACH_COMPL 0x03 #define GSM48_MT_GMM_ATTACH_REJ 0x04 #define GSM48_MT_GMM_DETACH_REQ 0x05 #define GSM48_MT_GMM_DETACH_ACK 0x06 #define GSM48_MT_GMM_RA_UPD_REQ 0x08 #define GSM48_MT_GMM_RA_UPD_ACK 0x09 #define GSM48_MT_GMM_RA_UPD_COMPL 0x0a #define GSM48_MT_GMM_RA_UPD_REJ 0x0b #define GSM48_MT_GMM_PTMSI_REALL_CMD 0x10 #define GSM48_MT_GMM_PTMSI_REALL_COMPL 0x11 #define GSM48_MT_GMM_AUTH_CIPH_REQ 0x12 #define GSM48_MT_GMM_AUTH_CIPH_RESP 0x13 #define GSM48_MT_GMM_AUTH_CIPH_REJ 0x14 #define GSM48_MT_GMM_ID_REQ 0x15 #define GSM48_MT_GMM_ID_RESP 0x16 #define GSM48_MT_GMM_STATUS 0x20 #define GSM48_MT_GMM_INFO 0x21 /* Table 10.4a, GPRS Session Management (GSM) */ #define GSM48_MT_GSM_ACT_PDP_REQ 0x41 #define GSM48_MT_GSM_ACT_PDP_ACK 0x42 #define GSM48_MT_GSM_ACT_PDP_REJ 0x43 #define GSM48_MT_GSM_REQ_PDP_ACT 0x44 #define GSM48_MT_GSM_REQ_PDP_ACT_REJ 0x45 #define GSM48_MT_GSM_DEACT_PDP_REQ 0x46 #define GSM48_MT_GSM_DEACT_PDP_ACK 0x47 #define GSM48_MT_GSM_ACT_AA_PDP_REQ 0x50 #define GSM48_MT_GSM_ACT_AA_PDP_ACK 0x51 #define GSM48_MT_GSM_ACT_AA_PDP_REJ 0x52 #define GSM48_MT_GSM_DEACT_AA_PDP_REQ 0x53 #define GSM48_MT_GSM_DEACT_AA_PDP_ACK 0x54 #define GSM48_MT_GSM_STATUS 0x55 /* Chapter 10.5.5.2 / Table 10.5.135 */ #define GPRS_ATT_T_ATTACH 1 #define GPRS_ATT_T_ATT_WHILE_IMSI 2 #define GPRS_ATT_T_COMBINED 3 extern const struct value_string *gprs_att_t_strs; /* Chapter 10.5.5.5 / Table 10.5.138 */ #define GPRS_DET_T_MO_GPRS 1 #define GPRS_DET_T_MO_IMSI 2 #define GPRS_DET_T_MO_COMBINED 3 /* Network to MS direction */ #define GPRS_DET_T_MT_REATT_REQ 1 #define GPRS_DET_T_MT_REATT_NOTREQ 2 #define GPRS_DET_T_MT_IMSI 3 extern const struct value_string *gprs_det_t_mo_strs; extern const struct value_string *gprs_det_t_mt_strs; /* Chapter 10.5.5.18 / Table 105.150 */ #define GPRS_UPD_T_RA 0 #define GPRS_UPD_T_RA_LA 1 #define GPRS_UPD_T_RA_LA_IMSI_ATT 2 #define GPRS_UPD_T_PERIODIC 3 extern const struct value_string *gprs_upd_t_strs; enum gsm48_gprs_ie_mm { GSM48_IE_GMM_CIPH_CKSN = 0x08, /* 10.5.1.2 */ GSM48_IE_GMM_TIMER_READY = 0x17, /* 10.5.7.3 */ GSM48_IE_GMM_ALLOC_PTMSI = 0x18, /* 10.5.1.4 */ GSM48_IE_GMM_PTMSI_SIG = 0x19, /* 10.5.5.8 */ GSM48_IE_GMM_AUTH_RAND = 0x21, /* 10.5.3.1 */ GSM48_IE_GMM_AUTH_SRES = 0x22, /* 10.5.3.2 */ GSM48_IE_GMM_IMEISV = 0x23, /* 10.5.1.4 */ GSM48_IE_GMM_CAUSE = 0x25, /* 10.5.5.14 */ GSM48_IE_GMM_DRX_PARAM = 0x27, /* 10.5.5.6 */ GSM48_IE_GMM_MS_NET_CAPA = 0x31, /* 10.5.5.12 */ GSM48_IE_GMM_PDP_CTX_STATUS = 0x32, /* 10.5.7.1 */ GSM48_IE_GMM_PS_LCS_CAPA = 0x33, /* 10.5.5.22 */ GSM48_IE_GMM_GMM_MBMS_CTX_ST = 0x35, /* 10.5.7.6 */ }; enum gsm48_gprs_ie_sm { GSM48_IE_GSM_APN = 0x28, /* 10.5.6.1 */ GSM48_IE_GSM_PROTO_CONF_OPT = 0x27, /* 10.5.6.3 */ GSM48_IE_GSM_PDP_ADDR = 0x2b, /* 10.5.6.4 */ GSM48_IE_GSM_AA_TMR = 0x29, /* 10.5.7.3 */ GSM48_IE_GSM_NAME_FULL = 0x43, /* 10.5.3.5a */ GSM48_IE_GSM_NAME_SHORT = 0x45, /* 10.5.3.5a */ GSM48_IE_GSM_TIMEZONE = 0x46, /* 10.5.3.8 */ GSM48_IE_GSM_UTC_AND_TZ = 0x47, /* 10.5.3.9 */ GSM48_IE_GSM_LSA_ID = 0x48, /* 10.5.3.11 */ /* Fake IEs that are not present on the Layer3 air interface, * but which we use to simplify internal APIs */ OSMO_IE_GSM_REQ_QOS = 0xfd, OSMO_IE_GSM_REQ_PDP_ADDR = 0xfe, OSMO_IE_GSM_SUB_QOS = 0xff, }; /* Chapter 9.4.15 / Table 9.4.15 */ struct gsm48_ra_upd_ack { uint8_t force_stby:4, /* 10.5.5.7 */ upd_result:4; /* 10.5.5.17 */ uint8_t ra_upd_timer; /* 10.5.7.3 */ struct gsm48_ra_id ra_id; /* 10.5.5.15 */ uint8_t data[0]; } __attribute__((packed)); /* Chapter 10.5.7.3 */ enum gsm48_gprs_tmr_unit { GPRS_TMR_2SECONDS = 0 << 5, GPRS_TMR_MINUTE = 1 << 5, GPRS_TMR_6MINUTE = 2 << 5, GPRS_TMR_DEACTIVATED = 7 << 5, }; #define GPRS_TMR_UNIT_MASK (7 << 5) #define GPRS_TMR_FACT_MASK ((1 << 5)-1) /* Chapter 9.4.2 / Table 9.4.2 */ struct gsm48_attach_ack { uint8_t att_result:4, /* 10.5.5.7 */ force_stby:4; /* 10.5.5.1 */ uint8_t ra_upd_timer; /* 10.5.7.3 */ uint8_t radio_prio; /* 10.5.7.2 */ struct gsm48_ra_id ra_id; /* 10.5.5.15 */ uint8_t data[0]; } __attribute__((packed)); /* Chapter 9.4.9 / Table 9.4.9 */ struct gsm48_auth_ciph_req { uint8_t ciph_alg:4, /* 10.5.5.3 */ imeisv_req:4; /* 10.5.5.10 */ uint8_t force_stby:4, /* 10.5.5.7 */ ac_ref_nr:4; /* 10.5.5.19 */ uint8_t data[0]; } __attribute__((packed)); /* optional: TV RAND, TV CKSN */ struct gsm48_auth_ciph_resp { uint8_t ac_ref_nr:4, spare:4; uint8_t data[0]; } __attribute__((packed)); /* Chapter 9.5.1 / Table 9.5.1 */ struct gsm48_act_pdp_ctx_req { uint8_t req_nsapi; uint8_t req_llc_sapi; uint8_t data[0]; } __attribute__((packed)); /* Chapter 10.5.5.14 / Table 10.5.147 */ enum gsm48_gmm_cause { GMM_CAUSE_IMSI_UNKNOWN = 0x02, GMM_CAUSE_ILLEGAL_MS = 0x03, GMM_CAUSE_ILLEGAL_ME = 0x06, GMM_CAUSE_GPRS_NOTALLOWED = 0x07, GMM_CAUSE_GPRS_OTHER_NOTALLOWED = 0x08, GMM_CAUSE_MS_ID_NOT_DERIVED = 0x09, GMM_CAUSE_IMPL_DETACHED = 0x0a, GMM_CAUSE_PLMN_NOTALLOWED = 0x0b, GMM_CAUSE_LA_NOTALLOWED = 0x0c, GMM_CAUSE_ROAMING_NOTALLOWED = 0x0d, GMM_CAUSE_NO_GPRS_PLMN = 0x0e, GMM_CAUSE_MSC_TEMP_NOTREACH = 0x10, GMM_CAUSE_NET_FAIL = 0x11, GMM_CAUSE_CONGESTION = 0x16, GMM_CAUSE_SEM_INCORR_MSG = 0x5f, GMM_CAUSE_INV_MAND_INFO = 0x60, GMM_CAUSE_MSGT_NOTEXIST_NOTIMPL = 0x61, GMM_CAUSE_MSGT_INCOMP_P_STATE = 0x62, GMM_CAUSE_IE_NOTEXIST_NOTIMPL = 0x63, GMM_CAUSE_COND_IE_ERR = 0x64, GMM_CAUSE_MSG_INCOMP_P_STATE = 0x65, GMM_CAUSE_PROTO_ERR_UNSPEC = 0x6f, }; extern const struct value_string *gsm48_gmm_cause_names; /* Chapter 10.4.6.6 / Table 10.5.157 */ enum gsm48_gsm_cause { GSM_CAUSE_INSUFF_RSRC = 0x1a, GSM_CAUSE_MISSING_APN = 0x1b, GSM_CAUSE_UNKNOWN_PDP = 0x1c, GSM_CAUSE_AUTH_FAILED = 0x1d, GSM_CAUSE_ACT_REJ_GGSN = 0x1e, GSM_CAUSE_ACT_REJ_UNSPEC = 0x1f, GSM_CAUSE_SERV_OPT_NOTSUPP = 0x20, GSM_CAUSE_REQ_SERV_OPT_NOTSUB = 0x21, GSM_CAUSE_SERV_OPT_TEMP_OOO = 0x22, GSM_CAUSE_NSAPI_IN_USE = 0x23, GSM_CAUSE_DEACT_REGULAR = 0x24, GSM_CAUSE_QOS_NOT_ACCEPTED = 0x25, GSM_CAUSE_NET_FAIL = 0x26, GSM_CAUSE_REACT_RQD = 0x27, GSM_CAUSE_FEATURE_NOTSUPP = 0x28, GSM_CAUSE_INVALID_TRANS_ID = 0x51, GSM_CAUSE_SEM_INCORR_MSG = 0x5f, GSM_CAUSE_INV_MAND_INFO = 0x60, GSM_CAUSE_MSGT_NOTEXIST_NOTIMPL = 0x61, GSM_CAUSE_MSGT_INCOMP_P_STATE = 0x62, GSM_CAUSE_IE_NOTEXIST_NOTIMPL = 0x63, GSM_CAUSE_COND_IE_ERR = 0x64, GSM_CAUSE_MSG_INCOMP_P_STATE = 0x65, GSM_CAUSE_PROTO_ERR_UNSPEC = 0x6f, }; extern const struct value_string *gsm48_gsm_cause_names; /* Section 6.1.2.2: Session management states on the network side */ enum gsm48_pdp_state { PDP_S_INACTIVE, PDP_S_ACTIVE_PENDING, PDP_S_ACTIVE, PDP_S_INACTIVE_PENDING, PDP_S_MODIFY_PENDING, }; /* Table 10.5.155/3GPP TS 24.008 */ enum gsm48_pdp_type_org { PDP_TYPE_ORG_ETSI = 0x00, PDP_TYPE_ORG_IETF = 0x01, }; enum gsm48_pdp_type_nr { PDP_TYPE_N_ETSI_RESERVED = 0x00, PDP_TYPE_N_ETSI_PPP = 0x01, PDP_TYPE_N_IETF_IPv4 = 0x21, PDP_TYPE_N_IETF_IPv6 = 0x57, }; /* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */ enum gsm48_qos_reliab_class { GSM48_QOS_RC_LLC_ACK_RLC_ACK_DATA_PROT = 2, GSM48_QOS_RC_LLC_UN_RLC_ACK_DATA_PROT = 3, GSM48_QOS_RC_LLC_UN_RLC_UN_PROT_DATA = 4, GSM48_QOS_RC_LLC_UN_RLC_UN_DATA_UN = 5, }; /* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */ enum gsm48_qos_preced_class { GSM48_QOS_PC_HIGH = 1, GSM48_QOS_PC_NORMAL = 2, GSM48_QOS_PC_LOW = 3, }; /* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */ enum gsm48_qos_peak_tput { GSM48_QOS_PEAK_TPUT_1000bps = 1, GSM48_QOS_PEAK_TPUT_2000bps = 2, GSM48_QOS_PEAK_TPUT_4000bps = 3, GSM48_QOS_PEAK_TPUT_8000bps = 4, GSM48_QOS_PEAK_TPUT_16000bps = 5, GSM48_QOS_PEAK_TPUT_32000bps = 6, GSM48_QOS_PEAK_TPUT_64000bps = 7, GSM48_QOS_PEAK_TPUT_128000bps = 8, GSM48_QOS_PEAK_TPUT_256000bps = 9, }; /* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */ enum gsm48_qos_mean_tput { GSM48_QOS_MEAN_TPUT_100bph = 1, GSM48_QOS_MEAN_TPUT_200bph = 2, GSM48_QOS_MEAN_TPUT_500bph = 3, GSM48_QOS_MEAN_TPUT_1000bph = 4, GSM48_QOS_MEAN_TPUT_2000bph = 5, GSM48_QOS_MEAN_TPUT_5000bph = 6, GSM48_QOS_MEAN_TPUT_10000bph = 7, GSM48_QOS_MEAN_TPUT_20000bph = 8, GSM48_QOS_MEAN_TPUT_50000bph = 9, GSM48_QOS_MEAN_TPUT_100kbph = 10, GSM48_QOS_MEAN_TPUT_200kbph = 11, GSM48_QOS_MEAN_TPUT_500kbph = 0xc, GSM48_QOS_MEAN_TPUT_1Mbph = 0xd, GSM48_QOS_MEAN_TPUT_2Mbph = 0xe, GSM48_QOS_MEAN_TPUT_5Mbph = 0xf, GSM48_QOS_MEAN_TPUT_10Mbph = 0x10, GSM48_QOS_MEAN_TPUT_20Mbph = 0x11, GSM48_QOS_MEAN_TPUT_50Mbph = 0x12, GSM48_QOS_MEAN_TPUT_BEST_EFFORT = 0x1f, }; /* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */ enum gsm48_qos_err_sdu { GSM48_QOS_ERRSDU_NODETECT = 1, GSM48_QOS_ERRSDU_YES = 2, GSM48_QOS_ERRSDU_NO = 3, }; /* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */ enum gsm48_qos_deliv_order { GSM48_QOS_DO_ORDERED = 1, GSM48_QOS_DO_UNORDERED = 2, }; /* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */ enum gsm48_qos_traf_class { GSM48_QOS_TC_CONVERSATIONAL = 1, GSM48_QOS_TC_STREAMING = 2, GSM48_QOS_TC_INTERACTIVE = 3, GSM48_QOS_TC_BACKGROUND = 4, }; /* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */ enum gsm48_qos_max_sdu_size { /* values below in 10 octet granularity */ GSM48_QOS_MAXSDU_1502 = 0x97, GSM48_QOS_MAXSDU_1510 = 0x98, GSM48_QOS_MAXSDU_1520 = 0x99, }; /* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */ enum gsm48_qos_max_bitrate { GSM48_QOS_MBRATE_1k = 0x01, GSM48_QOS_MBRATE_63k = 0x3f, GSM48_QOS_MBRATE_64k = 0x40, GSM48_QOS_MBRATE_568k = 0x7f, GSM48_QOS_MBRATE_576k = 0x80, GSM48_QOS_MBRATE_8640k = 0xfe, GSM48_QOS_MBRATE_0k = 0xff, }; /* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */ enum gsm48_qos_resid_ber { GSM48_QOS_RBER_5e_2 = 0x01, GSM48_QOS_RBER_1e_2 = 0x02, GSM48_QOS_RBER_5e_3 = 0x03, GSM48_QOS_RBER_4e_3 = 0x04, GSM48_QOS_RBER_1e_3 = 0x05, GSM48_QOS_RBER_1e_4 = 0x06, GSM48_QOS_RBER_1e_5 = 0x07, GSM48_QOS_RBER_1e_6 = 0x08, GSM48_QOS_RBER_6e_8 = 0x09, }; /* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */ enum gsm48_qos_sdu_err { GSM48_QOS_SERR_1e_2 = 0x01, GSM48_QOS_SERR_7e_2 = 0x02, GSM48_QOS_SERR_1e_3 = 0x03, GSM48_QOS_SERR_1e_4 = 0x04, GSM48_QOS_SERR_1e_5 = 0x05, GSM48_QOS_SERR_1e_6 = 0x06, GSM48_QOS_SERR_1e_1 = 0x07, }; /* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */ struct gsm48_qos { /* octet 3 */ uint8_t reliab_class:3; uint8_t delay_class:3; uint8_t spare:2; /* octet 4 */ uint8_t preced_class:3; uint8_t spare2:1; uint8_t peak_tput:4; /* octet 5 */ uint8_t mean_tput:5; uint8_t spare3:3; /* octet 6 */ uint8_t deliv_err_sdu:3; uint8_t deliv_order:2; uint8_t traf_class:3; /* octet 7 */ uint8_t max_sdu_size; /* octet 8 */ uint8_t max_bitrate_up; /* octet 9 */ uint8_t max_bitrate_down; /* octet 10 */ uint8_t sdu_err_ratio:4; uint8_t resid_ber:4; /* octet 11 */ uint8_t handling_prio:2; uint8_t xfer_delay:6; /* octet 12 */ uint8_t guar_bitrate_up; /* octet 13 */ uint8_t guar_bitrate_down; /* octet 14 */ uint8_t src_stats_desc:4; uint8_t sig_ind:1; uint8_t spare5:3; /* octet 15 */ uint8_t max_bitrate_down_ext; /* octet 16 */ uint8_t guar_bitrate_down_ext; }; #endif /* _GSM48_GPRS_H */ openbsc-0.15.0/openbsc/include/openbsc/gsm_04_11.h000066400000000000000000000025241265565154000215120ustar00rootroot00000000000000#ifndef _GSM_04_11_H #define _GSM_04_11_H #include #define UM_SAPI_SMS 3 /* See GSM 04.05/04.06 */ /* SMS deliver PDU */ struct sms_deliver { uint8_t mti:2; /* message type indicator */ uint8_t mms:1; /* more messages to send */ uint8_t rp:1; /* reply path */ uint8_t udhi:1; /* user data header indicator */ uint8_t sri:1; /* status report indication */ uint8_t *orig_addr; /* originating address */ uint8_t pid; /* protocol identifier */ uint8_t dcs; /* data coding scheme */ /* service centre time stamp */ uint8_t ud_len; /* user data length */ uint8_t *user_data; /* user data */ uint8_t msg_ref; /* message reference */ uint8_t *smsc; }; struct msgb; int gsm0411_rcv_sms(struct gsm_subscriber_connection *conn, struct msgb *msg); struct gsm_sms *sms_alloc(void); void sms_free(struct gsm_sms *sms); struct gsm_sms *sms_from_text(struct gsm_subscriber *receiver, struct gsm_subscriber *sender, int dcs, const char *text); void _gsm411_sms_trans_free(struct gsm_trans *trans); int gsm411_send_sms_subscr(struct gsm_subscriber *subscr, struct gsm_sms *sms); int gsm411_send_sms(struct gsm_subscriber_connection *conn, struct gsm_sms *sms); void gsm411_sapi_n_reject(struct gsm_subscriber_connection *conn); uint8_t sms_next_rp_msg_ref(struct gsm_subscriber_connection *conn); #endif openbsc-0.15.0/openbsc/include/openbsc/gsm_04_80.h000066400000000000000000000012661265565154000215220ustar00rootroot00000000000000#ifndef _GSM_04_80_H #define _GSM_04_80_H #include #include #include struct gsm_subscriber_connection; int gsm0480_send_ussd_response(struct gsm_subscriber_connection *conn, const struct msgb *in_msg, const char* response_text, const struct ussd_request *req); int gsm0480_send_ussd_reject(struct gsm_subscriber_connection *conn, const struct msgb *msg, const struct ussd_request *request); int gsm0480_send_ussdNotify(struct gsm_subscriber_connection *conn, int level, const char *text); int gsm0480_send_releaseComplete(struct gsm_subscriber_connection *conn); #endif openbsc-0.15.0/openbsc/include/openbsc/gsm_data.h000066400000000000000000000261151265565154000217010ustar00rootroot00000000000000#ifndef _GSM_DATA_H #define _GSM_DATA_H #include #include #include #include /** annotations for msgb ownership */ #define __uses #define OBSC_NM_W_ACK_CB(__msgb) (__msgb)->cb[3] struct mncc_sock_state; struct gsm_subscriber_group; #define OBSC_LINKID_CB(__msgb) (__msgb)->cb[3] enum gsm_security_event { GSM_SECURITY_NOAVAIL, GSM_SECURITY_AUTH_FAILED, GSM_SECURITY_SUCCEEDED, GSM_SECURITY_ALREADY, }; struct msgb; typedef int gsm_cbfn(unsigned int hooknum, unsigned int event, struct msgb *msg, void *data, void *param); /* Real authentication information containing Ki */ enum gsm_auth_algo { AUTH_ALGO_NONE, AUTH_ALGO_XOR, AUTH_ALGO_COMP128v1, }; struct gsm_auth_info { enum gsm_auth_algo auth_algo; unsigned int a3a8_ki_len; uint8_t a3a8_ki[16]; }; struct gsm_auth_tuple { int use_count; int key_seq; uint8_t rand[16]; uint8_t sres[4]; uint8_t kc[8]; }; #define GSM_KEY_SEQ_INVAL 7 /* GSM 04.08 - 10.5.1.2 */ /* * LOCATION UPDATING REQUEST state * * Our current operation is: * - Get imei/tmsi * - Accept/Reject according to global policy */ struct gsm_loc_updating_operation { struct osmo_timer_list updating_timer; unsigned int waiting_for_imsi : 1; unsigned int waiting_for_imei : 1; unsigned int key_seq : 4; }; /* * AUTHENTICATION/CIPHERING state */ struct gsm_security_operation { struct gsm_auth_tuple atuple; gsm_cbfn *cb; void *cb_data; }; /* * A dummy to keep a connection up for at least * a couple of seconds to work around MSC issues. */ struct gsm_anchor_operation { struct osmo_timer_list timeout; }; /* Maximum number of neighbor cells whose average we track */ #define MAX_NEIGH_MEAS 10 /* Maximum size of the averaging window for neighbor cells */ #define MAX_WIN_NEIGH_AVG 10 /* processed neighbor measurements for one cell */ struct neigh_meas_proc { uint16_t arfcn; uint8_t bsic; uint8_t rxlev[MAX_WIN_NEIGH_AVG]; unsigned int rxlev_cnt; uint8_t last_seen_nr; }; /* the per subscriber data for lchan */ struct gsm_subscriber_connection { struct llist_head entry; /* To whom we are allocated at the moment */ struct gsm_subscriber *subscr; /* LU expiration handling */ uint8_t expire_timer_stopped; /* SMS helpers for libmsc */ uint8_t next_rp_ref; /* * Operations that have a state and might be pending */ struct gsm_loc_updating_operation *loc_operation; struct gsm_security_operation *sec_operation; struct gsm_anchor_operation *anch_operation; /* Are we part of a special "silent" call */ int silent_call; /* MNCC rtp bridge markers */ int mncc_rtp_bridge; int mncc_rtp_create_pending; int mncc_rtp_connect_pending; /* bsc structures */ struct osmo_bsc_sccp_con *sccp_con; /* back pointers */ int in_release; struct gsm_lchan *lchan; struct gsm_lchan *ho_lchan; struct gsm_bts *bts; /* for assignment handling */ struct osmo_timer_list T10; struct gsm_lchan *secondary_lchan; }; #define ROLE_BSC #include "gsm_data_shared.h" /* Some statistics of our network */ struct gsmnet_stats { struct { struct osmo_counter *total; struct osmo_counter *no_channel; } chreq; struct { struct osmo_counter *attempted; struct osmo_counter *no_channel; /* no channel available */ struct osmo_counter *timeout; /* T3103 timeout */ struct osmo_counter *completed; /* HO COMPL received */ struct osmo_counter *failed; /* HO FAIL received */ } handover; struct { struct osmo_counter *attach; struct osmo_counter *normal; struct osmo_counter *periodic; struct osmo_counter *detach; } loc_upd_type; struct { struct osmo_counter *reject; struct osmo_counter *accept; } loc_upd_resp; struct { struct osmo_counter *attempted; struct osmo_counter *detached; struct osmo_counter *completed; struct osmo_counter *expired; } paging; struct { struct osmo_counter *submitted; /* MO SMS submissions */ struct osmo_counter *no_receiver; struct osmo_counter *delivered; /* MT SMS deliveries */ struct osmo_counter *rp_err_mem; struct osmo_counter *rp_err_other; } sms; struct { struct osmo_counter *mo_setup; struct osmo_counter *mo_connect_ack; struct osmo_counter *mt_setup; struct osmo_counter *mt_connect; } call; struct { struct osmo_counter *rf_fail; struct osmo_counter *rll_err; } chan; struct { struct osmo_counter *oml_fail; struct osmo_counter *rsl_fail; } bts; }; enum gsm_auth_policy { GSM_AUTH_POLICY_CLOSED, /* only subscribers authorized in DB */ GSM_AUTH_POLICY_ACCEPT_ALL, /* accept everyone, even if not authorized in DB */ GSM_AUTH_POLICY_TOKEN, /* accept first, send token per sms, then revoke authorization */ }; #define GSM_T3101_DEFAULT 10 #define GSM_T3105_DEFAULT 40 #define GSM_T3113_DEFAULT 60 #define GSM_T3122_DEFAULT 10 struct gsm_network { /* global parameters */ uint16_t country_code; uint16_t network_code; char *name_long; char *name_short; enum gsm_auth_policy auth_policy; enum gsm48_reject_value reject_cause; int a5_encryption; int neci; int send_mm_info; struct { int active; /* Window RXLEV averaging */ unsigned int win_rxlev_avg; /* number of SACCH frames */ /* Window RXQUAL averaging */ unsigned int win_rxqual_avg; /* number of SACCH frames */ /* Window RXLEV neighbouring cells averaging */ unsigned int win_rxlev_avg_neigh; /* number of SACCH frames */ /* how often should we check for power budget HO */ unsigned int pwr_interval; /* SACCH frames */ /* how much better does a neighbor cell have to be ? */ unsigned int pwr_hysteresis; /* dBm */ /* maximum distacne before we try a handover */ unsigned int max_distance; /* TA values */ } handover; struct gsmnet_stats stats; /* layer 4 */ struct mncc_sock_state *mncc_state; int (*mncc_recv) (struct gsm_network *net, struct msgb *msg); struct llist_head upqueue; struct llist_head trans_list; struct bsc_api *bsc_api; unsigned int num_bts; struct llist_head bts_list; /* timer values */ int T3101; int T3103; int T3105; int T3107; int T3109; int T3111; int T3113; int T3115; int T3117; int T3119; int T3122; int T3141; /* timer to expire old location updates */ struct osmo_timer_list subscr_expire_timer; /* Radio Resource Location Protocol (TS 04.31) */ struct { enum rrlp_mode mode; } rrlp; /* enable the DTXu and DTXd for this network */ int dtx_enabled; enum gsm_chan_t ctype_by_chreq[16]; /* Use a TCH for handling requests of type paging any */ int pag_any_tch; /* MSC data in case we are a true BSC */ struct osmo_bsc_data *bsc_data; /* subscriber related features */ int create_subscriber; struct gsm_subscriber_group *subscr_group; struct gsm_sms_queue *sms_queue; /* nitb related control */ int avoid_tmsi; /* control interface */ struct ctrl_handle *ctrl; }; struct osmo_esme; enum gsm_sms_source_id { SMS_SOURCE_UNKNOWN = 0, SMS_SOURCE_MS, /* received from MS */ SMS_SOURCE_VTY, /* received from VTY */ SMS_SOURCE_SMPP, /* received via SMPP */ }; #define SMS_HDR_SIZE 128 #define SMS_TEXT_SIZE 256 struct gsm_sms_addr { uint8_t ton; uint8_t npi; char addr[21+1]; }; struct gsm_sms { unsigned long long id; struct gsm_subscriber *receiver; struct gsm_sms_addr src, dst; enum gsm_sms_source_id source; struct { struct osmo_esme *esme; uint32_t sequence_nr; int transaction_mode; char msg_id[16]; } smpp; unsigned long validity_minutes; uint8_t reply_path_req; uint8_t status_rep_req; uint8_t ud_hdr_ind; uint8_t protocol_id; uint8_t data_coding_scheme; uint8_t msg_ref; uint8_t user_data_len; uint8_t user_data[SMS_TEXT_SIZE]; char text[SMS_TEXT_SIZE]; }; struct gsm_network *gsm_network_init(uint16_t country_code, uint16_t network_code, int (*mncc_recv)(struct gsm_network *, struct msgb *)); int gsm_set_bts_type(struct gsm_bts *bts, enum gsm_bts_type type); /* Get reference to a neighbor cell on a given BCCH ARFCN */ struct gsm_bts *gsm_bts_neighbor(const struct gsm_bts *bts, uint16_t arfcn, uint8_t bsic); enum gsm_bts_type parse_btstype(const char *arg); const char *btstype2str(enum gsm_bts_type type); struct gsm_bts *gsm_bts_by_lac(struct gsm_network *net, unsigned int lac, struct gsm_bts *start_bts); extern void *tall_bsc_ctx; extern int ipacc_rtp_direct; /* this actaully refers to the IPA transport, not the BTS model */ static inline int is_ipaccess_bts(struct gsm_bts *bts) { switch (bts->type) { case GSM_BTS_TYPE_NANOBTS: case GSM_BTS_TYPE_OSMO_SYSMO: return 1; default: break; } return 0; } static inline int is_sysmobts_v2(struct gsm_bts *bts) { switch (bts->type) { case GSM_BTS_TYPE_OSMO_SYSMO: return 1; default: break; } return 0; } static inline int is_siemens_bts(struct gsm_bts *bts) { switch (bts->type) { case GSM_BTS_TYPE_BS11: return 1; default: break; } return 0; } static inline int is_nokia_bts(struct gsm_bts *bts) { switch (bts->type) { case GSM_BTS_TYPE_NOKIA_SITE: return 1; default: break; } return 0; } static inline int is_e1_bts(struct gsm_bts *bts) { switch (bts->type) { case GSM_BTS_TYPE_BS11: case GSM_BTS_TYPE_RBS2000: case GSM_BTS_TYPE_NOKIA_SITE: return 1; default: break; } return 0; } enum gsm_auth_policy gsm_auth_policy_parse(const char *arg); const char *gsm_auth_policy_name(enum gsm_auth_policy policy); enum rrlp_mode rrlp_mode_parse(const char *arg); const char *rrlp_mode_name(enum rrlp_mode mode); enum bts_gprs_mode bts_gprs_mode_parse(const char *arg, int *valid); const char *bts_gprs_mode_name(enum bts_gprs_mode mode); int bts_gprs_mode_is_compat(struct gsm_bts *bts, enum bts_gprs_mode mode); int gsm48_ra_id_by_bts(uint8_t *buf, struct gsm_bts *bts); void gprs_ra_id_by_bts(struct gprs_ra_id *raid, struct gsm_bts *bts); struct gsm_meas_rep *lchan_next_meas_rep(struct gsm_lchan *lchan); int gsm_btsmodel_set_feature(struct gsm_bts_model *model, enum gsm_bts_features feat); int gsm_bts_model_register(struct gsm_bts_model *model); struct gsm_subscriber_connection *subscr_con_allocate(struct gsm_lchan *lchan); void subscr_con_free(struct gsm_subscriber_connection *conn); struct gsm_bts *gsm_bts_alloc_register(struct gsm_network *net, enum gsm_bts_type type, uint8_t tsc, uint8_t bsic); void set_ts_e1link(struct gsm_bts_trx_ts *ts, uint8_t e1_nr, uint8_t e1_ts, uint8_t e1_ts_ss); void gsm_trx_lock_rf(struct gsm_bts_trx *trx, int locked); int gsm_bts_has_feature(struct gsm_bts *bts, enum gsm_bts_features feat); struct gsm_bts_trx *gsm_bts_trx_by_nr(struct gsm_bts *bts, int nr); int gsm_bts_trx_set_system_infos(struct gsm_bts_trx *trx); int gsm_bts_set_system_infos(struct gsm_bts *bts); /* generic E1 line operations for all ISDN-based BTS. */ extern struct e1inp_line_ops bts_isdn_e1inp_line_ops; extern const struct value_string bts_type_names[_NUM_GSM_BTS_TYPE+1]; extern const struct value_string bts_type_descs[_NUM_GSM_BTS_TYPE+1]; /* control interface handling */ int bsc_base_ctrl_cmds_install(void); int msc_ctrl_cmds_install(void); /* dependency handling */ void bts_depend_mark(struct gsm_bts *bts, int dep); void bts_depend_clear(struct gsm_bts *bts, int dep); int bts_depend_check(struct gsm_bts *bts); int bts_depend_is_depedency(struct gsm_bts *base, struct gsm_bts *other); #endif /* _GSM_DATA_H */ openbsc-0.15.0/openbsc/include/openbsc/gsm_data_shared.h000066400000000000000000000426341265565154000232330ustar00rootroot00000000000000#ifndef _GSM_DATA_SHAREDH #define _GSM_DATA_SHAREDH #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef ROLE_BSC #include #endif struct osmo_bsc_data; struct osmo_bsc_sccp_con; struct gsm_sms_queue; /* RRLP mode of operation */ enum rrlp_mode { RRLP_MODE_NONE, RRLP_MODE_MS_BASED, RRLP_MODE_MS_PREF, RRLP_MODE_ASS_PREF, }; /* Channel Request reason */ enum gsm_chreq_reason_t { GSM_CHREQ_REASON_EMERG, GSM_CHREQ_REASON_PAG, GSM_CHREQ_REASON_CALL, GSM_CHREQ_REASON_LOCATION_UPD, GSM_CHREQ_REASON_OTHER, }; #define TRX_NR_TS 8 #define TS_MAX_LCHAN 8 #define HARDCODED_ARFCN 123 #define HARDCODED_TSC 7 #define HARDCODED_BSIC 0x3f /* NCC = 7 / BCC = 7 */ /* for multi-drop config */ #define HARDCODED_BTS0_TS 1 #define HARDCODED_BTS1_TS 6 #define HARDCODED_BTS2_TS 11 enum gsm_hooks { GSM_HOOK_NM_SWLOAD, GSM_HOOK_RR_PAGING, GSM_HOOK_RR_SECURITY, }; enum gsm_paging_event { GSM_PAGING_SUCCEEDED, GSM_PAGING_EXPIRED, GSM_PAGING_OOM, GSM_PAGING_BUSY, }; enum bts_gprs_mode { BTS_GPRS_NONE = 0, BTS_GPRS_GPRS = 1, BTS_GPRS_EGPRS = 2, }; struct gsm_lchan; struct gsm_subscriber; struct gsm_mncc; struct osmo_rtp_socket; struct rtp_socket; struct bsc_api; /* Network Management State */ struct gsm_nm_state { uint8_t operational; uint8_t administrative; uint8_t availability; }; struct gsm_abis_mo { uint8_t obj_class; uint8_t procedure_pending; struct abis_om_obj_inst obj_inst; const char *name; struct gsm_nm_state nm_state; struct tlv_parsed *nm_attr; struct gsm_bts *bts; }; #define MAX_A5_KEY_LEN (128/8) #define A38_XOR_MIN_KEY_LEN 12 #define A38_XOR_MAX_KEY_LEN 16 #define A38_COMP128_KEY_LEN 16 #define RSL_ENC_ALG_A5(x) (x+1) /* is the data link established? who established it? */ #define LCHAN_SAPI_UNUSED 0 #define LCHAN_SAPI_MS 1 #define LCHAN_SAPI_NET 2 #define LCHAN_SAPI_REL 3 /* state of a logical channel */ enum gsm_lchan_state { LCHAN_S_NONE, /* channel is not active */ LCHAN_S_ACT_REQ, /* channel activation requested */ LCHAN_S_ACTIVE, /* channel is active and operational */ LCHAN_S_REL_REQ, /* channel release has been requested */ LCHAN_S_REL_ERR, /* channel is in an error state */ LCHAN_S_BROKEN, /* channel is somehow unusable */ LCHAN_S_INACTIVE, /* channel is set inactive */ }; /* BTS ONLY */ #define MAX_NUM_UL_MEAS 104 #define LC_UL_M_F_L1_VALID (1 << 0) #define LC_UL_M_F_RES_VALID (1 << 1) struct bts_ul_meas { /* BER in units of 0.01%: 10.000 == 100% ber, 0 == 0% ber */ uint16_t ber10k; /* timing advance offset (in quarter bits) */ int16_t ta_offs_qbits; /* C/I ratio in dB */ float c_i; /* flags */ uint8_t is_sub:1; /* RSSI in dBm * -1 */ uint8_t inv_rssi; }; struct bts_codec_conf { uint8_t hr; uint8_t efr; uint8_t amr; }; struct amr_mode { uint8_t mode; uint8_t threshold; uint8_t hysteresis; }; struct amr_multirate_conf { uint8_t gsm48_ie[2]; struct amr_mode ms_mode[4]; struct amr_mode bts_mode[4]; uint8_t num_modes; }; /* /BTS ONLY */ enum lchan_csd_mode { LCHAN_CSD_M_NT, LCHAN_CSD_M_T_1200_75, LCHAN_CSD_M_T_600, LCHAN_CSD_M_T_1200, LCHAN_CSD_M_T_2400, LCHAN_CSD_M_T_9600, LCHAN_CSD_M_T_14400, LCHAN_CSD_M_T_29000, LCHAN_CSD_M_T_32000, }; /* State of the SAPIs in the lchan */ enum lchan_sapi_state { LCHAN_SAPI_S_NONE, LCHAN_SAPI_S_REQ, LCHAN_SAPI_S_ASSIGNED, LCHAN_SAPI_S_REL, LCHAN_SAPI_S_ERROR, }; struct gsm_lchan { /* The TS that we're part of */ struct gsm_bts_trx_ts *ts; /* The logical subslot number in the TS */ uint8_t nr; /* The logical channel type */ enum gsm_chan_t type; /* RSL channel mode */ enum rsl_cmod_spd rsl_cmode; /* If TCH, traffic channel mode */ enum gsm48_chan_mode tch_mode; enum lchan_csd_mode csd_mode; /* State */ enum gsm_lchan_state state; const char *broken_reason; /* Power levels for MS and BTS */ uint8_t bs_power; uint8_t ms_power; /* Encryption information */ struct { uint8_t alg_id; uint8_t key_len; uint8_t key[MAX_A5_KEY_LEN]; } encr; /* AMR bits */ uint8_t mr_ms_lv[7]; uint8_t mr_bts_lv[7]; /* Established data link layer services */ uint8_t sapis[8]; int sacch_deact; struct { uint32_t bound_ip; uint32_t connect_ip; uint16_t bound_port; uint16_t connect_port; uint16_t conn_id; uint8_t rtp_payload; uint8_t rtp_payload2; uint8_t speech_mode; #ifdef ROLE_BSC struct rtp_socket *rtp_socket; #else struct osmo_rtp_socket *rtp_socket; #endif } abis_ip; uint8_t rqd_ta; #ifdef ROLE_BSC struct osmo_timer_list T3101; struct osmo_timer_list T3109; struct osmo_timer_list T3111; struct osmo_timer_list error_timer; struct osmo_timer_list act_timer; struct osmo_timer_list rel_work; uint8_t error_cause; /* table of neighbor cell measurements */ struct neigh_meas_proc neigh_meas[MAX_NEIGH_MEAS]; /* cache of last measurement reports on this lchan */ struct gsm_meas_rep meas_rep[6]; int meas_rep_idx; /* GSM Random Access data */ struct gsm48_req_ref *rqd_ref; struct gsm_subscriber_connection *conn; #else /* Number of different GsmL1_Sapi_t used in osmo_bts_sysmo is 23. * Currently we don't share these headers so this is a magic number. */ struct llist_head sapi_cmds; uint8_t sapis_dl[23]; uint8_t sapis_ul[23]; struct lapdm_channel lapdm_ch; struct llist_head dl_tch_queue; struct { /* bitmask of all SI that are present/valid in si_buf */ uint32_t valid; uint32_t last; /* buffers where we put the pre-computed SI */ sysinfo_buf_t buf[_MAX_SYSINFO_TYPE]; } si; struct { uint8_t flags; /* RSL measurment result number, 0 at lchan_act */ uint8_t res_nr; /* current Tx power level of the BTS */ uint8_t bts_tx_pwr; /* number of measurements stored in array below */ uint8_t num_ul_meas; struct bts_ul_meas uplink[MAX_NUM_UL_MEAS]; /* last L1 header from the MS */ uint8_t l1_info[2]; struct gsm_meas_rep_unidir ul_res; } meas; struct { struct amr_multirate_conf amr_mr; struct { uint8_t buf[16]; uint8_t len; } last_sid; uint8_t last_cmr; } tch; /* BTS-side ciphering state (rx only, bi-directional, ...) */ uint8_t ciph_state; uint8_t ciph_ns; uint8_t loopback; struct { uint8_t active; uint8_t ref; /* T3105: PHYS INF retransmission */ struct osmo_timer_list t3105; /* counts up to Ny1 */ unsigned int phys_info_count; } ho; /* S counter for link loss */ int s; /* Kind of the release/activation. E.g. RSL or PCU */ int rel_act_kind; /* power handling */ struct { uint8_t current; uint8_t fixed; } ms_power_ctrl; #endif }; #define TS_F_PDCH_MODE 0x1000 /* One Timeslot in a TRX */ struct gsm_bts_trx_ts { struct gsm_bts_trx *trx; /* number of this timeslot at the TRX */ uint8_t nr; enum gsm_phys_chan_config pchan; unsigned int flags; struct gsm_abis_mo mo; struct tlv_parsed nm_attr; uint8_t nm_chan_comb; int tsc; /* -1 == use BTS TSC */ struct { /* Parameters below are configured by VTY */ int enabled; uint8_t maio; uint8_t hsn; struct bitvec arfcns; uint8_t arfcns_data[1024/8]; /* This is the pre-computed MA for channel assignments */ struct bitvec ma; uint8_t ma_len; /* part of ma_data that is used */ uint8_t ma_data[8]; /* 10.5.2.21: max 8 bytes value part */ } hopping; /* To which E1 subslot are we connected */ struct gsm_e1_subslot e1_link; struct gsm_lchan lchan[TS_MAX_LCHAN]; }; /* One TRX in a BTS */ struct gsm_bts_trx { /* list header in bts->trx_list */ struct llist_head list; struct gsm_bts *bts; /* number of this TRX in the BTS */ uint8_t nr; /* human readable name / description */ char *description; /* how do we talk RSL with this TRX? */ struct gsm_e1_subslot rsl_e1_link; uint8_t rsl_tei; struct e1inp_sign_link *rsl_link; /* Some BTS (specifically Ericsson RBS) have a per-TRX OML Link */ struct e1inp_sign_link *oml_link; struct gsm_abis_mo mo; struct tlv_parsed nm_attr; struct { struct gsm_abis_mo mo; } bb_transc; uint16_t arfcn; int nominal_power; /* in dBm */ unsigned int max_power_red; /* in actual dB */ #ifndef ROLE_BSC struct trx_power_params power_params; int ms_power_control; struct { void *l1h; } role_bts; #endif union { struct { struct { struct gsm_abis_mo mo; } bbsig; struct { struct gsm_abis_mo mo; } pa; } bs11; struct { unsigned int test_state; uint8_t test_nr; struct rxlev_stats rxlev_stat; } ipaccess; }; struct gsm_bts_trx_ts ts[TRX_NR_TS]; }; #define GSM_BTS_SI(bts, i) (void *)(bts->si_buf[i]) enum gsm_bts_type { GSM_BTS_TYPE_UNKNOWN, GSM_BTS_TYPE_BS11, GSM_BTS_TYPE_NANOBTS, GSM_BTS_TYPE_RBS2000, GSM_BTS_TYPE_NOKIA_SITE, GSM_BTS_TYPE_OSMO_SYSMO, _NUM_GSM_BTS_TYPE }; struct vty; struct gsm_bts_model { struct llist_head list; enum gsm_bts_type type; const char *name; bool started; int (*start)(struct gsm_network *net); int (*oml_rcvmsg)(struct msgb *msg); void (*e1line_bind_ops)(struct e1inp_line *line); void (*config_write_bts)(struct vty *vty, struct gsm_bts *bts); void (*config_write_trx)(struct vty *vty, struct gsm_bts_trx *trx); void (*config_write_ts)(struct vty *vty, struct gsm_bts_trx_ts *ts); struct tlv_definition nm_att_tlvdef; struct bitvec features; uint8_t _features_data[128/8]; }; enum gsm_bts_features { BTS_FEAT_HSCSD, BTS_FEAT_GPRS, BTS_FEAT_EGPRS, BTS_FEAT_ECSD, BTS_FEAT_HOPPING, BTS_FEAT_MULTI_TSC, }; /* * This keeps track of the paging status of one BTS. It * includes a number of pending requests, a back pointer * to the gsm_bts, a timer and some more state. */ struct gsm_bts_paging_state { /* pending requests */ struct llist_head pending_requests; struct gsm_bts *bts; struct osmo_timer_list work_timer; struct osmo_timer_list credit_timer; /* free chans needed */ int free_chans_need; /* load */ uint16_t available_slots; }; struct gsm_envabtse { struct gsm_abis_mo mo; }; struct gsm_bts_gprs_nsvc { struct gsm_bts *bts; /* data read via VTY config file, to configure the BTS * via OML from BSC */ int id; uint16_t nsvci; uint16_t local_port; /* on the BTS */ uint16_t remote_port; /* on the SGSN */ uint32_t remote_ip; /* on the SGSN */ struct gsm_abis_mo mo; }; enum gprs_rlc_par { RLC_T3142, RLC_T3169, RLC_T3191, RLC_T3193, RLC_T3195, RLC_N3101, RLC_N3103, RLC_N3105, CV_COUNTDOWN, T_DL_TBF_EXT, /* ms */ T_UL_TBF_EXT, /* ms */ _NUM_RLC_PAR }; enum gprs_cs { GPRS_CS1, GPRS_CS2, GPRS_CS3, GPRS_CS4, GPRS_MCS1, GPRS_MCS2, GPRS_MCS3, GPRS_MCS4, GPRS_MCS5, GPRS_MCS6, GPRS_MCS7, GPRS_MCS8, GPRS_MCS9, _NUM_GRPS_CS }; struct gprs_rlc_cfg { uint16_t parameter[_NUM_RLC_PAR]; struct { uint16_t repeat_time; /* ms */ uint8_t repeat_count; } paging; uint32_t cs_mask; /* bitmask of gprs_cs */ uint8_t initial_cs; uint8_t initial_mcs; }; enum neigh_list_manual_mode { NL_MODE_AUTOMATIC = 0, NL_MODE_MANUAL = 1, NL_MODE_MANUAL_SI5SEP = 2, /* SI2 and SI5 have separate neighbor lists */ }; enum bts_loc_fix { BTS_LOC_FIX_INVALID = 0, BTS_LOC_FIX_2D = 1, BTS_LOC_FIX_3D = 2, }; extern const struct value_string bts_loc_fix_names[]; struct bts_location { struct llist_head list; time_t tstamp; enum bts_loc_fix valid; double lat; double lon; double height; }; /* One BTS */ struct gsm_bts { /* list header in net->bts_list */ struct llist_head list; /* Geographical location of the BTS */ struct llist_head loc_list; /* number of ths BTS in network */ uint8_t nr; /* human readable name / description */ char *description; /* Cell Identity */ uint16_t cell_identity; /* location area code of this BTS */ uint16_t location_area_code; /* Training Sequence Code */ uint8_t tsc; /* Base Station Identification Code (BSIC) */ uint8_t bsic; /* type of BTS */ enum gsm_bts_type type; struct gsm_bts_model *model; enum gsm_band band; /* maximum Tx power that the MS is permitted to use in this cell */ int ms_max_power; /* how do we talk OML with this TRX? */ struct gsm_e1_subslot oml_e1_link; uint8_t oml_tei; struct e1inp_sign_link *oml_link; /* Abis network management O&M handle */ struct abis_nm_h *nmh; struct gsm_abis_mo mo; /* number of this BTS on given E1 link */ uint8_t bts_nr; /* paging state and control */ struct gsm_bts_paging_state paging; /* CCCH is on C0 */ struct gsm_bts_trx *c0; struct { struct gsm_abis_mo mo; } site_mgr; /* bitmask of all SI that are present/valid in si_buf */ uint32_t si_valid; /* buffers where we put the pre-computed SI */ sysinfo_buf_t si_buf[_MAX_SYSINFO_TYPE]; /* TimeZone hours, mins, and bts specific */ struct { int hr; int mn; int override; int dst; } tz; /* ip.accesss Unit ID's have Site/BTS/TRX layout */ union { struct { uint16_t site_id; uint16_t bts_id; uint32_t flags; uint32_t rsl_ip; } ip_access; struct { struct { struct gsm_abis_mo mo; } cclk; struct { struct gsm_abis_mo mo; } rack; struct gsm_envabtse envabtse[4]; } bs11; struct { struct { struct gsm_abis_mo mo; struct llist_head conn_groups; } is; struct { struct gsm_abis_mo mo; struct llist_head conn_groups; } con; struct { struct gsm_abis_mo mo; } dp; struct { struct gsm_abis_mo mo; } tf; } rbs2000; struct { uint8_t bts_type; unsigned int configured:1, skip_reset:1, no_loc_rel_cnf:1, bts_reset_timer_cnf, did_reset:1, wait_reset:1; struct osmo_timer_list reset_timer; } nokia; }; /* Not entirely sure how ip.access specific this is */ struct { enum bts_gprs_mode mode; struct { struct gsm_abis_mo mo; uint16_t nsei; uint8_t timer[7]; } nse; struct { struct gsm_abis_mo mo; uint16_t bvci; uint8_t timer[11]; struct gprs_rlc_cfg rlc_cfg; } cell; struct gsm_bts_gprs_nsvc nsvc[2]; uint8_t rac; uint8_t net_ctrl_ord; } gprs; /* RACH NM values */ int rach_b_thresh; int rach_ldavg_slots; /* transceivers */ int num_trx; struct llist_head trx_list; /* SI related items */ int force_combined_si; int bcch_change_mark; #ifdef ROLE_BSC /* Abis NM queue */ struct llist_head abis_queue; int abis_nm_pend; struct gsm_network *network; /* should the channel allocator allocate channels from high TRX to TRX0, * rather than starting from TRX0 and go upwards? */ int chan_alloc_reverse; enum neigh_list_manual_mode neigh_list_manual_mode; /* parameters from which we build SYSTEM INFORMATION */ struct { struct gsm48_rach_control rach_control; uint8_t ncc_permitted; struct gsm48_cell_sel_par cell_sel_par; struct gsm48_si_selection_params cell_ro_sel_par; /* rest octet */ struct gsm48_cell_options cell_options; struct gsm48_control_channel_descr chan_desc; struct bitvec neigh_list; struct bitvec cell_alloc; struct bitvec si5_neigh_list; struct { /* bitmask large enough for all possible ARFCN's */ uint8_t neigh_list[1024/8]; uint8_t cell_alloc[1024/8]; /* If the user wants a different neighbor list in SI5 than in SI2 */ uint8_t si5_neigh_list[1024/8]; } data; } si_common; /* do we use static (user-defined) system information messages? (bitmask) */ uint32_t si_mode_static; /* exclude the BTS from the global RF Lock handling */ int excl_from_rf_lock; /* supported codecs beside FR */ struct bts_codec_conf codec; /* BTS dependencies bit field */ uint32_t depends_on[256/(8*4)]; /* full and half rate multirate config */ struct amr_multirate_conf mr_full; struct amr_multirate_conf mr_half; #endif /* ROLE_BSC */ void *role; }; struct gsm_bts *gsm_bts_alloc(void *talloc_ctx); struct gsm_bts *gsm_bts_num(struct gsm_network *net, int num); struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts); struct gsm_bts_trx *gsm_bts_trx_num(const struct gsm_bts *bts, int num); const struct value_string gsm_pchant_names[12]; const struct value_string gsm_pchant_descs[12]; const struct value_string gsm_lchant_names[8]; const char *gsm_pchan_name(enum gsm_phys_chan_config c); enum gsm_phys_chan_config gsm_pchan_parse(const char *name); const char *gsm_lchant_name(enum gsm_chan_t c); const char *gsm_chreq_name(enum gsm_chreq_reason_t c); char *gsm_trx_name(const struct gsm_bts_trx *trx); char *gsm_ts_name(const struct gsm_bts_trx_ts *ts); char *gsm_lchan_name(const struct gsm_lchan *lchan); const char *gsm_lchans_name(enum gsm_lchan_state s); void gsm_abis_mo_reset(struct gsm_abis_mo *mo); struct gsm_abis_mo * gsm_objclass2mo(struct gsm_bts *bts, uint8_t obj_class, const struct abis_om_obj_inst *obj_inst); struct gsm_nm_state * gsm_objclass2nmstate(struct gsm_bts *bts, uint8_t obj_class, const struct abis_om_obj_inst *obj_inst); void * gsm_objclass2obj(struct gsm_bts *bts, uint8_t obj_class, const struct abis_om_obj_inst *obj_inst); /* reset the state of all MO in the BTS */ void gsm_bts_mo_reset(struct gsm_bts *bts); uint8_t gsm_ts2chan_nr(const struct gsm_bts_trx_ts *ts, uint8_t lchan_nr); uint8_t gsm_lchan2chan_nr(const struct gsm_lchan *lchan); /* return the gsm_lchan for the CBCH (if it exists at all) */ struct gsm_lchan *gsm_bts_get_cbch(struct gsm_bts *bts); /* * help with parsing regexps */ int gsm_parse_reg(void *ctx, regex_t *reg, char **str, int argc, const char **argv) __attribute__ ((warn_unused_result)); static inline uint8_t gsm_ts_tsc(const struct gsm_bts_trx_ts *ts) { if (ts->tsc != -1) return ts->tsc; else return ts->trx->bts->tsc; } #endif openbsc-0.15.0/openbsc/include/openbsc/gsm_subscriber.h000066400000000000000000000072051265565154000231320ustar00rootroot00000000000000#ifndef _GSM_SUBSCR_H #define _GSM_SUBSCR_H #include "gsm_data.h" #include #define GSM_IMEI_LENGTH 17 #define GSM_IMSI_LENGTH 17 #define GSM_NAME_LENGTH 160 #define GSM_EXTENSION_LENGTH 15 /* MSISDN can only be 15 digits length */ #define GSM_MIN_EXTEN 20000 #define GSM_MAX_EXTEN 49999 #define GSM_SUBSCRIBER_FIRST_CONTACT 0x00000001 /* gprs_sgsn.h defines additional flags including and above bit 16 (0x10000) */ #define tmsi_from_string(str) strtoul(str, NULL, 10) #define GSM_SUBSCRIBER_NO_EXPIRATION 0x0 struct vty; struct sgsn_mm_ctx; struct sgsn_subscriber_data; struct subscr_request; struct gsm_subscriber_group { struct gsm_network *net; int keep_subscr; }; struct gsm_equipment { long long unsigned int id; char imei[GSM_IMEI_LENGTH]; char name[GSM_NAME_LENGTH]; struct gsm48_classmark1 classmark1; uint8_t classmark2_len; uint8_t classmark2[3]; uint8_t classmark3_len; uint8_t classmark3[14]; }; struct gsm_subscriber { struct gsm_subscriber_group *group; long long unsigned int id; char imsi[GSM_IMSI_LENGTH]; uint32_t tmsi; uint16_t lac; char name[GSM_NAME_LENGTH]; char extension[GSM_EXTENSION_LENGTH]; int authorized; time_t expire_lu; /* Don't delete subscribers even if group->keep_subscr is not set */ int keep_in_ram; /* Temporary field which is not stored in the DB/HLR */ uint32_t flags; /* Every user can only have one equipment in use at any given * point in time */ struct gsm_equipment equipment; /* for internal management */ int use_count; struct llist_head entry; /* pending requests */ int is_paging; struct llist_head requests; /* GPRS/SGSN related fields */ struct sgsn_subscriber_data *sgsn_data; }; enum gsm_subscriber_field { GSM_SUBSCRIBER_IMSI, GSM_SUBSCRIBER_TMSI, GSM_SUBSCRIBER_EXTENSION, GSM_SUBSCRIBER_ID, }; enum gsm_subscriber_update_reason { GSM_SUBSCRIBER_UPDATE_ATTACHED, GSM_SUBSCRIBER_UPDATE_DETACHED, GSM_SUBSCRIBER_UPDATE_EQUIPMENT, }; struct gsm_subscriber *subscr_get(struct gsm_subscriber *subscr); struct gsm_subscriber *subscr_put(struct gsm_subscriber *subscr); struct gsm_subscriber *subscr_create_subscriber(struct gsm_subscriber_group *sgrp, const char *imsi); struct gsm_subscriber *subscr_get_by_tmsi(struct gsm_subscriber_group *sgrp, uint32_t tmsi); struct gsm_subscriber *subscr_get_by_imsi(struct gsm_subscriber_group *sgrp, const char *imsi); struct gsm_subscriber *subscr_get_by_extension(struct gsm_subscriber_group *sgrp, const char *ext); struct gsm_subscriber *subscr_get_by_id(struct gsm_subscriber_group *sgrp, unsigned long long id); struct gsm_subscriber *subscr_get_or_create(struct gsm_subscriber_group *sgrp, const char *imsi); int subscr_update(struct gsm_subscriber *s, struct gsm_bts *bts, int reason); struct gsm_subscriber *subscr_active_by_tmsi(struct gsm_subscriber_group *sgrp, uint32_t tmsi); struct gsm_subscriber *subscr_active_by_imsi(struct gsm_subscriber_group *sgrp, const char *imsi); char *subscr_name(struct gsm_subscriber *subscr); int subscr_purge_inactive(struct gsm_subscriber_group *sgrp); void subscr_update_from_db(struct gsm_subscriber *subscr); void subscr_expire(struct gsm_subscriber_group *sgrp); int subscr_update_expire_lu(struct gsm_subscriber *subscr, struct gsm_bts *bts); /* * Paging handling with authentication */ struct subscr_request *subscr_request_channel(struct gsm_subscriber *subscr, int type, gsm_cbfn *cbfn, void *param); void subscr_remove_request(struct subscr_request *req); /* internal */ struct gsm_subscriber *subscr_alloc(void); extern struct llist_head active_subscribers; #endif /* _GSM_SUBSCR_H */ openbsc-0.15.0/openbsc/include/openbsc/handover.h000066400000000000000000000012101265565154000217150ustar00rootroot00000000000000#ifndef _HANDOVER_H #define _HANDOVER_H struct gsm_subscriber_connection; /* Hand over the specified logical channel to the specified new BTS. * This is the main entry point for the actual handover algorithm, * after it has decided it wants to initiate HO to a specific BTS */ int bsc_handover_start(struct gsm_lchan *old_lchan, struct gsm_bts *bts); /* clear any operation for this connection */ void bsc_clear_handover(struct gsm_subscriber_connection *conn, int free_lchan); /* Return the old lchan or NULL. This is meant for audio handling */ struct gsm_lchan *bsc_handover_pending(struct gsm_lchan *new_lchan); #endif /* _HANDOVER_H */ openbsc-0.15.0/openbsc/include/openbsc/handover_decision.h000066400000000000000000000001761265565154000236040ustar00rootroot00000000000000#ifndef _HANDOVER_DECISION_H #define _HANDOVER_DECISION_H void on_dso_load_ho_dec(void); #endif /* _HANDOVER_DECISION_H */ openbsc-0.15.0/openbsc/include/openbsc/ipaccess.h000066400000000000000000000022551265565154000217130ustar00rootroot00000000000000#ifndef _IPACCESS_H #define _IPACCESS_H #include #include "gsm_subscriber.h" #include #include struct ipac_msgt_sccp_state { uint8_t src_ref[3]; uint8_t dst_ref[3]; uint8_t trans_id; uint8_t invoke_id; char imsi[GSM_IMSI_LENGTH]; uint8_t data[0]; } __attribute__((packed)); /* * @add_remove 0 for remove, 1 for add, 3 to asK * @nr_lacs Number of extra lacs inside this package * @lac One lac entry */ struct ipac_ext_lac_cmd { uint8_t add_remove; uint8_t nr_extra_lacs; uint16_t lac; uint8_t data[0]; } __attribute__((packed)); void ipaccess_drop_oml(struct gsm_bts *bts); void ipaccess_drop_rsl(struct gsm_bts_trx *trx); struct sdp_header_item { struct sdp_header_entry header_entry; struct llist_head entry; off_t absolute_offset; }; struct sdp_header { struct sdp_firmware firmware_info; /* for more_magic a list of sdp_header_entry_list */ struct llist_head header_list; /* the entry of the sdp_header */ struct llist_head entry; }; int ipaccess_analyze_file(int fd, const unsigned int st_size, const unsigned base_offset, struct llist_head *list); #endif /* _IPACCESS_H */ openbsc-0.15.0/openbsc/include/openbsc/meas_feed.h000066400000000000000000000014351265565154000220300ustar00rootroot00000000000000#ifndef _OPENBSC_MEAS_FEED_H #define _OPENBSC_MEAS_FEED_H #include #include struct meas_feed_hdr { uint8_t msg_type; uint8_t reserved; uint16_t version; }; struct meas_feed_meas { struct meas_feed_hdr hdr; char imsi[15+1]; char name[31+1]; char scenario[31+1]; struct gsm_meas_rep mr; /* The logical channel type, enum gsm_chan_t */ uint8_t lchan_type; /* The physical channel type, enum gsm_phys_chan_config */ uint8_t pchan_type; /* number of ths BTS in network */ uint8_t bts_nr; /* number of this TRX in the BTS */ uint8_t trx_nr; /* number of this timeslot at the TRX */ uint8_t ts_nr; /* The logical subslot number in the TS */ uint8_t ss_nr; }; enum meas_feed_msgtype { MEAS_FEED_MEAS = 0, }; #define MEAS_FEED_VERSION 1 #endif openbsc-0.15.0/openbsc/include/openbsc/meas_rep.h000066400000000000000000000031571265565154000217160ustar00rootroot00000000000000#ifndef _MEAS_REP_H #define _MEAS_REP_H #include #include #define MRC_F_PROCESSED 0x0001 /* extracted from a L3 measurement report IE */ struct gsm_meas_rep_cell { uint8_t rxlev; uint8_t bsic; uint8_t neigh_idx; uint16_t arfcn; unsigned int flags; }; #define MEAS_REP_F_UL_DTX 0x01 #define MEAS_REP_F_DL_VALID 0x02 #define MEAS_REP_F_BA1 0x04 #define MEAS_REP_F_DL_DTX 0x08 #define MEAS_REP_F_MS_TO 0x10 #define MEAS_REP_F_MS_L1 0x20 #define MEAS_REP_F_FPC 0x40 /* parsed uplink and downlink measurement result */ struct gsm_meas_rep { /* back-pointer to the logical channel */ struct gsm_lchan *lchan; /* number of the measurement report */ uint8_t nr; /* flags, see MEAS_REP_F_* */ unsigned int flags; /* uplink and downlink rxlev, rxqual; full and sub */ struct gsm_meas_rep_unidir ul; struct gsm_meas_rep_unidir dl; uint8_t bs_power; uint8_t ms_timing_offset; struct { int8_t pwr; /* MS power in dBm */ uint8_t ta; /* MS timing advance */ } ms_l1; /* neighbor measurement reports for up to 6 cells */ int num_cell; struct gsm_meas_rep_cell cell[6]; }; /* obtain an average over the last 'num' fields in the meas reps */ int get_meas_rep_avg(const struct gsm_lchan *lchan, enum meas_rep_field field, unsigned int num); /* Check if N out of M last values for FIELD are >= bd */ int meas_rep_n_out_of_m_be(const struct gsm_lchan *lchan, enum meas_rep_field field, unsigned int n, unsigned int m, int be); unsigned int calc_initial_idx(unsigned int array_size, unsigned int meas_rep_idx, unsigned int num_values); #endif /* _MEAS_REP_H */ openbsc-0.15.0/openbsc/include/openbsc/mgcp.h000066400000000000000000000164021265565154000210460ustar00rootroot00000000000000/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */ /* * (C) 2009-2012 by Holger Hans Peter Freyther * (C) 2009-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 . * */ #ifndef OPENBSC_MGCP_H #define OPENBSC_MGCP_H #include #include #include #include "debug.h" #include #include #include #include #define RTP_PORT_DEFAULT 4000 #define RTP_PORT_NET_DEFAULT 16000 /** * Calculate the RTP audio port for the given multiplex * and the direction. This allows a semi static endpoint * to port calculation removing the need for the BSC * and the MediaGateway to communicate. * * Port usage explained: * base + (multiplex * 2) + 0 == local port to wait for network packets * base + (multiplex * 2) + 1 == local port for rtcp * * The above port will receive packets from the BTS that need * to be patched and forwarded to the network. * The above port will receive packets from the network that * need to be patched and forwarded to the BTS. * * We assume to have a static BTS IP address so we can differentiate * network and BTS. * */ static inline int rtp_calculate_port(int multiplex, int base) { return base + (multiplex * 2); } /* * Handling of MGCP Endpoints and the MGCP Config */ struct mgcp_endpoint; struct mgcp_config; struct mgcp_trunk_config; struct mgcp_rtp_end; #define MGCP_ENDP_CRCX 1 #define MGCP_ENDP_DLCX 2 #define MGCP_ENDP_MDCX 3 /* * what to do with the msg? * - continue as usual? * - reject and send a failure code? * - defer? do not send anything */ #define MGCP_POLICY_CONT 4 #define MGCP_POLICY_REJECT 5 #define MGCP_POLICY_DEFER 6 typedef int (*mgcp_realloc)(struct mgcp_trunk_config *cfg, int endpoint); typedef int (*mgcp_change)(struct mgcp_trunk_config *cfg, int endpoint, int state); typedef int (*mgcp_policy)(struct mgcp_trunk_config *cfg, int endpoint, int state, const char *transactio_id); typedef int (*mgcp_reset)(struct mgcp_trunk_config *cfg); typedef int (*mgcp_rqnt)(struct mgcp_endpoint *endp, char tone); /** * Return: * < 0 in case no audio was processed * >= 0 in case audio was processed. The remaining payload * length will be returned. */ typedef int (*mgcp_processing)(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end, char *data, int *len, int buf_size); typedef int (*mgcp_processing_setup)(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end, struct mgcp_rtp_end *src_end); typedef void (*mgcp_get_format)(struct mgcp_endpoint *endp, int *payload_type, const char**subtype_name, const char**fmtp_extra); #define PORT_ALLOC_STATIC 0 #define PORT_ALLOC_DYNAMIC 1 /** * This holds information on how to allocate ports */ struct mgcp_port_range { int mode; /* addr or NULL to fall-back to default */ char *bind_addr; /* pre-allocated from a base? */ int base_port; /* dynamically allocated */ int range_start; int range_end; int last_port; }; #define MGCP_KEEPALIVE_ONCE (-1) struct mgcp_trunk_config { struct llist_head entry; struct mgcp_config *cfg; int trunk_nr; int trunk_type; char *audio_fmtp_extra; char *audio_name; int audio_payload; int audio_send_ptime; int audio_send_name; int audio_loop; int no_audio_transcoding; int omit_rtcp; int keepalive_interval; /* RTP patching */ int force_constant_ssrc; /* 0: don't, 1: once */ int force_aligned_timing; /* spec handling */ int force_realloc; /* timer */ struct osmo_timer_list keepalive_timer; unsigned int number_endpoints; struct mgcp_endpoint *endpoints; }; enum mgcp_role { MGCP_BSC = 0, MGCP_BSC_NAT, }; struct mgcp_config { int source_port; char *local_ip; char *source_addr; char *bts_ip; char *call_agent_addr; struct in_addr bts_in; /* transcoder handling */ char *transcoder_ip; struct in_addr transcoder_in; int transcoder_remote_base; /* RTP processing */ mgcp_processing rtp_processing_cb; mgcp_processing_setup setup_rtp_processing_cb; mgcp_get_format get_net_downlink_format_cb; struct osmo_wqueue gw_fd; struct mgcp_port_range bts_ports; struct mgcp_port_range net_ports; struct mgcp_port_range transcoder_ports; int endp_dscp; int bts_force_ptime; mgcp_change change_cb; mgcp_policy policy_cb; mgcp_reset reset_cb; mgcp_realloc realloc_cb; mgcp_rqnt rqnt_cb; void *data; uint32_t last_call_id; /* trunk handling */ struct mgcp_trunk_config trunk; struct llist_head trunks; /* only used for start with a static configuration */ int last_net_port; int last_bts_port; enum mgcp_role role; /* osmux translator: 0 means disabled, 1 means enabled */ int osmux; /* The BSC-NAT may ask for enabling osmux on demand. This tells us if * the osmux socket is already initialized. */ int osmux_init; /* osmux batch factor: from 1 to 4 maximum */ int osmux_batch; /* osmux batch size (in bytes) */ int osmux_batch_size; /* osmux port */ uint16_t osmux_port; /* Pad circuit with dummy messages until we see the first voice * message. */ uint16_t osmux_dummy; }; /* config management */ struct mgcp_config *mgcp_config_alloc(void); int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg, enum mgcp_role role); int mgcp_vty_init(void); int mgcp_endpoints_allocate(struct mgcp_trunk_config *cfg); void mgcp_release_endp(struct mgcp_endpoint *endp); void mgcp_initialize_endp(struct mgcp_endpoint *endp); int mgcp_reset_transcoder(struct mgcp_config *cfg); void mgcp_format_stats(struct mgcp_endpoint *endp, char *stats, size_t size); int mgcp_parse_stats(struct msgb *msg, uint32_t *ps, uint32_t *os, uint32_t *pr, uint32_t *_or, int *loss, uint32_t *jitter); void mgcp_trunk_set_keepalive(struct mgcp_trunk_config *tcfg, int interval); /* * format helper functions */ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg); /* adc helper */ static inline int mgcp_timeslot_to_endpoint(int multiplex, int timeslot) { if (timeslot == 0) { LOGP(DMGCP, LOGL_ERROR, "Timeslot should not be 0\n"); timeslot = 255; } return timeslot + (32 * multiplex); } static inline void mgcp_endpoint_to_timeslot(int endpoint, int *multiplex, int *timeslot) { *multiplex = endpoint / 32; *timeslot = endpoint % 32; } int mgcp_send_reset_ep(struct mgcp_endpoint *endp, int endpoint); int mgcp_send_reset_all(struct mgcp_config *cfg); int mgcp_create_bind(const char *source_addr, struct osmo_fd *fd, int port); int mgcp_send(struct mgcp_endpoint *endp, int dest, int is_rtp, struct sockaddr_in *addr, char *buf, int rc); int mgcp_udp_send(int fd, struct in_addr *addr, int port, char *buf, int len); #endif openbsc-0.15.0/openbsc/include/openbsc/mgcp_internal.h000066400000000000000000000175561265565154000227550ustar00rootroot00000000000000/* MGCP Private Data */ /* * (C) 2009-2012 by Holger Hans Peter Freyther * (C) 2009-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 . * */ #pragma once #include #include #define CI_UNUSED 0 enum mgcp_connection_mode { MGCP_CONN_NONE = 0, MGCP_CONN_RECV_ONLY = 1, MGCP_CONN_SEND_ONLY = 2, MGCP_CONN_RECV_SEND = MGCP_CONN_RECV_ONLY | MGCP_CONN_SEND_ONLY, MGCP_CONN_LOOPBACK = 4 | MGCP_CONN_RECV_SEND, }; enum mgcp_trunk_type { MGCP_TRUNK_VIRTUAL, MGCP_TRUNK_E1, }; struct mgcp_rtp_stream_state { uint32_t ssrc; uint16_t last_seq; uint32_t last_timestamp; uint32_t err_ts_counter; int32_t last_tsdelta; uint32_t last_arrival_time; }; struct mgcp_rtp_state { int initialized; int patch_ssrc; uint32_t orig_ssrc; int seq_offset; int32_t timestamp_offset; uint32_t packet_duration; struct mgcp_rtp_stream_state in_stream; struct mgcp_rtp_stream_state out_stream; /* jitter and packet loss calculation */ int stats_initialized; uint16_t stats_base_seq; uint16_t stats_max_seq; uint32_t stats_ssrc; uint32_t stats_jitter; int32_t stats_transit; int stats_cycles; }; struct mgcp_rtp_codec { uint32_t rate; int channels; uint32_t frame_duration_num; uint32_t frame_duration_den; int payload_type; char *audio_name; char *subtype_name; }; struct mgcp_rtp_end { /* statistics */ unsigned int packets; unsigned int octets; unsigned int dropped_packets; struct in_addr addr; /* in network byte order */ int rtp_port, rtcp_port; /* audio codec information */ struct mgcp_rtp_codec codec; struct mgcp_rtp_codec alt_codec; /* TODO/XXX: make it generic */ /* per endpoint data */ int frames_per_packet; uint32_t packet_duration_ms; char *fmtp_extra; int output_enabled; int force_output_ptime; /* RTP patching */ int force_constant_ssrc; /* -1: always, 0: don't, 1: once */ int force_aligned_timing; void *rtp_process_data; /* * Each end has a socket... */ struct osmo_fd rtp; struct osmo_fd rtcp; int local_port; int local_alloc; }; enum { MGCP_TAP_BTS_IN, MGCP_TAP_BTS_OUT, MGCP_TAP_NET_IN, MGCP_TAP_NET_OUT, /* last element */ MGCP_TAP_COUNT }; struct mgcp_rtp_tap { int enabled; struct sockaddr_in forward; }; struct mgcp_lco { char *string; char *codec; int pkt_period_min; /* time in ms */ int pkt_period_max; /* time in ms */ }; enum mgcp_type { MGCP_RTP_DEFAULT = 0, MGCP_RTP_TRANSCODED, MGCP_OSMUX_BSC, MGCP_OSMUX_BSC_NAT, }; #include struct mgcp_endpoint { int allocated; uint32_t ci; char *callid; struct mgcp_lco local_options; int conn_mode; int orig_mode; /* backpointer */ struct mgcp_config *cfg; struct mgcp_trunk_config *tcfg; /* port status for bts/net */ struct mgcp_rtp_end bts_end; struct mgcp_rtp_end net_end; /* * For transcoding we will send from the local_port * of trans_bts and it will arrive at trans_net from * where we will forward it to the network. */ struct mgcp_rtp_end trans_bts; struct mgcp_rtp_end trans_net; enum mgcp_type type; /* sequence bits */ struct mgcp_rtp_state net_state; struct mgcp_rtp_state bts_state; /* fields for re-transmission */ char *last_trans; char *last_response; /* tap for the endpoint */ struct mgcp_rtp_tap taps[MGCP_TAP_COUNT]; struct { /* Osmux state: disabled, activating, active */ enum osmux_state state; /* Allocated Osmux circuit ID for this endpoint */ int allocated_cid; /* Used Osmux circuit ID for this endpoint */ uint8_t cid; /* handle to batch messages */ struct osmux_in_handle *in; /* handle to unbatch messages */ struct osmux_out_handle out; /* statistics */ struct { uint32_t chunks; uint32_t octets; } stats; } osmux; }; #define for_each_line(line, save) \ for (line = strline_r(NULL, &save); line;\ line = strline_r(NULL, &save)) static inline char *strline_r(char *str, char **saveptr) { char *result; if (str) *saveptr = str; result = *saveptr; if (*saveptr != NULL) { *saveptr = strpbrk(*saveptr, "\r\n"); if (*saveptr != NULL) { char *eos = *saveptr; if ((*saveptr)[0] == '\r' && (*saveptr)[1] == '\n') (*saveptr)++; (*saveptr)++; if ((*saveptr)[0] == '\0') *saveptr = NULL; *eos = '\0'; } } return result; } #define ENDPOINT_NUMBER(endp) abs((int)(endp - endp->tcfg->endpoints)) /** * Internal structure while parsing a request */ struct mgcp_parse_data { struct mgcp_config *cfg; struct mgcp_endpoint *endp; char *trans; char *save; int found; }; int mgcp_send_dummy(struct mgcp_endpoint *endp); int mgcp_bind_bts_rtp_port(struct mgcp_endpoint *endp, int rtp_port); int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port); int mgcp_bind_trans_bts_rtp_port(struct mgcp_endpoint *enp, int rtp_port); int mgcp_bind_trans_net_rtp_port(struct mgcp_endpoint *enp, int rtp_port); int mgcp_free_rtp_port(struct mgcp_rtp_end *end); /* For transcoding we need to manage an in and an output that are connected */ static inline int endp_back_channel(int endpoint) { return endpoint + 60; } struct mgcp_trunk_config *mgcp_trunk_alloc(struct mgcp_config *cfg, int index); struct mgcp_trunk_config *mgcp_trunk_num(struct mgcp_config *cfg, int index); void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change, struct mgcp_rtp_end *rtp); uint32_t mgcp_rtp_packet_duration(struct mgcp_endpoint *endp, struct mgcp_rtp_end *rtp); void mgcp_state_calc_loss(struct mgcp_rtp_state *s, struct mgcp_rtp_end *, uint32_t *expected, int *loss); uint32_t mgcp_state_calc_jitter(struct mgcp_rtp_state *); /* payload processing default functions */ int mgcp_rtp_processing_default(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end, char *data, int *len, int buf_size); int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end, struct mgcp_rtp_end *src_end); void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp, int *payload_type, const char**subtype_name, const char**fmtp_extra); /* internal RTP Annex A counting */ void mgcp_rtp_annex_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *state, const uint16_t seq, const int32_t transit, const uint32_t ssrc); int mgcp_set_ip_tos(int fd, int tos); enum { MGCP_DEST_NET = 0, MGCP_DEST_BTS, }; #define MGCP_DUMMY_LOAD 0x23 /** * SDP related information */ /* Assume audio frame length of 20ms */ #define DEFAULT_RTP_AUDIO_FRAME_DUR_NUM 20 #define DEFAULT_RTP_AUDIO_FRAME_DUR_DEN 1000 #define DEFAULT_RTP_AUDIO_PACKET_DURATION_MS 20 #define DEFAULT_RTP_AUDIO_DEFAULT_RATE 8000 #define DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS 1 #define PTYPE_UNDEFINED (-1) int mgcp_parse_sdp_data(struct mgcp_endpoint *endp, struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p); int mgcp_set_audio_info(void *ctx, struct mgcp_rtp_codec *codec, int payload_type, const char *audio_name); /** * Internal network related */ static inline const char *mgcp_net_src_addr(struct mgcp_endpoint *endp) { if (endp->cfg->net_ports.bind_addr) return endp->cfg->net_ports.bind_addr; return endp->cfg->source_addr; } static inline const char *mgcp_bts_src_addr(struct mgcp_endpoint *endp) { if (endp->cfg->bts_ports.bind_addr) return endp->cfg->bts_ports.bind_addr; return endp->cfg->source_addr; } openbsc-0.15.0/openbsc/include/openbsc/mgcp_transcode.h000066400000000000000000000041771265565154000231160ustar00rootroot00000000000000/* * (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 . * */ #ifndef OPENBSC_MGCP_TRANSCODE_H #define OPENBSC_MGCP_TRANSCODE_H #include "bscconfig.h" #include #ifdef HAVE_BCG729 #include #include #endif enum audio_format { AF_INVALID, AF_S16, AF_L16, AF_GSM, AF_G729, AF_PCMA, AF_PCMU }; struct mgcp_process_rtp_state { /* decoding */ enum audio_format src_fmt; union { gsm gsm_handle; #ifdef HAVE_BCG729 bcg729DecoderChannelContextStruct *g729_dec; #endif } src; size_t src_frame_size; size_t src_samples_per_frame; /* processing */ /* encoding */ enum audio_format dst_fmt; union { gsm gsm_handle; #ifdef HAVE_BCG729 bcg729EncoderChannelContextStruct *g729_enc; #endif } dst; size_t dst_frame_size; size_t dst_samples_per_frame; int dst_packet_duration; int is_running; uint16_t next_seq; uint32_t next_time; int16_t samples[10*160]; size_t sample_cnt; size_t sample_offs; }; int mgcp_transcoding_setup(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end, struct mgcp_rtp_end *src_end); void mgcp_transcoding_net_downlink_format(struct mgcp_endpoint *endp, int *payload_type, const char**audio_name, const char**fmtp_extra); int mgcp_transcoding_process_rtp(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end, char *data, int *len, int buf_size); int mgcp_transcoding_get_frame_size(void *state_, int nsamples, int dst); #endif /* OPENBSC_MGCP_TRANSCODE_H */ openbsc-0.15.0/openbsc/include/openbsc/misdn.h000066400000000000000000000016611265565154000212330ustar00rootroot00000000000000/* (C) 2008 by Harald Welte * 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 . * */ #ifndef MISDN_H #define MISDN_H #include int mi_setup(int cardnr, struct e1inp_line *line, int release_l2); int mi_e1_line_update(struct e1inp_line *line); #endif openbsc-0.15.0/openbsc/include/openbsc/mncc.h000066400000000000000000000130051265565154000210340ustar00rootroot00000000000000/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */ /* (C) 2008-2009 by Harald Welte * (C) 2008, 2009 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 . * */ #ifndef _MNCC_H #define _MNCC_H #include #include #include struct gsm_network; struct msgb; /* One end of a call */ struct gsm_call { struct llist_head entry; /* network handle */ void *net; /* the 'local' transaction */ uint32_t callref; /* the 'remote' transaction */ uint32_t remote_ref; }; #define MNCC_SETUP_REQ 0x0101 #define MNCC_SETUP_IND 0x0102 #define MNCC_SETUP_RSP 0x0103 #define MNCC_SETUP_CNF 0x0104 #define MNCC_SETUP_COMPL_REQ 0x0105 #define MNCC_SETUP_COMPL_IND 0x0106 /* MNCC_REJ_* is perfomed via MNCC_REL_* */ #define MNCC_CALL_CONF_IND 0x0107 #define MNCC_CALL_PROC_REQ 0x0108 #define MNCC_PROGRESS_REQ 0x0109 #define MNCC_ALERT_REQ 0x010a #define MNCC_ALERT_IND 0x010b #define MNCC_NOTIFY_REQ 0x010c #define MNCC_NOTIFY_IND 0x010d #define MNCC_DISC_REQ 0x010e #define MNCC_DISC_IND 0x010f #define MNCC_REL_REQ 0x0110 #define MNCC_REL_IND 0x0111 #define MNCC_REL_CNF 0x0112 #define MNCC_FACILITY_REQ 0x0113 #define MNCC_FACILITY_IND 0x0114 #define MNCC_START_DTMF_IND 0x0115 #define MNCC_START_DTMF_RSP 0x0116 #define MNCC_START_DTMF_REJ 0x0117 #define MNCC_STOP_DTMF_IND 0x0118 #define MNCC_STOP_DTMF_RSP 0x0119 #define MNCC_MODIFY_REQ 0x011a #define MNCC_MODIFY_IND 0x011b #define MNCC_MODIFY_RSP 0x011c #define MNCC_MODIFY_CNF 0x011d #define MNCC_MODIFY_REJ 0x011e #define MNCC_HOLD_IND 0x011f #define MNCC_HOLD_CNF 0x0120 #define MNCC_HOLD_REJ 0x0121 #define MNCC_RETRIEVE_IND 0x0122 #define MNCC_RETRIEVE_CNF 0x0123 #define MNCC_RETRIEVE_REJ 0x0124 #define MNCC_USERINFO_REQ 0x0125 #define MNCC_USERINFO_IND 0x0126 #define MNCC_REJ_REQ 0x0127 #define MNCC_REJ_IND 0x0128 #define MNCC_BRIDGE 0x0200 #define MNCC_FRAME_RECV 0x0201 #define MNCC_FRAME_DROP 0x0202 #define MNCC_LCHAN_MODIFY 0x0203 #define MNCC_RTP_CREATE 0x0204 #define MNCC_RTP_CONNECT 0x0205 #define MNCC_RTP_FREE 0x0206 #define GSM_TCHF_FRAME 0x0300 #define GSM_TCHF_FRAME_EFR 0x0301 #define GSM_TCHH_FRAME 0x0302 #define GSM_TCH_FRAME_AMR 0x0303 #define GSM_BAD_FRAME 0x03ff #define MNCC_SOCKET_HELLO 0x0400 #define GSM_MAX_FACILITY 128 #define GSM_MAX_SSVERSION 128 #define GSM_MAX_USERUSER 128 #define MNCC_F_BEARER_CAP 0x0001 #define MNCC_F_CALLED 0x0002 #define MNCC_F_CALLING 0x0004 #define MNCC_F_REDIRECTING 0x0008 #define MNCC_F_CONNECTED 0x0010 #define MNCC_F_CAUSE 0x0020 #define MNCC_F_USERUSER 0x0040 #define MNCC_F_PROGRESS 0x0080 #define MNCC_F_EMERGENCY 0x0100 #define MNCC_F_FACILITY 0x0200 #define MNCC_F_SSVERSION 0x0400 #define MNCC_F_CCCAP 0x0800 #define MNCC_F_KEYPAD 0x1000 #define MNCC_F_SIGNAL 0x2000 struct gsm_mncc { /* context based information */ uint32_t msg_type; uint32_t callref; /* which fields are present */ uint32_t fields; /* data derived informations (MNCC_F_ based) */ struct gsm_mncc_bearer_cap bearer_cap; struct gsm_mncc_number called; struct gsm_mncc_number calling; struct gsm_mncc_number redirecting; struct gsm_mncc_number connected; struct gsm_mncc_cause cause; struct gsm_mncc_progress progress; struct gsm_mncc_useruser useruser; struct gsm_mncc_facility facility; struct gsm_mncc_cccap cccap; struct gsm_mncc_ssversion ssversion; struct { int sup; int inv; } clir; int signal; /* data derived information, not MNCC_F based */ int keypad; int more; int notify; /* 0..127 */ int emergency; char imsi[16]; unsigned char lchan_type; unsigned char lchan_mode; }; struct gsm_data_frame { uint32_t msg_type; uint32_t callref; unsigned char data[0]; }; #define MNCC_SOCK_VERSION 5 struct gsm_mncc_hello { uint32_t msg_type; uint32_t version; /* send the sizes of the structs */ uint32_t mncc_size; uint32_t data_frame_size; /* send some offsets */ uint32_t called_offset; uint32_t signal_offset; uint32_t emergency_offset; uint32_t lchan_type_offset; }; struct gsm_mncc_rtp { uint32_t msg_type; uint32_t callref; uint32_t ip; uint16_t port; uint32_t payload_type; uint32_t payload_msg_type; }; char *get_mncc_name(int value); void mncc_set_cause(struct gsm_mncc *data, int loc, int val); void cc_tx_to_mncc(struct gsm_network *net, struct msgb *msg); /* input from CC code into mncc_builtin */ int int_mncc_recv(struct gsm_network *net, struct msgb *msg); /* input from CC code into mncc_sock */ int mncc_sock_from_cc(struct gsm_network *net, struct msgb *msg); int mncc_sock_init(struct gsm_network *gsmnet); #define mncc_is_data_frame(msg_type) \ (msg_type == GSM_TCHF_FRAME \ || msg_type == GSM_TCHF_FRAME_EFR \ || msg_type == GSM_TCHH_FRAME \ || msg_type == GSM_TCH_FRAME_AMR \ || msg_type == GSM_BAD_FRAME) #endif openbsc-0.15.0/openbsc/include/openbsc/mncc_int.h000066400000000000000000000003021265565154000217020ustar00rootroot00000000000000#ifndef _MNCC_INT_H #define _MNCC_INT_H #include struct mncc_int { uint8_t def_codec[2]; }; extern struct mncc_int mncc_int; uint8_t mncc_codec_for_mode(int lchan_type); #endif openbsc-0.15.0/openbsc/include/openbsc/nat_rewrite_trie.h000066400000000000000000000025721265565154000234710ustar00rootroot00000000000000/* * (C) 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 . * */ #ifndef NAT_REWRITE_FILE_H #define NAT_REWRITE_FILE_H #include 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 openbsc-0.15.0/openbsc/include/openbsc/network_listen.h000066400000000000000000000007351265565154000231710ustar00rootroot00000000000000#ifndef _OPENBSC_NWL_H #define _OPENBSC_NWL_H #include #include void ipac_nwl_init(void); /* Start a NWL test. It will raise the S_IPAC_TEST_COMPLETE signal. */ int ipac_nwl_test_start(struct gsm_bts_trx *trx, uint8_t testnr, const uint8_t *phys_conf, unsigned int phys_conf_len); int ipac_rxlevstat2whitelist(uint16_t *buf, const struct rxlev_stats *st, uint8_t min_rxlev, uint16_t max_num_arfcns); #endif /* _OPENBSC_NWL_H */ openbsc-0.15.0/openbsc/include/openbsc/openbscdefines.h000066400000000000000000000020551265565154000231060ustar00rootroot00000000000000/* * (C) 2009 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 . * */ #ifndef OPENBSCDEFINES_H #define OPENBSCDEFINES_H #ifdef BUILDING_ON_WINDOWS #ifdef BUILDING_OPENBSC #define BSC_API __declspec(dllexport) #else #define BSC_API __declspec(dllimport) #endif #else #define BSC_API __attribute__((visibility("default"))) #endif #endif openbsc-0.15.0/openbsc/include/openbsc/osmo_bsc.h000066400000000000000000000034731265565154000217300ustar00rootroot00000000000000/* OpenBSC BSC code */ #ifndef OSMO_BSC_H #define OSMO_BSC_H #include "bsc_api.h" #include "bsc_msg_filter.h" #define BSS_SEND_USSD 1 enum bsc_con { BSC_CON_SUCCESS, BSC_CON_REJECT_NO_LINK, BSC_CON_REJECT_RF_GRACE, BSC_CON_NO_MEM, }; struct sccp_connection; struct osmo_msc_data; struct bsc_msc_connection; struct osmo_bsc_sccp_con { struct llist_head entry; int ciphering_handled; /* for audio handling */ uint16_t cic; int rtp_port; /* for advanced ping/pong */ int send_ping; /* SCCP connection realted */ struct sccp_connection *sccp; struct osmo_msc_data *msc; struct osmo_timer_list sccp_it_timeout; struct osmo_timer_list sccp_cc_timeout; struct llist_head sccp_queue; unsigned int sccp_queue_size; struct gsm_subscriber_connection *conn; uint8_t new_subscriber; struct bsc_filter_state filter_state; }; struct bsc_api *osmo_bsc_api(); int bsc_queue_for_msc(struct osmo_bsc_sccp_con *conn, struct msgb *msg); int bsc_open_connection(struct osmo_bsc_sccp_con *sccp, struct msgb *msg); enum bsc_con bsc_create_new_connection(struct gsm_subscriber_connection *conn, struct osmo_msc_data *msc, int send_ping); int bsc_delete_connection(struct osmo_bsc_sccp_con *sccp); struct osmo_msc_data *bsc_find_msc(struct gsm_subscriber_connection *conn, struct msgb *); int bsc_scan_bts_msg(struct gsm_subscriber_connection *conn, struct msgb *msg); int bsc_scan_msc_msg(struct gsm_subscriber_connection *conn, struct msgb *msg); int bsc_send_welcome_ussd(struct gsm_subscriber_connection *conn); int bsc_handle_udt(struct osmo_msc_data *msc, struct msgb *msg, unsigned int length); int bsc_handle_dt1(struct osmo_bsc_sccp_con *conn, struct msgb *msg, unsigned int len); int bsc_ctrl_cmds_install(); void bsc_gen_location_state_trap(struct gsm_bts *bts); struct llist_head *bsc_access_lists(void); #endif openbsc-0.15.0/openbsc/include/openbsc/osmo_bsc_grace.h000066400000000000000000000020721265565154000230630ustar00rootroot00000000000000/* * (C) 2010-2013 by Holger Hans Peter Freyther * (C) 2010-2013 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 . * */ #ifndef OSMO_BSC_GRACE_H #define OSMO_BSC_GRACE_H #include "gsm_data.h" struct osmo_msc_data; int bsc_grace_allow_new_connection(struct gsm_network *net, struct gsm_bts *bts); int bsc_grace_paging_request(struct gsm_subscriber *sub, int type, struct osmo_msc_data *msc); #endif openbsc-0.15.0/openbsc/include/openbsc/osmo_bsc_rf.h000066400000000000000000000033271265565154000224150ustar00rootroot00000000000000#ifndef OSMO_BSC_RF #define OSMO_BSC_RF #include #include #include enum osmo_bsc_rf_opstate { OSMO_BSC_RF_OPSTATE_INOPERATIONAL, OSMO_BSC_RF_OPSTATE_OPERATIONAL, }; enum osmo_bsc_rf_adminstate { OSMO_BSC_RF_ADMINSTATE_UNLOCKED, OSMO_BSC_RF_ADMINSTATE_LOCKED, }; enum osmo_bsc_rf_policy { OSMO_BSC_RF_POLICY_OFF, OSMO_BSC_RF_POLICY_ON, OSMO_BSC_RF_POLICY_GRACE, OSMO_BSC_RF_POLICY_UNKNOWN, }; struct gsm_network; struct osmo_bsc_rf { /* the value of signal.h */ int policy; struct osmo_fd listen; struct gsm_network *gsm_network; const char *last_state_command; char *last_rf_lock_ctrl_command; /* delay the command */ char last_request; struct osmo_timer_list delay_cmd; /* verify that RF is up as it should be */ struct osmo_timer_list rf_check; /* some handling for the automatic grace switch */ struct osmo_timer_list grace_timeout; /* auto RF switch-off due lack of MSC connection */ struct osmo_timer_list auto_off_timer; }; struct osmo_bsc_rf_conn { struct osmo_wqueue queue; struct osmo_bsc_rf *rf; }; const char *osmo_bsc_rf_get_opstate_name(enum osmo_bsc_rf_opstate opstate); const char *osmo_bsc_rf_get_adminstate_name(enum osmo_bsc_rf_adminstate adminstate); const char *osmo_bsc_rf_get_policy_name(enum osmo_bsc_rf_policy policy); enum osmo_bsc_rf_opstate osmo_bsc_rf_get_opstate_by_bts(struct gsm_bts *bts); enum osmo_bsc_rf_adminstate osmo_bsc_rf_get_adminstate_by_bts(struct gsm_bts *bts); enum osmo_bsc_rf_policy osmo_bsc_rf_get_policy_by_bts(struct gsm_bts *bts); struct osmo_bsc_rf *osmo_bsc_rf_create(const char *path, struct gsm_network *net); void osmo_bsc_rf_schedule_lock(struct osmo_bsc_rf *rf, char cmd); #endif openbsc-0.15.0/openbsc/include/openbsc/osmo_msc.h000066400000000000000000000003161265565154000217340ustar00rootroot00000000000000/* Routines for the MSC handling */ #ifndef OSMO_MSC_H #define OSMO_MSC_H #include "bsc_api.h" struct bsc_api *msc_bsc_api(); void msc_release_connection(struct gsm_subscriber_connection *conn); #endif openbsc-0.15.0/openbsc/include/openbsc/osmo_msc_data.h000066400000000000000000000057031265565154000227320ustar00rootroot00000000000000/* * Data for the true BSC * * (C) 2010-2015 by Holger Hans Peter Freyther * (C) 2010-2015 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 . * */ #ifndef _OSMO_MSC_DATA_H #define _OSMO_MSC_DATA_H #include "bsc_msc.h" #include #include #include struct osmo_bsc_rf; struct gsm_network; struct gsm_audio_support { uint8_t hr : 1, ver : 7; }; enum { MSC_CON_TYPE_NORMAL, MSC_CON_TYPE_LOCAL, }; struct osmo_msc_data { struct llist_head entry; /* Back pointer */ struct gsm_network *network; int allow_emerg; int type; /* local call routing */ char *local_pref; regex_t local_pref_reg; /* Connection data */ char *bsc_token; uint8_t bsc_key[16]; uint8_t bsc_key_present; int ping_timeout; int pong_timeout; struct osmo_timer_list ping_timer; struct osmo_timer_list pong_timer; int advanced_ping; struct bsc_msc_connection *msc_con; int core_mnc; int core_mcc; int core_lac; int core_ci; int rtp_base; /* audio codecs */ struct gsm48_multi_rate_conf amr_conf; struct gsm_audio_support **audio_support; int audio_length; /* destinations */ struct llist_head dests; /* ussd welcome text */ char *ussd_welcome_txt; /* mgcp agent */ struct osmo_wqueue mgcp_agent; int nr; /* ussd msc connection lost text */ char *ussd_msc_lost_txt; /* ussd text when MSC has entered the grace period */ char *ussd_grace_txt; char *acc_lst_name; }; /* * Per BSC data. */ struct osmo_bsc_data { struct gsm_network *network; /* msc configuration */ struct llist_head mscs; /* rf ctl related bits */ char *mid_call_txt; int mid_call_timeout; char *rf_ctrl_name; struct osmo_bsc_rf *rf_ctrl; int auto_off_timeout; /* ussd text when there is no MSC available */ char *ussd_no_msc_txt; char *acc_lst_name; }; int osmo_bsc_msc_init(struct osmo_msc_data *msc); int osmo_bsc_sccp_init(struct gsm_network *gsmnet); int msc_queue_write(struct bsc_msc_connection *conn, struct msgb *msg, int proto); int msc_queue_write_with_ping(struct bsc_msc_connection *, struct msgb *msg, int proto); int osmo_bsc_audio_init(struct gsm_network *network); struct osmo_msc_data *osmo_msc_data_find(struct gsm_network *, int); struct osmo_msc_data *osmo_msc_data_alloc(struct gsm_network *, int); #endif openbsc-0.15.0/openbsc/include/openbsc/osmux.h000066400000000000000000000017301265565154000212710ustar00rootroot00000000000000#ifndef _OPENBSC_OSMUX_H_ #define _OPENBSC_OSMUX_H_ #include #define OSMUX_PORT 1984 enum { OSMUX_ROLE_BSC = 0, OSMUX_ROLE_BSC_NAT, }; int osmux_init(int role, struct mgcp_config *cfg); int osmux_enable_endpoint(struct mgcp_endpoint *endp, int role, struct in_addr *addr, uint16_t port); void osmux_disable_endpoint(struct mgcp_endpoint *endp); void osmux_allocate_cid(struct mgcp_endpoint *endp); void osmux_release_cid(struct mgcp_endpoint *endp); int osmux_xfrm_to_rtp(struct mgcp_endpoint *endp, int type, char *buf, int rc); int osmux_xfrm_to_osmux(int type, char *buf, int rc, struct mgcp_endpoint *endp); int osmux_send_dummy(struct mgcp_endpoint *endp); int osmux_get_cid(void); void osmux_put_cid(uint8_t osmux_cid); int osmux_used_cid(void); enum osmux_state { OSMUX_STATE_DISABLED = 0, OSMUX_STATE_ACTIVATING, OSMUX_STATE_ENABLED, }; enum osmux_usage { OSMUX_USAGE_OFF = 0, OSMUX_USAGE_ON = 1, OSMUX_USAGE_ONLY = 2, }; #endif openbsc-0.15.0/openbsc/include/openbsc/paging.h000066400000000000000000000045321265565154000213660ustar00rootroot00000000000000/* Paging helper and manager.... */ /* (C) 2009 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 . * */ #ifndef PAGING_H #define PAGING_H #include #include #include #include "gsm_data.h" #include "gsm_subscriber.h" #include /** * A pending paging request */ struct gsm_paging_request { /* list_head for list of all paging requests */ struct llist_head entry; /* the subscriber which we're paging. Later gsm_paging_request * should probably become a part of the gsm_subscriber struct? */ struct gsm_subscriber *subscr; /* back-pointer to the BTS on which we are paging */ struct gsm_bts *bts; /* what kind of channel type do we ask the MS to establish */ int chan_type; /* Timer 3113: how long do we try to page? */ struct osmo_timer_list T3113; /* How often did we ask the BTS to page? */ int attempts; /* callback to be called in case paging completes */ gsm_cbfn *cbfn; void *cbfn_param; }; /* schedule paging request */ int paging_request(struct gsm_network *network, struct gsm_subscriber *subscr, int type, gsm_cbfn *cbfn, void *data); int paging_request_bts(struct gsm_bts *bts, struct gsm_subscriber *subscr, int type, gsm_cbfn *cbfn, void *data); /* stop paging requests */ void paging_request_stop(struct gsm_bts *bts, struct gsm_subscriber *subscr, struct gsm_subscriber_connection *conn, struct msgb *msg); /* update paging load */ void paging_update_buffer_space(struct gsm_bts *bts, uint16_t); /* pending paging requests */ unsigned int paging_pending_requests_nr(struct gsm_bts *bts); void *paging_get_data(struct gsm_bts *bts, struct gsm_subscriber *subscr); #endif openbsc-0.15.0/openbsc/include/openbsc/rest_octets.h000066400000000000000000000055571265565154000224670ustar00rootroot00000000000000#ifndef _REST_OCTETS_H #define _REST_OCTETS_H #include /* generate SI1 rest octets */ int rest_octets_si1(uint8_t *data, uint8_t *nch_pos, int is1800_net); struct gsm48_si_selection_params { uint16_t penalty_time:5, temp_offs:3, cell_resel_off:6, cbq:1, present:1; }; struct gsm48_si_power_offset { uint8_t power_offset:2, present:1; }; struct gsm48_si3_gprs_ind { uint8_t si13_position:1, ra_colour:3, present:1; }; struct gsm48_lsa_params { uint32_t prio_thr:3, lsa_offset:3, mcc:12, mnc:12; unsigned int present; }; struct gsm48_si_ro_info { struct gsm48_si_selection_params selection_params; struct gsm48_si_power_offset power_offset; uint8_t si2ter_indicator; uint8_t early_cm_ctrl; struct { uint8_t where:3, present:1; } scheduling; struct gsm48_si3_gprs_ind gprs_ind; /* SI 4 specific */ struct gsm48_lsa_params lsa_params; uint16_t cell_id; uint8_t break_ind; /* do we have SI7 + SI8 ? */ }; /* Generate SI3 Rest Octests (Chapter 10.5.2.34 / Table 10.4.72) */ int rest_octets_si3(uint8_t *data, const struct gsm48_si_ro_info *si3); /* Generate SI4 Rest Octets (Chapter 10.5.2.35) */ int rest_octets_si4(uint8_t *data, const struct gsm48_si_ro_info *si4, int len); enum pbcch_carrier_type { PBCCH_BCCH, PBCCH_ARFCN, PBCCH_MAIO }; /* TS 03.60 Chapter 6.3.3.1: Network Mode of Operation */ enum gprs_nmo { GPRS_NMO_I = 0, /* CS pagin on GPRS paging or traffic channel */ GPRS_NMO_II = 1, /* all paging on CCCH */ GPRS_NMO_III = 2, /* no paging coordination */ }; /* TS 04.60 12.24 */ struct gprs_cell_options { enum gprs_nmo nmo; /* T3168: wait for packet uplink assignment message */ uint32_t t3168; /* in milliseconds */ /* T3192: wait for release of the TBF after reception of the final block */ uint32_t t3192; /* in milliseconds */ uint32_t drx_timer_max;/* in seconds */ uint32_t bs_cv_max; uint8_t ext_info_present; struct { uint8_t egprs_supported; uint8_t use_egprs_p_ch_req; uint8_t bep_period; uint8_t pfc_supported; uint8_t dtm_supported; uint8_t bss_paging_coordination; } ext_info; }; /* TS 04.60 Table 12.9.2 */ struct gprs_power_ctrl_pars { uint8_t alpha; uint8_t t_avg_w; uint8_t t_avg_t; uint8_t pc_meas_chan; uint8_t n_avg_i; }; struct gsm48_si13_info { struct gprs_cell_options cell_opts; struct gprs_power_ctrl_pars pwr_ctrl_pars; uint8_t bcch_change_mark; uint8_t si_change_field; uint8_t pbcch_present; union { struct { uint8_t rac; uint8_t spgc_ccch_sup; uint8_t net_ctrl_ord; uint8_t prio_acc_thr; } no_pbcch; struct { uint8_t psi1_rep_per; uint8_t pb; uint8_t tsc; uint8_t tn; enum pbcch_carrier_type carrier_type; uint16_t arfcn; uint8_t maio; } pbcch; }; }; /* Generate SI13 Rest Octests (Chapter 10.5.2.37b) */ int rest_octets_si13(uint8_t *data, const struct gsm48_si13_info *si13); #endif /* _REST_OCTETS_H */ openbsc-0.15.0/openbsc/include/openbsc/rrlp.h000066400000000000000000000001251265565154000210720ustar00rootroot00000000000000#ifndef _RRLP_H #define _RRLP_H void on_dso_load_rrlp(void); #endif /* _RRLP_H */ openbsc-0.15.0/openbsc/include/openbsc/rs232.h000066400000000000000000000002741265565154000207730ustar00rootroot00000000000000#ifndef _RS232_H #define _RS232_H int rs232_setup(const char *serial_port, unsigned int delay_ms, struct gsm_bts *bts); int handle_serial_msg(struct msgb *msg); #endif /* _RS232_H */ openbsc-0.15.0/openbsc/include/openbsc/rtp_proxy.h000066400000000000000000000046141265565154000221700ustar00rootroot00000000000000#ifndef _RTP_PROXY_H #define _RTP_PROXY_H /* RTP proxy handling for ip.access nanoBTS */ /* (C) 2009 by Harald Welte * 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 . * */ #include #include #include #include #define RTP_PT_GSM_FULL 3 #define RTP_PT_GSM_HALF 96 #define RTP_PT_GSM_EFR 97 #define RTP_PT_AMR 98 #define RTP_LEN_GSM_FULL 33 #define RTP_LEN_GSM_HALF 15 #define RTP_LEN_GSM_EFR 31 #define RTP_GSM_DURATION 160 enum rtp_rx_action { RTP_NONE, RTP_PROXY, RTP_RECV_UPSTREAM, }; enum rtp_tx_action { RTP_SEND_NONE, RTP_SEND_DOWNSTREAM, }; struct rtp_sub_socket { struct sockaddr_in sin_local; struct sockaddr_in sin_remote; struct osmo_fd bfd; /* linked list of to-be-transmitted msgb's */ struct llist_head tx_queue; }; struct rtp_socket { struct llist_head list; struct rtp_sub_socket rtp; struct rtp_sub_socket rtcp; /* what should we do on receive? */ enum rtp_rx_action rx_action; union { struct { struct rtp_socket *other_sock; } proxy; struct { struct gsm_network *net; uint32_t callref; } receive; }; enum rtp_tx_action tx_action; struct { uint16_t sequence; uint32_t timestamp; uint32_t ssrc; struct timeval last_tv; } transmit; }; struct rtp_socket *rtp_socket_create(void); int rtp_socket_bind(struct rtp_socket *rs, uint32_t ip); int rtp_socket_connect(struct rtp_socket *rs, uint32_t ip, uint16_t port); int rtp_socket_proxy(struct rtp_socket *this, struct rtp_socket *other); int rtp_socket_upstream(struct rtp_socket *this, struct gsm_network *net, uint32_t callref); int rtp_socket_free(struct rtp_socket *rs); int rtp_send_frame(struct rtp_socket *rs, struct gsm_data_frame *frame); #endif /* _RTP_PROXY_H */ openbsc-0.15.0/openbsc/include/openbsc/sgsn.h000066400000000000000000000056751265565154000211040ustar00rootroot00000000000000#ifndef _SGSN_H #define _SGSN_H #include #include #include #include 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 }; 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; 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 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 gprs_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; }; extern struct sgsn_instance *sgsn; /* sgsn_vty.c */ int sgsn_vty_init(void); int sgsn_parse_config(const char *config_file, struct sgsn_config *cfg); /* 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); /* 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 openbsc-0.15.0/openbsc/include/openbsc/signal.h000066400000000000000000000135451265565154000214020ustar00rootroot00000000000000/* Generic signalling/notification infrastructure */ /* (C) 2009-2010, 2015 by Holger Hans Peter Freyther * (C) 2009 by Harald Welte * (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 . * */ #ifndef OPENBSC_SIGNAL_H #define OPENBSC_SIGNAL_H #include #include #include #include /* * Signalling subsystems */ enum signal_subsystems { SS_PAGING, SS_SMS, SS_ABISIP, SS_NM, SS_LCHAN, SS_SUBSCR, SS_SCALL, SS_CHALLOC, SS_IPAC_NWL, SS_RF, SS_MSC, SS_HO, SS_CCCH, SS_SGSN, }; /* SS_PAGING signals */ enum signal_paging { S_PAGING_SUCCEEDED, S_PAGING_EXPIRED, }; /* SS_SMS signals */ enum signal_sms { S_SMS_SUBMITTED, /* A SMS has been successfully submitted to us */ S_SMS_DELIVERED, /* A SMS has been successfully delivered to a MS */ S_SMS_SMMA, /* A MS tells us it has more space available */ S_SMS_MEM_EXCEEDED, /* A MS tells us it has no more space available */ S_SMS_UNKNOWN_ERROR, /* A MS tells us it has an error */ }; /* SS_ABISIP signals */ enum signal_abisip { S_ABISIP_CRCX_ACK, S_ABISIP_MDCX_ACK, S_ABISIP_DLCX_IND, }; /* SS_NM signals */ enum signal_nm { S_NM_SW_ACTIV_REP, /* GSM 12.21 software activated report */ S_NM_FAIL_REP, /* GSM 12.21 failure event report */ S_NM_NACK, /* GSM 12.21 various NM_MT_*_NACK happened */ S_NM_IPACC_NACK, /* GSM 12.21 nanoBTS extensions NM_MT_IPACC_*_*_NACK happened */ S_NM_IPACC_ACK, /* GSM 12.21 nanoBTS extensions NM_MT_IPACC_*_*_ACK happened */ S_NM_IPACC_RESTART_ACK, /* nanoBTS has send a restart ack */ S_NM_IPACC_RESTART_NACK,/* nanoBTS has send a restart ack */ S_NM_TEST_REP, /* GSM 12.21 Test Report */ S_NM_STATECHG_OPER, /* Operational State changed*/ S_NM_STATECHG_ADM, /* Administrative State changed */ S_NM_OM2K_CONF_RES, /* OM2K Configuration Result */ }; /* SS_LCHAN signals */ enum signal_lchan { /* * The lchan got freed with an use_count != 0 and error * recovery needs to be carried out from within the * signal handler. */ S_LCHAN_UNEXPECTED_RELEASE, S_LCHAN_ACTIVATE_ACK, /* 08.58 Channel Activate ACK */ S_LCHAN_ACTIVATE_NACK, /* 08.58 Channel Activate NACK */ S_LCHAN_HANDOVER_COMPL, /* 04.08 Handover Completed */ S_LCHAN_HANDOVER_FAIL, /* 04.08 Handover Failed */ S_LCHAN_HANDOVER_DETECT, /* 08.58 Handover Detect */ S_LCHAN_MEAS_REP, /* 08.58 Measurement Report */ }; /* SS_CHALLOC signals */ enum signal_challoc { S_CHALLOC_ALLOC_FAIL, /* allocation of lchan has failed */ S_CHALLOC_FREED, /* lchan has been successfully freed */ }; /* SS_SUBSCR signals */ enum signal_subscr { S_SUBSCR_ATTACHED, S_SUBSCR_DETACHED, S_SUBSCR_IDENTITY, /* we've received some identity information */ }; /* SS_SCALL signals */ enum signal_scall { S_SCALL_SUCCESS, S_SCALL_EXPIRED, S_SCALL_DETACHED, }; /* SS_IPAC_NWL signals */ enum signal_ipaccess { S_IPAC_NWL_COMPLETE, }; enum signal_global { S_GLOBAL_BTS_CLOSE_OM, }; /* SS_RF signals */ enum signal_rf { S_RF_OFF, S_RF_ON, S_RF_GRACE, }; struct gsm_subscriber; struct paging_signal_data { struct gsm_subscriber *subscr; struct gsm_bts *bts; int paging_result; /* NULL in case the paging didn't work */ struct gsm_subscriber_connection *conn; }; struct scall_signal_data { struct gsm_subscriber *subscr; struct gsm_subscriber_connection *conn; void *data; }; struct ipacc_ack_signal_data { struct gsm_bts_trx *trx; uint8_t msg_type; }; struct abis_om2k_mo; struct nm_statechg_signal_data { struct gsm_bts *bts; uint8_t obj_class; void *obj; struct gsm_nm_state *old_state; struct gsm_nm_state *new_state; /* This pointer is vaold for TS 12.21 MO */ struct abis_om_obj_inst *obj_inst; /* This pointer is vaold for RBS2000 MO */ struct abis_om2k_mo *om2k_mo; }; struct nm_om2k_signal_data { struct gsm_bts *bts; void *obj; struct abis_om2k_mo *om2k_mo; uint8_t accordance_ind; }; struct nm_nack_signal_data { struct msgb *msg; struct gsm_bts *bts; uint8_t mt; }; struct challoc_signal_data { struct gsm_bts *bts; struct gsm_lchan *lchan; enum gsm_chan_t type; }; struct rf_signal_data { struct gsm_network *net; }; struct sms_signal_data { /* The transaction where this occured */ struct gsm_trans *trans; /* Can be NULL for SMMA */ struct gsm_sms *sms; /* int paging result. Only the ones with > 0 */ int paging_result; }; struct lchan_signal_data { /* The lchan the signal happened on */ struct gsm_lchan *lchan; /* Measurement reports on this lchan */ struct gsm_meas_rep *mr; }; /* MSC signals */ enum signal_msc { S_MSC_LOST, S_MSC_CONNECTED, S_MSC_AUTHENTICATED, }; struct osmo_msc_data; struct msc_signal_data { struct osmo_msc_data *data; }; /* SS_CCCH signals */ enum signal_ccch { S_CCCH_PAGING_LOAD, S_CCCH_RACH_LOAD, }; struct ccch_signal_data { struct gsm_bts *bts; uint16_t pg_buf_space; uint16_t rach_slot_count; uint16_t rach_busy_count; uint16_t rach_access_count; }; /* GPRS SGSN signals SS_SGSN */ enum signal_sgsn { S_SGSN_ATTACH, S_SGSN_DETACH, S_SGSN_UPDATE, S_SGSN_PDP_ACT, S_SGSN_PDP_DEACT, S_SGSN_PDP_TERMINATE, S_SGSN_PDP_FREE, S_SGSN_MM_FREE, }; struct sgsn_mm_ctx; struct sgsn_signal_data { struct sgsn_mm_ctx *mm; struct sgsn_pdp_ctx *pdp; /* non-NULL for PDP_ACT, PDP_DEACT, PDP_FREE */ }; #endif openbsc-0.15.0/openbsc/include/openbsc/silent_call.h000066400000000000000000000007261265565154000224130ustar00rootroot00000000000000#ifndef _SILENT_CALL_H #define _SILENT_CALL_H struct gsm_subscriber_connection; extern int gsm_silent_call_start(struct gsm_subscriber *subscr, void *data, int type); extern int gsm_silent_call_stop(struct gsm_subscriber *subscr); extern int silent_call_rx(struct gsm_subscriber_connection *conn, struct msgb *msg); extern int silent_call_reroute(struct gsm_subscriber_connection *conn, struct msgb *msg); #endif /* _SILENT_CALL_H */ openbsc-0.15.0/openbsc/include/openbsc/smpp.h000066400000000000000000000001631265565154000210740ustar00rootroot00000000000000#pragma once int smpp_openbsc_init(void *ctx, uint16_t port); void smpp_openbsc_set_net(struct gsm_network *net); openbsc-0.15.0/openbsc/include/openbsc/sms_queue.h000066400000000000000000000007311265565154000221240ustar00rootroot00000000000000#ifndef SMS_QUEUE_H #define SMS_QUEUE_H struct gsm_network; struct gsm_sms_queue; struct vty; int sms_queue_start(struct gsm_network *, int in_flight); int sms_queue_trigger(struct gsm_sms_queue *); /* vty helper functions */ int sms_queue_stats(struct gsm_sms_queue *, struct vty* vty); int sms_queue_set_max_pending(struct gsm_sms_queue *, int max); int sms_queue_set_max_failure(struct gsm_sms_queue *, int fail); int sms_queue_clear(struct gsm_sms_queue *); #endif openbsc-0.15.0/openbsc/include/openbsc/socket.h000066400000000000000000000005021265565154000214020ustar00rootroot00000000000000#ifndef _BSC_SOCKET_H #define _BSC_SOCKET_H #include #ifndef IPPROTO_GRE #define IPPROTO_GRE 47 #endif int make_sock(struct osmo_fd *bfd, int proto, uint32_t ip, uint16_t port, int priv_nr, int (*cb)(struct osmo_fd *fd, unsigned int what), void *data); #endif /* _BSC_SOCKET_H */ openbsc-0.15.0/openbsc/include/openbsc/system_information.h000066400000000000000000000002611265565154000240450ustar00rootroot00000000000000#ifndef _SYSTEM_INFO_H #define _SYSTEM_INFO_H #include struct gsm_bts; int gsm_generate_si(struct gsm_bts *bts, enum osmo_sysinfo_type type); #endif openbsc-0.15.0/openbsc/include/openbsc/token_auth.h000066400000000000000000000001501265565154000222520ustar00rootroot00000000000000#ifndef _TOKEN_AUTH_H #define _TOKEN_AUTH_H void on_dso_load_token(void); #endif /* _TOKEN_AUTH_H */ openbsc-0.15.0/openbsc/include/openbsc/transaction.h000066400000000000000000000037621265565154000224520ustar00rootroot00000000000000#ifndef _TRANSACT_H #define _TRANSACT_H #include #include #include #include #include #include #include /* One transaction */ struct gsm_trans { /* Entry in list of all transactions */ struct llist_head entry; /* Back pointer to the netweork struct */ struct gsm_network *net; /* The protocol within which we live */ uint8_t protocol; /* The current transaction ID */ uint8_t transaction_id; /* To whom we belong, unique identifier of remote MM entity */ struct gsm_subscriber *subscr; /* The associated connection we are using to transmit messages */ struct gsm_subscriber_connection *conn; /* reference from MNCC or other application */ uint32_t callref; /* if traffic channel receive was requested */ int tch_recv; /* is thats one paging? */ struct subscr_request *paging_request; union { struct { /* current call state */ int state; /* current timer and message queue */ int Tcurrent; /* current CC timer */ int T308_second; /* used to send release again */ struct osmo_timer_list timer; struct gsm_mncc msg; /* stores setup/disconnect/release message */ } cc; struct { struct gsm411_smc_inst smc_inst; struct gsm411_smr_inst smr_inst; struct gsm_sms *sms; } sms; }; }; struct gsm_trans *trans_find_by_id(struct gsm_subscriber_connection *conn, uint8_t proto, uint8_t trans_id); struct gsm_trans *trans_find_by_callref(struct gsm_network *net, uint32_t callref); struct gsm_trans *trans_alloc(struct gsm_network *net, struct gsm_subscriber *subscr, uint8_t protocol, uint8_t trans_id, uint32_t callref); void trans_free(struct gsm_trans *trans); int trans_assign_trans_id(struct gsm_network *net, struct gsm_subscriber *subscr, uint8_t protocol, uint8_t ti_flag); int trans_has_conn(const struct gsm_subscriber_connection *conn); #endif openbsc-0.15.0/openbsc/include/openbsc/trau_mux.h000066400000000000000000000051451265565154000217660ustar00rootroot00000000000000/* Simple TRAU frame reflector to route voice calls */ /* (C) 2009 by Harald Welte * 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 . * */ /* The "TRAU mux map" defines which particular 16kbit sub-slot (in which E1 * timeslot on which E1 interface) should be directly muxed to which other * sub-slot. Entries in the mux map are always bi-directional. * * The idea of all this is to directly switch voice channels in the BSC * from one phone to another. We do this right now since we don't support * any external interface for voice channels, and in the future as an * optimization to routing them externally. */ #include #include #include struct decoded_trau_frame; /* map a TRAU mux map entry */ int trau_mux_map(const struct gsm_e1_subslot *src, const struct gsm_e1_subslot *dst); int trau_mux_map_lchan(const struct gsm_lchan *src, const struct gsm_lchan *dst); /* unmap a TRAU mux map entry */ int trau_mux_unmap(const struct gsm_e1_subslot *ss, uint32_t callref); /* we get called by subchan_demux */ int trau_mux_input(struct gsm_e1_subslot *src_e1_ss, const uint8_t *trau_bits, int num_bits); /* add a trau receiver */ int trau_recv_lchan(struct gsm_lchan *lchan, uint32_t callref); /* send trau from application */ int trau_send_frame(struct gsm_lchan *lchan, struct gsm_data_frame *frame); /* switch trau muxer to new lchan */ int switch_trau_mux(struct gsm_lchan *old_lchan, struct gsm_lchan *new_lchan); /* callback invoked if we receive TRAU frames */ int subch_cb(struct subch_demux *dmx, int ch, uint8_t *data, int len, void *_priv); /* TRAU frame transcoding */ struct msgb *trau_decode_fr(uint32_t callref, const struct decoded_trau_frame *tf); struct msgb *trau_decode_efr(uint32_t callref, const struct decoded_trau_frame *tf); void trau_encode_fr(struct decoded_trau_frame *tf, const unsigned char *data); void trau_encode_efr(struct decoded_trau_frame *tf, const unsigned char *data); openbsc-0.15.0/openbsc/include/openbsc/trau_upqueue.h000066400000000000000000000002211265565154000226340ustar00rootroot00000000000000#ifndef _TRAU_UPQUEUE_H #define _TRAU_UPQUEUE_H void trau_tx_to_mncc(struct gsm_network *net, struct msgb *msg); #endif /* _TRAU_UPQUEUE_H */ openbsc-0.15.0/openbsc/include/openbsc/ussd.h000066400000000000000000000003241265565154000210720ustar00rootroot00000000000000#ifndef _USSD_H #define _USSD_H /* Handler function for mobile-originated USSD messages */ #include int handle_rcv_ussd(struct gsm_subscriber_connection *conn, struct msgb *msg); #endif openbsc-0.15.0/openbsc/include/openbsc/vty.h000066400000000000000000000017041265565154000207410ustar00rootroot00000000000000#ifndef OPENBSC_VTY_H #define OPENBSC_VTY_H #include #include #include struct gsm_network; struct vty; void openbsc_vty_print_statistics(struct vty *vty, struct gsm_network *); struct buffer *vty_argv_to_buffer(int argc, const char *argv[], int base); extern struct cmd_element cfg_description_cmd; extern struct cmd_element cfg_no_description_cmd; enum bsc_vty_node { GSMNET_NODE = _LAST_OSMOVTY_NODE + 1, BTS_NODE, TRX_NODE, TS_NODE, SUBSCR_NODE, MGCP_NODE, GBPROXY_NODE, SGSN_NODE, OML_NODE, NAT_NODE, NAT_BSC_NODE, MSC_NODE, OM2K_NODE, TRUNK_NODE, PGROUP_NODE, MNCC_INT_NODE, NITB_NODE, BSC_NODE, SMPP_NODE, SMPP_ESME_NODE, }; extern int bsc_vty_is_config_node(struct vty *vty, int node); extern void bsc_replace_string(void *ctx, char **dst, const char *newstr); struct log_info; int bsc_vty_init(const struct log_info *cat); int bsc_vty_init_extra(void); #endif openbsc-0.15.0/openbsc/openbsc.pc.in000066400000000000000000000003401265565154000172470ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@/ Name: OpenBSC Description: OpenBSC base station controller Requires: Version: @VERSION@ Libs: -L${libdir} -lopenbsc Cflags: -I${includedir} openbsc-0.15.0/openbsc/osmoappdesc.py000066400000000000000000000042721265565154000175640ustar00rootroot00000000000000#!/usr/bin/env python # (C) 2013 by Katerina Barone-Adesi # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU 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 General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see # Most systems won't be able to use these, so they're separated out nitb_e1_configs = [ "doc/examples/osmo-nitb/bs11/openbsc-2bts-2trx.cfg", "doc/examples/osmo-nitb/bs11/openbsc-1bts-2trx-hopping.cfg", "doc/examples/osmo-nitb/bs11/openbsc-1bts-2trx.cfg", "doc/examples/osmo-nitb/bs11/openbsc.cfg", "doc/examples/osmo-nitb/nokia/openbsc_nokia_3trx.cfg", "doc/examples/osmo-nitb/nanobts/openbsc-multitrx.cfg", "doc/examples/osmo-nitb/rbs2308/openbsc.cfg" ] app_configs = { "osmo-bsc": ["doc/examples/osmo-bsc/osmo-bsc.cfg"], "nat": ["doc/examples/osmo-bsc_nat/osmo-bsc_nat.cfg"], "mgcp": ["doc/examples/osmo-bsc_mgcp/mgcp.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"], "nitb": ["doc/examples/osmo-nitb/nanobts/openbsc-multitrx.cfg", "doc/examples/osmo-nitb/nanobts/openbsc.cfg"] } apps = [(4242, "src/osmo-bsc/osmo-bsc", "OsmoBSC", "osmo-bsc"), (4244, "src/osmo-bsc_nat/osmo-bsc_nat", "OsmoBSCNAT", "nat"), (4243, "src/osmo-bsc_mgcp/osmo-bsc_mgcp", "OpenBSC MGCP", "mgcp"), (4246, "src/gprs/osmo-gbproxy", "OsmoGbProxy", "gbproxy"), (4245, "src/gprs/osmo-sgsn", "OsmoSGSN", "sgsn"), (4242, "src/osmo-nitb/osmo-nitb", "OpenBSC", "nitb") ] vty_command = ["./src/osmo-nitb/osmo-nitb", "-c", "doc/examples/osmo-nitb/nanobts/openbsc.cfg"] vty_app = apps[-1] openbsc-0.15.0/openbsc/src/000077500000000000000000000000001265565154000154575ustar00rootroot00000000000000openbsc-0.15.0/openbsc/src/Makefile.am000066400000000000000000000007321265565154000175150ustar00rootroot00000000000000AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(COVERAGE_CFLAGS) AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(COVERAGE_LDFLAGS) SUBDIRS = libcommon libmgcp libbsc libmsc libtrau libfilter osmo-nitb osmo-bsc_mgcp utils ipaccess gprs # Conditional modules if BUILD_NAT SUBDIRS += osmo-bsc_nat endif if BUILD_BSC SUBDIRS += osmo-bsc endif openbsc-0.15.0/openbsc/src/gprs/000077500000000000000000000000001265565154000164325ustar00rootroot00000000000000openbsc-0.15.0/openbsc/src/gprs/.gitignore000066400000000000000000000000301265565154000204130ustar00rootroot00000000000000gsn_restart osmo_*.cfg* openbsc-0.15.0/openbsc/src/gprs/Makefile.am000066400000000000000000000023721265565154000204720ustar00rootroot00000000000000AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) AM_CFLAGS=-Wall -fno-strict-aliasing $(LIBOSMOCORE_CFLAGS) \ $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOCTRL_CFLAGS) \ $(LIBOSMOABIS_CFLAGS) $(LIBOSMOGB_CFLAGS) $(COVERAGE_CFLAGS) \ $(LIBCARES_CFLAGS) OSMO_LIBS = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) \ $(LIBOSMOCTRL_LIBS) $(LIBOSMOGB_LIBS) noinst_HEADERS = gprs_sndcp.h bin_PROGRAMS = osmo-gbproxy if HAVE_LIBGTP if HAVE_LIBCARES bin_PROGRAMS += osmo-sgsn 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 osmo_gbproxy_LDADD = $(top_builddir)/src/libcommon/libcommon.a \ $(OSMO_LIBS) -lrt osmo_sgsn_SOURCES = gprs_gmm.c gprs_sgsn.c gprs_sndcp.c gprs_sndcp_vty.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_gsup_messages.c gprs_utils.c gprs_gsup_client.c \ gsm_04_08_gprs.c sgsn_cdr.c sgsn_ares.c osmo_sgsn_LDADD = \ $(top_builddir)/src/libcommon/libcommon.a \ -lgtp $(OSMO_LIBS) $(LIBOSMOABIS_LIBS) $(LIBCARES_LIBS) \ $(LIBCRYPTO_LIBS) -lrt openbsc-0.15.0/openbsc/src/gprs/crc24.c000066400000000000000000000101221265565154000175070ustar00rootroot00000000000000/* GPRS LLC CRC-24 Implementation */ /* (C) 2008-2009 by Harald Welte * * 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 . * */ #include /* 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; } openbsc-0.15.0/openbsc/src/gprs/gb_proxy.c000066400000000000000000001171371265565154000204410ustar00rootroot00000000000000/* NS-over-IP proxy */ /* (C) 2010 by Harald Welte * (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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static const struct rate_ctr_desc global_ctr_description[] = { { "inv-bvci", "Invalid BVC Identifier " }, { "inv-lai", "Invalid Location Area Identifier" }, { "inv-rai", "Invalid Routing Area Identifier " }, { "inv-nsei", "No BVC established for NSEI " }, { "proto-err.bss", "BSSGP protocol error (BSS )" }, { "proto-err.sgsn", "BSSGP protocol error (SGSN)" }, { "not-supp.bss", "Feature not supported (BSS )" }, { "not-supp.sgsn", "Feature not supported (SGSN)" }, { "restart.sgsn", "Restarted RESET procedure (SGSN)" }, { "tx-err.sgsn", "NS Transmission error (SGSN)" }, { "error", "Other error " }, { "mod-peer-err", "Patch error: no peer " }, }; static const struct rate_ctr_group_desc global_ctrg_desc = { .group_name_prefix = "gbproxy.global", .group_description = "GBProxy Global Statistics", .num_ctr = ARRAY_SIZE(global_ctr_description), .ctr_desc = global_ctr_description, }; static int gbprox_relay2peer(struct msgb *old_msg, struct gbproxy_peer *peer, uint16_t ns_bvci); static int gbprox_relay2sgsn(struct gbproxy_config *cfg, struct msgb *old_msg, uint16_t ns_bvci, uint16_t sgsn_nsei); static void gbproxy_reset_imsi_acquisition(struct gbproxy_link_info* link_info); static int check_peer_nsei(struct gbproxy_peer *peer, uint16_t nsei) { if (peer->nsei != nsei) { LOGP(DGPRS, LOGL_NOTICE, "Peer entry doesn't match current NSEI " "BVCI=%u via NSEI=%u (expected NSEI=%u)\n", peer->bvci, nsei, peer->nsei); rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_INV_NSEI]); return 0; } return 1; } /* strip off the NS header */ static void strip_ns_hdr(struct msgb *msg) { int strip_len = msgb_bssgph(msg) - msg->data; msgb_pull(msg, strip_len); } /* Transmit Chapter 9.2.10 Identity Request */ static void gprs_put_identity_req(struct msgb *msg, uint8_t id_type) { struct gsm48_hdr *gh; id_type &= GSM_MI_TYPE_MASK; gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); gh->proto_discr = GSM48_PDISC_MM_GPRS; gh->msg_type = GSM48_MT_GMM_ID_REQ; gh->data[0] = id_type; } /* Transmit Chapter 9.4.6.2 Detach Accept (mobile originated detach) */ static void gprs_put_mo_detach_acc(struct msgb *msg) { struct gsm48_hdr *gh; gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); gh->proto_discr = GSM48_PDISC_MM_GPRS; gh->msg_type = GSM48_MT_GMM_DETACH_ACK; gh->data[0] = 0; /* no force to standby */ } static void gprs_push_llc_ui(struct msgb *msg, int is_uplink, unsigned sapi, unsigned nu) { const uint8_t e_bit = 0; const uint8_t pm_bit = 1; const uint8_t cr_bit = is_uplink ? 0 : 1; uint8_t *llc; uint8_t *fcs_field; uint32_t fcs; nu &= 0x01ff; /* 9 Bit */ llc = msgb_push(msg, 3); llc[0] = (cr_bit << 6) | (sapi & 0x0f); llc[1] = 0xc0 | (nu >> 6); /* UI frame */ llc[2] = (nu << 2) | ((e_bit & 1) << 1) | (pm_bit & 1); fcs = gprs_llc_fcs(llc, msgb_length(msg)); fcs_field = msgb_put(msg, 3); fcs_field[0] = (uint8_t)(fcs >> 0); fcs_field[1] = (uint8_t)(fcs >> 8); fcs_field[2] = (uint8_t)(fcs >> 16); } static void gprs_push_bssgp_dl_unitdata(struct msgb *msg, uint32_t tlli) { struct bssgp_ud_hdr *budh; uint8_t *llc = msgb_data(msg); size_t llc_size = msgb_length(msg); const size_t llc_ie_hdr_size = 3; const uint8_t qos_profile[] = {0x00, 0x50, 0x20}; /* hard-coded */ const uint8_t lifetime[] = {0x02, 0x58}; /* 6s hard-coded */ const size_t bssgp_overhead = sizeof(*budh) + TVLV_GROSS_LEN(sizeof(lifetime)) + llc_ie_hdr_size; uint8_t *ie; uint32_t tlli_be = htonl(tlli); budh = (struct bssgp_ud_hdr *)msgb_push(msg, bssgp_overhead); budh->pdu_type = BSSGP_PDUT_DL_UNITDATA; memcpy(&budh->tlli, &tlli_be, sizeof(budh->tlli)); memcpy(&budh->qos_profile, qos_profile, sizeof(budh->qos_profile)); ie = budh->data; tvlv_put(ie, BSSGP_IE_PDU_LIFETIME, sizeof(lifetime), lifetime); ie += TVLV_GROSS_LEN(sizeof(lifetime)); /* Note: Add alignment before the LLC IE if inserting other IE */ *(ie++) = BSSGP_IE_LLC_PDU; *(ie++) = llc_size / 256; *(ie++) = llc_size % 256; OSMO_ASSERT(ie == llc); msgb_bssgph(msg) = (uint8_t *)budh; msgb_tlli(msg) = tlli; } /* update peer according to the BSS message */ static void gbprox_update_current_raid(uint8_t *raid_enc, struct gbproxy_peer *peer, const char *log_text) { struct gbproxy_patch_state *state = &peer->patch_state; const int old_local_mcc = state->local_mcc; const int old_local_mnc = state->local_mnc; struct gprs_ra_id raid; if (!raid_enc) return; gsm48_parse_ra(&raid, raid_enc); /* save source side MCC/MNC */ if (!peer->cfg->core_mcc || raid.mcc == peer->cfg->core_mcc) { state->local_mcc = 0; } else { state->local_mcc = raid.mcc; } if (!peer->cfg->core_mnc || raid.mnc == peer->cfg->core_mnc) { state->local_mnc = 0; } else { state->local_mnc = raid.mnc; } if (old_local_mcc != state->local_mcc || old_local_mnc != state->local_mnc) LOGP(DGPRS, LOGL_NOTICE, "Patching RAID %sactivated, msg: %s, " "local: %d-%d, core: %d-%d\n", state->local_mcc || state->local_mnc ? "" : "de", log_text, state->local_mcc, state->local_mnc, peer->cfg->core_mcc, peer->cfg->core_mnc); } uint32_t gbproxy_make_bss_ptmsi(struct gbproxy_peer *peer, uint32_t sgsn_ptmsi) { uint32_t bss_ptmsi; int max_retries = 23; if (!peer->cfg->patch_ptmsi) { bss_ptmsi = sgsn_ptmsi; } else { do { bss_ptmsi = rand_r(&peer->cfg->bss_ptmsi_state); bss_ptmsi = bss_ptmsi | 0xC0000000; if (gbproxy_link_info_by_ptmsi(peer, bss_ptmsi)) bss_ptmsi = GSM_RESERVED_TMSI; } while (bss_ptmsi == GSM_RESERVED_TMSI && max_retries--); } if (bss_ptmsi == GSM_RESERVED_TMSI) LOGP(DGPRS, LOGL_ERROR, "Failed to allocate a BSS P-TMSI\n"); return bss_ptmsi; } uint32_t gbproxy_make_sgsn_tlli(struct gbproxy_peer *peer, struct gbproxy_link_info *link_info, uint32_t bss_tlli) { uint32_t sgsn_tlli; int max_retries = 23; if (!peer->cfg->patch_ptmsi) { sgsn_tlli = bss_tlli; } else if (link_info->sgsn_tlli.ptmsi != GSM_RESERVED_TMSI && gprs_tlli_type(bss_tlli) == TLLI_FOREIGN) { sgsn_tlli = gprs_tmsi2tlli(link_info->sgsn_tlli.ptmsi, TLLI_FOREIGN); } else if (link_info->sgsn_tlli.ptmsi != GSM_RESERVED_TMSI && gprs_tlli_type(bss_tlli) == TLLI_LOCAL) { sgsn_tlli = gprs_tmsi2tlli(link_info->sgsn_tlli.ptmsi, TLLI_LOCAL); } else { do { /* create random TLLI, 0b01111xxx... */ sgsn_tlli = rand_r(&peer->cfg->sgsn_tlli_state); sgsn_tlli = (sgsn_tlli & 0x7fffffff) | 0x78000000; if (gbproxy_link_info_by_any_sgsn_tlli(peer, sgsn_tlli)) sgsn_tlli = 0; } while (!sgsn_tlli && max_retries--); } if (!sgsn_tlli) LOGP(DGPRS, LOGL_ERROR, "Failed to allocate an SGSN TLLI\n"); return sgsn_tlli; } void gbproxy_reset_link(struct gbproxy_link_info *link_info) { gbproxy_reset_imsi_acquisition(link_info); } /* Returns != 0 iff IMSI acquisition was in progress */ static int gbproxy_restart_imsi_acquisition(struct gbproxy_link_info* link_info) { int in_progress = 0; if (!link_info) return 0; if (link_info->imsi_acq_pending) in_progress = 1; gbproxy_link_info_discard_messages(link_info); link_info->imsi_acq_pending = 0; return in_progress; } static void gbproxy_reset_imsi_acquisition(struct gbproxy_link_info* link_info) { gbproxy_restart_imsi_acquisition(link_info); link_info->vu_gen_tx_bss = GBPROXY_INIT_VU_GEN_TX; } static void gbproxy_flush_stored_messages(struct gbproxy_peer *peer, struct msgb *msg, time_t now, struct gbproxy_link_info* link_info, struct gprs_gb_parse_context *parse_ctx) { int rc; struct msgb *stored_msg; /* Got identity response with IMSI, assuming the request had * been generated by the gbproxy */ LOGP(DLLC, LOGL_DEBUG, "NSEI=%d(BSS) IMSI acquisition succeeded, " "flushing stored messages\n", msgb_nsei(msg)); /* Patch and flush stored messages towards the SGSN */ while ((stored_msg = msgb_dequeue(&link_info->stored_msgs))) { struct gprs_gb_parse_context tmp_parse_ctx = {0}; tmp_parse_ctx.to_bss = 0; tmp_parse_ctx.peer_nsei = msgb_nsei(stored_msg); int len_change = 0; gprs_gb_parse_bssgp(msgb_bssgph(stored_msg), msgb_bssgp_len(stored_msg), &tmp_parse_ctx); gbproxy_patch_bssgp(msg, msgb_bssgph(stored_msg), msgb_bssgp_len(stored_msg), peer, link_info, &len_change, &tmp_parse_ctx); gbproxy_update_link_state_after(peer, link_info, now, &tmp_parse_ctx); rc = gbprox_relay2sgsn(peer->cfg, stored_msg, msgb_bvci(msg), link_info->sgsn_nsei); if (rc < 0) LOGP(DLLC, LOGL_ERROR, "NSEI=%d(BSS) failed to send stored message " "(%s)\n", msgb_nsei(msg), parse_ctx->llc_msg_name ? parse_ctx->llc_msg_name : "BSSGP"); msgb_free(stored_msg); } } static int gbproxy_gsm48_to_peer(struct gbproxy_peer *peer, struct gbproxy_link_info* link_info, uint16_t bvci, struct msgb *msg /* Takes msg ownership */) { int rc; /* Workaround to avoid N(U) collisions and to enable a restart * of the IMSI acquisition procedure. This will work unless the * SGSN has an initial V(UT) within [256-32, 256+n_retries] * (see GSM 04.64, 8.4.2). */ gprs_push_llc_ui(msg, 0, GPRS_SAPI_GMM, link_info->vu_gen_tx_bss); link_info->vu_gen_tx_bss = (link_info->vu_gen_tx_bss + 1) % 512; gprs_push_bssgp_dl_unitdata(msg, link_info->tlli.current); rc = gbprox_relay2peer(msg, peer, bvci); msgb_free(msg); return rc; } static void gbproxy_acquire_imsi(struct gbproxy_peer *peer, struct gbproxy_link_info* link_info, uint16_t bvci) { struct msgb *idreq_msg; /* Send IDENT REQ */ idreq_msg = gsm48_msgb_alloc(); gprs_put_identity_req(idreq_msg, GSM_MI_TYPE_IMSI); gbproxy_gsm48_to_peer(peer, link_info, bvci, idreq_msg); } static void gbproxy_tx_detach_acc(struct gbproxy_peer *peer, struct gbproxy_link_info* link_info, uint16_t bvci) { struct msgb *detacc_msg; /* Send DETACH ACC */ detacc_msg = gsm48_msgb_alloc(); gprs_put_mo_detach_acc(detacc_msg); gbproxy_gsm48_to_peer(peer, link_info, bvci, detacc_msg); } /* Return != 0 iff msg still needs to be processed */ static int gbproxy_imsi_acquisition(struct gbproxy_peer *peer, struct msgb *msg, time_t now, struct gbproxy_link_info* link_info, struct gprs_gb_parse_context *parse_ctx) { struct msgb *stored_msg; if (!link_info) return 1; if (!link_info->imsi_acq_pending && link_info->imsi_len > 0) return 1; if (parse_ctx->g48_hdr) switch (parse_ctx->g48_hdr->msg_type) { case GSM48_MT_GMM_RA_UPD_REQ: case GSM48_MT_GMM_ATTACH_REQ: if (gbproxy_restart_imsi_acquisition(link_info)) { LOGP(DLLC, LOGL_INFO, "NSEI=%d(BSS) IMSI acquisition was in progress " "when receiving an %s.\n", msgb_nsei(msg), parse_ctx->llc_msg_name); } break; case GSM48_MT_GMM_DETACH_REQ: /* Nothing has been sent to the SGSN yet */ if (link_info->imsi_acq_pending) { LOGP(DLLC, LOGL_INFO, "NSEI=%d(BSS) IMSI acquisition was in progress " "when receiving a DETACH_REQ.\n", msgb_nsei(msg)); } if (!parse_ctx->invalidate_tlli) { LOGP(DLLC, LOGL_INFO, "NSEI=%d(BSS) IMSI not yet acquired, " "faking a DETACH_ACC.\n", msgb_nsei(msg)); gbproxy_tx_detach_acc(peer, link_info, msgb_bvci(msg)); parse_ctx->invalidate_tlli = 1; } gbproxy_reset_imsi_acquisition(link_info); gbproxy_update_link_state_after(peer, link_info, now, parse_ctx); return 0; } if (link_info->imsi_acq_pending && link_info->imsi_len > 0) { int is_ident_resp = parse_ctx->g48_hdr && parse_ctx->g48_hdr->proto_discr == GSM48_PDISC_MM_GPRS && parse_ctx->g48_hdr->msg_type == GSM48_MT_GMM_ID_RESP; /* The IMSI is now available */ gbproxy_flush_stored_messages(peer, msg, now, link_info, parse_ctx); gbproxy_reset_imsi_acquisition(link_info); /* This message is most probably the response to the ident * request sent by gbproxy_acquire_imsi(). Don't forward it to * the SGSN. */ return !is_ident_resp; } /* The message cannot be processed since the IMSI is still missing */ /* Enqueue unpatched messages */ LOGP(DLLC, LOGL_INFO, "NSEI=%d(BSS) IMSI acquisition in progress, " "storing message (%s)\n", msgb_nsei(msg), parse_ctx->llc_msg_name ? parse_ctx->llc_msg_name : "BSSGP"); stored_msg = gprs_msgb_copy(msg, "process_bssgp_ul"); msgb_enqueue(&link_info->stored_msgs, stored_msg); if (!link_info->imsi_acq_pending) { LOGP(DLLC, LOGL_INFO, "NSEI=%d(BSS) IMSI is required but not available, " "initiating identification procedure (%s)\n", msgb_nsei(msg), parse_ctx->llc_msg_name ? parse_ctx->llc_msg_name : "BSSGP"); gbproxy_acquire_imsi(peer, link_info, msgb_bvci(msg)); /* There is no explicit retransmission handling, the * implementation relies on the MS doing proper retransmissions * of the triggering message instead */ link_info->imsi_acq_pending = 1; } return 0; } struct gbproxy_peer *gbproxy_find_peer(struct gbproxy_config *cfg, struct msgb *msg, struct gprs_gb_parse_context *parse_ctx) { struct gbproxy_peer *peer = NULL; if (msgb_bvci(msg) >= 2) peer = gbproxy_peer_by_bvci(cfg, msgb_bvci(msg)); if (!peer && !parse_ctx->to_bss) peer = gbproxy_peer_by_nsei(cfg, msgb_nsei(msg)); if (!peer) peer = gbproxy_peer_by_bssgp_tlv(cfg, &parse_ctx->bssgp_tp); if (!peer) { LOGP(DLLC, LOGL_INFO, "NSEI=%d(%s) patching: didn't find peer for message, " "PDU %d\n", msgb_nsei(msg), parse_ctx->to_bss ? "BSS" : "SGSN", parse_ctx->pdu_type); /* Increment counter */ rate_ctr_inc(&cfg->ctrg->ctr[GBPROX_GLOB_CTR_PATCH_PEER_ERR]); } return peer; } /* patch BSSGP message */ static int gbprox_process_bssgp_ul(struct gbproxy_config *cfg, struct msgb *msg, struct gbproxy_peer *peer) { struct gprs_gb_parse_context parse_ctx = {0}; int rc; int len_change = 0; time_t now; struct timespec ts = {0,}; struct gbproxy_link_info *link_info = NULL; uint32_t sgsn_nsei = cfg->nsip_sgsn_nsei; if (!cfg->core_mcc && !cfg->core_mnc && !cfg->core_apn && !cfg->acquire_imsi && !cfg->patch_ptmsi && !cfg->route_to_sgsn2) return 1; parse_ctx.to_bss = 0; parse_ctx.peer_nsei = msgb_nsei(msg); /* Parse BSSGP/LLC */ rc = gprs_gb_parse_bssgp(msgb_bssgph(msg), msgb_bssgp_len(msg), &parse_ctx); if (!rc && !parse_ctx.need_decryption) { LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(BSS) patching: failed to parse invalid %s message\n", msgb_nsei(msg), gprs_gb_message_name(&parse_ctx, "NS_UNITDATA")); gprs_gb_log_parse_context(LOGL_NOTICE, &parse_ctx, "NS_UNITDATA"); LOGP(DGPRS, LOGL_NOTICE, "NSEI=%u(BSS) invalid message was: %s\n", msgb_nsei(msg), msgb_hexdump(msg)); return 0; } /* Get peer */ if (!peer) peer = gbproxy_find_peer(cfg, msg, &parse_ctx); if (!peer) return 0; clock_gettime(CLOCK_MONOTONIC, &ts); now = ts.tv_sec; gbprox_update_current_raid(parse_ctx.bssgp_raid_enc, peer, parse_ctx.llc_msg_name); gprs_gb_log_parse_context(LOGL_DEBUG, &parse_ctx, "NS_UNITDATA"); link_info = gbproxy_update_link_state_ul(peer, now, &parse_ctx); if (parse_ctx.g48_hdr) { switch (parse_ctx.g48_hdr->msg_type) { case GSM48_MT_GMM_ATTACH_REQ: rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_ATTACH_REQS]); break; default: break; } } if (link_info && cfg->route_to_sgsn2) { if (cfg->acquire_imsi && link_info->imsi_len == 0) sgsn_nsei = 0xffff; else if (gbproxy_imsi_matches(cfg, GBPROX_MATCH_ROUTING, link_info)) sgsn_nsei = cfg->nsip_sgsn2_nsei; } if (link_info) link_info->sgsn_nsei = sgsn_nsei; /* Handle IMSI acquisition */ if (cfg->acquire_imsi) { rc = gbproxy_imsi_acquisition(peer, msg, now, link_info, &parse_ctx); if (rc <= 0) return rc; } gbproxy_patch_bssgp(msg, msgb_bssgph(msg), msgb_bssgp_len(msg), peer, link_info, &len_change, &parse_ctx); gbproxy_update_link_state_after(peer, link_info, now, &parse_ctx); if (sgsn_nsei != cfg->nsip_sgsn_nsei) { /* Send message directly to the selected SGSN */ rc = gbprox_relay2sgsn(cfg, msg, msgb_bvci(msg), sgsn_nsei); /* Don't let the calling code handle the transmission */ return 0; } return 1; } /* patch BSSGP message to use core_mcc/mnc on the SGSN side */ static void gbprox_process_bssgp_dl(struct gbproxy_config *cfg, struct msgb *msg, struct gbproxy_peer *peer) { struct gprs_gb_parse_context parse_ctx = {0}; int rc; int len_change = 0; time_t now; struct timespec ts = {0,}; struct gbproxy_link_info *link_info = NULL; if (!cfg->core_mcc && !cfg->core_mnc && !cfg->core_apn && !cfg->acquire_imsi && !cfg->patch_ptmsi && !cfg->route_to_sgsn2) return; parse_ctx.to_bss = 1; parse_ctx.peer_nsei = msgb_nsei(msg); rc = gprs_gb_parse_bssgp(msgb_bssgph(msg), msgb_bssgp_len(msg), &parse_ctx); if (!rc && !parse_ctx.need_decryption) { LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(SGSN) patching: failed to parse invalid %s message\n", msgb_nsei(msg), gprs_gb_message_name(&parse_ctx, "NS_UNITDATA")); gprs_gb_log_parse_context(LOGL_NOTICE, &parse_ctx, "NS_UNITDATA"); LOGP(DGPRS, LOGL_NOTICE, "NSEI=%u(SGSN) invalid message was: %s\n", msgb_nsei(msg), msgb_hexdump(msg)); return; } /* Get peer */ if (!peer) peer = gbproxy_find_peer(cfg, msg, &parse_ctx); if (!peer) return; clock_gettime(CLOCK_MONOTONIC, &ts); now = ts.tv_sec; if (parse_ctx.g48_hdr) { switch (parse_ctx.g48_hdr->msg_type) { case GSM48_MT_GMM_ATTACH_REJ: rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_ATTACH_REJS]); break; default: break; } } gprs_gb_log_parse_context(LOGL_DEBUG, &parse_ctx, "NS_UNITDATA"); link_info = gbproxy_update_link_state_dl(peer, now, &parse_ctx); gbproxy_patch_bssgp(msg, msgb_bssgph(msg), msgb_bssgp_len(msg), peer, link_info, &len_change, &parse_ctx); gbproxy_update_link_state_after(peer, link_info, now, &parse_ctx); return; } /* feed a message down the NS-VC associated with the specified peer */ static int gbprox_relay2sgsn(struct gbproxy_config *cfg, struct msgb *old_msg, uint16_t ns_bvci, uint16_t sgsn_nsei) { /* create a copy of the message so the old one can * be free()d safely when we return from gbprox_rcvmsg() */ struct msgb *msg = gprs_msgb_copy(old_msg, "msgb_relay2sgsn"); int rc; DEBUGP(DGPRS, "NSEI=%u proxying BTS->SGSN (NS_BVCI=%u, NSEI=%u)\n", msgb_nsei(msg), ns_bvci, sgsn_nsei); msgb_bvci(msg) = ns_bvci; msgb_nsei(msg) = sgsn_nsei; strip_ns_hdr(msg); rc = gprs_ns_sendmsg(bssgp_nsi, msg); if (rc < 0) rate_ctr_inc(&cfg->ctrg->ctr[GBPROX_GLOB_CTR_TX_ERR_SGSN]); return rc; } /* feed a message down the NS-VC associated with the specified peer */ static int gbprox_relay2peer(struct msgb *old_msg, struct gbproxy_peer *peer, uint16_t ns_bvci) { /* create a copy of the message so the old one can * be free()d safely when we return from gbprox_rcvmsg() */ struct msgb *msg = gprs_msgb_copy(old_msg, "msgb_relay2peer"); int rc; DEBUGP(DGPRS, "NSEI=%u proxying SGSN->BSS (NS_BVCI=%u, NSEI=%u)\n", msgb_nsei(msg), ns_bvci, peer->nsei); msgb_bvci(msg) = ns_bvci; msgb_nsei(msg) = peer->nsei; /* Strip the old NS header, it will be replaced with a new one */ strip_ns_hdr(msg); rc = gprs_ns_sendmsg(bssgp_nsi, msg); if (rc < 0) rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_TX_ERR]); return rc; } static int block_unblock_peer(struct gbproxy_config *cfg, uint16_t ptp_bvci, uint8_t pdu_type) { struct gbproxy_peer *peer; peer = gbproxy_peer_by_bvci(cfg, ptp_bvci); if (!peer) { LOGP(DGPRS, LOGL_ERROR, "BVCI=%u: Cannot find BSS\n", ptp_bvci); rate_ctr_inc(&cfg->ctrg->ctr[GBPROX_GLOB_CTR_INV_BVCI]); return -ENOENT; } switch (pdu_type) { case BSSGP_PDUT_BVC_BLOCK_ACK: peer->blocked = 1; rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_BLOCKED]); break; case BSSGP_PDUT_BVC_UNBLOCK_ACK: peer->blocked = 0; rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_UNBLOCKED]); break; default: break; } return 0; } /* Send a message to a peer identified by ptp_bvci but using ns_bvci * in the NS hdr */ static int gbprox_relay2bvci(struct gbproxy_config *cfg, struct msgb *msg, uint16_t ptp_bvci, uint16_t ns_bvci) { struct gbproxy_peer *peer; peer = gbproxy_peer_by_bvci(cfg, ptp_bvci); if (!peer) { LOGP(DGPRS, LOGL_ERROR, "BVCI=%u: Cannot find BSS\n", ptp_bvci); rate_ctr_inc(&cfg->ctrg->ctr[GBPROX_GLOB_CTR_INV_BVCI]); return -ENOENT; } return gbprox_relay2peer(msg, peer, ns_bvci); } int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx) { return 0; } /* Receive an incoming PTP message from a BSS-side NS-VC */ static int gbprox_rx_ptp_from_bss(struct gbproxy_config *cfg, struct msgb *msg, uint16_t nsei, uint16_t nsvci, uint16_t ns_bvci) { struct gbproxy_peer *peer; struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg); uint8_t pdu_type = bgph->pdu_type; int rc; peer = gbproxy_peer_by_bvci(cfg, ns_bvci); if (!peer) { LOGP(DGPRS, LOGL_NOTICE, "Didn't find peer for " "BVCI=%u for PTP message from NSVC=%u/NSEI=%u (BSS), " "discarding message\n", ns_bvci, nsvci, nsei); return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, &ns_bvci, msg); } check_peer_nsei(peer, nsei); rc = gbprox_process_bssgp_ul(cfg, msg, peer); if (!rc) return 0; switch (pdu_type) { case BSSGP_PDUT_FLOW_CONTROL_BVC: if (!cfg->route_to_sgsn2) break; /* Send a copy to the secondary SGSN */ gbprox_relay2sgsn(cfg, msg, ns_bvci, cfg->nsip_sgsn2_nsei); break; default: break; } return gbprox_relay2sgsn(cfg, msg, ns_bvci, cfg->nsip_sgsn_nsei); } /* Receive an incoming PTP message from a SGSN-side NS-VC */ static int gbprox_rx_ptp_from_sgsn(struct gbproxy_config *cfg, struct msgb *msg, uint16_t nsei, uint16_t nsvci, uint16_t ns_bvci) { struct gbproxy_peer *peer; struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg); uint8_t pdu_type = bgph->pdu_type; peer = gbproxy_peer_by_bvci(cfg, ns_bvci); /* Send status messages before patching */ if (!peer) { LOGP(DGPRS, LOGL_INFO, "Didn't find peer for " "BVCI=%u for message from NSVC=%u/NSEI=%u (SGSN)\n", ns_bvci, nsvci, nsei); rate_ctr_inc(&cfg->ctrg-> ctr[GBPROX_GLOB_CTR_INV_BVCI]); return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, &ns_bvci, msg); } if (peer->blocked) { LOGP(DGPRS, LOGL_NOTICE, "Dropping PDU for " "blocked BVCI=%u via NSVC=%u/NSEI=%u\n", ns_bvci, nsvci, nsei); rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_DROPPED]); return bssgp_tx_status(BSSGP_CAUSE_BVCI_BLOCKED, &ns_bvci, msg); } switch (pdu_type) { case BSSGP_PDUT_FLOW_CONTROL_BVC_ACK: case BSSGP_PDUT_BVC_BLOCK_ACK: case BSSGP_PDUT_BVC_UNBLOCK_ACK: if (cfg->route_to_sgsn2 && nsei == cfg->nsip_sgsn2_nsei) /* Hide ACKs from the secondary SGSN, the primary SGSN * is responsible to send them. */ return 0; break; default: break; } /* Optionally patch the message */ gbprox_process_bssgp_dl(cfg, msg, peer); return gbprox_relay2peer(msg, peer, ns_bvci); } /* Receive an incoming signalling message from a BSS-side NS-VC */ static int gbprox_rx_sig_from_bss(struct gbproxy_config *cfg, struct msgb *msg, uint16_t nsei, uint16_t ns_bvci) { struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg); struct tlv_parsed tp; uint8_t pdu_type = bgph->pdu_type; int data_len = msgb_bssgp_len(msg) - sizeof(*bgph); struct gbproxy_peer *from_peer = NULL; struct gprs_ra_id raid; int copy_to_sgsn2 = 0; int rc; if (ns_bvci != 0 && ns_bvci != 1) { LOGP(DGPRS, LOGL_NOTICE, "NSEI=%u BVCI=%u is not signalling\n", nsei, ns_bvci); return -EINVAL; } /* we actually should never see those two for BVCI == 0, but double-check * just to make sure */ if (pdu_type == BSSGP_PDUT_UL_UNITDATA || pdu_type == BSSGP_PDUT_DL_UNITDATA) { LOGP(DGPRS, LOGL_NOTICE, "NSEI=%u UNITDATA not allowed in " "signalling\n", nsei); return -EINVAL; } bssgp_tlv_parse(&tp, bgph->data, data_len); switch (pdu_type) { case BSSGP_PDUT_SUSPEND: case BSSGP_PDUT_RESUME: /* We implement RAI snooping during SUSPEND/RESUME, since it * establishes a relationsip between BVCI/peer and the routeing * area identification. The snooped information is then used * for routing the {SUSPEND,RESUME}_[N]ACK back to the correct * BSSGP */ if (!TLVP_PRESENT(&tp, BSSGP_IE_ROUTEING_AREA)) goto err_mand_ie; from_peer = gbproxy_peer_by_nsei(cfg, nsei); if (!from_peer) goto err_no_peer; memcpy(from_peer->ra, TLVP_VAL(&tp, BSSGP_IE_ROUTEING_AREA), sizeof(from_peer->ra)); gsm48_parse_ra(&raid, from_peer->ra); LOGP(DGPRS, LOGL_INFO, "NSEI=%u BSSGP SUSPEND/RESUME " "RAI snooping: RAI %u-%u-%u-%u behind BVCI=%u\n", nsei, raid.mcc, raid.mnc, raid.lac, raid.rac , from_peer->bvci); /* FIXME: This only supports one BSS per RA */ break; case BSSGP_PDUT_BVC_RESET: /* If we receive a BVC reset on the signalling endpoint, we * don't want the SGSN to reset, as the signalling endpoint * is common for all point-to-point BVCs (and thus all BTS) */ if (TLVP_PRESENT(&tp, BSSGP_IE_BVCI)) { uint16_t bvci = ntohs(tlvp_val16_unal(&tp, BSSGP_IE_BVCI)); LOGP(DGPRS, LOGL_INFO, "NSEI=%u Rx BVC RESET (BVCI=%u)\n", nsei, bvci); if (bvci == 0) { /* FIXME: only do this if SGSN is alive! */ LOGP(DGPRS, LOGL_INFO, "NSEI=%u Tx fake " "BVC RESET ACK of BVCI=0\n", nsei); return bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_RESET_ACK, nsei, 0, ns_bvci); } from_peer = gbproxy_peer_by_bvci(cfg, bvci); if (!from_peer) { /* if a PTP-BVC is reset, and we don't know that * PTP-BVCI yet, we should allocate a new peer */ LOGP(DGPRS, LOGL_INFO, "Allocationg new peer for " "BVCI=%u via NSEI=%u\n", bvci, nsei); from_peer = gbproxy_peer_alloc(cfg, bvci); from_peer->nsei = nsei; } if (!check_peer_nsei(from_peer, nsei)) from_peer->nsei = nsei; if (TLVP_PRESENT(&tp, BSSGP_IE_CELL_ID)) { struct gprs_ra_id raid; /* We have a Cell Identifier present in this * PDU, this means we can extend our local * state information about this particular cell * */ memcpy(from_peer->ra, TLVP_VAL(&tp, BSSGP_IE_CELL_ID), sizeof(from_peer->ra)); gsm48_parse_ra(&raid, from_peer->ra); LOGP(DGPRS, LOGL_INFO, "NSEI=%u/BVCI=%u " "Cell ID %u-%u-%u-%u\n", nsei, bvci, raid.mcc, raid.mnc, raid.lac, raid.rac); } if (cfg->route_to_sgsn2) copy_to_sgsn2 = 1; } break; } /* Normally, we can simply pass on all signalling messages from BSS to * SGSN */ rc = gbprox_process_bssgp_ul(cfg, msg, from_peer); if (!rc) return 0; if (copy_to_sgsn2) gbprox_relay2sgsn(cfg, msg, ns_bvci, cfg->nsip_sgsn2_nsei); return gbprox_relay2sgsn(cfg, msg, ns_bvci, cfg->nsip_sgsn_nsei); err_no_peer: LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(BSS) cannot find peer based on NSEI\n", nsei); rate_ctr_inc(&cfg->ctrg->ctr[GBPROX_GLOB_CTR_INV_NSEI]); return bssgp_tx_status(BSSGP_CAUSE_INV_MAND_INF, NULL, msg); err_mand_ie: LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(BSS) missing mandatory RA IE\n", nsei); rate_ctr_inc(&cfg->ctrg->ctr[GBPROX_GLOB_CTR_PROTO_ERR_BSS]); return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg); } /* Receive paging request from SGSN, we need to relay to proper BSS */ static int gbprox_rx_paging(struct gbproxy_config *cfg, struct msgb *msg, struct tlv_parsed *tp, uint32_t nsei, uint16_t ns_bvci) { struct gbproxy_peer *peer = NULL; int errctr = GBPROX_GLOB_CTR_PROTO_ERR_SGSN; LOGP(DGPRS, LOGL_INFO, "NSEI=%u(SGSN) BSSGP PAGING ", nsei); if (TLVP_PRESENT(tp, BSSGP_IE_BVCI)) { uint16_t bvci = ntohs(tlvp_val16_unal(tp, BSSGP_IE_BVCI)); LOGPC(DGPRS, LOGL_INFO, "routing by BVCI to peer BVCI=%u\n", bvci); errctr = GBPROX_GLOB_CTR_OTHER_ERR; } else if (TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA)) { peer = gbproxy_peer_by_rai(cfg, TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA)); LOGPC(DGPRS, LOGL_INFO, "routing by RAI to peer BVCI=%u\n", peer ? peer->bvci : -1); errctr = GBPROX_GLOB_CTR_INV_RAI; } else if (TLVP_PRESENT(tp, BSSGP_IE_LOCATION_AREA)) { peer = gbproxy_peer_by_lai(cfg, TLVP_VAL(tp, BSSGP_IE_LOCATION_AREA)); LOGPC(DGPRS, LOGL_INFO, "routing by LAI to peer BVCI=%u\n", peer ? peer->bvci : -1); errctr = GBPROX_GLOB_CTR_INV_LAI; } else LOGPC(DGPRS, LOGL_INFO, "\n"); if (!peer) { LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(SGSN) BSSGP PAGING: " "unable to route, missing IE\n", nsei); rate_ctr_inc(&cfg->ctrg->ctr[errctr]); return -EINVAL; } return gbprox_relay2peer(msg, peer, ns_bvci); } /* Receive an incoming BVC-RESET message from the SGSN */ static int rx_reset_from_sgsn(struct gbproxy_config *cfg, struct msgb *orig_msg, struct msgb *msg, struct tlv_parsed *tp, uint32_t nsei, uint16_t ns_bvci) { struct gbproxy_peer *peer; uint16_t ptp_bvci; if (!TLVP_PRESENT(tp, BSSGP_IE_BVCI)) { rate_ctr_inc(&cfg->ctrg-> ctr[GBPROX_GLOB_CTR_PROTO_ERR_SGSN]); return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, orig_msg); } ptp_bvci = ntohs(tlvp_val16_unal(tp, BSSGP_IE_BVCI)); if (ptp_bvci >= 2) { /* A reset for a PTP BVC was received, forward it to its * respective peer */ peer = gbproxy_peer_by_bvci(cfg, ptp_bvci); if (!peer) { LOGP(DGPRS, LOGL_ERROR, "NSEI=%u BVCI=%u: Cannot find BSS\n", nsei, ptp_bvci); rate_ctr_inc(&cfg->ctrg-> ctr[GBPROX_GLOB_CTR_INV_BVCI]); return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, &ptp_bvci, orig_msg); } return gbprox_relay2peer(msg, peer, ns_bvci); } /* A reset for the Signalling entity has been received * from the SGSN. As the signalling BVCI is shared * among all the BSS's that we multiplex, it needs to * be relayed */ llist_for_each_entry(peer, &cfg->bts_peers, list) gbprox_relay2peer(msg, peer, ns_bvci); return 0; } /* Receive an incoming signalling message from the SGSN-side NS-VC */ static int gbprox_rx_sig_from_sgsn(struct gbproxy_config *cfg, struct msgb *orig_msg, uint32_t nsei, uint16_t ns_bvci) { struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(orig_msg); struct tlv_parsed tp; uint8_t pdu_type = bgph->pdu_type; int data_len; struct gbproxy_peer *peer; uint16_t bvci; struct msgb *msg; int rc = 0; int cause; if (ns_bvci != 0 && ns_bvci != 1) { LOGP(DGPRS, LOGL_NOTICE, "NSEI=%u(SGSN) BVCI=%u is not " "signalling\n", nsei, ns_bvci); /* FIXME: Send proper error message */ return -EINVAL; } /* we actually should never see those two for BVCI == 0, but double-check * just to make sure */ if (pdu_type == BSSGP_PDUT_UL_UNITDATA || pdu_type == BSSGP_PDUT_DL_UNITDATA) { LOGP(DGPRS, LOGL_NOTICE, "NSEI=%u(SGSN) UNITDATA not allowed in " "signalling\n", nsei); return bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, orig_msg); } msg = gprs_msgb_copy(orig_msg, "rx_sig_from_sgsn"); gbprox_process_bssgp_dl(cfg, msg, NULL); /* Update message info */ bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg); data_len = msgb_bssgp_len(orig_msg) - sizeof(*bgph); rc = bssgp_tlv_parse(&tp, bgph->data, data_len); switch (pdu_type) { case BSSGP_PDUT_BVC_RESET: rc = rx_reset_from_sgsn(cfg, msg, orig_msg, &tp, nsei, ns_bvci); break; case BSSGP_PDUT_BVC_RESET_ACK: if (cfg->route_to_sgsn2 && nsei == cfg->nsip_sgsn2_nsei) break; /* fall through */ case BSSGP_PDUT_FLUSH_LL: /* simple case: BVCI IE is mandatory */ if (!TLVP_PRESENT(&tp, BSSGP_IE_BVCI)) goto err_mand_ie; bvci = ntohs(tlvp_val16_unal(&tp, BSSGP_IE_BVCI)); rc = gbprox_relay2bvci(cfg, msg, bvci, ns_bvci); break; case BSSGP_PDUT_PAGING_PS: case BSSGP_PDUT_PAGING_CS: /* process the paging request (LAI/RAI lookup) */ rc = gbprox_rx_paging(cfg, msg, &tp, nsei, ns_bvci); break; case BSSGP_PDUT_STATUS: /* Some exception has occurred */ LOGP(DGPRS, LOGL_NOTICE, "NSEI=%u(SGSN) BSSGP STATUS ", nsei); if (!TLVP_PRESENT(&tp, BSSGP_IE_CAUSE)) { LOGPC(DGPRS, LOGL_NOTICE, "\n"); goto err_mand_ie; } cause = *TLVP_VAL(&tp, BSSGP_IE_CAUSE); LOGPC(DGPRS, LOGL_NOTICE, "cause=0x%02x(%s) ", *TLVP_VAL(&tp, BSSGP_IE_CAUSE), bssgp_cause_str(cause)); if (TLVP_PRESENT(&tp, BSSGP_IE_BVCI)) { bvci = ntohs(tlvp_val16_unal(&tp, BSSGP_IE_BVCI)); LOGPC(DGPRS, LOGL_NOTICE, "BVCI=%u\n", bvci); if (cause == BSSGP_CAUSE_UNKNOWN_BVCI) rc = gbprox_relay2bvci(cfg, msg, bvci, ns_bvci); } else LOGPC(DGPRS, LOGL_NOTICE, "\n"); break; /* those only exist in the SGSN -> BSS direction */ case BSSGP_PDUT_SUSPEND_ACK: case BSSGP_PDUT_SUSPEND_NACK: case BSSGP_PDUT_RESUME_ACK: case BSSGP_PDUT_RESUME_NACK: /* RAI IE is mandatory */ if (!TLVP_PRESENT(&tp, BSSGP_IE_ROUTEING_AREA)) goto err_mand_ie; peer = gbproxy_peer_by_rai(cfg, TLVP_VAL(&tp, BSSGP_IE_ROUTEING_AREA)); if (!peer) goto err_no_peer; rc = gbprox_relay2peer(msg, peer, ns_bvci); break; case BSSGP_PDUT_BVC_BLOCK_ACK: case BSSGP_PDUT_BVC_UNBLOCK_ACK: if (!TLVP_PRESENT(&tp, BSSGP_IE_BVCI)) goto err_mand_ie; bvci = ntohs(tlvp_val16_unal(&tp, BSSGP_IE_BVCI)); if (bvci == 0) { LOGP(DGPRS, LOGL_NOTICE, "NSEI=%u(SGSN) BSSGP " "%sBLOCK_ACK for signalling BVCI ?!?\n", nsei, pdu_type == BSSGP_PDUT_BVC_UNBLOCK_ACK ? "UN":""); /* should we send STATUS ? */ rate_ctr_inc(&cfg->ctrg-> ctr[GBPROX_GLOB_CTR_INV_BVCI]); } else { /* Mark BVC as (un)blocked */ block_unblock_peer(cfg, bvci, pdu_type); } rc = gbprox_relay2bvci(cfg, msg, bvci, ns_bvci); break; case BSSGP_PDUT_SGSN_INVOKE_TRACE: LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(SGSN) BSSGP INVOKE TRACE not supported\n",nsei); rate_ctr_inc(&cfg->ctrg-> ctr[GBPROX_GLOB_CTR_NOT_SUPPORTED_SGSN]); rc = bssgp_tx_status(BSSGP_CAUSE_PDU_INCOMP_FEAT, NULL, orig_msg); break; default: LOGP(DGPRS, LOGL_NOTICE, "BSSGP PDU type 0x%02x unknown\n", pdu_type); rate_ctr_inc(&cfg->ctrg-> ctr[GBPROX_GLOB_CTR_PROTO_ERR_SGSN]); rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, orig_msg); break; } msgb_free(msg); return rc; err_mand_ie: LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(SGSN) missing mandatory IE\n", nsei); rate_ctr_inc(&cfg->ctrg-> ctr[GBPROX_GLOB_CTR_PROTO_ERR_SGSN]); msgb_free(msg); return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, orig_msg); err_no_peer: LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(SGSN) cannot find peer based on RAI\n", nsei); rate_ctr_inc(&cfg->ctrg-> ctr[GBPROX_GLOB_CTR_INV_RAI]); msgb_free(msg); return bssgp_tx_status(BSSGP_CAUSE_INV_MAND_INF, NULL, orig_msg); } static int gbproxy_is_sgsn_nsei(struct gbproxy_config *cfg, uint16_t nsei) { return nsei == cfg->nsip_sgsn_nsei || (cfg->route_to_sgsn2 && nsei == cfg->nsip_sgsn2_nsei); } /* 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 rc; int remote_end_is_sgsn = gbproxy_is_sgsn_nsei(cfg, nsei); /* Only BVCI=0 messages need special treatment */ if (ns_bvci == 0 || ns_bvci == 1) { if (remote_end_is_sgsn) rc = gbprox_rx_sig_from_sgsn(cfg, msg, nsei, ns_bvci); else rc = gbprox_rx_sig_from_bss(cfg, msg, nsei, ns_bvci); } else { /* All other BVCI are PTP */ if (remote_end_is_sgsn) rc = gbprox_rx_ptp_from_sgsn(cfg, msg, nsei, nsvci, ns_bvci); else rc = gbprox_rx_ptp_from_bss(cfg, msg, nsei, nsvci, ns_bvci); } return rc; } int gbprox_reset_persistent_nsvcs(struct gprs_ns_inst *nsi) { struct gprs_nsvc *nsvc; llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) { if (!nsvc->persistent) continue; gprs_nsvc_reset(nsvc, NS_CAUSE_OM_INTERVENTION); } return 0; } /* Signal handler for signals from NS layer */ int gbprox_signal(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct gbproxy_config *cfg = handler_data; struct ns_signal_data *nssd = signal_data; struct gprs_nsvc *nsvc = nssd->nsvc; struct gbproxy_peer *peer; int remote_end_is_sgsn = gbproxy_is_sgsn_nsei(cfg, nsvc->nsei); if (subsys != SS_L_NS) return 0; if (signal == S_NS_RESET && remote_end_is_sgsn) { /* We have received a NS-RESET from the NSEI and NSVC * of the SGSN. This might happen with SGSN that start * their own NS-RESET procedure without waiting for our * NS-RESET */ nsvc->remote_end_is_sgsn = 1; } if (signal == S_NS_ALIVE_EXP && nsvc->remote_end_is_sgsn) { LOGP(DGPRS, LOGL_NOTICE, "Tns alive expired too often, " "re-starting RESET procedure\n"); rate_ctr_inc(&cfg->ctrg-> ctr[GBPROX_GLOB_CTR_RESTART_RESET_SGSN]); gprs_ns_nsip_connect(nsvc->nsi, &nsvc->ip.bts_addr, nsvc->nsei, nsvc->nsvci); } if (!nsvc->remote_end_is_sgsn) { /* from BSS to SGSN */ peer = gbproxy_peer_by_nsei(cfg, nsvc->nsei); if (!peer) { LOGP(DGPRS, LOGL_NOTICE, "signal %u for unknown peer " "NSEI=%u/NSVCI=%u\n", signal, nsvc->nsei, nsvc->nsvci); return 0; } switch (signal) { case S_NS_RESET: case S_NS_BLOCK: if (!peer->blocked) break; LOGP(DGPRS, LOGL_NOTICE, "Converting NS_RESET from " "NSEI=%u/NSVCI=%u into BSSGP_BVC_BLOCK to SGSN\n", nsvc->nsei, nsvc->nsvci); bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_BLOCK, nsvc->nsei, peer->bvci, 0); break; } } else { /* Forward this message to all NS-VC to BSS */ struct gprs_ns_inst *nsi = cfg->nsi; struct gprs_nsvc *next_nsvc; llist_for_each_entry(next_nsvc, &nsi->gprs_nsvcs, list) { if (next_nsvc->remote_end_is_sgsn) continue; /* Note that the following does not start the full * procedures including timer based retransmissions. */ switch (signal) { case S_NS_RESET: gprs_ns_tx_reset(next_nsvc, nssd->cause); break; case S_NS_BLOCK: gprs_ns_tx_block(next_nsvc, nssd->cause); break; case S_NS_UNBLOCK: gprs_ns_tx_unblock(next_nsvc); break; } } } return 0; } void gbprox_reset(struct gbproxy_config *cfg) { struct gbproxy_peer *peer, *tmp; llist_for_each_entry_safe(peer, tmp, &cfg->bts_peers, list) gbproxy_peer_free(peer); rate_ctr_group_free(cfg->ctrg); gbproxy_init_config(cfg); } int gbproxy_init_config(struct gbproxy_config *cfg) { struct timespec tp; INIT_LLIST_HEAD(&cfg->bts_peers); cfg->ctrg = rate_ctr_group_alloc(tall_bsc_ctx, &global_ctrg_desc, 0); clock_gettime(CLOCK_REALTIME, &tp); cfg->bss_ptmsi_state = tp.tv_sec + tp.tv_nsec; cfg->sgsn_tlli_state = tp.tv_sec - tp.tv_nsec; return 0; } openbsc-0.15.0/openbsc/src/gprs/gb_proxy_main.c000066400000000000000000000166531265565154000214460ustar00rootroot00000000000000/* NS-over-IP proxy */ /* (C) 2010 by Harald Welte * (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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../../bscconfig.h" #define _GNU_SOURCE #include 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 \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: 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 void *tall_msgb_ctx; 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"); tall_msgb_ctx = talloc_named_const(tall_bsc_ctx, 0, "msgb"); signal(SIGINT, &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(&gprs_log_info); gbproxy_vty_init(); handle_options(argc, argv); rate_ctr_init(tall_bsc_ctx); rc = telnet_init(tall_bsc_ctx, &dummy_network, OSMO_VTY_PORT_GBPROXY); if (rc < 0) exit(1); 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); } 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); } openbsc-0.15.0/openbsc/src/gprs/gb_proxy_patch.c000066400000000000000000000273341265565154000216170ustar00rootroot00000000000000/* 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 . * */ #include #include #include #include #include #include #include #include /* 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, gprs_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, gprs_apn_to_str(str1, apn, apn_len), gprs_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; } openbsc-0.15.0/openbsc/src/gprs/gb_proxy_peer.c000066400000000000000000000131131265565154000214410ustar00rootroot00000000000000/* Gb proxy peer handling */ /* (C) 2010 by Harald Welte * (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 . * */ #include #include #include #include #include #include #include #include #include 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 " }, { "tlli-unknown", "TLLI from SGSN unknown " }, { "tlli-cache", "TLLI cache size " }, }; 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, }; /* 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); 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; } openbsc-0.15.0/openbsc/src/gprs/gb_proxy_tlli.c000066400000000000000000000500651265565154000214610ustar00rootroot00000000000000/* 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 . * */ #include #include #include #include #include #include #include #include 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 void gbproxy_unregister_link_info(struct gbproxy_peer *peer, struct gbproxy_link_info *link_info) { if (!link_info) return; 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; } 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; } 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; } void 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) { 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); 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); } } 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); } openbsc-0.15.0/openbsc/src/gprs/gb_proxy_vty.c000066400000000000000000000541701265565154000213400ustar00rootroot00000000000000/* * (C) 2010 by Harald Welte * (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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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", gprs_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; } openbsc-0.15.0/openbsc/src/gprs/gprs_gb_parse.c000066400000000000000000000373501265565154000214230ustar00rootroot00000000000000/* 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 . * */ #include #include #include #include #include #include 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 (gprs_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 */ gprs_shift_v_fixed(&data, &data_len, 3, NULL); /* Get Mobile identity */ if (gprs_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 (gprs_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 */ gprs_shift_v_fixed(&data, &data_len, 3, NULL); if (gprs_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) */ gprs_match_tv_fixed(&data, &data_len, GSM48_IE_GMM_PTMSI_SIG, 3, NULL); /* Skip Negotiated READY timer value (GPRS timer, opt, TV, length 2) */ gprs_match_tv_fixed(&data, &data_len, GSM48_IE_GMM_TIMER_READY, 1, NULL); /* Allocated P-TMSI (Mobile identity, opt, TLV, length 7) */ if (gprs_match_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 (gprs_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 (gprs_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 (gprs_match_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 */ gprs_shift_v_fixed(&data, &data_len, 1, NULL); if (gprs_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 (gprs_shift_v_fixed(&data, &data_len, 1, &value) <= 0) return 0; cause = value[0]; /* Force to standby, 1/2 */ /* spare bits, 1/2 */ if (gprs_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 */ gprs_shift_v_fixed(&data, &data_len, 2, NULL); if (gprs_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) */ gprs_match_tv_fixed(&data, &data_len, GSM48_IE_GMM_PTMSI_SIG, 3, NULL); /* Allocated P-TMSI (Mobile identity, opt, TLV, length 7) */ if (gprs_match_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 (gprs_shift_lv(&data, &data_len, &value, &value_len) > 0 && gprs_is_mi_tmsi(value, value_len)) parse_ctx->new_ptmsi_enc = value + 1; if (gprs_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 (gprs_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 */ gprs_shift_v_fixed(&data, &data_len, 2, NULL); /* Skip Requested QoS (support 04.08 and 24.008) */ if (gprs_shift_lv(&data, &data_len, NULL, &value_len) <= 0 || value_len < 4 || value_len > 14) /* invalid */ return 0; /* Skip Requested PDP address */ if (gprs_shift_lv(&data, &data_len, NULL, &value_len) <= 0 || value_len < 2 || value_len > 18) /* invalid */ return 0; /* Access point name */ old_len = gprs_match_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; if (gprs_shift_v_fixed(&data, &data_len, sizeof(*g48h), (uint8_t **)&g48h) <= 0) return 0; parse_ctx->g48_hdr = g48h; if ((g48h->proto_discr & 0x0f) != GSM48_PDISC_MM_GPRS && (g48h->proto_discr & 0x0f) != GSM48_PDISC_SM_GPRS) return 1; switch (g48h->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: 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); 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"; } openbsc-0.15.0/openbsc/src/gprs/gprs_gmm.c000066400000000000000000001701551265565154000204220ustar00rootroot00000000000000/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */ /* (C) 2009-2015 by Harald Welte * (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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define PTMSI_ALLOC extern struct sgsn_instance *sgsn; static const struct tlv_definition gsm48_gmm_att_tlvdef = { .def = { [GSM48_IE_GMM_CIPH_CKSN] = { TLV_TYPE_FIXED, 1 }, [GSM48_IE_GMM_TIMER_READY] = { TLV_TYPE_TV, 1 }, [GSM48_IE_GMM_ALLOC_PTMSI] = { TLV_TYPE_TLV, 0 }, [GSM48_IE_GMM_PTMSI_SIG] = { TLV_TYPE_FIXED, 3 }, [GSM48_IE_GMM_AUTH_RAND] = { TLV_TYPE_FIXED, 16 }, [GSM48_IE_GMM_AUTH_SRES] = { TLV_TYPE_FIXED, 4 }, [GSM48_IE_GMM_IMEISV] = { TLV_TYPE_TLV, 0 }, [GSM48_IE_GMM_DRX_PARAM] = { TLV_TYPE_FIXED, 2 }, [GSM48_IE_GMM_MS_NET_CAPA] = { TLV_TYPE_TLV, 0 }, [GSM48_IE_GMM_PDP_CTX_STATUS] = { TLV_TYPE_TLV, 0 }, [GSM48_IE_GMM_PS_LCS_CAPA] = { TLV_TYPE_TLV, 0 }, [GSM48_IE_GMM_GMM_MBMS_CTX_ST] = { TLV_TYPE_TLV, 0 }, }, }; static const struct tlv_definition gsm48_sm_att_tlvdef = { .def = { [GSM48_IE_GSM_APN] = { TLV_TYPE_TLV, 0 }, [GSM48_IE_GSM_PROTO_CONF_OPT] = { TLV_TYPE_TLV, 0 }, [GSM48_IE_GSM_PDP_ADDR] = { TLV_TYPE_TLV, 0 }, [GSM48_IE_GSM_AA_TMR] = { TLV_TYPE_TV, 1 }, [GSM48_IE_GSM_NAME_FULL] = { TLV_TYPE_TLV, 0 }, [GSM48_IE_GSM_NAME_SHORT] = { TLV_TYPE_TLV, 0 }, [GSM48_IE_GSM_TIMEZONE] = { TLV_TYPE_FIXED, 1 }, [GSM48_IE_GSM_UTC_AND_TZ] = { TLV_TYPE_FIXED, 7 }, [GSM48_IE_GSM_LSA_ID] = { TLV_TYPE_TLV, 0 }, }, }; static int gsm48_gmm_authorize(struct sgsn_mm_ctx *ctx); /* Our implementation, should be kept in SGSN */ static void mmctx_timer_cb(void *_mm); static void mmctx_timer_start(struct sgsn_mm_ctx *mm, unsigned int T, unsigned int seconds) { if (osmo_timer_pending(&mm->timer)) LOGMMCTXP(LOGL_ERROR, mm, "Starting MM timer %u while old " "timer %u pending\n", T, mm->T); mm->T = T; mm->num_T_exp = 0; /* FIXME: we should do this only once ? */ mm->timer.data = mm; mm->timer.cb = &mmctx_timer_cb; osmo_timer_schedule(&mm->timer, seconds, 0); } static void mmctx_timer_stop(struct sgsn_mm_ctx *mm, unsigned int T) { if (mm->T != T) LOGMMCTXP(LOGL_ERROR, mm, "Stopping MM timer %u but " "%u is running\n", T, mm->T); osmo_timer_del(&mm->timer); } time_t gprs_max_time_to_idle(void) { return sgsn->cfg.timers.T3314 + (sgsn->cfg.timers.T3312 + 4 * 60); } /* Send a message through the underlying layer */ static int gsm48_gmm_sendmsg(struct msgb *msg, int command, struct sgsn_mm_ctx *mm) { if (mm) rate_ctr_inc(&mm->ctrg->ctr[GMM_CTR_PKTS_SIG_OUT]); /* caller needs to provide TLLI, BVCI and NSEI */ return gprs_llc_tx_ui(msg, GPRS_SAPI_GMM, command, mm); } /* copy identifiers from old message to new message, this * is required so lower layers can route it correctly */ static void gmm_copy_id(struct msgb *msg, const struct msgb *old) { msgb_tlli(msg) = msgb_tlli(old); msgb_bvci(msg) = msgb_bvci(old); msgb_nsei(msg) = msgb_nsei(old); } /* Store BVCI/NSEI in MM context */ static void msgid2mmctx(struct sgsn_mm_ctx *mm, const struct msgb *msg) { mm->bvci = msgb_bvci(msg); mm->nsei = msgb_nsei(msg); } /* Store BVCI/NSEI in MM context */ static void mmctx2msgid(struct msgb *msg, const struct sgsn_mm_ctx *mm) { msgb_tlli(msg) = mm->tlli; msgb_bvci(msg) = mm->bvci; msgb_nsei(msg) = mm->nsei; } static void mm_ctx_cleanup_free(struct sgsn_mm_ctx *ctx, const char *log_text) { LOGMMCTXP(LOGL_INFO, ctx, "Cleaning MM context due to %s\n", log_text); /* Mark MM state as deregistered */ ctx->mm_state = GMM_DEREGISTERED; sgsn_mm_ctx_cleanup_free(ctx); } /* Chapter 9.4.18 */ static int _tx_status(struct msgb *msg, uint8_t cause, struct sgsn_mm_ctx *mmctx, int sm) { struct gsm48_hdr *gh; /* MMCTX might be NULL! */ DEBUGP(DMM, "<- GPRS MM STATUS (cause: %s)\n", get_value_string(gsm48_gmm_cause_names, cause)); gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); if (sm) { gh->proto_discr = GSM48_PDISC_SM_GPRS; gh->msg_type = GSM48_MT_GSM_STATUS; } else { gh->proto_discr = GSM48_PDISC_MM_GPRS; gh->msg_type = GSM48_MT_GMM_STATUS; } gh->data[0] = cause; return gsm48_gmm_sendmsg(msg, 0, mmctx); } static int gsm48_tx_gmm_status(struct sgsn_mm_ctx *mmctx, uint8_t cause) { struct msgb *msg = gsm48_msgb_alloc(); mmctx2msgid(msg, mmctx); return _tx_status(msg, cause, mmctx, 0); } static int gsm48_tx_sm_status(struct sgsn_mm_ctx *mmctx, uint8_t cause) { struct msgb *msg = gsm48_msgb_alloc(); mmctx2msgid(msg, mmctx); return _tx_status(msg, cause, mmctx, 1); } static int _tx_detach_req(struct msgb *msg, uint8_t detach_type, uint8_t cause, struct sgsn_mm_ctx *mmctx) { struct gsm48_hdr *gh; /* MMCTX might be NULL! */ DEBUGP(DMM, "<- GPRS MM DETACH REQ (type: %s, cause: %s)\n", get_value_string(gprs_det_t_mt_strs, detach_type), get_value_string(gsm48_gmm_cause_names, cause)); gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); gh->proto_discr = GSM48_PDISC_MM_GPRS; gh->msg_type = GSM48_MT_GMM_DETACH_REQ; gh->data[0] = detach_type & 0x07; msgb_tv_put(msg, GSM48_IE_GMM_CAUSE, cause); return gsm48_gmm_sendmsg(msg, 0, mmctx); } static int gsm48_tx_gmm_detach_req(struct sgsn_mm_ctx *mmctx, uint8_t detach_type, uint8_t cause) { struct msgb *msg = gsm48_msgb_alloc(); mmctx2msgid(msg, mmctx); return _tx_detach_req(msg, detach_type, cause, mmctx); } static int gsm48_tx_gmm_detach_req_oldmsg(struct msgb *oldmsg, uint8_t detach_type, uint8_t cause) { struct msgb *msg = gsm48_msgb_alloc(); gmm_copy_id(msg, oldmsg); return _tx_detach_req(msg, detach_type, cause, NULL); } static struct gsm48_qos default_qos = { .delay_class = 4, /* best effort */ .reliab_class = GSM48_QOS_RC_LLC_UN_RLC_ACK_DATA_PROT, .peak_tput = GSM48_QOS_PEAK_TPUT_32000bps, .preced_class = GSM48_QOS_PC_NORMAL, .mean_tput = GSM48_QOS_MEAN_TPUT_BEST_EFFORT, .traf_class = GSM48_QOS_TC_INTERACTIVE, .deliv_order = GSM48_QOS_DO_UNORDERED, .deliv_err_sdu = GSM48_QOS_ERRSDU_YES, .max_sdu_size = GSM48_QOS_MAXSDU_1520, .max_bitrate_up = GSM48_QOS_MBRATE_63k, .max_bitrate_down = GSM48_QOS_MBRATE_63k, .resid_ber = GSM48_QOS_RBER_5e_2, .sdu_err_ratio = GSM48_QOS_SERR_1e_2, .handling_prio = 3, .xfer_delay = 0x10, /* 200ms */ .guar_bitrate_up = GSM48_QOS_MBRATE_0k, .guar_bitrate_down = GSM48_QOS_MBRATE_0k, .sig_ind = 0, /* not optimised for signalling */ .max_bitrate_down_ext = 0, /* use octet 9 */ .guar_bitrate_down_ext = 0, /* use octet 13 */ }; /* Chapter 9.4.2: Attach accept */ static int gsm48_tx_gmm_att_ack(struct sgsn_mm_ctx *mm) { struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh; struct gsm48_attach_ack *aa; uint8_t *mid; #if 0 uint8_t *ptsig; #endif LOGMMCTXP(LOGL_INFO, mm, "<- GPRS ATTACH ACCEPT (new P-TMSI=0x%08x)\n", mm->p_tmsi); mmctx2msgid(msg, mm); gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->proto_discr = GSM48_PDISC_MM_GPRS; gh->msg_type = GSM48_MT_GMM_ATTACH_ACK; aa = (struct gsm48_attach_ack *) msgb_put(msg, sizeof(*aa)); aa->force_stby = 0; /* not indicated */ aa->att_result = 1; /* GPRS only */ aa->ra_upd_timer = gprs_secs_to_tmr_floor(sgsn->cfg.timers.T3312); aa->radio_prio = 4; /* lowest */ gsm48_construct_ra(aa->ra_id.digits, &mm->ra); #if 0 /* Optional: P-TMSI signature */ msgb_v_put(msg, GSM48_IE_GMM_PTMSI_SIG); ptsig = msgb_put(msg, 3); ptsig[0] = mm->p_tmsi_sig >> 16; ptsig[1] = mm->p_tmsi_sig >> 8; ptsig[2] = mm->p_tmsi_sig & 0xff; #endif /* Optional: Negotiated Ready timer value * (fixed 44s, default value, GSM 04.08, table 11.4a) to safely limit * the inactivity time READY->STANDBY. */ msgb_tv_put(msg, GSM48_IE_GMM_TIMER_READY, gprs_secs_to_tmr_floor(sgsn->cfg.timers.T3314)); #ifdef PTMSI_ALLOC /* Optional: Allocated P-TMSI */ mid = msgb_put(msg, GSM48_MID_TMSI_LEN); gsm48_generate_mid_from_tmsi(mid, mm->p_tmsi); mid[0] = GSM48_IE_GMM_ALLOC_PTMSI; #endif /* Optional: MS-identity (combined attach) */ /* Optional: GMM cause (partial attach result for combined attach) */ return gsm48_gmm_sendmsg(msg, 0, mm); } /* Chapter 9.4.5: Attach reject */ static int _tx_gmm_att_rej(struct msgb *msg, uint8_t gmm_cause, const struct sgsn_mm_ctx *mm) { struct gsm48_hdr *gh; LOGMMCTXP(LOGL_NOTICE, mm, "<- GPRS ATTACH REJECT: %s\n", get_value_string(gsm48_gmm_cause_names, gmm_cause)); gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); gh->proto_discr = GSM48_PDISC_MM_GPRS; gh->msg_type = GSM48_MT_GMM_ATTACH_REJ; gh->data[0] = gmm_cause; return gsm48_gmm_sendmsg(msg, 0, NULL); } static int gsm48_tx_gmm_att_rej_oldmsg(const struct msgb *old_msg, uint8_t gmm_cause) { struct msgb *msg = gsm48_msgb_alloc(); gmm_copy_id(msg, old_msg); return _tx_gmm_att_rej(msg, gmm_cause, NULL); } static int gsm48_tx_gmm_att_rej(struct sgsn_mm_ctx *mm, uint8_t gmm_cause) { struct msgb *msg = gsm48_msgb_alloc(); mmctx2msgid(msg, mm); return _tx_gmm_att_rej(msg, gmm_cause, mm); } /* Chapter 9.4.6.2 Detach accept */ static int _tx_detach_ack(struct msgb *msg, uint8_t force_stby, struct sgsn_mm_ctx *mm) { struct gsm48_hdr *gh; /* MMCTX might be NULL! */ DEBUGP(DMM, "<- GPRS MM DETACH ACC (force-standby: %d)\n", force_stby); gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); gh->proto_discr = GSM48_PDISC_MM_GPRS; gh->msg_type = GSM48_MT_GMM_DETACH_ACK; gh->data[0] = force_stby; return gsm48_gmm_sendmsg(msg, 0, mm); } static int gsm48_tx_gmm_det_ack(struct sgsn_mm_ctx *mm, uint8_t force_stby) { struct msgb *msg = gsm48_msgb_alloc(); mmctx2msgid(msg, mm); return _tx_detach_ack(msg, force_stby, mm); } static int gsm48_tx_gmm_det_ack_oldmsg(struct msgb *oldmsg, uint8_t force_stby) { struct msgb *msg = gsm48_msgb_alloc(); gmm_copy_id(msg, oldmsg); return _tx_detach_ack(msg, force_stby, NULL); } /* Transmit Chapter 9.4.12 Identity Request */ static int gsm48_tx_gmm_id_req(struct sgsn_mm_ctx *mm, uint8_t id_type) { struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh; LOGMMCTXP(LOGL_DEBUG, mm, "<- GPRS IDENTITY REQUEST: mi_type=%s\n", gsm48_mi_type_name(id_type)); mmctx2msgid(msg, mm); gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); gh->proto_discr = GSM48_PDISC_MM_GPRS; gh->msg_type = GSM48_MT_GMM_ID_REQ; /* 10.5.5.9 ID type 2 + identity type and 10.5.5.7 'force to standby' IE */ gh->data[0] = id_type & 0xf; return gsm48_gmm_sendmsg(msg, 1, mm); } /* Section 9.4.9: Authentication and Ciphering Request */ static int gsm48_tx_gmm_auth_ciph_req(struct sgsn_mm_ctx *mm, uint8_t *rand, uint8_t key_seq, uint8_t algo) { struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh; struct gsm48_auth_ciph_req *acreq; uint8_t *m_rand, *m_cksn; LOGMMCTXP(LOGL_INFO, mm, "<- GPRS AUTH AND CIPHERING REQ (rand = %s)\n", osmo_hexdump(rand, 16)); mmctx2msgid(msg, mm); gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->proto_discr = GSM48_PDISC_MM_GPRS; gh->msg_type = GSM48_MT_GMM_AUTH_CIPH_REQ; acreq = (struct gsm48_auth_ciph_req *) msgb_put(msg, sizeof(*acreq)); acreq->ciph_alg = algo & 0xf; acreq->imeisv_req = 0x1; acreq->force_stby = 0x0; acreq->ac_ref_nr = 0x0; /* FIXME: increment this? */ /* Only if authentication is requested we need to set RAND + CKSN */ if (rand) { m_rand = msgb_put(msg, 16+1); m_rand[0] = GSM48_IE_GMM_AUTH_RAND; memcpy(m_rand+1, rand, 16); m_cksn = msgb_put(msg, 1); m_cksn[0] = (GSM48_IE_GMM_CIPH_CKSN << 4) | (key_seq & 0x07); } /* FIXME: make sure we don't send any other messages to the MS */ return gsm48_gmm_sendmsg(msg, 1, mm); } /* Section 9.4.11: Authentication and Ciphering Reject */ static int gsm48_tx_gmm_auth_ciph_rej(struct sgsn_mm_ctx *mm) { struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh; LOGMMCTXP(LOGL_NOTICE, mm, "<- GPRS AUTH AND CIPH REJECT\n"); mmctx2msgid(msg, mm); gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->proto_discr = GSM48_PDISC_MM_GPRS; gh->msg_type = GSM48_MT_GMM_AUTH_CIPH_REJ; return gsm48_gmm_sendmsg(msg, 0, mm); } /* Section 9.4.10: Authentication and Ciphering Response */ static int gsm48_rx_gmm_auth_ciph_resp(struct sgsn_mm_ctx *ctx, struct msgb *msg) { struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); struct gsm48_auth_ciph_resp *acr = (struct gsm48_auth_ciph_resp *)gh->data; struct tlv_parsed tp; struct gsm_auth_tuple *at; int rc; LOGMMCTXP(LOGL_INFO, ctx, "-> GPRS AUTH AND CIPH RESPONSE\n"); if (ctx->auth_triplet.key_seq == GSM_KEY_SEQ_INVAL) { LOGMMCTXP(LOGL_NOTICE, ctx, "Unexpected Auth & Ciph Response (ignored)\n"); return 0; } /* Stop T3360 */ mmctx_timer_stop(ctx, 3360); tlv_parse(&tp, &gsm48_gmm_att_tlvdef, acr->data, (msg->data + msg->len) - acr->data, 0, 0); /* FIXME: compare ac_ref? */ if (!TLVP_PRESENT(&tp, GSM48_IE_GMM_AUTH_SRES) || !TLVP_PRESENT(&tp, GSM48_IE_GMM_IMEISV)) { /* TODO: missing mandatory IE, return STATUS or REJ? */ LOGMMCTXP(LOGL_ERROR, ctx, "Missing mandantory IE\n"); return -EINVAL; } /* Compare SRES with what we expected */ LOGMMCTXP(LOGL_DEBUG, ctx, "checking received auth info, SRES = %s\n", osmo_hexdump(TLVP_VAL(&tp, GSM48_IE_GMM_AUTH_SRES), TLVP_LEN(&tp, GSM48_IE_GMM_AUTH_SRES))); at = &ctx->auth_triplet; if (TLVP_LEN(&tp, GSM48_IE_GMM_AUTH_SRES) != sizeof(at->sres) || memcmp(TLVP_VAL(&tp, GSM48_IE_GMM_AUTH_SRES), at->sres, sizeof(at->sres)) != 0) { LOGMMCTXP(LOGL_NOTICE, ctx, "Received SRES doesn't match\n"); rc = gsm48_tx_gmm_auth_ciph_rej(ctx); mm_ctx_cleanup_free(ctx, "GPRS AUTH AND CIPH REJECT"); return rc; } ctx->is_authenticated = 1; /* FIXME: enable LLC cipheirng */ /* Check if we can let the mobile station enter */ return gsm48_gmm_authorize(ctx); } static void extract_subscr_msisdn(struct sgsn_mm_ctx *ctx) { struct gsm_mncc_number called; uint8_t msisdn[sizeof(ctx->subscr->sgsn_data->msisdn) + 1]; /* Convert MSISDN from encoded to string.. */ if (!ctx->subscr) return; if (ctx->subscr->sgsn_data->msisdn_len < 1) return; /* prepare the data for the decoder */ memset(&called, 0, sizeof(called)); msisdn[0] = ctx->subscr->sgsn_data->msisdn_len; memcpy(&msisdn[1], ctx->subscr->sgsn_data->msisdn, ctx->subscr->sgsn_data->msisdn_len); /* decode the string now */ gsm48_decode_called(&called, msisdn); /* Prepend a '+' for international numbers */ if (called.plan == 1 && called.type == 1) { ctx->msisdn[0] = '+'; strncpy(&ctx->msisdn[1], called.number, sizeof(ctx->msisdn) - 1); } else { strncpy(&ctx->msisdn[0], called.number, sizeof(ctx->msisdn) - 1); } } static void extract_subscr_hlr(struct sgsn_mm_ctx *ctx) { struct gsm_mncc_number called; uint8_t hlr_number[sizeof(ctx->subscr->sgsn_data->hlr) + 1]; if (!ctx->subscr) return; if (ctx->subscr->sgsn_data->hlr_len < 1) return; /* prepare the data for the decoder */ memset(&called, 0, sizeof(called)); hlr_number[0] = ctx->subscr->sgsn_data->hlr_len; memcpy(&hlr_number[1], ctx->subscr->sgsn_data->hlr, ctx->subscr->sgsn_data->hlr_len); /* decode the string now */ gsm48_decode_called(&called, hlr_number); if (called.plan != 1) { LOGMMCTXP(LOGL_ERROR, ctx, "Numbering plan(%d) not allowed\n", called.plan); return; } if (called.type != 1) { LOGMMCTXP(LOGL_ERROR, ctx, "Numbering type(%d) not allowed\n", called.type); return; } strncpy(&ctx->hlr[0], called.number, sizeof(ctx->hlr) - 1); } /* Check if we can already authorize a subscriber */ static int gsm48_gmm_authorize(struct sgsn_mm_ctx *ctx) { #ifndef PTMSI_ALLOC struct sgsn_signal_data sig_data; #endif /* Request IMSI and IMEI from the MS if they are unknown */ if (!strlen(ctx->imei)) { ctx->t3370_id_type = GSM_MI_TYPE_IMEI; mmctx_timer_start(ctx, 3370, sgsn->cfg.timers.T3370); return gsm48_tx_gmm_id_req(ctx, GSM_MI_TYPE_IMEI); } if (!strlen(ctx->imsi)) { ctx->t3370_id_type = GSM_MI_TYPE_IMSI; mmctx_timer_start(ctx, 3370, sgsn->cfg.timers.T3370); return gsm48_tx_gmm_id_req(ctx, GSM_MI_TYPE_IMSI); } /* All information required for authentication is available */ ctx->t3370_id_type = GSM_MI_TYPE_NONE; if (ctx->auth_state == SGSN_AUTH_UNKNOWN) { /* Request authorization, this leads to a call to * sgsn_auth_update which in turn calls * gsm0408_gprs_access_granted or gsm0408_gprs_access_denied */ sgsn_auth_request(ctx); /* Note that gsm48_gmm_authorize can be called recursively via * sgsn_auth_request iff ctx->auth_info changes to AUTH_ACCEPTED */ return 0; } if (ctx->auth_state == SGSN_AUTH_AUTHENTICATE && !ctx->is_authenticated) { struct gsm_auth_tuple *at = &ctx->auth_triplet; mmctx_timer_start(ctx, 3360, sgsn->cfg.timers.T3360); return gsm48_tx_gmm_auth_ciph_req(ctx, at->rand, at->key_seq, GPRS_ALGO_GEA0); } if (ctx->auth_state == SGSN_AUTH_AUTHENTICATE && ctx->is_authenticated && ctx->auth_triplet.key_seq != GSM_KEY_SEQ_INVAL) { /* Check again for authorization */ sgsn_auth_request(ctx); return 0; } if (ctx->auth_state != SGSN_AUTH_ACCEPTED) { LOGMMCTXP(LOGL_NOTICE, ctx, "authorization is denied, aborting procedure\n"); return -EACCES; } /* The MS is authorized */ switch (ctx->pending_req) { case 0: LOGMMCTXP(LOGL_INFO, ctx, "no pending request, authorization completed\n"); break; case GSM48_MT_GMM_ATTACH_REQ: extract_subscr_msisdn(ctx); extract_subscr_hlr(ctx); #ifdef PTMSI_ALLOC /* Start T3350 and re-transmit up to 5 times until ATTACH COMPLETE */ mmctx_timer_start(ctx, 3350, sgsn->cfg.timers.T3350); ctx->t3350_mode = GMM_T3350_MODE_ATT; #else memset(&sig_data, 0, sizeof(sig_data)); sig_data.mm = mmctx; osmo_signal_dispatch(SS_SGSN, S_SGSN_ATTACH, &sig_data); ctx->mm_state = GMM_REGISTERED_NORMAL; #endif return gsm48_tx_gmm_att_ack(ctx); default: LOGMMCTXP(LOGL_ERROR, ctx, "only Attach Request is supported yet, " "got request type %u\n", ctx->pending_req); break; } return 0; } void gsm0408_gprs_authenticate(struct sgsn_mm_ctx *ctx) { ctx->is_authenticated = 0; gsm48_gmm_authorize(ctx); } void gsm0408_gprs_access_granted(struct sgsn_mm_ctx *ctx) { switch (ctx->mm_state) { case GMM_COMMON_PROC_INIT: LOGMMCTXP(LOGL_NOTICE, ctx, "Authorized, continuing procedure, IMSI=%s\n", ctx->imsi); /* Continue with the authorization */ gsm48_gmm_authorize(ctx); break; default: LOGMMCTXP(LOGL_INFO, ctx, "Authorized, ignored, IMSI=%s\n", ctx->imsi); } } void gsm0408_gprs_access_denied(struct sgsn_mm_ctx *ctx, int gmm_cause) { if (gmm_cause == SGSN_ERROR_CAUSE_NONE) gmm_cause = GMM_CAUSE_GPRS_NOTALLOWED; switch (ctx->mm_state) { case GMM_COMMON_PROC_INIT: LOGMMCTXP(LOGL_NOTICE, ctx, "Not authorized, rejecting ATTACH REQUEST " "with cause '%s' (%d)\n", get_value_string(gsm48_gmm_cause_names, gmm_cause), gmm_cause); gsm48_tx_gmm_att_rej(ctx, gmm_cause); mm_ctx_cleanup_free(ctx, "GPRS ATTACH REJECT"); break; case GMM_REGISTERED_NORMAL: case GMM_REGISTERED_SUSPENDED: LOGMMCTXP(LOGL_NOTICE, ctx, "Authorization lost, detaching " "with cause '%s' (%d)\n", get_value_string(gsm48_gmm_cause_names, gmm_cause), gmm_cause); gsm48_tx_gmm_detach_req( ctx, GPRS_DET_T_MT_REATT_NOTREQ, gmm_cause); mm_ctx_cleanup_free(ctx, "auth lost"); break; default: LOGMMCTXP(LOGL_INFO, ctx, "Authorization lost, cause is '%s' (%d)\n", get_value_string(gsm48_gmm_cause_names, gmm_cause), gmm_cause); mm_ctx_cleanup_free(ctx, "auth lost"); } } void gsm0408_gprs_access_cancelled(struct sgsn_mm_ctx *ctx, int gmm_cause) { if (gmm_cause != SGSN_ERROR_CAUSE_NONE) { LOGMMCTXP(LOGL_INFO, ctx, "Cancelled with cause '%s' (%d), deleting context\n", get_value_string(gsm48_gmm_cause_names, gmm_cause), gmm_cause); gsm0408_gprs_access_denied(ctx, gmm_cause); return; } LOGMMCTXP(LOGL_INFO, ctx, "Cancelled, deleting context silently\n"); mm_ctx_cleanup_free(ctx, "access cancelled"); } /* Parse Chapter 9.4.13 Identity Response */ static int gsm48_rx_gmm_id_resp(struct sgsn_mm_ctx *ctx, struct msgb *msg) { struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); uint8_t mi_type = gh->data[1] & GSM_MI_TYPE_MASK; char mi_string[GSM48_MI_SIZE]; gsm48_mi_to_string(mi_string, sizeof(mi_string), &gh->data[1], gh->data[0]); if (!ctx) { DEBUGP(DMM, "from unknown TLLI 0x%08x?!? This should not happen\n", msgb_tlli(msg)); return -EINVAL; } LOGMMCTXP(LOGL_DEBUG, ctx, "-> GMM IDENTITY RESPONSE: MI(%s)=%s\n", gsm48_mi_type_name(mi_type), mi_string); if (ctx->t3370_id_type == GSM_MI_TYPE_NONE) { LOGMMCTXP(LOGL_NOTICE, ctx, "Got unexpected IDENTITY RESPONSE: MI(%s)=%s, " "ignoring message\n", gsm48_mi_type_name(mi_type), mi_string); return -EINVAL; } if (mi_type == ctx->t3370_id_type) mmctx_timer_stop(ctx, 3370); switch (mi_type) { case GSM_MI_TYPE_IMSI: /* we already have a mm context with current TLLI, but no * P-TMSI / IMSI yet. What we now need to do is to fill * this initial context with data from the HLR */ if (strlen(ctx->imsi) == 0) { /* Check if we already have a MM context for this IMSI */ struct sgsn_mm_ctx *ictx; ictx = sgsn_mm_ctx_by_imsi(mi_string); if (ictx) { /* Handle it like in gsm48_rx_gmm_det_req, * except that no messages are sent to the BSS */ LOGMMCTXP(LOGL_NOTICE, ctx, "Deleting old MM Context for same IMSI " "p_tmsi_old=0x%08x\n", ictx->p_tmsi); mm_ctx_cleanup_free(ictx, "GPRS IMSI re-use"); } } strncpy(ctx->imsi, mi_string, sizeof(ctx->imsi) - 1); break; case GSM_MI_TYPE_IMEI: strncpy(ctx->imei, mi_string, sizeof(ctx->imei) - 1); break; case GSM_MI_TYPE_IMEISV: break; } /* Check if we can let the mobile station enter */ return gsm48_gmm_authorize(ctx); } /* Section 9.4.1 Attach request */ static int gsm48_rx_gmm_att_req(struct sgsn_mm_ctx *ctx, struct msgb *msg, struct gprs_llc_llme *llme) { struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); uint8_t *cur = gh->data, *msnc, *mi, *ms_ra_acc_cap; uint8_t msnc_len, att_type, mi_len, mi_type, ms_ra_acc_cap_len; uint16_t drx_par; uint32_t tmsi; char mi_string[GSM48_MI_SIZE]; struct gprs_ra_id ra_id; uint16_t cid; enum gsm48_gmm_cause reject_cause; int rc; LOGMMCTXP(LOGL_INFO, ctx, "-> GMM ATTACH REQUEST "); /* As per TS 04.08 Chapter 4.7.1.4, the attach request arrives either * with a foreign TLLI (P-TMSI that was allocated to the MS before), * or with random TLLI. */ cid = bssgp_parse_cell_id(&ra_id, msgb_bcid(msg)); /* MS network capability 10.5.5.12 */ msnc_len = *cur++; msnc = cur; if (msnc_len > sizeof(ctx->ms_network_capa.buf)) goto err_inval; cur += msnc_len; /* aTTACH Type 10.5.5.2 */ att_type = *cur++ & 0x0f; /* DRX parameter 10.5.5.6 */ drx_par = *cur++ << 8; drx_par |= *cur++; /* Mobile Identity (P-TMSI or IMSI) 10.5.1.4 */ mi_len = *cur++; mi = cur; if (mi_len > 8) goto err_inval; mi_type = *mi & GSM_MI_TYPE_MASK; cur += mi_len; gsm48_mi_to_string(mi_string, sizeof(mi_string), mi, mi_len); DEBUGPC(DMM, "MI(%s) type=\"%s\" ", mi_string, get_value_string(gprs_att_t_strs, att_type)); /* Old routing area identification 10.5.5.15. Skip it */ cur += 6; /* MS Radio Access Capability 10.5.5.12a */ ms_ra_acc_cap_len = *cur++; ms_ra_acc_cap = cur; if (ms_ra_acc_cap_len > sizeof(ctx->ms_radio_access_capa.buf)) goto err_inval; cur += ms_ra_acc_cap_len; LOGPC(DMM, LOGL_INFO, "\n"); /* Optional: Old P-TMSI Signature, Requested READY timer, TMSI Status */ switch (mi_type) { case GSM_MI_TYPE_IMSI: /* Try to find MM context based on IMSI */ if (!ctx) ctx = sgsn_mm_ctx_by_imsi(mi_string); if (!ctx) { #if 0 return gsm48_tx_gmm_att_rej(msg, GMM_CAUSE_IMSI_UNKNOWN); #else ctx = sgsn_mm_ctx_alloc(0, &ra_id); if (!ctx) { reject_cause = GMM_CAUSE_NET_FAIL; goto rejected; } strncpy(ctx->imsi, mi_string, sizeof(ctx->imsi) - 1); #endif } ctx->tlli = msgb_tlli(msg); ctx->llme = llme; msgid2mmctx(ctx, msg); break; case GSM_MI_TYPE_TMSI: memcpy(&tmsi, mi+1, 4); tmsi = ntohl(tmsi); /* Try to find MM context based on P-TMSI */ if (!ctx) ctx = sgsn_mm_ctx_by_ptmsi(tmsi); if (!ctx) { /* Allocate a context as most of our code expects one. * Context will not have an IMSI ultil ID RESP is received */ ctx = sgsn_mm_ctx_alloc(msgb_tlli(msg), &ra_id); ctx->p_tmsi = tmsi; } ctx->tlli = msgb_tlli(msg); ctx->llme = llme; msgid2mmctx(ctx, msg); break; default: LOGMMCTXP(LOGL_NOTICE, ctx, "Rejecting ATTACH REQUEST with " "MI type %s\n", gsm48_mi_type_name(mi_type)); reject_cause = GMM_CAUSE_MS_ID_NOT_DERIVED; goto rejected; } /* Update MM Context with currient RA and Cell ID */ ctx->ra = ra_id; ctx->cell_id = cid; /* Update MM Context with other data */ ctx->drx_parms = drx_par; ctx->ms_radio_access_capa.len = ms_ra_acc_cap_len; memcpy(ctx->ms_radio_access_capa.buf, ms_ra_acc_cap, ctx->ms_radio_access_capa.len); ctx->ms_network_capa.len = msnc_len; memcpy(ctx->ms_network_capa.buf, msnc, msnc_len); #ifdef PTMSI_ALLOC /* Allocate a new P-TMSI (+ P-TMSI signature) and update TLLI */ /* Don't change the P-TMSI if a P-TMSI re-assignment is under way */ if (ctx->mm_state != GMM_COMMON_PROC_INIT) { ctx->p_tmsi_old = ctx->p_tmsi; ctx->p_tmsi = sgsn_alloc_ptmsi(); } ctx->mm_state = GMM_COMMON_PROC_INIT; #endif /* Even if there is no P-TMSI allocated, the MS will switch from * foreign TLLI to local TLLI */ ctx->tlli_new = gprs_tmsi2tlli(ctx->p_tmsi, TLLI_LOCAL); /* Inform LLC layer about new TLLI but keep old active */ gprs_llgmm_assign(ctx->llme, ctx->tlli, ctx->tlli_new, GPRS_ALGO_GEA0, NULL); ctx->pending_req = GSM48_MT_GMM_ATTACH_REQ; return gsm48_gmm_authorize(ctx); err_inval: LOGPC(DMM, LOGL_INFO, "\n"); reject_cause = GMM_CAUSE_SEM_INCORR_MSG; rejected: /* Send ATTACH REJECT */ LOGMMCTXP(LOGL_NOTICE, ctx, "Rejecting Attach Request with cause '%s' (%d)\n", get_value_string(gsm48_gmm_cause_names, reject_cause), reject_cause); rc = gsm48_tx_gmm_att_rej_oldmsg(msg, reject_cause); if (ctx) mm_ctx_cleanup_free(ctx, "GPRS ATTACH REJ"); else /* TLLI unassignment */ gprs_llgmm_assign(llme, llme->tlli, 0xffffffff, GPRS_ALGO_GEA0, NULL); return rc; } /* Section 4.7.4.1 / 9.4.5.2 MO Detach request */ static int gsm48_rx_gmm_det_req(struct sgsn_mm_ctx *ctx, struct msgb *msg) { struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); uint8_t detach_type, power_off; int rc = 0; detach_type = gh->data[0] & 0x7; power_off = gh->data[0] & 0x8; /* FIXME: In 24.008 there is an optional P-TMSI and P-TMSI signature IE */ LOGMMCTXP(LOGL_INFO, ctx, "-> GMM DETACH REQUEST TLLI=0x%08x type=%s %s\n", msgb_tlli(msg), get_value_string(gprs_det_t_mo_strs, detach_type), power_off ? "Power-off" : ""); /* Only send the Detach Accept (MO) if power off isn't indicated, * see 04.08, 4.7.4.1.2/3 for details */ if (!power_off) { /* force_stby = 0 */ if (ctx) rc = gsm48_tx_gmm_det_ack(ctx, 0); else rc = gsm48_tx_gmm_det_ack_oldmsg(msg, 0); } if (ctx) { struct sgsn_signal_data sig_data; memset(&sig_data, 0, sizeof(sig_data)); sig_data.mm = ctx; osmo_signal_dispatch(SS_SGSN, S_SGSN_DETACH, &sig_data); mm_ctx_cleanup_free(ctx, "GPRS DETACH REQUEST"); } return rc; } /* Chapter 9.4.15: Routing area update accept */ static int gsm48_tx_gmm_ra_upd_ack(struct sgsn_mm_ctx *mm) { struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh; struct gsm48_ra_upd_ack *rua; uint8_t *mid; LOGMMCTXP(LOGL_INFO, mm, "<- ROUTING AREA UPDATE ACCEPT\n"); mmctx2msgid(msg, mm); gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->proto_discr = GSM48_PDISC_MM_GPRS; gh->msg_type = GSM48_MT_GMM_RA_UPD_ACK; rua = (struct gsm48_ra_upd_ack *) msgb_put(msg, sizeof(*rua)); rua->force_stby = 0; /* not indicated */ rua->upd_result = 0; /* RA updated */ rua->ra_upd_timer = gprs_secs_to_tmr_floor(sgsn->cfg.timers.T3312); gsm48_construct_ra(rua->ra_id.digits, &mm->ra); #if 0 /* Optional: P-TMSI signature */ msgb_v_put(msg, GSM48_IE_GMM_PTMSI_SIG); ptsig = msgb_put(msg, 3); ptsig[0] = mm->p_tmsi_sig >> 16; ptsig[1] = mm->p_tmsi_sig >> 8; ptsig[2] = mm->p_tmsi_sig & 0xff; #endif #ifdef PTMSI_ALLOC /* Optional: Allocated P-TMSI */ mid = msgb_put(msg, GSM48_MID_TMSI_LEN); gsm48_generate_mid_from_tmsi(mid, mm->p_tmsi); mid[0] = GSM48_IE_GMM_ALLOC_PTMSI; #endif /* Optional: Negotiated READY timer value */ msgb_tv_put(msg, GSM48_IE_GMM_TIMER_READY, gprs_secs_to_tmr_floor(sgsn->cfg.timers.T3314)); /* Option: MS ID, ... */ return gsm48_gmm_sendmsg(msg, 0, mm); } /* Chapter 9.4.17: Routing area update reject */ static int gsm48_tx_gmm_ra_upd_rej(struct msgb *old_msg, uint8_t cause) { struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh; LOGP(DMM, LOGL_NOTICE, "<- ROUTING AREA UPDATE REJECT\n"); gmm_copy_id(msg, old_msg); gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 2); gh->proto_discr = GSM48_PDISC_MM_GPRS; gh->msg_type = GSM48_MT_GMM_RA_UPD_REJ; gh->data[0] = cause; gh->data[1] = 0; /* ? */ /* Option: P-TMSI signature, allocated P-TMSI, MS ID, ... */ return gsm48_gmm_sendmsg(msg, 0, NULL); } static void process_ms_ctx_status(struct sgsn_mm_ctx *mmctx, const uint8_t *pdp_status) { struct sgsn_pdp_ctx *pdp, *pdp2; /* 24.008 4.7.5.1.3: If the PDP context status information element is * included in ROUTING AREA UPDATE REQUEST message, then the network * shall deactivate all those PDP contexts locally (without peer to * peer signalling between the MS and the network), which are not in SM * state PDP-INACTIVE on network side but are indicated by the MS as * being in state PDP-INACTIVE. */ llist_for_each_entry_safe(pdp, pdp2, &mmctx->pdp_list, list) { if (pdp->nsapi < 8) { if (!(pdp_status[0] & (1 << pdp->nsapi))) { LOGMMCTXP(LOGL_NOTICE, mmctx, "Dropping PDP context for NSAPI=%u " "due to PDP CTX STATUS IE= 0x%02x%02x\n", pdp->nsapi, pdp_status[1], pdp_status[0]); sgsn_delete_pdp_ctx(pdp); } } else { if (!(pdp_status[1] & (1 << (pdp->nsapi - 8)))) { LOGMMCTXP(LOGL_NOTICE, mmctx, "Dropping PDP context for NSAPI=%u " "due to PDP CTX STATUS IE= 0x%02x%02x\n", pdp->nsapi, pdp_status[1], pdp_status[0]); sgsn_delete_pdp_ctx(pdp); } } } } /* Chapter 9.4.14: Routing area update request */ static int gsm48_rx_gmm_ra_upd_req(struct sgsn_mm_ctx *mmctx, struct msgb *msg, struct gprs_llc_llme *llme) { #ifndef PTMSI_ALLOC struct sgsn_signal_data sig_data; #endif struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); uint8_t *cur = gh->data; uint8_t ms_ra_acc_cap_len; struct gprs_ra_id old_ra_id; struct tlv_parsed tp; uint8_t upd_type; enum gsm48_gmm_cause reject_cause; int rc; /* Update Type 10.5.5.18 */ upd_type = *cur++ & 0x0f; LOGP(DMM, LOGL_INFO, "-> GMM RA UPDATE REQUEST type=\"%s\"\n", get_value_string(gprs_upd_t_strs, upd_type)); /* Old routing area identification 10.5.5.15 */ gsm48_parse_ra(&old_ra_id, cur); cur += 6; /* MS Radio Access Capability 10.5.5.12a */ ms_ra_acc_cap_len = *cur++; if (ms_ra_acc_cap_len > 52) { reject_cause = GMM_CAUSE_PROTO_ERR_UNSPEC; goto rejected; } cur += ms_ra_acc_cap_len; /* Optional: Old P-TMSI Signature, Requested READY timer, TMSI Status, * DRX parameter, MS network capability */ tlv_parse(&tp, &gsm48_gmm_att_tlvdef, cur, (msg->data + msg->len) - cur, 0, 0); switch (upd_type) { case GPRS_UPD_T_RA_LA: case GPRS_UPD_T_RA_LA_IMSI_ATT: LOGP(DMM, LOGL_NOTICE, "Update type %i unsupported in Mode III, is your SI13 corrupt?\n", upd_type); reject_cause = GMM_CAUSE_PROTO_ERR_UNSPEC; goto rejected; case GPRS_UPD_T_RA: case GPRS_UPD_T_PERIODIC: break; } /* Look-up the MM context based on old RA-ID and TLLI */ mmctx = sgsn_mm_ctx_by_tlli(msgb_tlli(msg), &old_ra_id); if (!mmctx || mmctx->mm_state == GMM_DEREGISTERED) { /* send a XID reset to re-set all LLC sequence numbers * in the MS */ LOGMMCTXP(LOGL_NOTICE, mmctx, "LLC XID RESET\n"); gprs_llgmm_reset(llme); /* The MS has to perform GPRS attach */ /* Device is still IMSI attached for CS but initiate GPRS ATTACH, * see GSM 04.08, 4.7.5.1.4 and G.6 */ reject_cause = GMM_CAUSE_IMPL_DETACHED; goto rejected; } /* Store new BVCI/NSEI in MM context (FIXME: delay until we ack?) */ msgid2mmctx(mmctx, msg); /* Bump the statistics of received signalling msgs for this MM context */ rate_ctr_inc(&mmctx->ctrg->ctr[GMM_CTR_PKTS_SIG_IN]); /* Update the MM context with the new RA-ID */ bssgp_parse_cell_id(&mmctx->ra, msgb_bcid(msg)); /* Update the MM context with the new (i.e. foreign) TLLI */ mmctx->tlli = msgb_tlli(msg); /* FIXME: Update the MM context with the MS radio acc capabilities */ /* FIXME: Update the MM context with the MS network capabilities */ rate_ctr_inc(&mmctx->ctrg->ctr[GMM_CTR_RA_UPDATE]); #ifdef PTMSI_ALLOC /* Don't change the P-TMSI if a P-TMSI re-assignment is under way */ if (mmctx->mm_state != GMM_COMMON_PROC_INIT) { mmctx->p_tmsi_old = mmctx->p_tmsi; mmctx->p_tmsi = sgsn_alloc_ptmsi(); } /* Start T3350 and re-transmit up to 5 times until ATTACH COMPLETE */ mmctx->t3350_mode = GMM_T3350_MODE_RAU; mmctx_timer_start(mmctx, 3350, sgsn->cfg.timers.T3350); mmctx->mm_state = GMM_COMMON_PROC_INIT; #else /* Make sure we are NORMAL (i.e. not SUSPENDED anymore) */ mmctx->mm_state = GMM_REGISTERED_NORMAL; memset(&sig_data, 0, sizeof(sig_data)); sig_data.mm = mmctx; osmo_signal_dispatch(SS_SGSN, S_SGSN_UPDATE, &sig_data); #endif /* Even if there is no P-TMSI allocated, the MS will switch from * foreign TLLI to local TLLI */ mmctx->tlli_new = gprs_tmsi2tlli(mmctx->p_tmsi, TLLI_LOCAL); /* Inform LLC layer about new TLLI but keep old active */ gprs_llgmm_assign(mmctx->llme, mmctx->tlli, mmctx->tlli_new, GPRS_ALGO_GEA0, NULL); /* Look at PDP Context Status IE and see if MS's view of * activated/deactivated NSAPIs agrees with our view */ if (TLVP_PRESENT(&tp, GSM48_IE_GMM_PDP_CTX_STATUS)) { const uint8_t *pdp_status = TLVP_VAL(&tp, GSM48_IE_GMM_PDP_CTX_STATUS); process_ms_ctx_status(mmctx, pdp_status); } /* Send RA UPDATE ACCEPT */ return gsm48_tx_gmm_ra_upd_ack(mmctx); rejected: /* Send RA UPDATE REJECT */ LOGMMCTXP(LOGL_NOTICE, mmctx, "Rejecting RA Update Request with cause '%s' (%d)\n", get_value_string(gsm48_gmm_cause_names, reject_cause), reject_cause); rc = gsm48_tx_gmm_ra_upd_rej(msg, reject_cause); if (mmctx) mm_ctx_cleanup_free(mmctx, "GPRS RA UPDATE REJ"); else /* TLLI unassignment */ gprs_llgmm_assign(llme, llme->tlli, 0xffffffff, GPRS_ALGO_GEA0, NULL); return rc; } static int gsm48_rx_gmm_status(struct sgsn_mm_ctx *mmctx, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); LOGMMCTXP(LOGL_INFO, mmctx, "-> GPRS MM STATUS (cause: %s)\n", get_value_string(gsm48_gmm_cause_names, gh->data[0])); return 0; } /* GPRS Mobility Management */ static int gsm0408_rcv_gmm(struct sgsn_mm_ctx *mmctx, struct msgb *msg, struct gprs_llc_llme *llme) { struct sgsn_signal_data sig_data; struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); int rc; /* MMCTX can be NULL when called */ if (!mmctx && gh->msg_type != GSM48_MT_GMM_ATTACH_REQ && gh->msg_type != GSM48_MT_GMM_RA_UPD_REQ) { LOGP(DMM, LOGL_NOTICE, "Cannot handle GMM for unknown MM CTX\n"); /* 4.7.10 */ if (gh->msg_type == GSM48_MT_GMM_STATUS) { /* TLLI unassignment */ gprs_llgmm_assign(llme, llme->tlli, 0xffffffff, GPRS_ALGO_GEA0, NULL); return 0; } /* Don't reply or establish a LLME on DETACH_ACK */ if (gh->msg_type == GSM48_MT_GMM_DETACH_ACK) { /* TLLI unassignment */ return gprs_llgmm_assign(llme, llme->tlli, 0xffffffff, GPRS_ALGO_GEA0, NULL); } gprs_llgmm_reset(llme); /* Don't force it into re-attachment */ if (gh->msg_type == GSM48_MT_GMM_DETACH_REQ) { /* Handle Detach Request */ rc = gsm48_rx_gmm_det_req(NULL, msg); /* TLLI unassignment */ gprs_llgmm_assign(llme, llme->tlli, 0xffffffff, GPRS_ALGO_GEA0, NULL); return rc; } /* Force the MS to re-attach */ rc = sgsn_force_reattach_oldmsg(msg); /* TLLI unassignment */ gprs_llgmm_assign(llme, llme->tlli, 0xffffffff, GPRS_ALGO_GEA0, NULL); return rc; } switch (gh->msg_type) { case GSM48_MT_GMM_RA_UPD_REQ: rc = gsm48_rx_gmm_ra_upd_req(mmctx, msg, llme); break; case GSM48_MT_GMM_ATTACH_REQ: rc = gsm48_rx_gmm_att_req(mmctx, msg, llme); break; /* For all the following types mmctx can not be NULL */ case GSM48_MT_GMM_ID_RESP: rc = gsm48_rx_gmm_id_resp(mmctx, msg); break; case GSM48_MT_GMM_STATUS: rc = gsm48_rx_gmm_status(mmctx, msg); break; case GSM48_MT_GMM_DETACH_REQ: rc = gsm48_rx_gmm_det_req(mmctx, msg); break; case GSM48_MT_GMM_DETACH_ACK: LOGMMCTXP(LOGL_INFO, mmctx, "-> DETACH ACK\n"); mm_ctx_cleanup_free(mmctx, "GPRS DETACH ACK"); rc = 0; break; case GSM48_MT_GMM_ATTACH_COMPL: /* only in case SGSN offered new P-TMSI */ LOGMMCTXP(LOGL_INFO, mmctx, "-> ATTACH COMPLETE\n"); mmctx_timer_stop(mmctx, 3350); mmctx->t3350_mode = GMM_T3350_MODE_NONE; mmctx->p_tmsi_old = 0; mmctx->pending_req = 0; /* Unassign the old TLLI */ mmctx->tlli = mmctx->tlli_new; gprs_llgmm_assign(mmctx->llme, 0xffffffff, mmctx->tlli_new, GPRS_ALGO_GEA0, NULL); mmctx->mm_state = GMM_REGISTERED_NORMAL; rc = 0; memset(&sig_data, 0, sizeof(sig_data)); sig_data.mm = mmctx; osmo_signal_dispatch(SS_SGSN, S_SGSN_ATTACH, &sig_data); break; case GSM48_MT_GMM_RA_UPD_COMPL: /* only in case SGSN offered new P-TMSI */ LOGMMCTXP(LOGL_INFO, mmctx, "-> ROUTING AREA UPDATE COMPLETE\n"); mmctx_timer_stop(mmctx, 3350); mmctx->t3350_mode = GMM_T3350_MODE_NONE; mmctx->p_tmsi_old = 0; mmctx->pending_req = 0; /* Unassign the old TLLI */ mmctx->tlli = mmctx->tlli_new; gprs_llgmm_assign(mmctx->llme, 0xffffffff, mmctx->tlli_new, GPRS_ALGO_GEA0, NULL); mmctx->mm_state = GMM_REGISTERED_NORMAL; rc = 0; memset(&sig_data, 0, sizeof(sig_data)); sig_data.mm = mmctx; osmo_signal_dispatch(SS_SGSN, S_SGSN_UPDATE, &sig_data); break; case GSM48_MT_GMM_PTMSI_REALL_COMPL: LOGMMCTXP(LOGL_INFO, mmctx, "-> PTMSI REALLLICATION COMPLETE\n"); mmctx_timer_stop(mmctx, 3350); mmctx->t3350_mode = GMM_T3350_MODE_NONE; mmctx->p_tmsi_old = 0; mmctx->pending_req = 0; /* Unassign the old TLLI */ mmctx->tlli = mmctx->tlli_new; //gprs_llgmm_assign(mmctx->llme, 0xffffffff, mmctx->tlli_new, GPRS_ALGO_GEA0, NULL); rc = 0; break; case GSM48_MT_GMM_AUTH_CIPH_RESP: rc = gsm48_rx_gmm_auth_ciph_resp(mmctx, msg); break; default: LOGMMCTXP(LOGL_NOTICE, mmctx, "Unknown GSM 04.08 GMM msg type 0x%02x\n", gh->msg_type); rc = gsm48_tx_gmm_status(mmctx, GMM_CAUSE_MSGT_NOTEXIST_NOTIMPL); break; } return rc; } static void mmctx_timer_cb(void *_mm) { struct sgsn_mm_ctx *mm = _mm; struct gsm_auth_tuple *at; mm->num_T_exp++; switch (mm->T) { case 3350: /* waiting for ATTACH COMPLETE */ if (mm->num_T_exp >= 5) { LOGMMCTXP(LOGL_NOTICE, mm, "T3350 expired >= 5 times\n"); mm_ctx_cleanup_free(mm, "T3350"); /* FIXME: should we return some error? */ break; } /* re-transmit the respective msg and re-start timer */ switch (mm->t3350_mode) { case GMM_T3350_MODE_ATT: gsm48_tx_gmm_att_ack(mm); break; case GMM_T3350_MODE_RAU: gsm48_tx_gmm_ra_upd_ack(mm); break; case GMM_T3350_MODE_PTMSI_REALL: /* FIXME */ break; case GMM_T3350_MODE_NONE: LOGMMCTXP(LOGL_NOTICE, mm, "T3350 mode wasn't set, ignoring timeout\n"); break; } osmo_timer_schedule(&mm->timer, sgsn->cfg.timers.T3350, 0); break; case 3360: /* waiting for AUTH AND CIPH RESP */ if (mm->num_T_exp >= 5) { LOGMMCTXP(LOGL_NOTICE, mm, "T3360 expired >= 5 times\n"); mm_ctx_cleanup_free(mm, "T3360"); break; } /* Re-transmit the respective msg and re-start timer */ if (mm->auth_triplet.key_seq == GSM_KEY_SEQ_INVAL) { LOGMMCTXP(LOGL_ERROR, mm, "timeout: invalid auth triplet reference\n"); mm_ctx_cleanup_free(mm, "T3360"); break; } at = &mm->auth_triplet; gsm48_tx_gmm_auth_ciph_req(mm, at->rand, at->key_seq, GPRS_ALGO_GEA0); osmo_timer_schedule(&mm->timer, sgsn->cfg.timers.T3360, 0); break; case 3370: /* waiting for IDENTITY RESPONSE */ if (mm->num_T_exp >= 5) { LOGMMCTXP(LOGL_NOTICE, mm, "T3370 expired >= 5 times\n"); gsm48_tx_gmm_att_rej(mm, GMM_CAUSE_MS_ID_NOT_DERIVED); mm_ctx_cleanup_free(mm, "GPRS ATTACH REJECT (T3370)"); break; } /* re-tranmit IDENTITY REQUEST and re-start timer */ gsm48_tx_gmm_id_req(mm, mm->t3370_id_type); osmo_timer_schedule(&mm->timer, sgsn->cfg.timers.T3370, 0); break; default: LOGMMCTXP(LOGL_ERROR, mm, "timer expired in unknown mode %u\n", mm->T); } } /* GPRS SESSION MANAGEMENT */ static void pdpctx_timer_cb(void *_mm); static void pdpctx_timer_start(struct sgsn_pdp_ctx *pdp, unsigned int T, unsigned int seconds) { if (osmo_timer_pending(&pdp->timer)) LOGMMCTXP(LOGL_ERROR, pdp->mm, "Starting MM timer %u while old " "timer %u pending\n", T, pdp->T); pdp->T = T; pdp->num_T_exp = 0; /* FIXME: we should do this only once ? */ pdp->timer.data = pdp; pdp->timer.cb = &pdpctx_timer_cb; osmo_timer_schedule(&pdp->timer, seconds, 0); } #if 0 static void msgb_put_pdp_addr_ipv4(struct msgb *msg, uint32_t ipaddr) { uint8_t v[6]; v[0] = PDP_TYPE_ORG_IETF; v[1] = PDP_TYPE_N_IETF_IPv4; *(uint32_t *)(v+2) = htonl(ipaddr); msgb_tlv_put(msg, GSM48_IE_GSM_PDP_ADDR, sizeof(v), v); } static void msgb_put_pdp_addr_ppp(struct msgb *msg) { uint8_t v[2]; v[0] = PDP_TYPE_ORG_ETSI; v[1] = PDP_TYPE_N_ETSI_PPP; msgb_tlv_put(msg, GSM48_IE_GSM_PDP_ADDR, sizeof(v), v); } #endif /* Section 9.5.2: Ativate PDP Context Accept */ int gsm48_tx_gsm_act_pdp_acc(struct sgsn_pdp_ctx *pdp) { struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh; uint8_t transaction_id = pdp->ti ^ 0x8; /* flip */ LOGPDPCTXP(LOGL_INFO, pdp, "<- ACTIVATE PDP CONTEXT ACK\n"); mmctx2msgid(msg, pdp->mm); gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->proto_discr = GSM48_PDISC_SM_GPRS | (transaction_id << 4); gh->msg_type = GSM48_MT_GSM_ACT_PDP_ACK; /* Negotiated LLC SAPI */ msgb_v_put(msg, pdp->sapi); /* FIXME: copy QoS parameters from original request */ //msgb_lv_put(msg, pdp->lib->qos_neg.l, pdp->lib->qos_neg.v); msgb_lv_put(msg, sizeof(default_qos), (uint8_t *)&default_qos); /* Radio priority 10.5.7.2 */ msgb_v_put(msg, pdp->lib->radio_pri); /* PDP address */ /* 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->lib->eua.v[0] &= ~0xf0; msgb_tlv_put(msg, GSM48_IE_GSM_PDP_ADDR, pdp->lib->eua.l, pdp->lib->eua.v); pdp->lib->eua.v[0] |= 0xf0; /* Optional: Protocol configuration options (FIXME: why 'req') */ if (pdp->lib->pco_req.l) msgb_tlv_put(msg, GSM48_IE_GSM_PROTO_CONF_OPT, pdp->lib->pco_req.l, pdp->lib->pco_req.v); /* Optional: Packet Flow Identifier */ return gsm48_gmm_sendmsg(msg, 0, pdp->mm); } /* Section 9.5.3: Activate PDP Context reject */ 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) { struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh; uint8_t transaction_id = tid ^ 0x8; /* flip */ LOGMMCTXP(LOGL_NOTICE, mm, "<- ACTIVATE PDP CONTEXT REJ(cause=%u)\n", cause); mmctx2msgid(msg, mm); gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->proto_discr = GSM48_PDISC_SM_GPRS | (transaction_id << 4); gh->msg_type = GSM48_MT_GSM_ACT_PDP_REJ; msgb_v_put(msg, cause); if (pco_len && pco_v) msgb_tlv_put(msg, GSM48_IE_GSM_PROTO_CONF_OPT, pco_len, pco_v); return gsm48_gmm_sendmsg(msg, 0, mm); } /* Section 9.5.8: Deactivate PDP Context Request */ static int _gsm48_tx_gsm_deact_pdp_req(struct sgsn_mm_ctx *mm, uint8_t tid, uint8_t sm_cause) { struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh; uint8_t transaction_id = tid ^ 0x8; /* flip */ LOGMMCTXP(LOGL_INFO, mm, "<- DEACTIVATE PDP CONTEXT REQ\n"); mmctx2msgid(msg, mm); gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->proto_discr = GSM48_PDISC_SM_GPRS | (transaction_id << 4); gh->msg_type = GSM48_MT_GSM_DEACT_PDP_REQ; msgb_v_put(msg, sm_cause); return gsm48_gmm_sendmsg(msg, 0, mm); } int gsm48_tx_gsm_deact_pdp_req(struct sgsn_pdp_ctx *pdp, uint8_t sm_cause) { pdpctx_timer_start(pdp, 3395, sgsn->cfg.timers.T3395); return _gsm48_tx_gsm_deact_pdp_req(pdp->mm, pdp->ti, sm_cause); } /* Section 9.5.9: Deactivate PDP Context Accept */ static int _gsm48_tx_gsm_deact_pdp_acc(struct sgsn_mm_ctx *mm, uint8_t tid) { struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh; uint8_t transaction_id = tid ^ 0x8; /* flip */ LOGMMCTXP(LOGL_INFO, mm, "<- DEACTIVATE PDP CONTEXT ACK\n"); mmctx2msgid(msg, mm); gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->proto_discr = GSM48_PDISC_SM_GPRS | (transaction_id << 4); gh->msg_type = GSM48_MT_GSM_DEACT_PDP_ACK; return gsm48_gmm_sendmsg(msg, 0, mm); } int gsm48_tx_gsm_deact_pdp_acc(struct sgsn_pdp_ctx *pdp) { return _gsm48_tx_gsm_deact_pdp_acc(pdp->mm, pdp->ti); } static int activate_ggsn(struct sgsn_mm_ctx *mmctx, struct sgsn_ggsn_ctx *ggsn, const uint8_t transaction_id, const uint8_t req_nsapi, const uint8_t req_llc_sapi, struct tlv_parsed *tp, int destroy_ggsn) { struct sgsn_pdp_ctx *pdp; LOGMMCTXP(LOGL_DEBUG, mmctx, "Using GGSN %u\n", ggsn->id); ggsn->gsn = sgsn->gsn; pdp = sgsn_create_pdp_ctx(ggsn, mmctx, req_nsapi, tp); if (!pdp) return -1; /* Store SAPI and Transaction Identifier */ pdp->sapi = req_llc_sapi; pdp->ti = transaction_id; pdp->destroy_ggsn = destroy_ggsn; return 0; } static void ggsn_lookup_cb(void *arg, int status, int timeouts, struct hostent *hostent) { struct sgsn_ggsn_ctx *ggsn; struct sgsn_ggsn_lookup *lookup = arg; struct in_addr *addr = NULL; /* The context is gone while we made a request */ if (!lookup->mmctx) { talloc_free(lookup); return; } if (status != ARES_SUCCESS) { struct sgsn_mm_ctx *mmctx = lookup->mmctx; LOGMMCTXP(LOGL_ERROR, mmctx, "DNS query failed.\n"); /* Need to try with three digits now */ if (lookup->state == SGSN_GGSN_2DIGIT) { char *hostname; int rc; lookup->state = SGSN_GGSN_3DIGIT; hostname = osmo_apn_qualify_from_imsi(mmctx->imsi, lookup->apn_str, 1); LOGMMCTXP(LOGL_DEBUG, mmctx, "Going to query %s\n", hostname); rc = sgsn_ares_query(sgsn, hostname, ggsn_lookup_cb, lookup); if (rc != 0) { LOGMMCTXP(LOGL_ERROR, mmctx, "Couldn't start GGSN\n"); goto reject_due_failure; } return; } LOGMMCTXP(LOGL_ERROR, mmctx, "Couldn't resolve GGSN\n"); goto reject_due_failure; } if (hostent->h_length != sizeof(struct in_addr)) { LOGMMCTXP(LOGL_ERROR, lookup->mmctx, "Wrong addr size(%zu)\n", sizeof(struct in_addr)); goto reject_due_failure; } /* Get the first addr from the list */ addr = (struct in_addr *) hostent->h_addr_list[0]; if (!addr) { LOGMMCTXP(LOGL_ERROR, lookup->mmctx, "No host address.\n"); goto reject_due_failure; } ggsn = sgsn_ggsn_ctx_alloc(UINT32_MAX); if (!ggsn) { LOGMMCTXP(LOGL_ERROR, lookup->mmctx, "Failed to create ggsn.\n"); goto reject_due_failure; } ggsn->remote_addr = *addr; LOGMMCTXP(LOGL_NOTICE, lookup->mmctx, "Selected %s as GGSN.\n", inet_ntoa(*addr)); /* forget about the ggsn look-up */ lookup->mmctx->ggsn_lookup = NULL; activate_ggsn(lookup->mmctx, ggsn, lookup->ti, lookup->nsapi, lookup->sapi, &lookup->tp, 1); /* Now free it */ talloc_free(lookup); return; reject_due_failure: gsm48_tx_gsm_act_pdp_rej(lookup->mmctx, lookup->ti, GMM_CAUSE_NET_FAIL, 0, NULL); lookup->mmctx->ggsn_lookup = NULL; talloc_free(lookup); } static int do_act_pdp_req(struct sgsn_mm_ctx *mmctx, struct msgb *msg) { struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); struct gsm48_act_pdp_ctx_req *act_req = (struct gsm48_act_pdp_ctx_req *) gh->data; uint8_t req_qos_len, req_pdpa_len; uint8_t *req_qos, *req_pdpa; struct tlv_parsed tp; uint8_t transaction_id = (gh->proto_discr >> 4); struct sgsn_ggsn_ctx *ggsn; struct sgsn_pdp_ctx *pdp; enum gsm48_gsm_cause gsm_cause; char apn_str[GSM_APN_LENGTH] = { 0, }; char *hostname; int rc; LOGMMCTXP(LOGL_INFO, mmctx, "-> ACTIVATE PDP CONTEXT REQ: SAPI=%u NSAPI=%u ", act_req->req_llc_sapi, act_req->req_nsapi); /* FIXME: length checks! */ req_qos_len = act_req->data[0]; req_qos = act_req->data + 1; /* 10.5.6.5 */ req_pdpa_len = act_req->data[1 + req_qos_len]; req_pdpa = act_req->data + 1 + req_qos_len + 1; /* 10.5.6.4 */ switch (req_pdpa[0] & 0xf) { case 0x0: DEBUGPC(DMM, "ETSI "); break; case 0x1: DEBUGPC(DMM, "IETF "); break; case 0xf: DEBUGPC(DMM, "Empty "); break; } switch (req_pdpa[1]) { case 0x21: DEBUGPC(DMM, "IPv4 "); if (req_pdpa_len >= 6) { struct in_addr ia; ia.s_addr = ntohl(*((uint32_t *) (req_pdpa+2))); DEBUGPC(DMM, "%s ", inet_ntoa(ia)); } break; case 0x57: DEBUGPC(DMM, "IPv6 "); if (req_pdpa_len >= 18) { /* FIXME: print IPv6 address */ } break; default: DEBUGPC(DMM, "0x%02x ", req_pdpa[1]); break; } LOGPC(DMM, LOGL_INFO, "\n"); /* Check if NSAPI is out of range (TS 04.65 / 7.2) */ if (act_req->req_nsapi < 5 || act_req->req_nsapi > 15) { /* Send reject with GSM_CAUSE_INV_MAND_INFO */ return gsm48_tx_gsm_act_pdp_rej(mmctx, transaction_id, GSM_CAUSE_INV_MAND_INFO, 0, NULL); } /* Optional: Access Point Name, Protocol Config Options */ if (req_pdpa + req_pdpa_len < msg->data + msg->len) tlv_parse(&tp, &gsm48_sm_att_tlvdef, req_pdpa + req_pdpa_len, (msg->data + msg->len) - (req_pdpa + req_pdpa_len), 0, 0); else memset(&tp, 0, sizeof(tp)); /* put the non-TLV elements in the TLV parser structure to * pass them on to the SGSN / GTP code */ tp.lv[OSMO_IE_GSM_REQ_QOS].len = req_qos_len; tp.lv[OSMO_IE_GSM_REQ_QOS].val = req_qos; tp.lv[OSMO_IE_GSM_REQ_PDP_ADDR].len = req_pdpa_len; tp.lv[OSMO_IE_GSM_REQ_PDP_ADDR].val = req_pdpa; /* Check if NSAPI is already in use */ pdp = sgsn_pdp_ctx_by_nsapi(mmctx, act_req->req_nsapi); if (pdp) { /* We already have a PDP context for this TLLI + NSAPI tuple */ if (pdp->sapi == act_req->req_llc_sapi && pdp->ti == transaction_id) { /* This apparently is a re-transmission of a PDP CTX * ACT REQ (our ACT ACK must have got dropped) */ return gsm48_tx_gsm_act_pdp_acc(pdp); } /* Send reject with GSM_CAUSE_NSAPI_IN_USE */ return gsm48_tx_gsm_act_pdp_rej(mmctx, transaction_id, GSM_CAUSE_NSAPI_IN_USE, 0, NULL); } if (mmctx->ggsn_lookup) { if (mmctx->ggsn_lookup->sapi == act_req->req_llc_sapi && mmctx->ggsn_lookup->ti == transaction_id) { LOGMMCTXP(LOGL_NOTICE, mmctx, "Re-transmission while doing look-up. Ignoring.\n"); return 0; } } /* Only increment counter for a real activation, after we checked * for re-transmissions */ rate_ctr_inc(&mmctx->ctrg->ctr[GMM_CTR_PDP_CTX_ACT]); /* Determine GGSN based on APN and subscription options */ ggsn = sgsn_mm_ctx_find_ggsn_ctx(mmctx, &tp, &gsm_cause, apn_str); if (ggsn) return activate_ggsn(mmctx, ggsn, transaction_id, act_req->req_nsapi, act_req->req_llc_sapi, &tp, 0); if (strlen(apn_str) == 0) goto no_context; if (!sgsn->cfg.dynamic_lookup) goto no_context; /* schedule a dynamic look-up */ mmctx->ggsn_lookup = talloc_zero(tall_bsc_ctx, struct sgsn_ggsn_lookup); if (!mmctx->ggsn_lookup) goto no_context; mmctx->ggsn_lookup->state = SGSN_GGSN_2DIGIT; mmctx->ggsn_lookup->mmctx = mmctx; strcpy(mmctx->ggsn_lookup->apn_str, apn_str); mmctx->ggsn_lookup->orig_msg = msg; mmctx->ggsn_lookup->tp = tp; mmctx->ggsn_lookup->ti = transaction_id; mmctx->ggsn_lookup->nsapi = act_req->req_nsapi; mmctx->ggsn_lookup->sapi = act_req->req_llc_sapi; hostname = osmo_apn_qualify_from_imsi(mmctx->imsi, mmctx->ggsn_lookup->apn_str, 0); LOGMMCTXP(LOGL_DEBUG, mmctx, "Going to query %s\n", hostname); rc = sgsn_ares_query(sgsn, hostname, ggsn_lookup_cb, mmctx->ggsn_lookup); if (rc != 0) { LOGMMCTXP(LOGL_ERROR, mmctx, "Failed to start ares query.\n"); goto no_context; } return 0; no_context: LOGMMCTXP(LOGL_ERROR, mmctx, "No GGSN context found!\n"); return gsm48_tx_gsm_act_pdp_rej(mmctx, transaction_id, gsm_cause, 0, NULL); } /* Section 9.5.1: Activate PDP Context Request */ static int gsm48_rx_gsm_act_pdp_req(struct sgsn_mm_ctx *mmctx, struct msgb *_msg) { struct msgb *msg; int rc; /* * This is painful. We might not have a static GGSN * configuration and then would need to copy the msg * and re-do most of this routine (or call it again * and make sure it only goes through the dynamic * resolving. The question is what to optimize for * and the dynamic resolution will be the right thing * in the long run. */ msg = gprs_msgb_copy(_msg, __func__); if (!msg) { struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(_msg); uint8_t transaction_id = (gh->proto_discr >> 4); LOGMMCTXP(LOGL_ERROR, mmctx, "-> ACTIVATE PDP CONTEXT REQ failed copy.\n"); /* Send reject with GSM_CAUSE_INV_MAND_INFO */ return gsm48_tx_gsm_act_pdp_rej(mmctx, transaction_id, GSM_CAUSE_NET_FAIL, 0, NULL); } rc = do_act_pdp_req(mmctx, _msg); msgb_free(msg); return rc; } /* Section 9.5.8: Deactivate PDP Context Request */ static int gsm48_rx_gsm_deact_pdp_req(struct sgsn_mm_ctx *mm, struct msgb *msg) { struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); uint8_t transaction_id = (gh->proto_discr >> 4); struct sgsn_pdp_ctx *pdp; LOGMMCTXP(LOGL_INFO, mm, "-> DEACTIVATE PDP CONTEXT REQ (cause: %s)\n", get_value_string(gsm48_gsm_cause_names, gh->data[0])); pdp = sgsn_pdp_ctx_by_tid(mm, transaction_id); if (!pdp) { LOGMMCTXP(LOGL_NOTICE, mm, "Deactivate PDP Context Request for " "non-existing PDP Context (IMSI=%s, TI=%u)\n", mm->imsi, transaction_id); return _gsm48_tx_gsm_deact_pdp_acc(mm, transaction_id); } return sgsn_delete_pdp_ctx(pdp); } /* Section 9.5.9: Deactivate PDP Context Accept */ static int gsm48_rx_gsm_deact_pdp_ack(struct sgsn_mm_ctx *mm, struct msgb *msg) { struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); uint8_t transaction_id = (gh->proto_discr >> 4); struct sgsn_pdp_ctx *pdp; LOGMMCTXP(LOGL_INFO, mm, "-> DEACTIVATE PDP CONTEXT ACK\n"); pdp = sgsn_pdp_ctx_by_tid(mm, transaction_id); if (!pdp) { LOGMMCTXP(LOGL_NOTICE, mm, "Deactivate PDP Context Accept for " "non-existing PDP Context (IMSI=%s, TI=%u)\n", mm->imsi, transaction_id); return 0; } return sgsn_delete_pdp_ctx(pdp); } static int gsm48_rx_gsm_status(struct sgsn_mm_ctx *ctx, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); LOGMMCTXP(LOGL_INFO, ctx, "-> GPRS SM STATUS (cause: %s)\n", get_value_string(gsm48_gsm_cause_names, gh->data[0])); return 0; } static void pdpctx_timer_cb(void *_pdp) { struct sgsn_pdp_ctx *pdp = _pdp; pdp->num_T_exp++; switch (pdp->T) { case 3395: /* waiting for PDP CTX DEACT ACK */ if (pdp->num_T_exp >= 4) { LOGPDPCTXP(LOGL_NOTICE, pdp, "T3395 expired >= 5 times\n"); pdp->state = PDP_STATE_INACTIVE; sgsn_delete_pdp_ctx(pdp); break; } gsm48_tx_gsm_deact_pdp_req(pdp, GSM_CAUSE_NET_FAIL); osmo_timer_schedule(&pdp->timer, sgsn->cfg.timers.T3395, 0); break; default: LOGPDPCTXP(LOGL_ERROR, pdp, "timer expired in unknown mode %u\n", pdp->T); } } /* GPRS Session Management */ static int gsm0408_rcv_gsm(struct sgsn_mm_ctx *mmctx, struct msgb *msg, struct gprs_llc_llme *llme) { struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); int rc; /* MMCTX can be NULL when called */ if (!mmctx) { LOGP(DMM, LOGL_NOTICE, "Cannot handle SM for unknown MM CTX\n"); /* 6.1.3.6 */ if (gh->msg_type == GSM48_MT_GSM_STATUS) return 0; return sgsn_force_reattach_oldmsg(msg); } switch (gh->msg_type) { case GSM48_MT_GSM_ACT_PDP_REQ: rc = gsm48_rx_gsm_act_pdp_req(mmctx, msg); break; case GSM48_MT_GSM_DEACT_PDP_REQ: rc = gsm48_rx_gsm_deact_pdp_req(mmctx, msg); break; case GSM48_MT_GSM_DEACT_PDP_ACK: rc = gsm48_rx_gsm_deact_pdp_ack(mmctx, msg); break; case GSM48_MT_GSM_STATUS: rc = gsm48_rx_gsm_status(mmctx, msg); break; case GSM48_MT_GSM_REQ_PDP_ACT_REJ: case GSM48_MT_GSM_ACT_AA_PDP_REQ: case GSM48_MT_GSM_DEACT_AA_PDP_REQ: LOGMMCTXP(LOGL_NOTICE, mmctx, "Unimplemented GSM 04.08 GSM msg type 0x%02x: %s\n", gh->msg_type, osmo_hexdump((uint8_t *)gh, msgb_l3len(msg))); rc = gsm48_tx_sm_status(mmctx, GSM_CAUSE_MSGT_NOTEXIST_NOTIMPL); break; default: LOGMMCTXP(LOGL_NOTICE, mmctx, "Unknown GSM 04.08 GSM msg type 0x%02x: %s\n", gh->msg_type, osmo_hexdump((uint8_t *)gh, msgb_l3len(msg))); rc = gsm48_tx_sm_status(mmctx, GSM_CAUSE_MSGT_NOTEXIST_NOTIMPL); break; } return rc; } int gsm0408_gprs_force_reattach_oldmsg(struct msgb *msg) { int rc; gprs_llgmm_reset_oldmsg(msg, GPRS_SAPI_GMM); rc = gsm48_tx_gmm_detach_req_oldmsg( msg, GPRS_DET_T_MT_REATT_REQ, GMM_CAUSE_IMPL_DETACHED); return rc; } int gsm0408_gprs_force_reattach(struct sgsn_mm_ctx *mmctx) { int rc; gprs_llgmm_reset(mmctx->llme); rc = gsm48_tx_gmm_detach_req( mmctx, GPRS_DET_T_MT_REATT_REQ, GMM_CAUSE_IMPL_DETACHED); mm_ctx_cleanup_free(mmctx, "forced reattach"); return rc; } /* Main entry point for incoming 04.08 GPRS messages */ int gsm0408_gprs_rcvmsg(struct msgb *msg, struct gprs_llc_llme *llme) { struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); uint8_t pdisc = gh->proto_discr & 0x0f; struct sgsn_mm_ctx *mmctx; struct gprs_ra_id ra_id; int rc = -EINVAL; bssgp_parse_cell_id(&ra_id, msgb_bcid(msg)); mmctx = sgsn_mm_ctx_by_tlli(msgb_tlli(msg), &ra_id); if (mmctx) { msgid2mmctx(mmctx, msg); rate_ctr_inc(&mmctx->ctrg->ctr[GMM_CTR_PKTS_SIG_IN]); mmctx->llme = llme; } /* MMCTX can be NULL */ switch (pdisc) { case GSM48_PDISC_MM_GPRS: rc = gsm0408_rcv_gmm(mmctx, msg, llme); break; case GSM48_PDISC_SM_GPRS: rc = gsm0408_rcv_gsm(mmctx, msg, llme); break; default: LOGMMCTXP(LOGL_NOTICE, mmctx, "Unknown GSM 04.08 discriminator 0x%02x: %s\n", pdisc, osmo_hexdump((uint8_t *)gh, msgb_l3len(msg))); /* FIXME: return status message */ break; } /* MMCTX can be invalid */ return rc; } int gprs_gmm_rx_suspend(struct gprs_ra_id *raid, uint32_t tlli) { struct sgsn_mm_ctx *mmctx; mmctx = sgsn_mm_ctx_by_tlli(tlli, raid); if (!mmctx) { LOGP(DMM, LOGL_NOTICE, "SUSPEND request for unknown " "TLLI=%08x\n", tlli); return -EINVAL; } if (mmctx->mm_state != GMM_REGISTERED_NORMAL && mmctx->mm_state != GMM_REGISTERED_SUSPENDED) { LOGMMCTXP(LOGL_NOTICE, mmctx, "SUSPEND request while state " "!= REGISTERED (TLLI=%08x)\n", tlli); return -EINVAL; } /* Transition from REGISTERED_NORMAL to REGISTERED_SUSPENDED */ mmctx->mm_state = GMM_REGISTERED_SUSPENDED; return 0; } int gprs_gmm_rx_resume(struct gprs_ra_id *raid, uint32_t tlli, uint8_t suspend_ref) { struct sgsn_mm_ctx *mmctx; /* FIXME: make use of suspend reference? */ mmctx = sgsn_mm_ctx_by_tlli(tlli, raid); if (!mmctx) { LOGP(DMM, LOGL_NOTICE, "RESUME request for unknown " "TLLI=%08x\n", tlli); return -EINVAL; } if (mmctx->mm_state != GMM_REGISTERED_NORMAL && mmctx->mm_state != GMM_REGISTERED_SUSPENDED) { LOGMMCTXP(LOGL_NOTICE, mmctx, "RESUME request while state " "!= SUSPENDED (TLLI=%08x)\n", tlli); /* FIXME: should we not simply ignore it? */ return -EINVAL; } /* Transition from SUSPENDED to NORMAL */ mmctx->mm_state = GMM_REGISTERED_NORMAL; return 0; } openbsc-0.15.0/openbsc/src/gprs/gprs_gsup_client.c000066400000000000000000000160151265565154000221500ustar00rootroot00000000000000/* GPRS Subscriber Update Protocol client */ /* (C) 2014 by Sysmocom s.f.m.c. GmbH * All Rights Reserved * * Author: Jacob Erlbeck * * 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 . * */ #include #include #include #include #include #include #include extern void *tall_bsc_ctx; static void start_test_procedure(struct gprs_gsup_client *gsupc); static void gsup_client_send_ping(struct gprs_gsup_client *gsupc) { struct msgb *msg = gprs_gsup_msgb_alloc(); msg->l2h = msgb_put(msg, 1); msg->l2h[0] = IPAC_MSGT_PING; ipa_msg_push_header(msg, IPAC_PROTO_IPACCESS); ipa_client_conn_send(gsupc->link, msg); } static int gsup_client_connect(struct gprs_gsup_client *gsupc) { int rc; if (gsupc->is_connected) return 0; if (osmo_timer_pending(&gsupc->connect_timer)) { LOGP(DLINP, LOGL_DEBUG, "GSUP connect: connect timer already running\n"); osmo_timer_del(&gsupc->connect_timer); } if (osmo_timer_pending(&gsupc->ping_timer)) { LOGP(DLINP, LOGL_DEBUG, "GSUP connect: ping timer already running\n"); osmo_timer_del(&gsupc->ping_timer); } if (ipa_client_conn_clear_queue(gsupc->link) > 0) LOGP(DLINP, LOGL_DEBUG, "GSUP connect: discarded stored messages\n"); rc = ipa_client_conn_open(gsupc->link); if (rc >= 0) { LOGP(DGPRS, LOGL_INFO, "GSUP connecting to %s:%d\n", gsupc->link->addr, gsupc->link->port); return 0; } LOGP(DGPRS, LOGL_INFO, "GSUP failed to connect to %s:%d: %s\n", gsupc->link->addr, gsupc->link->port, strerror(-rc)); if (rc == -EBADF || rc == -ENOTSOCK || rc == -EAFNOSUPPORT || rc == -EINVAL) return rc; osmo_timer_schedule(&gsupc->connect_timer, GPRS_GSUP_RECONNECT_INTERVAL, 0); LOGP(DGPRS, LOGL_INFO, "Scheduled timer to retry GSUP connect to %s:%d\n", gsupc->link->addr, gsupc->link->port); return 0; } static void connect_timer_cb(void *gsupc_) { struct gprs_gsup_client *gsupc = gsupc_; if (gsupc->is_connected) return; gsup_client_connect(gsupc); } static void gsup_client_send(struct gprs_gsup_client *gsupc, int proto_ext, struct msgb *msg_tx) { ipa_prepend_header_ext(msg_tx, proto_ext); ipa_msg_push_header(msg_tx, IPAC_PROTO_OSMO); ipa_client_conn_send(gsupc->link, msg_tx); /* msg_tx is now queued and will be freed. */ } static void gsup_client_updown_cb(struct ipa_client_conn *link, int up) { struct gprs_gsup_client *gsupc = link->data; LOGP(DGPRS, LOGL_INFO, "GSUP link to %s:%d %s\n", link->addr, link->port, up ? "UP" : "DOWN"); gsupc->is_connected = up; if (up) { start_test_procedure(gsupc); osmo_timer_del(&gsupc->connect_timer); } else { osmo_timer_del(&gsupc->ping_timer); osmo_timer_schedule(&gsupc->connect_timer, GPRS_GSUP_RECONNECT_INTERVAL, 0); } } static int gsup_client_read_cb(struct ipa_client_conn *link, struct msgb *msg) { struct ipaccess_head *hh = (struct ipaccess_head *) msg->data; struct ipaccess_head_ext *he = (struct ipaccess_head_ext *) msgb_l2(msg); struct gprs_gsup_client *gsupc = (struct gprs_gsup_client *)link->data; int rc; static struct ipaccess_unit ipa_dev = { .unit_name = "SGSN" }; msg->l2h = &hh->data[0]; rc = ipaccess_bts_handle_ccm(link, &ipa_dev, msg); if (rc < 0) { LOGP(DGPRS, LOGL_NOTICE, "GSUP received an invalid IPA/CCM message from %s:%d\n", link->addr, link->port); /* Link has been closed */ gsupc->is_connected = 0; msgb_free(msg); return -1; } if (rc == 1) { uint8_t msg_type = *(msg->l2h); /* CCM message */ if (msg_type == IPAC_MSGT_PONG) { LOGP(DGPRS, LOGL_DEBUG, "GSUP receiving PONG\n"); gsupc->got_ipa_pong = 1; } msgb_free(msg); return 0; } if (hh->proto != IPAC_PROTO_OSMO) goto invalid; if (!he || msgb_l2len(msg) < sizeof(*he) || he->proto != IPAC_PROTO_EXT_GSUP) goto invalid; msg->l2h = &he->data[0]; OSMO_ASSERT(gsupc->read_cb != NULL); gsupc->read_cb(gsupc, msg); /* Not freeing msg here, because that must be done by the read_cb. */ return 0; invalid: LOGP(DGPRS, LOGL_NOTICE, "GSUP received an invalid IPA message from %s:%d, size = %d\n", link->addr, link->port, msgb_length(msg)); msgb_free(msg); return -1; } static void ping_timer_cb(void *gsupc_) { struct gprs_gsup_client *gsupc = gsupc_; LOGP(DGPRS, LOGL_INFO, "GSUP ping callback (%s, %s PONG)\n", gsupc->is_connected ? "connected" : "not connected", gsupc->got_ipa_pong ? "got" : "didn't get"); if (gsupc->got_ipa_pong) { start_test_procedure(gsupc); return; } LOGP(DGPRS, LOGL_NOTICE, "GSUP ping timed out, reconnecting\n"); ipa_client_conn_close(gsupc->link); gsupc->is_connected = 0; gsup_client_connect(gsupc); } static void start_test_procedure(struct gprs_gsup_client *gsupc) { gsupc->ping_timer.data = gsupc; gsupc->ping_timer.cb = &ping_timer_cb; gsupc->got_ipa_pong = 0; osmo_timer_schedule(&gsupc->ping_timer, GPRS_GSUP_PING_INTERVAL, 0); LOGP(DGPRS, LOGL_DEBUG, "GSUP sending PING\n"); gsup_client_send_ping(gsupc); } struct gprs_gsup_client *gprs_gsup_client_create(const char *ip_addr, unsigned int tcp_port, gprs_gsup_read_cb_t read_cb) { struct gprs_gsup_client *gsupc; int rc; gsupc = talloc_zero(tall_bsc_ctx, struct gprs_gsup_client); OSMO_ASSERT(gsupc); gsupc->link = ipa_client_conn_create(gsupc, /* no e1inp */ NULL, 0, ip_addr, tcp_port, gsup_client_updown_cb, gsup_client_read_cb, /* default write_cb */ NULL, gsupc); if (!gsupc->link) goto failed; gsupc->connect_timer.data = gsupc; gsupc->connect_timer.cb = &connect_timer_cb; rc = gsup_client_connect(gsupc); if (rc < 0) goto failed; gsupc->read_cb = read_cb; return gsupc; failed: gprs_gsup_client_destroy(gsupc); return NULL; } void gprs_gsup_client_destroy(struct gprs_gsup_client *gsupc) { osmo_timer_del(&gsupc->connect_timer); osmo_timer_del(&gsupc->ping_timer); if (gsupc->link) { ipa_client_conn_close(gsupc->link); ipa_client_conn_destroy(gsupc->link); gsupc->link = NULL; } talloc_free(gsupc); } int gprs_gsup_client_send(struct gprs_gsup_client *gsupc, struct msgb *msg) { if (!gsupc) { msgb_free(msg); return -ENOTCONN; } if (!gsupc->is_connected) { msgb_free(msg); return -EAGAIN; } gsup_client_send(gsupc, IPAC_PROTO_EXT_GSUP, msg); return 0; } struct msgb *gprs_gsup_msgb_alloc(void) { return msgb_alloc_headroom(4000, 64, __func__); } openbsc-0.15.0/openbsc/src/gprs/gprs_gsup_messages.c000066400000000000000000000253151265565154000225040ustar00rootroot00000000000000/* GPRS Subscriber Update Protocol message encoder/decoder */ /* * (C) 2014 by Sysmocom s.f.m.c. GmbH * (C) 2015 by Holger Hans Peter Freyther * All Rights Reserved * * Author: Jacob Erlbeck * * 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 . * */ #include #include #include #include #include #include static uint64_t decode_big_endian(const uint8_t *data, size_t data_len) { uint64_t value = 0; while (data_len > 0) { value = (value << 8) + *data; data += 1; data_len -= 1; } return value; } static uint8_t *encode_big_endian(uint64_t value, size_t data_len) { static uint8_t buf[sizeof(uint64_t)]; int idx; OSMO_ASSERT(data_len <= ARRAY_SIZE(buf)); for (idx = data_len - 1; idx >= 0; idx--) { buf[idx] = (uint8_t)value; value = value >> 8; } return buf; } static int decode_pdp_info(uint8_t *data, size_t data_len, struct gprs_gsup_pdp_info *pdp_info) { int rc; uint8_t tag; uint8_t *value; size_t value_len; /* specific parts */ while (data_len > 0) { enum gprs_gsup_iei iei; rc = gprs_shift_tlv(&data, &data_len, &tag, &value, &value_len); if (rc < 0) return -GMM_CAUSE_PROTO_ERR_UNSPEC; iei = tag; switch (iei) { case GPRS_GSUP_PDP_CONTEXT_ID_IE: pdp_info->context_id = decode_big_endian(value, value_len); break; case GPRS_GSUP_PDP_TYPE_IE: pdp_info->pdp_type = decode_big_endian(value, value_len) & 0x0fff; break; case GPRS_GSUP_ACCESS_POINT_NAME_IE: pdp_info->apn_enc = value; pdp_info->apn_enc_len = value_len; break; case GPRS_GSUP_PDP_QOS_IE: pdp_info->qos_enc = value; pdp_info->qos_enc_len = value_len; break; default: LOGP(DGPRS, LOGL_ERROR, "GSUP IE type %d not expected in PDP info\n", iei); continue; } } return 0; } static int decode_auth_info(uint8_t *data, size_t data_len, struct gsm_auth_tuple *auth_tuple) { int rc; uint8_t tag; uint8_t *value; size_t value_len; enum gprs_gsup_iei iei; /* specific parts */ while (data_len > 0) { rc = gprs_shift_tlv(&data, &data_len, &tag, &value, &value_len); if (rc < 0) return -GMM_CAUSE_PROTO_ERR_UNSPEC; iei = tag; switch (iei) { case GPRS_GSUP_RAND_IE: if (value_len != sizeof(auth_tuple->rand)) goto parse_error; memcpy(auth_tuple->rand, value, value_len); break; case GPRS_GSUP_SRES_IE: if (value_len != sizeof(auth_tuple->sres)) goto parse_error; memcpy(auth_tuple->sres, value, value_len); break; case GPRS_GSUP_KC_IE: if (value_len != sizeof(auth_tuple->kc)) goto parse_error; memcpy(auth_tuple->kc, value, value_len); break; default: LOGP(DGPRS, LOGL_ERROR, "GSUP IE type %d not expected in PDP info\n", iei); continue; } } return 0; parse_error: LOGP(DGPRS, LOGL_ERROR, "GSUP IE type %d, length %zu invalid in PDP info\n", iei, value_len); return -1; } int gprs_gsup_decode(const uint8_t *const_data, size_t data_len, struct gprs_gsup_message *gsup_msg) { int rc; uint8_t tag; /* the shift/match functions expect non-const pointers, but we'll * either copy the data or cast pointers back to const before returning * them */ uint8_t *data = (uint8_t *)const_data; uint8_t *value; size_t value_len; static const struct gprs_gsup_pdp_info empty_pdp_info = {0}; static const struct gsm_auth_tuple empty_auth_info = {0}; static const struct gprs_gsup_message empty_gsup_message = {0}; *gsup_msg = empty_gsup_message; /* generic part */ rc = gprs_shift_v_fixed(&data, &data_len, 1, &value); if (rc < 0) return -GMM_CAUSE_INV_MAND_INFO; gsup_msg->message_type = decode_big_endian(value, 1); rc = gprs_match_tlv(&data, &data_len, GPRS_GSUP_IMSI_IE, &value, &value_len); if (rc <= 0) return -GMM_CAUSE_INV_MAND_INFO; if (value_len * 2 + 1 > sizeof(gsup_msg->imsi)) return -GMM_CAUSE_INV_MAND_INFO; /* Note that gsm48_decode_bcd_number expects the number of encoded IMSI * octets in the first octet. By coincidence (the TLV encoding) the byte * before the value part already contains this length so we can use it * here. */ OSMO_ASSERT(value[-1] == value_len); gsm48_decode_bcd_number(gsup_msg->imsi, sizeof(gsup_msg->imsi), value - 1, 0); /* specific parts */ while (data_len > 0) { enum gprs_gsup_iei iei; struct gprs_gsup_pdp_info pdp_info; struct gsm_auth_tuple auth_info; rc = gprs_shift_tlv(&data, &data_len, &tag, &value, &value_len); if (rc < 0) return -GMM_CAUSE_PROTO_ERR_UNSPEC; iei = tag; switch (iei) { case GPRS_GSUP_IMSI_IE: case GPRS_GSUP_PDP_TYPE_IE: case GPRS_GSUP_ACCESS_POINT_NAME_IE: case GPRS_GSUP_RAND_IE: case GPRS_GSUP_SRES_IE: case GPRS_GSUP_KC_IE: LOGP(DGPRS, LOGL_NOTICE, "GSUP IE type %d not expected (ignored)\n", iei); continue; case GPRS_GSUP_CAUSE_IE: gsup_msg->cause = decode_big_endian(value, value_len); break; case GPRS_GSUP_CANCEL_TYPE_IE: gsup_msg->cancel_type = decode_big_endian(value, value_len) + 1; break; case GPRS_GSUP_PDP_INFO_COMPL_IE: gsup_msg->pdp_info_compl = 1; break; case GPRS_GSUP_FREEZE_PTMSI_IE: gsup_msg->freeze_ptmsi = 1; break; case GPRS_GSUP_PDP_CONTEXT_ID_IE: /* When these IE appear in the top-level part of the * message, they are used by Delete Subscr Info to delete * single entries. We don't have an extra list for * these but use the PDP info list instead */ /* fall through */ case GPRS_GSUP_PDP_INFO_IE: if (gsup_msg->num_pdp_infos >= GPRS_GSUP_MAX_NUM_PDP_INFO) { LOGP(DGPRS, LOGL_ERROR, "GSUP IE type %d (PDP_INFO) max exceeded\n", iei); return -GMM_CAUSE_COND_IE_ERR; } pdp_info = empty_pdp_info; if (iei == GPRS_GSUP_PDP_INFO_IE) { rc = decode_pdp_info(value, value_len, &pdp_info); if (rc < 0) return rc; pdp_info.have_info = 1; } else { pdp_info.context_id = decode_big_endian(value, value_len); } gsup_msg->pdp_infos[gsup_msg->num_pdp_infos++] = pdp_info; break; case GPRS_GSUP_AUTH_TUPLE_IE: if (gsup_msg->num_auth_tuples >= GPRS_GSUP_MAX_NUM_AUTH_INFO) { LOGP(DGPRS, LOGL_ERROR, "GSUP IE type %d (AUTH_INFO) max exceeded\n", iei); return -GMM_CAUSE_INV_MAND_INFO; } auth_info = empty_auth_info; auth_info.key_seq = gsup_msg->num_auth_tuples; rc = decode_auth_info(value, value_len, &auth_info); if (rc < 0) return rc; gsup_msg->auth_tuples[gsup_msg->num_auth_tuples++] = auth_info; break; case GPRS_GSUP_MSISDN_IE: gsup_msg->msisdn_enc = value; gsup_msg->msisdn_enc_len = value_len; break; case GPRS_GSUP_HLR_NUMBER_IE: gsup_msg->hlr_enc = value; gsup_msg->hlr_enc_len = value_len; break; default: LOGP(DGPRS, LOGL_NOTICE, "GSUP IE type %d unknown\n", iei); continue; } } return 0; } static void encode_pdp_info(struct msgb *msg, enum gprs_gsup_iei iei, const struct gprs_gsup_pdp_info *pdp_info) { uint8_t *len_field; size_t old_len; uint8_t u8; len_field = msgb_tlv_put(msg, iei, 0, NULL) - 1; old_len = msgb_length(msg); u8 = pdp_info->context_id; msgb_tlv_put(msg, GPRS_GSUP_PDP_CONTEXT_ID_IE, sizeof(u8), &u8); if (pdp_info->pdp_type) { msgb_tlv_put(msg, GPRS_GSUP_PDP_TYPE_IE, GPRS_GSUP_PDP_TYPE_SIZE, encode_big_endian(pdp_info->pdp_type | 0xf000, GPRS_GSUP_PDP_TYPE_SIZE)); } if (pdp_info->apn_enc) { msgb_tlv_put(msg, GPRS_GSUP_ACCESS_POINT_NAME_IE, pdp_info->apn_enc_len, pdp_info->apn_enc); } if (pdp_info->qos_enc) { msgb_tlv_put(msg, GPRS_GSUP_PDP_QOS_IE, pdp_info->qos_enc_len, pdp_info->qos_enc); } /* Update length field */ *len_field = msgb_length(msg) - old_len; } static void encode_auth_info(struct msgb *msg, enum gprs_gsup_iei iei, const struct gsm_auth_tuple *auth_tuple) { uint8_t *len_field; size_t old_len; len_field = msgb_tlv_put(msg, iei, 0, NULL) - 1; old_len = msgb_length(msg); msgb_tlv_put(msg, GPRS_GSUP_RAND_IE, sizeof(auth_tuple->rand), auth_tuple->rand); msgb_tlv_put(msg, GPRS_GSUP_SRES_IE, sizeof(auth_tuple->sres), auth_tuple->sres); msgb_tlv_put(msg, GPRS_GSUP_KC_IE, sizeof(auth_tuple->kc), auth_tuple->kc); /* Update length field */ *len_field = msgb_length(msg) - old_len; } void gprs_gsup_encode(struct msgb *msg, const struct gprs_gsup_message *gsup_msg) { uint8_t u8; int idx; uint8_t bcd_buf[GSM48_MI_SIZE] = {0}; size_t bcd_len; /* generic part */ OSMO_ASSERT(gsup_msg->message_type); msgb_v_put(msg, gsup_msg->message_type); bcd_len = gsm48_encode_bcd_number(bcd_buf, sizeof(bcd_buf), 0, gsup_msg->imsi); OSMO_ASSERT(bcd_len > 1); /* Note that gsm48_encode_bcd_number puts the length into the first * octet. Since msgb_tlv_put will add this length byte, we'll have to * skip it */ msgb_tlv_put(msg, GPRS_GSUP_IMSI_IE, bcd_len - 1, &bcd_buf[1]); /* specific parts */ if (gsup_msg->msisdn_enc) msgb_tlv_put(msg, GPRS_GSUP_MSISDN_IE, gsup_msg->msisdn_enc_len, gsup_msg->msisdn_enc); if (gsup_msg->hlr_enc) msgb_tlv_put(msg, GPRS_GSUP_HLR_NUMBER_IE, gsup_msg->hlr_enc_len, gsup_msg->hlr_enc); if ((u8 = gsup_msg->cause)) msgb_tlv_put(msg, GPRS_GSUP_CAUSE_IE, sizeof(u8), &u8); if ((u8 = gsup_msg->cancel_type)) { u8 -= 1; msgb_tlv_put(msg, GPRS_GSUP_CANCEL_TYPE_IE, sizeof(u8), &u8); } if (gsup_msg->pdp_info_compl) msgb_tlv_put(msg, GPRS_GSUP_PDP_INFO_COMPL_IE, 0, &u8); if (gsup_msg->freeze_ptmsi) msgb_tlv_put(msg, GPRS_GSUP_FREEZE_PTMSI_IE, 0, &u8); for (idx = 0; idx < gsup_msg->num_pdp_infos; idx++) { const struct gprs_gsup_pdp_info *pdp_info; pdp_info = &gsup_msg->pdp_infos[idx]; if (pdp_info->context_id == 0) continue; if (pdp_info->have_info) { encode_pdp_info(msg, GPRS_GSUP_PDP_INFO_IE, pdp_info); } else { u8 = pdp_info->context_id; msgb_tlv_put(msg, GPRS_GSUP_PDP_CONTEXT_ID_IE, sizeof(u8), &u8); } } for (idx = 0; idx < gsup_msg->num_auth_tuples; idx++) { const struct gsm_auth_tuple *auth_info; auth_info = &gsup_msg->auth_tuples[idx]; if (auth_info->key_seq == GSM_KEY_SEQ_INVAL) continue; encode_auth_info(msg, GPRS_GSUP_AUTH_TUPLE_IE, auth_info); } } openbsc-0.15.0/openbsc/src/gprs/gprs_llc.c000066400000000000000000000515031265565154000204070ustar00rootroot00000000000000/* GPRS LLC protocol implementation as per 3GPP TS 04.64 */ /* (C) 2009-2010 by Harald Welte * * 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include static struct gprs_llc_llme *llme_alloc(uint32_t tlli); /* If the TLLI is foreign, return its local version */ static inline uint32_t tlli_foreign2local(uint32_t tlli) { uint32_t new_tlli; if (gprs_tlli_type(tlli) == TLLI_FOREIGN) { new_tlli = tlli | 0x40000000; LOGP(DLLC, LOGL_NOTICE, "TLLI 0x%08x is foreign, converting to " "local TLLI 0x%08x\n", tlli, new_tlli); } else new_tlli = tlli; return new_tlli; } /* Entry function from upper level (LLC), asking us to transmit a BSSGP PDU * to a remote MS (identified by TLLI) at a BTS identified by its BVCI and NSEI */ static int _bssgp_tx_dl_ud(struct msgb *msg, struct sgsn_mm_ctx *mmctx) { struct bssgp_dl_ud_par dup; const uint8_t qos_profile_default[3] = { 0x00, 0x00, 0x20 }; memset(&dup, 0, sizeof(dup)); /* before we have received some identity from the MS, we might * not yet have a MMC context (e.g. XID negotiation of primarly * LLC connection fro GMM sapi). */ if (mmctx) { dup.imsi = mmctx->imsi; dup.drx_parms = mmctx->drx_parms; dup.ms_ra_cap.len = mmctx->ms_radio_access_capa.len; dup.ms_ra_cap.v = mmctx->ms_radio_access_capa.buf; /* make sure we only send it to the right llme */ OSMO_ASSERT(msgb_tlli(msg) == mmctx->llme->tlli || msgb_tlli(msg) == mmctx->llme->old_tlli || tlli_foreign2local(msgb_tlli(msg)) == mmctx->llme->tlli || tlli_foreign2local(msgb_tlli(msg)) == mmctx->llme->old_tlli); } memcpy(&dup.qos_profile, qos_profile_default, sizeof(qos_profile_default)); return bssgp_tx_dl_ud(msg, 1000, &dup); } /* Section 8.9.9 LLC layer parameter default values */ static const struct gprs_llc_params llc_default_params[NUM_SAPIS] = { [1] = { .t200_201 = 5, .n200 = 3, .n201_u = 400, }, [2] = { .t200_201 = 5, .n200 = 3, .n201_u = 270, }, [3] = { .iov_i_exp = 27, .t200_201 = 5, .n200 = 3, .n201_u = 500, .n201_i = 1503, .mD = 1520, .mU = 1520, .kD = 16, .kU = 16, }, [5] = { .iov_i_exp = 27, .t200_201 = 10, .n200 = 3, .n201_u = 500, .n201_i = 1503, .mD = 760, .mU = 760, .kD = 8, .kU = 8, }, [7] = { .t200_201 = 20, .n200 = 3, .n201_u = 270, }, [8] = { .t200_201 = 20, .n200 = 3, .n201_u = 270, }, [9] = { .iov_i_exp = 27, .t200_201 = 20, .n200 = 3, .n201_u = 500, .n201_i = 1503, .mD = 380, .mU = 380, .kD = 4, .kU = 4, }, [11] = { .iov_i_exp = 27, .t200_201 = 40, .n200 = 3, .n201_u = 500, .n201_i = 1503, .mD = 190, .mU = 190, .kD = 2, .kU = 2, }, }; LLIST_HEAD(gprs_llc_llmes); void *llc_tall_ctx; /* lookup LLC Entity based on DLCI (TLLI+SAPI tuple) */ static struct gprs_llc_lle *lle_by_tlli_sapi(const uint32_t tlli, uint8_t sapi) { struct gprs_llc_llme *llme; llist_for_each_entry(llme, &gprs_llc_llmes, list) { if (llme->tlli == tlli || llme->old_tlli == tlli) return &llme->lle[sapi]; } return NULL; } struct gprs_llc_lle *gprs_lle_get_or_create(const uint32_t tlli, uint8_t sapi) { struct gprs_llc_llme *llme; struct gprs_llc_lle *lle; lle = lle_by_tlli_sapi(tlli, sapi); if (lle) return lle; lle = lle_by_tlli_sapi(tlli_foreign2local(tlli), sapi); if (lle) return lle; LOGP(DLLC, LOGL_NOTICE, "LLC: unknown TLLI 0x%08x, " "creating LLME on the fly\n", tlli); llme = llme_alloc(tlli); lle = &llme->lle[sapi]; return lle; } struct llist_head *gprs_llme_list(void) { return &gprs_llc_llmes; } /* lookup LLC Entity for RX based on DLCI (TLLI+SAPI tuple) */ static struct gprs_llc_lle *lle_for_rx_by_tlli_sapi(const uint32_t tlli, uint8_t sapi, enum gprs_llc_cmd cmd) { struct gprs_llc_lle *lle; /* We already know about this TLLI */ lle = lle_by_tlli_sapi(tlli, sapi); if (lle) return lle; /* Maybe it is a routing area update but we already know this sapi? */ if (gprs_tlli_type(tlli) == TLLI_FOREIGN) { lle = lle_by_tlli_sapi(tlli_foreign2local(tlli), sapi); if (lle) { LOGP(DLLC, LOGL_NOTICE, "LLC RX: Found a local entry for TLLI 0x%08x\n", tlli); return lle; } } /* 7.2.1.1 LLC belonging to unassigned TLLI+SAPI shall be discarded, * except UID and XID frames with SAPI=1 */ if (sapi == GPRS_SAPI_GMM && (cmd == GPRS_LLC_XID || cmd == GPRS_LLC_UI)) { struct gprs_llc_llme *llme; /* FIXME: don't use the TLLI but the 0xFFFF unassigned? */ llme = llme_alloc(tlli); LOGP(DLLC, LOGL_NOTICE, "LLC RX: unknown TLLI 0x%08x, " "creating LLME on the fly\n", tlli); lle = &llme->lle[sapi]; return lle; } LOGP(DLLC, LOGL_NOTICE, "unknown TLLI(0x%08x)/SAPI(%d): Silently dropping\n", tlli, sapi); return NULL; } static void lle_init(struct gprs_llc_llme *llme, uint8_t sapi) { struct gprs_llc_lle *lle = &llme->lle[sapi]; lle->llme = llme; lle->sapi = sapi; lle->state = GPRS_LLES_UNASSIGNED; /* Initialize according to parameters */ memcpy(&lle->params, &llc_default_params[sapi], sizeof(lle->params)); } static struct gprs_llc_llme *llme_alloc(uint32_t tlli) { struct gprs_llc_llme *llme; uint32_t i; llme = talloc_zero(llc_tall_ctx, struct gprs_llc_llme); if (!llme) return NULL; llme->tlli = tlli; llme->old_tlli = 0xffffffff; llme->state = GPRS_LLMS_UNASSIGNED; llme->age_timestamp = GPRS_LLME_RESET_AGE; for (i = 0; i < ARRAY_SIZE(llme->lle); i++) lle_init(llme, i); llist_add(&llme->list, &gprs_llc_llmes); return llme; } static void llme_free(struct gprs_llc_llme *llme) { llist_del(&llme->list); talloc_free(llme); } #if 0 /* FIXME: Unused code... */ static void t200_expired(void *data) { struct gprs_llc_lle *lle = data; /* 8.5.1.3: Expiry of T200 */ if (lle->retrans_ctr >= lle->params.n200) { /* FIXME: LLGM-STATUS-IND, LL-RELEASE-IND/CNF */ lle->state = GPRS_LLES_ASSIGNED_ADM; } switch (lle->state) { case GPRS_LLES_LOCAL_EST: /* FIXME: retransmit SABM */ /* FIXME: re-start T200 */ lle->retrans_ctr++; break; case GPRS_LLES_LOCAL_REL: /* FIXME: retransmit DISC */ /* FIXME: re-start T200 */ lle->retrans_ctr++; break; default: LOGP(DLLC, LOGL_ERROR, "LLC unhandled state: %d\n", lle->state); break; } } static void t201_expired(void *data) { struct gprs_llc_lle *lle = data; if (lle->retrans_ctr < lle->params.n200) { /* FIXME: transmit apropriate supervisory frame (8.6.4.1) */ /* FIXME: set timer T201 */ lle->retrans_ctr++; } } #endif int gprs_llc_tx_u(struct msgb *msg, uint8_t sapi, int command, enum gprs_llc_u_cmd u_cmd, int pf_bit) { uint8_t *fcs, *llch; uint8_t addr, ctrl; uint32_t fcs_calc; /* Identifiers from UP: (TLLI, SAPI) + (BVCI, NSEI) */ /* Address Field */ addr = sapi & 0xf; if (command) addr |= 0x40; /* 6.3 Figure 8 */ ctrl = 0xe0 | u_cmd; if (pf_bit) ctrl |= 0x10; /* prepend LLC UI header */ llch = msgb_push(msg, 2); llch[0] = addr; llch[1] = ctrl; /* append FCS to end of frame */ fcs = msgb_put(msg, 3); fcs_calc = gprs_llc_fcs(llch, fcs - llch); fcs[0] = fcs_calc & 0xff; fcs[1] = (fcs_calc >> 8) & 0xff; fcs[2] = (fcs_calc >> 16) & 0xff; /* Identifiers passed down: (BVCI, NSEI) */ /* Send BSSGP-DL-UNITDATA.req */ return _bssgp_tx_dl_ud(msg, NULL); } /* Send XID response to LLE */ static int gprs_llc_tx_xid(struct gprs_llc_lle *lle, struct msgb *msg, int command) { /* copy identifiers from LLE to ensure lower layers can route */ msgb_tlli(msg) = lle->llme->tlli; msgb_bvci(msg) = lle->llme->bvci; msgb_nsei(msg) = lle->llme->nsei; return gprs_llc_tx_u(msg, lle->sapi, command, GPRS_LLC_U_XID, 1); } /* Transmit a UI frame over the given SAPI */ int gprs_llc_tx_ui(struct msgb *msg, uint8_t sapi, int command, void *mmctx) { struct gprs_llc_lle *lle; uint8_t *fcs, *llch; uint8_t addr, ctrl[2]; uint32_t fcs_calc; uint16_t nu = 0; uint32_t oc; /* Identifiers from UP: (TLLI, SAPI) + (BVCI, NSEI) */ /* look-up or create the LL Entity for this (TLLI, SAPI) tuple */ lle = gprs_lle_get_or_create(msgb_tlli(msg), sapi); if (msg->len > lle->params.n201_u) { LOGP(DLLC, LOGL_ERROR, "Cannot Tx %u bytes (N201-U=%u)\n", msg->len, lle->params.n201_u); msgb_free(msg); return -EFBIG; } /* Update LLE's (BVCI, NSEI) tuple */ lle->llme->bvci = msgb_bvci(msg); lle->llme->nsei = msgb_nsei(msg); /* Obtain current values for N(u) and OC */ nu = lle->vu_send; oc = lle->oc_ui_send; /* Increment V(U) */ lle->vu_send = (lle->vu_send + 1) % 512; /* Increment Overflow Counter, if needed */ if ((lle->vu_send + 1) / 512) lle->oc_ui_send += 512; /* Address Field */ addr = sapi & 0xf; if (command) addr |= 0x40; /* Control Field */ ctrl[0] = 0xc0; ctrl[0] |= nu >> 6; ctrl[1] = (nu << 2) & 0xfc; ctrl[1] |= 0x01; /* Protected Mode */ /* prepend LLC UI header */ llch = msgb_push(msg, 3); llch[0] = addr; llch[1] = ctrl[0]; llch[2] = ctrl[1]; /* append FCS to end of frame */ fcs = msgb_put(msg, 3); fcs_calc = gprs_llc_fcs(llch, fcs - llch); fcs[0] = fcs_calc & 0xff; fcs[1] = (fcs_calc >> 8) & 0xff; fcs[2] = (fcs_calc >> 16) & 0xff; /* encrypt information field + FCS, if needed! */ if (lle->llme->algo != GPRS_ALGO_GEA0) { uint32_t iov_ui = 0; /* FIXME: randomly select for TLLI */ uint16_t crypt_len = (fcs + 3) - (llch + 3); uint8_t cipher_out[GSM0464_CIPH_MAX_BLOCK]; uint32_t iv; int rc, i; uint64_t kc = *(uint64_t *)&lle->llme->kc; /* Compute the 'Input' Paraemeter */ iv = gprs_cipher_gen_input_ui(iov_ui, sapi, nu, oc); /* Compute the keystream that we need to XOR with the data */ rc = gprs_cipher_run(cipher_out, crypt_len, lle->llme->algo, kc, iv, GPRS_CIPH_SGSN2MS); if (rc < 0) { LOGP(DLLC, LOGL_ERROR, "Error crypting UI frame: %d\n", rc); msgb_free(msg); return rc; } /* XOR the cipher output with the information field + FCS */ for (i = 0; i < crypt_len; i++) *(llch + 3 + i) ^= cipher_out[i]; /* Mark frame as encrypted */ ctrl[1] |= 0x02; } /* Identifiers passed down: (BVCI, NSEI) */ /* Send BSSGP-DL-UNITDATA.req */ return _bssgp_tx_dl_ud(msg, mmctx); } /* According to 6.4.1.6 / Figure 11 */ static int msgb_put_xid_par(struct msgb *msg, uint8_t type, uint8_t length, uint8_t *data) { uint8_t header_len = 1; uint8_t *cur; /* type is a 5-bit field... */ if (type > 0x1f) return -EINVAL; if (length > 3) header_len = 2; cur = msgb_put(msg, length + header_len); /* build the header without or with XL bit */ if (length <= 3) { *cur++ = (type << 2) | (length & 3); } else { *cur++ = 0x80 | (type << 2) | (length >> 6); *cur++ = (length << 2); } /* copy over the payload of the parameter*/ memcpy(cur, data, length); return length + header_len; } static void rx_llc_xid(struct gprs_llc_lle *lle, struct gprs_llc_hdr_parsed *gph) { /* FIXME: 8.5.3.3: check if XID is invalid */ if (gph->is_cmd) { /* FIXME: implement XID negotiation using SNDCP */ struct msgb *resp; uint8_t *xid; resp = msgb_alloc_headroom(4096, 1024, "LLC_XID"); xid = msgb_put(resp, gph->data_len); memcpy(xid, gph->data, gph->data_len); gprs_llc_tx_xid(lle, resp, 0); } else { /* FIXME: if we had sent a XID reset, send * LLGMM-RESET.conf to GMM */ /* FIXME: implement XID negotiation using SNDCP */ } } static int gprs_llc_hdr_rx(struct gprs_llc_hdr_parsed *gph, struct gprs_llc_lle *lle) { switch (gph->cmd) { case GPRS_LLC_SABM: /* Section 6.4.1.1 */ lle->v_sent = lle->v_ack = lle->v_recv = 0; if (lle->state == GPRS_LLES_ASSIGNED_ADM) { /* start re-establishment (8.7.1) */ } lle->state = GPRS_LLES_REMOTE_EST; /* FIXME: Send UA */ lle->state = GPRS_LLES_ABM; /* FIXME: process data */ break; case GPRS_LLC_DISC: /* Section 6.4.1.2 */ /* FIXME: Send UA */ /* terminate ABM */ lle->state = GPRS_LLES_ASSIGNED_ADM; break; case GPRS_LLC_UA: /* Section 6.4.1.3 */ if (lle->state == GPRS_LLES_LOCAL_EST) lle->state = GPRS_LLES_ABM; break; case GPRS_LLC_DM: /* Section 6.4.1.4: ABM cannot be performed */ if (lle->state == GPRS_LLES_LOCAL_EST) lle->state = GPRS_LLES_ASSIGNED_ADM; break; case GPRS_LLC_FRMR: /* Section 6.4.1.5 */ break; case GPRS_LLC_XID: /* Section 6.4.1.6 */ rx_llc_xid(lle, gph); break; case GPRS_LLC_UI: if (gprs_llc_is_retransmit(gph->seq_tx, lle->vu_recv)) { LOGP(DLLC, LOGL_NOTICE, "TLLI=%08x dropping UI, N(U=%d) not in window V(URV(UR:%d).\n", lle->llme ? lle->llme->tlli : -1, gph->seq_tx, lle->vu_recv); /* HACK: non-standard recovery handling. If remote LLE * is re-transmitting the same sequence number for * three times, don't discard the frame but pass it on * and 'learn' the new sequence number */ if (gph->seq_tx != lle->vu_recv_last) { lle->vu_recv_last = gph->seq_tx; lle->vu_recv_duplicates = 0; } else { lle->vu_recv_duplicates++; if (lle->vu_recv_duplicates < 3) return -EIO; LOGP(DLLC, LOGL_NOTICE, "TLLI=%08x recovering " "N(U=%d) after receiving %u duplicates\n", lle->llme ? lle->llme->tlli : -1, gph->seq_tx, lle->vu_recv_duplicates); } } /* Increment the sequence number that we expect in the next frame */ lle->vu_recv = (gph->seq_tx + 1) % 512; /* Increment Overflow Counter */ if ((gph->seq_tx + 1) / 512) lle->oc_ui_recv += 512; break; default: LOGP(DLLC, LOGL_NOTICE, "Unhandled command: %d\n", gph->cmd); break; } return 0; } /* receive an incoming LLC PDU (BSSGP-UL-UNITDATA-IND, 7.2.4.2) */ int gprs_llc_rcvmsg(struct msgb *msg, struct tlv_parsed *tv) { struct gprs_llc_hdr *lh = (struct gprs_llc_hdr *) msgb_llch(msg); struct gprs_llc_hdr_parsed llhp; struct gprs_llc_lle *lle; int rc = 0; /* Identifiers from DOWN: NSEI, BVCI, TLLI */ memset(&llhp, 0, sizeof(llhp)); rc = gprs_llc_hdr_parse(&llhp, (uint8_t *) lh, TLVP_LEN(tv, BSSGP_IE_LLC_PDU)); gprs_llc_hdr_dump(&llhp); if (rc < 0) { LOGP(DLLC, LOGL_NOTICE, "Error during LLC header parsing\n"); return rc; } switch (gprs_tlli_type(msgb_tlli(msg))) { case TLLI_LOCAL: case TLLI_FOREIGN: case TLLI_RANDOM: case TLLI_AUXILIARY: break; default: LOGP(DLLC, LOGL_ERROR, "Discarding frame with strange TLLI type\n"); break; } /* find the LLC Entity for this TLLI+SAPI tuple */ lle = lle_for_rx_by_tlli_sapi(msgb_tlli(msg), llhp.sapi, llhp.cmd); if (!lle) { switch (llhp.sapi) { case GPRS_SAPI_SNDCP3: case GPRS_SAPI_SNDCP5: case GPRS_SAPI_SNDCP9: case GPRS_SAPI_SNDCP11: /* Ask an upper layer for help. */ return sgsn_force_reattach_oldmsg(msg); default: break; } return 0; } /* reset age computation */ lle->llme->age_timestamp = GPRS_LLME_RESET_AGE; /* decrypt information field + FCS, if needed! */ if (llhp.is_encrypted) { uint32_t iov_ui = 0; /* FIXME: randomly select for TLLI */ uint16_t crypt_len = llhp.data_len + 3; uint8_t cipher_out[GSM0464_CIPH_MAX_BLOCK]; uint32_t iv; uint64_t kc = *(uint64_t *)&lle->llme->kc; int rc, i; if (lle->llme->algo == GPRS_ALGO_GEA0) { LOGP(DLLC, LOGL_NOTICE, "encrypted frame for LLC that " "has no KC/Algo! Dropping.\n"); return 0; } iv = gprs_cipher_gen_input_ui(iov_ui, lle->sapi, llhp.seq_tx, lle->oc_ui_recv); rc = gprs_cipher_run(cipher_out, crypt_len, lle->llme->algo, kc, iv, GPRS_CIPH_MS2SGSN); if (rc < 0) { LOGP(DLLC, LOGL_ERROR, "Error decrypting frame: %d\n", rc); return rc; } /* XOR the cipher output with the information field + FCS */ for (i = 0; i < crypt_len; i++) *(llhp.data + i) ^= cipher_out[i]; } else { if (lle->llme->algo != GPRS_ALGO_GEA0) { LOGP(DLLC, LOGL_NOTICE, "unencrypted frame for LLC " "that is supposed to be encrypted. Dropping.\n"); return 0; } } /* We have to do the FCS check _after_ decryption */ llhp.fcs_calc = gprs_llc_fcs((uint8_t *)lh, llhp.crc_length); if (llhp.fcs != llhp.fcs_calc) { LOGP(DLLC, LOGL_INFO, "Dropping frame with invalid FCS\n"); return -EIO; } /* Update LLE's (BVCI, NSEI) tuple */ lle->llme->bvci = msgb_bvci(msg); lle->llme->nsei = msgb_nsei(msg); /* Receive and Process the actual LLC frame */ rc = gprs_llc_hdr_rx(&llhp, lle); if (rc < 0) return rc; /* llhp.data is only set when we need to send LL_[UNIT]DATA_IND up */ if (llhp.cmd == GPRS_LLC_UI && llhp.data && llhp.data_len) { msgb_gmmh(msg) = llhp.data; switch (llhp.sapi) { case GPRS_SAPI_GMM: /* send LL_UNITDATA_IND to GMM */ rc = gsm0408_gprs_rcvmsg(msg, lle->llme); break; case GPRS_SAPI_SNDCP3: case GPRS_SAPI_SNDCP5: case GPRS_SAPI_SNDCP9: case GPRS_SAPI_SNDCP11: /* send LL_DATA_IND/LL_UNITDATA_IND to SNDCP */ rc = sndcp_llunitdata_ind(msg, lle, llhp.data, llhp.data_len); break; case GPRS_SAPI_SMS: /* FIXME */ case GPRS_SAPI_TOM2: case GPRS_SAPI_TOM8: /* FIXME: send LL_DATA_IND/LL_UNITDATA_IND to TOM */ default: LOGP(DLLC, LOGL_NOTICE, "Unsupported SAPI %u\n", llhp.sapi); rc = -EINVAL; break; } } return rc; } /* 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, enum gprs_ciph_algo alg, const uint8_t *kc) { unsigned int i; /* Update the crypto parameters */ llme->algo = alg; if (alg != GPRS_ALGO_GEA0) memcpy(llme->kc, kc, sizeof(llme->kc)); if (old_tlli == 0xffffffff && new_tlli != 0xffffffff) { /* TLLI Assignment 8.3.1 */ /* New TLLI shall be assigned and used when (re)transmitting LLC frames */ /* If old TLLI != 0xffffffff was assigned to LLME, then TLLI * old is unassigned. Only TLLI new shall be accepted when * received from peer. */ if (llme->old_tlli != 0xffffffff) { llme->old_tlli = 0xffffffff; llme->tlli = new_tlli; } else { /* If TLLI old == 0xffffffff was assigned to LLME, then this is * TLLI assignmemt according to 8.3.1 */ llme->old_tlli = 0xffffffff; llme->tlli = new_tlli; llme->state = GPRS_LLMS_ASSIGNED; /* 8.5.3.1 For all LLE's */ for (i = 0; i < ARRAY_SIZE(llme->lle); i++) { struct gprs_llc_lle *l = &llme->lle[i]; l->vu_send = l->vu_recv = 0; l->retrans_ctr = 0; l->state = GPRS_LLES_ASSIGNED_ADM; /* FIXME Set parameters according to table 9 */ } } } else if (old_tlli != 0xffffffff && new_tlli != 0xffffffff) { /* TLLI Change 8.3.2 */ /* Both TLLI Old and TLLI New are assigned; use New when * (re)transmitting. Accept both Old and New on Rx */ llme->old_tlli = old_tlli; llme->tlli = new_tlli; llme->state = GPRS_LLMS_ASSIGNED; } else if (old_tlli != 0xffffffff && new_tlli == 0xffffffff) { /* TLLI Unassignment 8.3.3) */ llme->tlli = llme->old_tlli = 0; llme->state = GPRS_LLMS_UNASSIGNED; for (i = 0; i < ARRAY_SIZE(llme->lle); i++) { struct gprs_llc_lle *l = &llme->lle[i]; l->state = GPRS_LLES_UNASSIGNED; } llme_free(llme); } else return -EINVAL; return 0; } /* Chapter 7.2.1.2 LLGMM-RESET.req */ int gprs_llgmm_reset(struct gprs_llc_llme *llme) { struct msgb *msg = msgb_alloc_headroom(4096, 1024, "LLC_XID"); int random = rand(); struct gprs_llc_lle *lle = &llme->lle[1]; /* First XID component must be RESET */ msgb_put_xid_par(msg, GPRS_LLC_XID_T_RESET, 0, NULL); /* randomly select new IOV-UI */ msgb_put_xid_par(msg, GPRS_LLC_XID_T_IOV_UI, 4, (uint8_t *) &random); /* Reset some of the LLC parameters. See GSM 04.64, 8.5.3.1 */ lle->vu_recv = 0; lle->vu_send = 0; lle->oc_ui_send = 0; lle->oc_ui_recv = 0; /* FIXME: Start T200, wait for XID response */ return gprs_llc_tx_xid(lle, msg, 1); } int gprs_llgmm_reset_oldmsg(struct msgb* oldmsg, uint8_t sapi) { struct msgb *msg = msgb_alloc_headroom(4096, 1024, "LLC_XID"); int random = rand(); /* First XID component must be RESET */ msgb_put_xid_par(msg, GPRS_LLC_XID_T_RESET, 0, NULL); /* randomly select new IOV-UI */ msgb_put_xid_par(msg, GPRS_LLC_XID_T_IOV_UI, 4, (uint8_t *) &random); /* FIXME: Start T200, wait for XID response */ msgb_tlli(msg) = msgb_tlli(oldmsg); msgb_bvci(msg) = msgb_bvci(oldmsg); msgb_nsei(msg) = msgb_nsei(oldmsg); return gprs_llc_tx_u(msg, sapi, 1, GPRS_LLC_U_XID, 1); } int gprs_llc_init(const char *cipher_plugin_path) { return gprs_cipher_load(cipher_plugin_path); } openbsc-0.15.0/openbsc/src/gprs/gprs_llc_parse.c000066400000000000000000000126101265565154000215750ustar00rootroot00000000000000/* GPRS LLC protocol implementation as per 3GPP TS 04.64 */ /* (C) 2009-2010 by Harald Welte * * 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include 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) { DEBUGP(DLLC, "LLC SAPI=%u %c %c FCS=0x%06x", gph->sapi, gph->is_cmd ? 'C' : 'R', gph->ack_req ? 'A' : ' ', 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; } openbsc-0.15.0/openbsc/src/gprs/gprs_llc_vty.c000066400000000000000000000064721265565154000213160ustar00rootroot00000000000000/* VTY interface for our GPRS LLC implementation */ /* (C) 2010 by Harald Welte * * 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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" }, }; 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 Age=%d: State %s%s", llme->tlli, llme->old_tlli, llme->bvci, llme->nsei, 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; } openbsc-0.15.0/openbsc/src/gprs/gprs_sgsn.c000066400000000000000000000503631265565154000206120ustar00rootroot00000000000000/* GPRS SGSN functionality */ /* (C) 2009 by Harald Welte * * 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "openbsc/gprs_llc.h" #include #include #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, }; 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, }; static int 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); } /* See 03.02 Chapter 2.6 */ static inline uint32_t tlli_foreign(uint32_t tlli) { return ((tlli | 0x80000000) & ~0x40000000); } /* 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; int tlli_type; llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) { if (tlli == ctx->tlli && ra_id_equals(raid, &ctx->ra)) return ctx; } tlli_type = gprs_tlli_type(tlli); switch (tlli_type) { case TLLI_LOCAL: llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) { if ((ctx->p_tmsi | 0xC0000000) == tlli || (ctx->p_tmsi_old && (ctx->p_tmsi_old | 0xC0000000) == tlli)) { ctx->tlli = tlli; return ctx; } } break; case TLLI_FOREIGN: llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) { if (tlli == tlli_foreign(ctx->tlli) && ra_id_equals(raid, &ctx->ra)) return ctx; } break; default: break; } 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 */ struct sgsn_mm_ctx *sgsn_mm_ctx_alloc(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->tlli = tlli; ctx->mm_state = GMM_DEREGISTERED; ctx->auth_triplet.key_seq = GSM_KEY_SEQ_INVAL; ctx->ctrg = rate_ctr_group_alloc(ctx, &mmctx_ctrg_desc, tlli); INIT_LLIST_HEAD(&ctx->pdp_list); llist_add(&ctx->list, &sgsn_mm_ctxts); return ctx; } /* 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 = mm->llme; uint32_t tlli = mm->tlli; struct sgsn_pdp_ctx *pdp, *pdp2; struct sgsn_signal_data sig_data; /* 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 gsm_subscriber *subscr = subscr_get(mm->subscr); gprs_subscr_cleanup(subscr); subscr_put(subscr); } sgsn_mm_ctx_free(mm); mm = NULL; /* TLLI unassignment, must be called after sgsn_mm_ctx_free */ gprs_llgmm_assign(llme, tlli, 0xffffffff, GPRS_ALGO_GEA0, NULL); } /* 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); llist_add(&pdp->list, &mm->pdp_list); llist_add(&pdp->g_list, &sgsn_pdp_ctxts); return pdp; } #include /* * 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"); /* Force the deactivation of the SNDCP layer */ sndcp_sm_deactivate_ind(&pdp->mm->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; 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->mm_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; } int sgsn_force_reattach_oldmsg(struct msgb *oldmsg) { return gsm0408_gprs_force_reattach_oldmsg(oldmsg); } 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_qos(struct tlv_parsed *tp, 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; } /** * 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; } gprs_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_qos(tp, 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_qos(tp, 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_qos(tp, pdp); selected_apn_str = req_apn_str; allow_any_apn = 1; continue; } if (strcasecmp(pdp->apn_str, req_apn_str) == 0) { insert_qos(tp, 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->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_assign(llme, llme->tlli, 0xffffffff, GPRS_ALGO_GEA0, NULL); } 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() { sgsn->llme_timer.cb = sgsn_llme_check_cb; sgsn->llme_timer.data = NULL; osmo_timer_schedule(&sgsn->llme_timer, GPRS_LLME_CHECK_TICK, 0); } openbsc-0.15.0/openbsc/src/gprs/gprs_sndcp.c000066400000000000000000000413201265565154000207400ustar00rootroot00000000000000/* GPRS SNDCP protocol implementation as per 3GPP TS 04.65 */ /* (C) 2010 by Harald Welte * (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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include "gprs_sndcp.h" /* Chapter 7.2: SN-PDU Formats */ struct sndcp_common_hdr { /* octet 1 */ uint8_t nsapi:4; uint8_t more:1; uint8_t type:1; uint8_t first:1; uint8_t spare:1; } __attribute__((packed)); /* PCOMP / DCOMP only exist in first fragment */ struct sndcp_comp_hdr { /* octet 2 */ uint8_t pcomp:4; uint8_t dcomp:4; } __attribute__((packed)); struct sndcp_udata_hdr { /* octet 3 */ uint8_t npdu_high:4; uint8_t seg_nr:4; /* octet 4 */ uint8_t npdu_low; } __attribute__((packed)); static void *tall_sndcp_ctx; /* A fragment queue entry, containing one framgent of a N-PDU */ struct defrag_queue_entry { struct llist_head list; /* segment number of this fragment */ uint32_t seg_nr; /* length of the data area of this fragment */ uint32_t data_len; /* pointer to the data of this fragment */ uint8_t *data; }; LLIST_HEAD(gprs_sndcp_entities); /* Enqueue a fragment into the defragment queue */ static int defrag_enqueue(struct gprs_sndcp_entity *sne, uint8_t seg_nr, uint8_t *data, uint32_t data_len) { struct defrag_queue_entry *dqe; dqe = talloc_zero(tall_sndcp_ctx, struct defrag_queue_entry); if (!dqe) return -ENOMEM; dqe->data = talloc_zero_size(dqe, data_len); if (!dqe->data) { talloc_free(dqe); return -ENOMEM; } dqe->seg_nr = seg_nr; dqe->data_len = data_len; llist_add(&dqe->list, &sne->defrag.frag_list); if (seg_nr > sne->defrag.highest_seg) sne->defrag.highest_seg = seg_nr; sne->defrag.seg_have |= (1 << seg_nr); sne->defrag.tot_len += data_len; memcpy(dqe->data, data, data_len); return 0; } /* return if we have all segments of this N-PDU */ static int defrag_have_all_segments(struct gprs_sndcp_entity *sne) { uint32_t seg_needed = 0; unsigned int i; /* create a bitmask of needed segments */ for (i = 0; i <= sne->defrag.highest_seg; i++) seg_needed |= (1 << i); if (seg_needed == sne->defrag.seg_have) return 1; return 0; } static struct defrag_queue_entry *defrag_get_seg(struct gprs_sndcp_entity *sne, uint32_t seg_nr) { struct defrag_queue_entry *dqe; llist_for_each_entry(dqe, &sne->defrag.frag_list, list) { if (dqe->seg_nr == seg_nr) { llist_del(&dqe->list); return dqe; } } return NULL; } /* Perform actual defragmentation and create an output packet */ static int defrag_segments(struct gprs_sndcp_entity *sne) { struct msgb *msg; unsigned int seg_nr; uint8_t *npdu; LOGP(DSNDCP, LOGL_DEBUG, "TLLI=0x%08x NSAPI=%u: Defragment output PDU %u " "num_seg=%u tot_len=%u\n", sne->lle->llme->tlli, sne->nsapi, sne->defrag.npdu, sne->defrag.highest_seg, sne->defrag.tot_len); msg = msgb_alloc_headroom(sne->defrag.tot_len+256, 128, "SNDCP Defrag"); if (!msg) return -ENOMEM; /* FIXME: message headers + identifiers */ npdu = msg->data; for (seg_nr = 0; seg_nr <= sne->defrag.highest_seg; seg_nr++) { struct defrag_queue_entry *dqe; uint8_t *data; dqe = defrag_get_seg(sne, seg_nr); if (!dqe) { LOGP(DSNDCP, LOGL_ERROR, "Segment %u missing\n", seg_nr); msgb_free(msg); return -EIO; } /* actually append the segment to the N-PDU */ data = msgb_put(msg, dqe->data_len); memcpy(data, dqe->data, dqe->data_len); /* release memory for the fragment queue entry */ talloc_free(dqe); } /* FIXME: cancel timer */ /* actually send the N-PDU to the SGSN core code, which then * hands it off to the correct GTP tunnel + GGSN via gtp_data_req() */ return sgsn_rx_sndcp_ud_ind(&sne->ra_id, sne->lle->llme->tlli, sne->nsapi, msg, sne->defrag.tot_len, npdu); } static int defrag_input(struct gprs_sndcp_entity *sne, struct msgb *msg, uint8_t *hdr, unsigned int len) { struct sndcp_common_hdr *sch; struct sndcp_udata_hdr *suh; uint16_t npdu_num; uint8_t *data; int rc; sch = (struct sndcp_common_hdr *) hdr; if (sch->first) { suh = (struct sndcp_udata_hdr *) (hdr + 1 + sizeof(struct sndcp_common_hdr)); } else suh = (struct sndcp_udata_hdr *) (hdr + sizeof(struct sndcp_common_hdr)); data = (uint8_t *)suh + sizeof(struct sndcp_udata_hdr); npdu_num = (suh->npdu_high << 8) | suh->npdu_low; LOGP(DSNDCP, LOGL_DEBUG, "TLLI=0x%08x NSAPI=%u: Input PDU %u Segment %u " "Length %u %s %s\n", sne->lle->llme->tlli, sne->nsapi, npdu_num, suh->seg_nr, len, sch->first ? "F " : "", sch->more ? "M" : ""); if (sch->first) { /* first segment of a new packet. Discard all leftover fragments of * previous packet */ if (!llist_empty(&sne->defrag.frag_list)) { struct defrag_queue_entry *dqe, *dqe2; LOGP(DSNDCP, LOGL_INFO, "TLLI=0x%08x NSAPI=%u: Dropping " "SN-PDU %u due to insufficient segments (%04x)\n", sne->lle->llme->tlli, sne->nsapi, sne->defrag.npdu, sne->defrag.seg_have); llist_for_each_entry_safe(dqe, dqe2, &sne->defrag.frag_list, list) { llist_del(&dqe->list); talloc_free(dqe); } } /* store the currently de-fragmented PDU number */ sne->defrag.npdu = npdu_num; /* Re-set fragmentation state */ sne->defrag.no_more = sne->defrag.highest_seg = sne->defrag.seg_have = 0; sne->defrag.tot_len = 0; /* FIXME: (re)start timer */ } if (sne->defrag.npdu != npdu_num) { LOGP(DSNDCP, LOGL_INFO, "Segment for different SN-PDU " "(%u != %u)\n", npdu_num, sne->defrag.npdu); /* FIXME */ } /* FIXME: check if seg_nr already exists */ /* make sure to subtract length of SNDCP header from 'len' */ rc = defrag_enqueue(sne, suh->seg_nr, data, len - (data - hdr)); if (rc < 0) return rc; if (!sch->more) { /* this is suppsed to be the last segment of the N-PDU, but it * might well be not the last to arrive */ sne->defrag.no_more = 1; } if (sne->defrag.no_more) { /* we have already received the last segment before, let's check * if all the previous segments exist */ if (defrag_have_all_segments(sne)) return defrag_segments(sne); } return 0; } static struct gprs_sndcp_entity *gprs_sndcp_entity_by_lle(const struct gprs_llc_lle *lle, uint8_t nsapi) { struct gprs_sndcp_entity *sne; llist_for_each_entry(sne, &gprs_sndcp_entities, list) { if (sne->lle == lle && sne->nsapi == nsapi) return sne; } return NULL; } static struct gprs_sndcp_entity *gprs_sndcp_entity_alloc(struct gprs_llc_lle *lle, uint8_t nsapi) { struct gprs_sndcp_entity *sne; sne = talloc_zero(tall_sndcp_ctx, struct gprs_sndcp_entity); if (!sne) return NULL; sne->lle = lle; sne->nsapi = nsapi; sne->defrag.timer.data = sne; //sne->fqueue.timer.cb = FIXME; sne->rx_state = SNDCP_RX_S_FIRST; INIT_LLIST_HEAD(&sne->defrag.frag_list); llist_add(&sne->list, &gprs_sndcp_entities); return sne; } /* Entry point for the SNSM-ACTIVATE.indication */ int sndcp_sm_activate_ind(struct gprs_llc_lle *lle, uint8_t nsapi) { LOGP(DSNDCP, LOGL_INFO, "SNSM-ACTIVATE.ind (lle=%p TLLI=%08x, " "SAPI=%u, NSAPI=%u)\n", lle, lle->llme->tlli, lle->sapi, nsapi); if (gprs_sndcp_entity_by_lle(lle, nsapi)) { LOGP(DSNDCP, LOGL_ERROR, "Trying to ACTIVATE " "already-existing entity (TLLI=%08x, NSAPI=%u)\n", lle->llme->tlli, nsapi); return -EEXIST; } if (!gprs_sndcp_entity_alloc(lle, nsapi)) { LOGP(DSNDCP, LOGL_ERROR, "Out of memory during ACTIVATE\n"); return -ENOMEM; } return 0; } /* Entry point for the SNSM-DEACTIVATE.indication */ int sndcp_sm_deactivate_ind(struct gprs_llc_lle *lle, uint8_t nsapi) { struct gprs_sndcp_entity *sne; LOGP(DSNDCP, LOGL_INFO, "SNSM-DEACTIVATE.ind (lle=%p, TLLI=%08x, " "SAPI=%u, NSAPI=%u)\n", lle, lle->llme->tlli, lle->sapi, nsapi); sne = gprs_sndcp_entity_by_lle(lle, nsapi); if (!sne) { LOGP(DSNDCP, LOGL_ERROR, "SNSM-DEACTIVATE.ind for non-" "existing TLLI=%08x SAPI=%u NSAPI=%u\n", lle->llme->tlli, lle->sapi, nsapi); return -ENOENT; } llist_del(&sne->list); /* frag queue entries are hierarchically allocated, so no need to * free them explicitly here */ talloc_free(sne); return 0; } /* Fragmenter state */ struct sndcp_frag_state { uint8_t frag_nr; struct msgb *msg; /* original message */ uint8_t *next_byte; /* first byte of next fragment */ struct gprs_sndcp_entity *sne; void *mmcontext; }; /* returns '1' if there are more fragments to send, '0' if none */ static int sndcp_send_ud_frag(struct sndcp_frag_state *fs) { struct gprs_sndcp_entity *sne = fs->sne; struct gprs_llc_lle *lle = sne->lle; struct sndcp_common_hdr *sch; struct sndcp_comp_hdr *scomph; struct sndcp_udata_hdr *suh; struct msgb *fmsg; unsigned int max_payload_len; unsigned int len; uint8_t *data; int rc, more; fmsg = msgb_alloc_headroom(fs->sne->lle->params.n201_u+256, 128, "SNDCP Frag"); if (!fmsg) { msgb_free(fs->msg); return -ENOMEM; } /* make sure lower layers route the fragment like the original */ msgb_tlli(fmsg) = msgb_tlli(fs->msg); msgb_bvci(fmsg) = msgb_bvci(fs->msg); msgb_nsei(fmsg) = msgb_nsei(fs->msg); /* prepend common SNDCP header */ sch = (struct sndcp_common_hdr *) msgb_put(fmsg, sizeof(*sch)); sch->nsapi = sne->nsapi; /* Set FIRST bit if we are the first fragment in a series */ if (fs->frag_nr == 0) sch->first = 1; sch->type = 1; /* append the compression header for first fragment */ if (sch->first) { scomph = (struct sndcp_comp_hdr *) msgb_put(fmsg, sizeof(*scomph)); scomph->pcomp = 0; scomph->dcomp = 0; } /* append the user-data header */ suh = (struct sndcp_udata_hdr *) msgb_put(fmsg, sizeof(*suh)); suh->npdu_low = sne->tx_npdu_nr & 0xff; suh->npdu_high = (sne->tx_npdu_nr >> 8) & 0xf; suh->seg_nr = fs->frag_nr % 0xf; /* calculate remaining length to be sent */ len = (fs->msg->data + fs->msg->len) - fs->next_byte; /* how much payload can we actually send via LLC? */ max_payload_len = lle->params.n201_u - (sizeof(*sch) + sizeof(*suh)); if (sch->first) max_payload_len -= sizeof(*scomph); /* check if we're exceeding the max */ if (len > max_payload_len) len = max_payload_len; /* copy the actual fragment data into our fmsg */ data = msgb_put(fmsg, len); memcpy(data, fs->next_byte, len); /* Increment fragment number and data pointer to next fragment */ fs->frag_nr++; fs->next_byte += len; /* determine if we have more fragemnts to send */ if ((fs->msg->data + fs->msg->len) <= fs->next_byte) more = 0; else more = 1; /* set the MORE bit of the SNDCP header accordingly */ sch->more = more; rc = gprs_llc_tx_ui(fmsg, lle->sapi, 0, fs->mmcontext); /* abort in case of error, do not advance frag_nr / next_byte */ if (rc < 0) { msgb_free(fs->msg); return rc; } if (!more) { /* we've sent all fragments */ msgb_free(fs->msg); memset(fs, 0, sizeof(*fs)); /* increment NPDU number for next frame */ sne->tx_npdu_nr = (sne->tx_npdu_nr + 1) % 0xfff; return 0; } /* default: more fragments to send */ return 1; } /* Request transmission of a SN-PDU over specified LLC Entity + SAPI */ int sndcp_unitdata_req(struct msgb *msg, struct gprs_llc_lle *lle, uint8_t nsapi, void *mmcontext) { struct gprs_sndcp_entity *sne; struct sndcp_common_hdr *sch; struct sndcp_comp_hdr *scomph; struct sndcp_udata_hdr *suh; struct sndcp_frag_state fs; /* Identifiers from UP: (TLLI, SAPI) + (BVCI, NSEI) */ sne = gprs_sndcp_entity_by_lle(lle, nsapi); if (!sne) { LOGP(DSNDCP, LOGL_ERROR, "Cannot find SNDCP Entity\n"); msgb_free(msg); return -EIO; } /* Check if we need to fragment this N-PDU into multiple SN-PDUs */ if (msg->len > lle->params.n201_u - (sizeof(*sch) + sizeof(*suh) + sizeof(*scomph))) { /* initialize the fragmenter state */ fs.msg = msg; fs.frag_nr = 0; fs.next_byte = msg->data; fs.sne = sne; fs.mmcontext = mmcontext; /* call function to generate and send fragments until all * of the N-PDU has been sent */ while (1) { int rc = sndcp_send_ud_frag(&fs); if (rc == 0) return 0; if (rc < 0) return rc; } /* not reached */ return 0; } /* this is the non-fragmenting case where we only build 1 SN-PDU */ /* prepend the user-data header */ suh = (struct sndcp_udata_hdr *) msgb_push(msg, sizeof(*suh)); suh->npdu_low = sne->tx_npdu_nr & 0xff; suh->npdu_high = (sne->tx_npdu_nr >> 8) & 0xf; suh->seg_nr = 0; sne->tx_npdu_nr = (sne->tx_npdu_nr + 1) % 0xfff; scomph = (struct sndcp_comp_hdr *) msgb_push(msg, sizeof(*scomph)); scomph->pcomp = 0; scomph->dcomp = 0; /* prepend common SNDCP header */ sch = (struct sndcp_common_hdr *) msgb_push(msg, sizeof(*sch)); sch->first = 1; sch->type = 1; sch->nsapi = nsapi; return gprs_llc_tx_ui(msg, lle->sapi, 0, mmcontext); } /* Section 5.1.2.17 LL-UNITDATA.ind */ int sndcp_llunitdata_ind(struct msgb *msg, struct gprs_llc_lle *lle, uint8_t *hdr, uint16_t len) { struct gprs_sndcp_entity *sne; struct sndcp_common_hdr *sch = (struct sndcp_common_hdr *)hdr; struct sndcp_comp_hdr *scomph = NULL; struct sndcp_udata_hdr *suh; uint8_t *npdu; uint16_t npdu_num __attribute__((unused)); int npdu_len; sch = (struct sndcp_common_hdr *) hdr; if (sch->first) { scomph = (struct sndcp_comp_hdr *) (hdr + 1); suh = (struct sndcp_udata_hdr *) (hdr + 1 + sizeof(struct sndcp_common_hdr)); } else suh = (struct sndcp_udata_hdr *) (hdr + sizeof(struct sndcp_common_hdr)); if (sch->type == 0) { LOGP(DSNDCP, LOGL_ERROR, "SN-DATA PDU at unitdata_ind() function\n"); return -EINVAL; } if (len < sizeof(*sch) + sizeof(*suh)) { LOGP(DSNDCP, LOGL_ERROR, "SN-UNITDATA PDU too short (%u)\n", len); return -EIO; } sne = gprs_sndcp_entity_by_lle(lle, sch->nsapi); if (!sne) { LOGP(DSNDCP, LOGL_ERROR, "Message for non-existing SNDCP Entity " "(lle=%p, TLLI=%08x, SAPI=%u, NSAPI=%u)\n", lle, lle->llme->tlli, lle->sapi, sch->nsapi); return -EIO; } /* FIXME: move this RA_ID up to the LLME or even higher */ bssgp_parse_cell_id(&sne->ra_id, msgb_bcid(msg)); /* any non-first segment is by definition something to defragment * as is any segment that tells us there are more segments */ if (!sch->first || sch->more) return defrag_input(sne, msg, hdr, len); if (scomph && (scomph->pcomp || scomph->dcomp)) { LOGP(DSNDCP, LOGL_ERROR, "We don't support compression yet\n"); return -EIO; } npdu_num = (suh->npdu_high << 8) | suh->npdu_low; npdu = (uint8_t *)suh + sizeof(*suh); npdu_len = (msg->data + msg->len) - npdu; if (npdu_len <= 0) { LOGP(DSNDCP, LOGL_ERROR, "Short SNDCP N-PDU: %d\n", npdu_len); return -EIO; } /* actually send the N-PDU to the SGSN core code, which then * hands it off to the correct GTP tunnel + GGSN via gtp_data_req() */ return sgsn_rx_sndcp_ud_ind(&sne->ra_id, lle->llme->tlli, sne->nsapi, msg, npdu_len, npdu); } #if 0 /* Section 5.1.2.1 LL-RESET.ind */ static int sndcp_ll_reset_ind(struct gprs_sndcp_entity *se) { /* treat all outstanding SNDCP-LLC request type primitives as not sent */ /* reset all SNDCP XID parameters to default values */ LOGP(DSNDCP, LOGL_NOTICE, "not implemented.\n"); return 0; } static int sndcp_ll_status_ind() { /* inform the SM sub-layer by means of SNSM-STATUS.req */ LOGP(DSNDCP, LOGL_NOTICE, "not implemented.\n"); return 0; } static struct sndcp_state_list {{ uint32_t states; unsigned int type; int (*rout)(struct gprs_sndcp_entity *se, struct msgb *msg); } sndcp_state_list[] = { { ALL_STATES, LL_RESET_IND, sndcp_ll_reset_ind }, { ALL_STATES, LL_ESTABLISH_IND, sndcp_ll_est_ind }, { SBIT(SNDCP_S_EST_RQD), LL_ESTABLISH_RESP, sndcp_ll_est_ind }, { SBIT(SNDCP_S_EST_RQD), LL_ESTABLISH_CONF, sndcp_ll_est_conf }, { SBIT(SNDCP_S_ }; static int sndcp_rx_llc_prim() { case LL_ESTABLISH_REQ: case LL_RELEASE_REQ: case LL_XID_REQ: case LL_DATA_REQ: LL_UNITDATA_REQ, /* TLLI, SN-PDU, Ref, QoS, Radio Prio, Ciph */ switch (prim) { case LL_RESET_IND: case LL_ESTABLISH_IND: case LL_ESTABLISH_RESP: case LL_ESTABLISH_CONF: case LL_RELEASE_IND: case LL_RELEASE_CONF: case LL_XID_IND: case LL_XID_RESP: case LL_XID_CONF: case LL_DATA_IND: case LL_DATA_CONF: case LL_UNITDATA_IND: case LL_STATUS_IND: } #endif openbsc-0.15.0/openbsc/src/gprs/gprs_sndcp.h000066400000000000000000000025011265565154000207430ustar00rootroot00000000000000#ifndef _INT_SNDCP_H #define _INT_SNDCP_H #include #include /* 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; }; /* 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; #endif /* INT_SNDCP_H */ openbsc-0.15.0/openbsc/src/gprs/gprs_sndcp_vty.c000066400000000000000000000037741265565154000216550ustar00rootroot00000000000000/* VTY interface for our GPRS SNDCP implementation */ /* (C) 2010 by Harald Welte * * 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gprs_sndcp.h" #include #include 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; } openbsc-0.15.0/openbsc/src/gprs/gprs_subscriber.c000066400000000000000000000535221265565154000220030ustar00rootroot00000000000000/* 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 . * */ #include #include #include #include #include #include #include #include #include #include #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; static int gsup_read_cb(struct gprs_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 = gprs_gsup_client_create( addr_str, sgi->cfg.gsup_server_port, &gsup_read_cb); if (!sgi->gsup_client) return -1; return 1; } static int gsup_read_cb(struct gprs_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 gsm_subscriber *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 gsm_subscriber *gprs_subscr_get_or_create(const char *imsi) { struct gsm_subscriber *subscr; subscr = subscr_get_or_create(NULL, imsi); if (!subscr) return NULL; if (!subscr->sgsn_data) subscr->sgsn_data = sgsn_subscriber_data_alloc(subscr); return subscr; } struct gsm_subscriber *gprs_subscr_get_by_imsi(const char *imsi) { return subscr_active_by_imsi(NULL, imsi); } void gprs_subscr_cleanup(struct gsm_subscriber *subscr) { if (subscr->sgsn_data->mm) { 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 gsm_subscriber *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 gsm_subscriber *subscr, struct gprs_gsup_message *gsup_msg) { struct msgb *msg = gprs_gsup_msgb_alloc(); if (strlen(gsup_msg->imsi) == 0 && subscr) strncpy(gsup_msg->imsi, subscr->imsi, sizeof(gsup_msg->imsi) - 1); gprs_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 gprs_gsup_client_send(sgsn->gsup_client, msg); } static int gprs_subscr_tx_gsup_error_reply(struct gsm_subscriber *subscr, struct gprs_gsup_message *gsup_orig, enum gsm48_gmm_cause cause) { struct gprs_gsup_message gsup_reply = {0}; strncpy(gsup_reply.imsi, gsup_orig->imsi, sizeof(gsup_reply.imsi) - 1); gsup_reply.cause = cause; gsup_reply.message_type = GPRS_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 gsm_subscriber *subscr, struct gprs_gsup_message *gsup_msg) { unsigned idx; struct sgsn_subscriber_data *sdata = subscr->sgsn_data; LOGGSUBSCRP(LOGL_INFO, subscr, "Got SendAuthenticationInfoResult, num_auth_tuples = %zu\n", gsup_msg->num_auth_tuples); if (gsup_msg->num_auth_tuples > 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_tuples; idx++) { size_t key_seq = gsup_msg->auth_tuples[idx].key_seq; 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] = gsup_msg->auth_tuples[idx]; } 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 gsm_subscriber *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 gsm_subscriber *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 gsm_subscriber *subscr, struct gprs_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_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 gprs_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; gprs_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; } } static int gprs_subscr_handle_gsup_upd_loc_res(struct gsm_subscriber *subscr, struct gprs_gsup_message *gsup_msg) { 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 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 gsm_subscriber *subscr, struct gprs_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 gsm_subscriber *subscr, struct gprs_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 gprs_gsup_message *gsup_msg) { if (GPRS_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 gsm_subscriber *subscr, struct gprs_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 gsm_subscriber *subscr, struct gprs_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 gsm_subscriber *subscr, struct gprs_gsup_message *gsup_msg) { struct gprs_gsup_message gsup_reply = {0}; int is_update_procedure = !gsup_msg->cancel_type || gsup_msg->cancel_type == GPRS_GSUP_CANCEL_TYPE_UPDATE; LOGGSUBSCRP(LOGL_INFO, subscr, "Cancelling MS subscriber (%s)\n", is_update_procedure ? "update procedure" : "subscription withdraw"); gsup_reply.message_type = GPRS_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 gprs_gsup_message *gsup_msg) { if (GPRS_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 (GPRS_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 gprs_gsup_message gsup_msg = {0}; struct gsm_subscriber *subscr; rc = gprs_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 (GPRS_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 && GPRS_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 GPRS_GSUP_MSGT_PURGE_MS_RESULT: case GPRS_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 of type 0x%02x\n", gsup_msg.message_type); switch (gsup_msg.message_type) { case GPRS_GSUP_MSGT_LOCATION_CANCEL_REQUEST: rc = gprs_subscr_handle_loc_cancel_req(subscr, &gsup_msg); break; case GPRS_GSUP_MSGT_SEND_AUTH_INFO_RESULT: rc = gprs_subscr_handle_gsup_auth_res(subscr, &gsup_msg); break; case GPRS_GSUP_MSGT_SEND_AUTH_INFO_ERROR: rc = gprs_subscr_handle_gsup_auth_err(subscr, &gsup_msg); break; case GPRS_GSUP_MSGT_UPDATE_LOCATION_RESULT: rc = gprs_subscr_handle_gsup_upd_loc_res(subscr, &gsup_msg); break; case GPRS_GSUP_MSGT_UPDATE_LOCATION_ERROR: rc = gprs_subscr_handle_gsup_upd_loc_err(subscr, &gsup_msg); break; case GPRS_GSUP_MSGT_PURGE_MS_ERROR: rc = gprs_subscr_handle_gsup_purge_err(subscr, &gsup_msg); break; case GPRS_GSUP_MSGT_PURGE_MS_RESULT: rc = gprs_subscr_handle_gsup_purge_res(subscr, &gsup_msg); break; case GPRS_GSUP_MSGT_INSERT_DATA_REQUEST: case GPRS_GSUP_MSGT_DELETE_DATA_REQUEST: LOGGSUBSCRP(LOGL_ERROR, subscr, "Rx GSUP message type %d not yet implemented\n", 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; default: LOGGSUBSCRP(LOGL_ERROR, subscr, "Rx GSUP message type %d not valid at SGSN\n", gsup_msg.message_type); if (GPRS_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; }; subscr_put(subscr); return rc; } int gprs_subscr_purge(struct gsm_subscriber *subscr) { struct sgsn_subscriber_data *sdata = subscr->sgsn_data; struct gprs_gsup_message gsup_msg = {0}; LOGGSUBSCRP(LOGL_INFO, subscr, "purging MS subscriber\n"); gsup_msg.message_type = GPRS_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); } int gprs_subscr_query_auth_info(struct gsm_subscriber *subscr) { struct gprs_gsup_message gsup_msg = {0}; LOGGSUBSCRP(LOGL_INFO, subscr, "subscriber auth info is not available\n"); gsup_msg.message_type = GPRS_GSUP_MSGT_SEND_AUTH_INFO_REQUEST; return gprs_subscr_tx_gsup_message(subscr, &gsup_msg); } int gprs_subscr_location_update(struct gsm_subscriber *subscr) { struct gprs_gsup_message gsup_msg = {0}; LOGGSUBSCRP(LOGL_INFO, subscr, "subscriber data is not available\n"); gsup_msg.message_type = GPRS_GSUP_MSGT_UPDATE_LOCATION_REQUEST; return gprs_subscr_tx_gsup_message(subscr, &gsup_msg); } void gprs_subscr_update(struct gsm_subscriber *subscr) { LOGGSUBSCRP(LOGL_DEBUG, subscr, "Updating subscriber data\n"); subscr->flags &= ~GPRS_SUBSCRIBER_UPDATE_LOCATION_PENDING; subscr->flags &= ~GSM_SUBSCRIBER_FIRST_CONTACT; if (subscr->sgsn_data->mm) sgsn_update_subscriber_data(subscr->sgsn_data->mm); } void gprs_subscr_update_auth_info(struct gsm_subscriber *subscr) { LOGGSUBSCRP(LOGL_DEBUG, subscr, "Updating subscriber authentication info\n"); subscr->flags &= ~GPRS_SUBSCRIBER_UPDATE_AUTH_INFO_PENDING; subscr->flags &= ~GSM_SUBSCRIBER_FIRST_CONTACT; if (subscr->sgsn_data->mm) sgsn_update_subscriber_data(subscr->sgsn_data->mm); } struct gsm_subscriber *gprs_subscr_get_or_create_by_mmctx(struct sgsn_mm_ctx *mmctx) { struct gsm_subscriber *subscr = NULL; if (mmctx->subscr) return 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 |= GSM_SUBSCRIBER_FIRST_CONTACT; subscr->flags &= ~GPRS_SUBSCRIBER_ENABLE_PURGE; } if (strcpy(subscr->equipment.imei, mmctx->imei) != 0) { strncpy(subscr->equipment.imei, mmctx->imei, GSM_IMEI_LENGTH-1); subscr->equipment.imei[GSM_IMEI_LENGTH-1] = 0; } if (subscr->lac != mmctx->ra.lac) subscr->lac = mmctx->ra.lac; subscr->sgsn_data->mm = mmctx; mmctx->subscr = subscr_get(subscr); return subscr; } int gprs_subscr_request_update_location(struct sgsn_mm_ctx *mmctx) { struct gsm_subscriber *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); subscr_put(subscr); return rc; } int gprs_subscr_request_auth_info(struct sgsn_mm_ctx *mmctx) { struct gsm_subscriber *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); subscr_put(subscr); return rc; } openbsc-0.15.0/openbsc/src/gprs/gprs_utils.c000066400000000000000000000203411265565154000207710ustar00rootroot00000000000000/* GPRS utility functions */ /* (C) 2010 by Harald Welte * (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 . * */ #include #include #include #include #include #include /* 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; } /* TODO: Move these conversion functions to a utils file. */ /** * out_str needs to have rest_chars amount of bytes or 1 whatever is bigger. */ char * gprs_apn_to_str(char *out_str, const uint8_t *apn_enc, size_t rest_chars) { char *str = out_str; while (rest_chars > 0 && apn_enc[0]) { size_t label_size = apn_enc[0]; if (label_size + 1 > rest_chars) return NULL; memmove(str, apn_enc + 1, label_size); str += label_size; rest_chars -= label_size + 1; apn_enc += label_size + 1; if (rest_chars) *(str++) = '.'; } str[0] = '\0'; return out_str; } 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); } /* TODO: Move shift functions to libosmocore */ int gprs_shift_v_fixed(uint8_t **data, size_t *data_len, size_t len, uint8_t **value) { if (len > *data_len) goto fail; if (value) *value = *data; *data += len; *data_len -= len; return len; fail: *data += *data_len; *data_len = 0; return -1; } int gprs_match_tv_fixed(uint8_t **data, size_t *data_len, uint8_t tag, size_t len, uint8_t **value) { size_t ie_len; if (*data_len == 0) goto fail; if ((*data)[0] != tag) return 0; if (len > *data_len - 1) goto fail; if (value) *value = *data + 1; ie_len = len + 1; *data += ie_len; *data_len -= ie_len; return ie_len; fail: *data += *data_len; *data_len = 0; return -1; } int gprs_match_tlv(uint8_t **data, size_t *data_len, uint8_t expected_tag, uint8_t **value, size_t *value_len) { int rc; uint8_t tag; uint8_t *old_data = *data; size_t old_data_len = *data_len; rc = gprs_shift_tlv(data, data_len, &tag, value, value_len); if (rc > 0 && tag != expected_tag) { *data = old_data; *data_len = old_data_len; return 0; } return rc; } int gprs_shift_tlv(uint8_t **data, size_t *data_len, uint8_t *tag, uint8_t **value, size_t *value_len) { size_t len; size_t ie_len; if (*data_len < 2) goto fail; len = (*data)[1]; if (len > *data_len - 2) goto fail; if (tag) *tag = (*data)[0]; if (value) *value = *data + 2; if (value_len) *value_len = len; ie_len = len + 2; *data += ie_len; *data_len -= ie_len; return ie_len; fail: *data += *data_len; *data_len = 0; return -1; } int gprs_shift_lv(uint8_t **data, size_t *data_len, uint8_t **value, size_t *value_len) { size_t len; size_t ie_len; if (*data_len < 1) goto fail; len = (*data)[0]; if (len > *data_len - 1) goto fail; if (value) *value = *data + 1; if (value_len) *value_len = len; ie_len = len + 1; *data += ie_len; *data_len -= ie_len; return ie_len; fail: *data += *data_len; *data_len = 0; return -1; } openbsc-0.15.0/openbsc/src/gprs/gsm_04_08_gprs.c000066400000000000000000000127571265565154000212450ustar00rootroot00000000000000/* (C) 2009-2010 by Harald Welte * (C) 2010 by On-Waves * (C) 2014-2015 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 . * */ #include #include /* Protocol related stuff, should go into libosmocore */ /* 10.5.5.14 GPRS MM Cause / Table 10.5.147 */ const struct value_string gsm48_gmm_cause_names_[] = { { GMM_CAUSE_IMSI_UNKNOWN, "IMSI unknown in HLR" }, { GMM_CAUSE_ILLEGAL_MS, "Illegal MS" }, { GMM_CAUSE_ILLEGAL_ME, "Illegal ME" }, { GMM_CAUSE_GPRS_NOTALLOWED, "GPRS services not allowed" }, { GMM_CAUSE_GPRS_OTHER_NOTALLOWED, "GPRS services and non-GPRS services not allowed" }, { GMM_CAUSE_MS_ID_NOT_DERIVED, "MS identity cannot be derived by the network" }, { GMM_CAUSE_IMPL_DETACHED, "Implicitly detached" }, { GMM_CAUSE_PLMN_NOTALLOWED, "PLMN not allowed" }, { GMM_CAUSE_LA_NOTALLOWED, "Location Area not allowed" }, { GMM_CAUSE_ROAMING_NOTALLOWED, "Roaming not allowed in this location area" }, { GMM_CAUSE_NO_GPRS_PLMN, "GPRS services not allowed in this PLMN" }, { GMM_CAUSE_MSC_TEMP_NOTREACH, "MSC temporarily not reachable" }, { GMM_CAUSE_NET_FAIL, "Network failure" }, { GMM_CAUSE_CONGESTION, "Congestion" }, { GMM_CAUSE_SEM_INCORR_MSG, "Semantically incorrect message" }, { GMM_CAUSE_INV_MAND_INFO, "Invalid mandatory information" }, { GMM_CAUSE_MSGT_NOTEXIST_NOTIMPL, "Message type non-existant or not implemented" }, { GMM_CAUSE_MSGT_INCOMP_P_STATE, "Message type not compatible with protocol state" }, { GMM_CAUSE_IE_NOTEXIST_NOTIMPL, "Information element non-existent or not implemented" }, { GMM_CAUSE_COND_IE_ERR, "Conditional IE error" }, { GMM_CAUSE_MSG_INCOMP_P_STATE, "Message not compatible with protocol state " }, { GMM_CAUSE_PROTO_ERR_UNSPEC, "Protocol error, unspecified" }, { 0, NULL } }; const struct value_string *gsm48_gmm_cause_names = gsm48_gmm_cause_names_; /* 10.5.6.6 SM Cause / Table 10.5.157 */ const struct value_string gsm48_gsm_cause_names_[] = { { GSM_CAUSE_INSUFF_RSRC, "Insufficient resources" }, { GSM_CAUSE_MISSING_APN, "Missing or unknown APN" }, { GSM_CAUSE_UNKNOWN_PDP, "Unknown PDP address or PDP type" }, { GSM_CAUSE_AUTH_FAILED, "User Authentication failed" }, { GSM_CAUSE_ACT_REJ_GGSN, "Activation rejected by GGSN" }, { GSM_CAUSE_ACT_REJ_UNSPEC, "Activation rejected, unspecified" }, { GSM_CAUSE_SERV_OPT_NOTSUPP, "Service option not supported" }, { GSM_CAUSE_REQ_SERV_OPT_NOTSUB, "Requested service option not subscribed" }, { GSM_CAUSE_SERV_OPT_TEMP_OOO, "Service option temporarily out of order" }, { GSM_CAUSE_NSAPI_IN_USE, "NSAPI already used" }, { GSM_CAUSE_DEACT_REGULAR, "Regular deactivation" }, { GSM_CAUSE_QOS_NOT_ACCEPTED, "QoS not accepted" }, { GSM_CAUSE_NET_FAIL, "Network Failure" }, { GSM_CAUSE_REACT_RQD, "Reactivation required" }, { GSM_CAUSE_FEATURE_NOTSUPP, "Feature not supported " }, { GSM_CAUSE_INVALID_TRANS_ID, "Invalid transaction identifier" }, { GSM_CAUSE_SEM_INCORR_MSG, "Semantically incorrect message" }, { GSM_CAUSE_INV_MAND_INFO, "Invalid mandatory information" }, { GSM_CAUSE_MSGT_NOTEXIST_NOTIMPL, "Message type non-existant or not implemented" }, { GSM_CAUSE_MSGT_INCOMP_P_STATE, "Message type not compatible with protocol state" }, { GSM_CAUSE_IE_NOTEXIST_NOTIMPL, "Information element non-existent or not implemented" }, { GSM_CAUSE_COND_IE_ERR, "Conditional IE error" }, { GSM_CAUSE_MSG_INCOMP_P_STATE, "Message not compatible with protocol state " }, { GSM_CAUSE_PROTO_ERR_UNSPEC, "Protocol error, unspecified" }, { 0, NULL } }; const struct value_string *gsm48_gsm_cause_names = gsm48_gsm_cause_names_; /* 10.5.5.2 */ const struct value_string gprs_att_t_strs_[] = { { GPRS_ATT_T_ATTACH, "GPRS attach" }, { GPRS_ATT_T_ATT_WHILE_IMSI, "GPRS attach while IMSI attached" }, { GPRS_ATT_T_COMBINED, "Combined GPRS/IMSI attach" }, { 0, NULL } }; const struct value_string *gprs_att_t_strs = gprs_att_t_strs_; const struct value_string gprs_upd_t_strs_[] = { { GPRS_UPD_T_RA, "RA updating" }, { GPRS_UPD_T_RA_LA, "combined RA/LA updating" }, { GPRS_UPD_T_RA_LA_IMSI_ATT, "combined RA/LA updating + IMSI attach" }, { GPRS_UPD_T_PERIODIC, "periodic updating" }, { 0, NULL } }; const struct value_string *gprs_upd_t_strs = gprs_upd_t_strs_; /* 10.5.5.5 */ const struct value_string gprs_det_t_mo_strs_[] = { { GPRS_DET_T_MO_GPRS, "GPRS detach" }, { GPRS_DET_T_MO_IMSI, "IMSI detach" }, { GPRS_DET_T_MO_COMBINED, "Combined GPRS/IMSI detach" }, { 0, NULL } }; const struct value_string *gprs_det_t_mo_strs = gprs_det_t_mo_strs_; const struct value_string gprs_det_t_mt_strs_[] = { { GPRS_DET_T_MT_REATT_REQ, "re-attach required" }, { GPRS_DET_T_MT_REATT_NOTREQ, "re-attach not required" }, { GPRS_DET_T_MT_IMSI, "IMSI detach (after VLR failure)" }, { 0, NULL } }; const struct value_string *gprs_det_t_mt_strs = gprs_det_t_mt_strs_; openbsc-0.15.0/openbsc/src/gprs/osmo_sgsn.cfg000066400000000000000000000007221265565154000211230ustar00rootroot00000000000000! ! 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 openbsc-0.15.0/openbsc/src/gprs/sgsn_ares.c000066400000000000000000000107531265565154000205700ustar00rootroot00000000000000/* 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 . * */ #include #include #include 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) { sgsn->ares_timer.cb = ares_timeout_cb; sgsn->ares_timer.data = sgsn; 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_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); openbsc-0.15.0/openbsc/src/gprs/sgsn_auth.c000066400000000000000000000173341265565154000206010ustar00rootroot00000000000000/* MS authorization and subscriber data handling */ /* (C) 2009-2010 by Harald Welte * * 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 . * */ #include #include #include #include #include #include const struct value_string auth_state_names[] = { { SGSN_AUTH_ACCEPTED, "accepted"}, { SGSN_AUTH_REJECTED, "rejected"}, { SGSN_AUTH_UNKNOWN, "unknown"}, { 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; strncpy(acl->imsi, imsi, sizeof(acl->imsi) - 1); 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 gsm_subscriber *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); 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); 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 gsm_subscriber *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; } openbsc-0.15.0/openbsc/src/gprs/sgsn_cdr.c000066400000000000000000000151011265565154000203760ustar00rootroot00000000000000/* 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 . * */ #include #include #include #include #include #include #include #include #include #include #include /* 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->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) { gprs_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->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); signal_data->pdp->cdr_timer.cb = cdr_pdp_timeout; signal_data->pdp->cdr_timer.data = 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; } openbsc-0.15.0/openbsc/src/gprs/sgsn_ctrl.c000066400000000000000000000041331265565154000205750ustar00rootroot00000000000000/* 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 . * */ #include #include #include #include #include #include #include extern vector ctrl_node_vec; static int verify_subscriber_list(struct ctrl_cmd *cmd, const char *v, void *d) { return 1; } static int set_subscriber_list(struct ctrl_cmd *cmd, void *d) { cmd->reply = "Get only attribute"; return CTRL_CMD_ERROR; } 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(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, uint16_t port) { return ctrl_interface_setup(net, port, NULL); } openbsc-0.15.0/openbsc/src/gprs/sgsn_libgtp.c000066400000000000000000000502611265565154000211150ustar00rootroot00000000000000/* 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 * (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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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) { pdp->msisdn.l = mmctx->subscr->sgsn_data->msisdn_len; if (pdp->msisdn.l > sizeof(pdp->msisdn.v)) pdp->msisdn.l = sizeof(pdp->msisdn.l); memcpy(pdp->msisdn.v, mmctx->subscr->sgsn_data->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); } /* 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 */ 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)); /* Assume we are a GERAN system */ pdp->rattype.l = 1; pdp->rattype.v[0] = 2; pdp->rattype_given = 1; /* Include RAI and ULI all the time */ pdp->rai_given = 1; pdp->rai.l = 6; raid = mmctx->ra; raid.lac = 0xFFFE; raid.rac = 0xFF; gsm48_construct_ra(pdp->rai.v, &raid); 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->cell_id); /* 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 } }; /* 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_signal_data sig_data; 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; } /* Activate the SNDCP layer */ sndcp_sm_activate_ind(&pctx->mm->llme->lle[pctx->sapi], pctx->nsapi); /* 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 */ return gsm48_tx_gsm_act_pdp_acc(pctx); 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; } /* 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) { /* Deactivate the SNDCP layer */ sndcp_sm_deactivate_ind(&pctx->mm->llme->lle[pctx->sapi], pctx->nsapi); /* 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; DEBUGP(DGPRS, "GTP DATA IND from GGSN, length=%u\n", len); 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 (imsi=%s) without MM context!\n", mm->imsi); return -EIO; } msg = msgb_alloc_headroom(len+256, 128, "GTP->SNDCP"); ud = msgb_put(msg, len); memcpy(ud, packet, len); msgb_tlli(msg) = mm->tlli; msgb_bvci(msg) = mm->bvci; msgb_nsei(msg) = mm->nsei; switch (mm->mm_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->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->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->tlli, mm->mm_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->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 */ sgi->gtp_timer.cb = sgsn_gtp_tmr_cb; sgi->gtp_timer.data = 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; } openbsc-0.15.0/openbsc/src/gprs/sgsn_main.c000066400000000000000000000227661265565154000205710ustar00rootroot00000000000000/* GPRS SGSN Implementation */ /* (C) 2010 by Harald Welte * (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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../../bscconfig.h" #define _GNU_SOURCE #include 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 2 or later \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, }, }; 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: 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 void *tall_msgb_ctx; 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'}, {"log-level", 1, 0, 'e'}, {NULL, 0, 0, 0} }; c = getopt_long(argc, argv, "hd:Dc:sTe:", 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 '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, }, }; 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 ctrl_handle *ctrl; struct gsm_network dummy_network; int rc; tall_bsc_ctx = talloc_named_const(NULL, 0, "osmo_sgsn"); tall_msgb_ctx = talloc_named_const(tall_bsc_ctx, 0, "msgb"); signal(SIGINT, &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(&gprs_log_info); sgsn_vty_init(); handle_options(argc, argv); rate_ctr_init(tall_bsc_ctx); rc = telnet_init(tall_bsc_ctx, &dummy_network, OSMO_VTY_PORT_SGSN); if (rc < 0) exit(1); ctrl = sgsn_controlif_setup(NULL, 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); } 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_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, &sgsn_inst.cfg); if (rc < 0) { LOGP(DGPRS, LOGL_FATAL, "Cannot parse config file\n"); exit(2); } 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); } } 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); } openbsc-0.15.0/openbsc/src/gprs/sgsn_vty.c000066400000000000000000000710511265565154000204560ustar00rootroot00000000000000/* * (C) 2010-2013 by Harald Welte * (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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static struct sgsn_config *g_cfg = NULL; const struct value_string sgsn_auth_pol_strs[] = { { SGSN_AUTH_POLICY_OPEN, "accept-all" }, { SGSN_AUTH_POLICY_CLOSED, "closed" }, { SGSN_AUTH_POLICY_ACL_ONLY, "acl-only" }, { SGSN_AUTH_POLICY_REMOTE, "remote" }, { 0, NULL } }; /* Section 11.2.2 / Table 11.3a GPRS Mobility management timers – MS side */ #define GSM0408_T3312_SECS (10*60) /* periodic RAU interval, default 54min */ /* Section 11.2.2 / Table 11.4 MM timers netwokr side */ #define GSM0408_T3322_SECS 6 /* DETACH_REQ -> DETACH_ACC */ #define GSM0408_T3350_SECS 6 /* waiting for ATT/RAU/TMSI COMPL */ #define GSM0408_T3360_SECS 6 /* waiting for AUTH/CIPH RESP */ #define GSM0408_T3370_SECS 6 /* waiting for ID RESP */ /* Section 11.2.2 / Table 11.4a MM timers netwokr side */ #define GSM0408_T3313_SECS 30 /* waiting for paging response */ #define GSM0408_T3314_SECS 44 /* force to STBY on expiry, Ready timer */ #define GSM0408_T3316_SECS 44 /* Section 11.3 / Table 11.2d Timers of Session Management - network side */ #define GSM0408_T3385_SECS 8 /* wait for ACT PDP CTX REQ */ #define GSM0408_T3386_SECS 8 /* wait for MODIFY PDP CTX ACK */ #define GSM0408_T3395_SECS 8 /* wait for DEACT PDP CTX ACK */ #define GSM0408_T3397_SECS 8 /* wait for DEACT AA PDP CTX ACK */ #define DECLARE_TIMER(number, doc) \ DEFUN(cfg_sgsn_T##number, \ cfg_sgsn_T##number##_cmd, \ "timer t" #number " <0-65535>", \ "Configure GPRS Timers\n" \ doc "Timer Value in seconds\n") \ { \ int value = atoi(argv[0]); \ \ if (value < 0 || value > 65535) { \ vty_out(vty, "Timer value %s out of range.%s", \ argv[0], VTY_NEWLINE); \ return CMD_WARNING; \ } \ \ g_cfg->timers.T##number = value; \ return CMD_SUCCESS; \ } DECLARE_TIMER(3312, "Periodic RA Update timer (s)") DECLARE_TIMER(3322, "Detach reqest -> accept timer (s)") DECLARE_TIMER(3350, "Waiting for ATT/RAU/TMSI_COMPL timer (s)") DECLARE_TIMER(3360, "Waiting for AUTH/CIPH response timer (s)") DECLARE_TIMER(3370, "Waiting for IDENTITY response timer (s)") DECLARE_TIMER(3313, "Waiting for paging response timer (s)") DECLARE_TIMER(3314, "Force to STANDBY on expiry timer (s)") DECLARE_TIMER(3316, "") DECLARE_TIMER(3385, "Wait for ACT PDP CTX REQ timer (s)") DECLARE_TIMER(3386, "Wait for MODIFY PDP CTX ACK timer (s)") DECLARE_TIMER(3395, "Wait for DEACT PDP CTX ACK timer (s)") DECLARE_TIMER(3397, "Wait for DEACT AA PDP CTX ACK timer (s)") #define GSM48_MAX_APN_LEN 102 /* 10.5.6.1 */ static char *gprs_apn2str(uint8_t *apn, unsigned int len) { static char apnbuf[GSM48_MAX_APN_LEN+1]; unsigned int i = 0; if (!apn) return ""; if (len > sizeof(apnbuf)-1) len = sizeof(apnbuf)-1; memcpy(apnbuf, apn, len); apnbuf[len] = '\0'; /* replace the domain name step sizes with dots */ while (i < len) { unsigned int step = apnbuf[i]; apnbuf[i] = '.'; i += step+1; } return apnbuf+1; } char *gprs_pdpaddr2str(uint8_t *pdpa, uint8_t len) { static char str[INET6_ADDRSTRLEN + 10]; if (!pdpa || len < 2) return "none"; switch (pdpa[0] & 0x0f) { case PDP_TYPE_ORG_IETF: switch (pdpa[1]) { case PDP_TYPE_N_IETF_IPv4: if (len < 2 + 4) break; strcpy(str, "IPv4 "); inet_ntop(AF_INET, pdpa+2, str+5, sizeof(str)-5); return str; case PDP_TYPE_N_IETF_IPv6: if (len < 2 + 8) break; strcpy(str, "IPv6 "); inet_ntop(AF_INET6, pdpa+2, str+5, sizeof(str)-5); return str; default: break; } break; case PDP_TYPE_ORG_ETSI: if (pdpa[1] == PDP_TYPE_N_ETSI_PPP) return "PPP"; break; default: break; } return "invalid"; } static struct cmd_node sgsn_node = { SGSN_NODE, "%s(config-sgsn)# ", 1, }; static int config_write_sgsn(struct vty *vty) { struct sgsn_ggsn_ctx *gctx; struct imsi_acl_entry *acl; struct apn_ctx *actx; struct ares_addr_node *server; vty_out(vty, "sgsn%s", VTY_NEWLINE); vty_out(vty, " gtp local-ip %s%s", inet_ntoa(g_cfg->gtp_listenaddr.sin_addr), VTY_NEWLINE); llist_for_each_entry(gctx, &sgsn_ggsn_ctxts, list) { if (gctx->id == UINT32_MAX) continue; vty_out(vty, " ggsn %u remote-ip %s%s", gctx->id, inet_ntoa(gctx->remote_addr), VTY_NEWLINE); vty_out(vty, " ggsn %u gtp-version %u%s", gctx->id, gctx->gtp_version, VTY_NEWLINE); } if (sgsn->cfg.dynamic_lookup) vty_out(vty, " ggsn dynamic%s", VTY_NEWLINE); for (server = sgsn->ares_servers; server; server = server->next) vty_out(vty, " grx-dns-add %s%s", inet_ntoa(server->addr.addr4), VTY_NEWLINE); vty_out(vty, " auth-policy %s%s", get_value_string(sgsn_auth_pol_strs, g_cfg->auth_policy), VTY_NEWLINE); if (g_cfg->gsup_server_addr.sin_addr.s_addr) vty_out(vty, " gsup remote-ip %s%s", inet_ntoa(g_cfg->gsup_server_addr.sin_addr), VTY_NEWLINE); if (g_cfg->gsup_server_port) vty_out(vty, " gsup remote-port %d%s", g_cfg->gsup_server_port, VTY_NEWLINE); llist_for_each_entry(acl, &g_cfg->imsi_acl, list) vty_out(vty, " imsi-acl add %s%s", acl->imsi, VTY_NEWLINE); if (llist_empty(&sgsn_apn_ctxts)) vty_out(vty, " ! apn * ggsn 0%s", VTY_NEWLINE); llist_for_each_entry(actx, &sgsn_apn_ctxts, list) { if (strlen(actx->imsi_prefix) > 0) vty_out(vty, " apn %s imsi-prefix %s ggsn %u%s", actx->name, actx->imsi_prefix, actx->ggsn->id, VTY_NEWLINE); else vty_out(vty, " apn %s ggsn %u%s", actx->name, actx->ggsn->id, VTY_NEWLINE); } if (g_cfg->cdr.filename) vty_out(vty, " cdr filename %s%s", g_cfg->cdr.filename, VTY_NEWLINE); else vty_out(vty, " no cdr filename%s", VTY_NEWLINE); vty_out(vty, " cdr interval %d%s", g_cfg->cdr.interval, VTY_NEWLINE); vty_out(vty, " timer t3312 %d%s", g_cfg->timers.T3312, VTY_NEWLINE); vty_out(vty, " timer t3322 %d%s", g_cfg->timers.T3322, VTY_NEWLINE); vty_out(vty, " timer t3350 %d%s", g_cfg->timers.T3350, VTY_NEWLINE); vty_out(vty, " timer t3360 %d%s", g_cfg->timers.T3360, VTY_NEWLINE); vty_out(vty, " timer t3370 %d%s", g_cfg->timers.T3370, VTY_NEWLINE); vty_out(vty, " timer t3313 %d%s", g_cfg->timers.T3313, VTY_NEWLINE); vty_out(vty, " timer t3314 %d%s", g_cfg->timers.T3314, VTY_NEWLINE); vty_out(vty, " timer t3316 %d%s", g_cfg->timers.T3316, VTY_NEWLINE); vty_out(vty, " timer t3385 %d%s", g_cfg->timers.T3385, VTY_NEWLINE); vty_out(vty, " timer t3386 %d%s", g_cfg->timers.T3386, VTY_NEWLINE); vty_out(vty, " timer t3395 %d%s", g_cfg->timers.T3395, VTY_NEWLINE); vty_out(vty, " timer t3397 %d%s", g_cfg->timers.T3397, VTY_NEWLINE); return CMD_SUCCESS; } #define SGSN_STR "Configure the SGSN\n" #define GGSN_STR "Configure the GGSN information\n" DEFUN(cfg_sgsn, cfg_sgsn_cmd, "sgsn", SGSN_STR) { vty->node = SGSN_NODE; return CMD_SUCCESS; } DEFUN(cfg_sgsn_bind_addr, cfg_sgsn_bind_addr_cmd, "gtp local-ip A.B.C.D", "GTP Parameters\n" "Set the IP address for the local GTP bind\n" "IPv4 Address\n") { inet_aton(argv[0], &g_cfg->gtp_listenaddr.sin_addr); return CMD_SUCCESS; } DEFUN(cfg_ggsn_remote_ip, cfg_ggsn_remote_ip_cmd, "ggsn <0-255> remote-ip A.B.C.D", GGSN_STR "GGSN Number\n" IP_STR "IPv4 Address\n") { uint32_t id = atoi(argv[0]); struct sgsn_ggsn_ctx *ggc = sgsn_ggsn_ctx_find_alloc(id); inet_aton(argv[1], &ggc->remote_addr); return CMD_SUCCESS; } #if 0 DEFUN(cfg_ggsn_remote_port, cfg_ggsn_remote_port_cmd, "ggsn <0-255> remote-port <0-65535>", "") { uint32_t id = atoi(argv[0]); struct sgsn_ggsn_ctx *ggc = sgsn_ggsn_ctx_find_alloc(id); uint16_t port = atoi(argv[1]); } #endif DEFUN(cfg_ggsn_gtp_version, cfg_ggsn_gtp_version_cmd, "ggsn <0-255> gtp-version (0|1)", GGSN_STR "GGSN Number\n" "GTP Version\n" "Version 0\n" "Version 1\n") { uint32_t id = atoi(argv[0]); struct sgsn_ggsn_ctx *ggc = sgsn_ggsn_ctx_find_alloc(id); if (atoi(argv[1])) ggc->gtp_version = 1; else ggc->gtp_version = 0; return CMD_SUCCESS; } DEFUN(cfg_ggsn_dynamic_lookup, cfg_ggsn_dynamic_lookup_cmd, "ggsn dynamic", GGSN_STR "Enable dynamic GRX based look-up (requires restart)\n") { sgsn->cfg.dynamic_lookup = 1; return CMD_SUCCESS; } 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; } #define APN_STR "Configure the information per APN\n" #define APN_GW_STR "The APN gateway name optionally prefixed by '*' (wildcard)\n" static int add_apn_ggsn_mapping(struct vty *vty, const char *apn_str, const char *imsi_prefix, int ggsn_id) { struct apn_ctx *actx; struct sgsn_ggsn_ctx *ggsn; ggsn = sgsn_ggsn_ctx_by_id(ggsn_id); if (ggsn == NULL) { vty_out(vty, "%% a GGSN with id %d has not been defined%s", ggsn_id, VTY_NEWLINE); return CMD_WARNING; } actx = sgsn_apn_ctx_find_alloc(apn_str, imsi_prefix); if (!actx) { vty_out(vty, "%% unable to create APN context for %s/%s%s", apn_str, imsi_prefix, VTY_NEWLINE); return CMD_WARNING; } actx->ggsn = ggsn; return CMD_SUCCESS; } DEFUN(cfg_apn_ggsn, cfg_apn_ggsn_cmd, "apn APNAME ggsn <0-255>", APN_STR APN_GW_STR "Select the GGSN to use when the APN gateway prefix matches\n" "The GGSN id") { return add_apn_ggsn_mapping(vty, argv[0], "", atoi(argv[1])); } DEFUN(cfg_apn_imsi_ggsn, cfg_apn_imsi_ggsn_cmd, "apn APNAME imsi-prefix IMSIPRE ggsn <0-255>", APN_STR APN_GW_STR "Restrict rule to a certain IMSI prefix\n" "An IMSI prefix\n" "Select the GGSN to use when APN gateway and IMSI prefix match\n" "The GGSN id") { return add_apn_ggsn_mapping(vty, argv[0], argv[1], atoi(argv[2])); } const struct value_string gprs_mm_st_strs[] = { { GMM_DEREGISTERED, "DEREGISTERED" }, { GMM_COMMON_PROC_INIT, "COMMON PROCEDURE (INIT)" }, { GMM_REGISTERED_NORMAL, "REGISTERED (NORMAL)" }, { GMM_REGISTERED_SUSPENDED, "REGISTERED (SUSPENDED)" }, { GMM_DEREGISTERED_INIT, "DEREGISTERED (INIT)" }, { 0, NULL } }; static void vty_dump_pdp(struct vty *vty, const char *pfx, struct sgsn_pdp_ctx *pdp) { const char *imsi = pdp->mm ? pdp->mm->imsi : "(detaching)"; vty_out(vty, "%sPDP Context IMSI: %s, SAPI: %u, NSAPI: %u%s", pfx, imsi, pdp->sapi, pdp->nsapi, VTY_NEWLINE); vty_out(vty, "%s APN: %s%s", pfx, gprs_apn2str(pdp->lib->apn_use.v, pdp->lib->apn_use.l), VTY_NEWLINE); vty_out(vty, "%s PDP Address: %s%s", pfx, gprs_pdpaddr2str(pdp->lib->eua.v, pdp->lib->eua.l), VTY_NEWLINE); vty_out_rate_ctr_group(vty, " ", pdp->ctrg); } static void vty_dump_mmctx(struct vty *vty, const char *pfx, struct sgsn_mm_ctx *mm, int pdp) { vty_out(vty, "%sMM Context for IMSI %s, IMEI %s, P-TMSI %08x%s", pfx, mm->imsi, mm->imei, mm->p_tmsi, VTY_NEWLINE); vty_out(vty, "%s MSISDN: %s, TLLI: %08x%s HLR: %s", pfx, mm->msisdn, mm->tlli, mm->hlr, VTY_NEWLINE); vty_out(vty, "%s MM State: %s, Routeing Area: %u-%u-%u-%u, " "Cell ID: %u%s", pfx, get_value_string(gprs_mm_st_strs, mm->mm_state), mm->ra.mcc, mm->ra.mnc, mm->ra.lac, mm->ra.rac, mm->cell_id, VTY_NEWLINE); vty_out_rate_ctr_group(vty, " ", mm->ctrg); if (pdp) { struct sgsn_pdp_ctx *pdp; llist_for_each_entry(pdp, &mm->pdp_list, list) vty_dump_pdp(vty, " ", pdp); } } DEFUN(show_sgsn, show_sgsn_cmd, "show sgsn", SHOW_STR "Display information about the SGSN") { if (sgsn->gsup_client) { struct ipa_client_conn *link = sgsn->gsup_client->link; vty_out(vty, " Remote authorization: %sconnected to %s:%d via GSUP%s", sgsn->gsup_client->is_connected ? "" : "not ", link->addr, link->port, VTY_NEWLINE); } /* FIXME: statistics */ return CMD_SUCCESS; } #define MMCTX_STR "MM Context\n" #define INCLUDE_PDP_STR "Include PDP Context Information\n" #if 0 DEFUN(show_mmctx_tlli, show_mmctx_tlli_cmd, "show mm-context tlli HEX [pdp]", SHOW_STR MMCTX_STR "Identify by TLLI\n" "TLLI\n" INCLUDE_PDP_STR) { uint32_t tlli; struct sgsn_mm_ctx *mm; tlli = strtoul(argv[0], NULL, 16); mm = sgsn_mm_ctx_by_tlli(tlli); if (!mm) { vty_out(vty, "No MM context for TLLI %08x%s", tlli, VTY_NEWLINE); return CMD_WARNING; } vty_dump_mmctx(vty, "", mm, argv[1] ? 1 : 0); return CMD_SUCCESS; } #endif DEFUN(swow_mmctx_imsi, show_mmctx_imsi_cmd, "show mm-context imsi IMSI [pdp]", SHOW_STR MMCTX_STR "Identify by IMSI\n" "IMSI of the MM Context\n" INCLUDE_PDP_STR) { struct sgsn_mm_ctx *mm; mm = sgsn_mm_ctx_by_imsi(argv[0]); if (!mm) { vty_out(vty, "No MM context for IMSI %s%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } vty_dump_mmctx(vty, "", mm, argv[1] ? 1 : 0); return CMD_SUCCESS; } DEFUN(swow_mmctx_all, show_mmctx_all_cmd, "show mm-context all [pdp]", SHOW_STR MMCTX_STR "All MM Contexts\n" INCLUDE_PDP_STR) { struct sgsn_mm_ctx *mm; llist_for_each_entry(mm, &sgsn_mm_ctxts, list) vty_dump_mmctx(vty, "", mm, argv[0] ? 1 : 0); return CMD_SUCCESS; } DEFUN(show_pdpctx_all, show_pdpctx_all_cmd, "show pdp-context all", SHOW_STR "Display information on PDP Context\n" "Show everything\n") { struct sgsn_pdp_ctx *pdp; llist_for_each_entry(pdp, &sgsn_pdp_ctxts, g_list) vty_dump_pdp(vty, "", pdp); return CMD_SUCCESS; } DEFUN(imsi_acl, cfg_imsi_acl_cmd, "imsi-acl (add|del) IMSI", "Access Control List of foreign IMSIs\n" "Add IMSI to ACL\n" "Remove IMSI from ACL\n" "IMSI of subscriber\n") { const char *op = argv[0]; const char *imsi = argv[1]; int rc; if (!strcmp(op, "add")) rc = sgsn_acl_add(imsi, g_cfg); else rc = sgsn_acl_del(imsi, g_cfg); if (rc < 0) { vty_out(vty, "%% unable to %s ACL%s", op, VTY_NEWLINE); return CMD_WARNING; } return CMD_SUCCESS; } DEFUN(cfg_auth_policy, cfg_auth_policy_cmd, "auth-policy (accept-all|closed|acl-only|remote)", "Autorization Policy of SGSN\n" "Accept all IMSIs (DANGEROUS)\n" "Accept only home network subscribers or those in the ACL\n" "Accept only subscribers in the ACL\n" "Use remote subscription data only (HLR)\n") { int val = get_string_value(sgsn_auth_pol_strs, argv[0]); OSMO_ASSERT(val >= SGSN_AUTH_POLICY_OPEN && val <= SGSN_AUTH_POLICY_REMOTE); g_cfg->auth_policy = val; g_cfg->require_authentication = (val == SGSN_AUTH_POLICY_REMOTE); g_cfg->require_update_location = (val == SGSN_AUTH_POLICY_REMOTE); return CMD_SUCCESS; } /* Subscriber */ #include static void subscr_dump_full_vty(struct vty *vty, struct gsm_subscriber *subscr, int pending) { char expire_time[200]; struct gsm_auth_tuple *at; int at_idx; struct sgsn_subscriber_pdp_data *pdp; vty_out(vty, " ID: %llu, Authorized: %d%s", subscr->id, subscr->authorized, VTY_NEWLINE); if (strlen(subscr->name)) vty_out(vty, " Name: '%s'%s", subscr->name, VTY_NEWLINE); if (strlen(subscr->extension)) vty_out(vty, " Extension: %s%s", subscr->extension, VTY_NEWLINE); vty_out(vty, " LAC: %d/0x%x%s", subscr->lac, subscr->lac, VTY_NEWLINE); vty_out(vty, " IMSI: %s%s", subscr->imsi, VTY_NEWLINE); if (subscr->tmsi != GSM_RESERVED_TMSI) vty_out(vty, " TMSI: %08X%s", subscr->tmsi, VTY_NEWLINE); if (subscr->sgsn_data->msisdn_len > 0) vty_out(vty, " MSISDN (BCD): %s%s", osmo_hexdump(subscr->sgsn_data->msisdn, subscr->sgsn_data->msisdn_len), VTY_NEWLINE); if (strlen(subscr->equipment.imei) > 0) vty_out(vty, " IMEI: %s%s", subscr->equipment.imei, VTY_NEWLINE); for (at_idx = 0; at_idx < ARRAY_SIZE(subscr->sgsn_data->auth_triplets); at_idx++) { at = &subscr->sgsn_data->auth_triplets[at_idx]; if (at->key_seq == GSM_KEY_SEQ_INVAL) continue; vty_out(vty, " A3A8 tuple (used %d times): ", at->use_count); vty_out(vty, " seq # : %d, ", at->key_seq); vty_out(vty, " RAND : %s, ", osmo_hexdump(at->rand, sizeof(at->rand))); vty_out(vty, " SRES : %s, ", osmo_hexdump(at->sres, sizeof(at->sres))); vty_out(vty, " Kc : %s%s", osmo_hexdump(at->kc, sizeof(at->kc)), VTY_NEWLINE); } llist_for_each_entry(pdp, &subscr->sgsn_data->pdp_list, list) { vty_out(vty, " PDP info: Id: %d, Type: 0x%04x, APN: '%s' QoS: %s%s", pdp->context_id, pdp->pdp_type, pdp->apn_str, osmo_hexdump(pdp->qos_subscribed, pdp->qos_subscribed_len), VTY_NEWLINE); } /* print the expiration time of a subscriber */ if (subscr->expire_lu) { strftime(expire_time, sizeof(expire_time), "%a, %d %b %Y %T %z", localtime(&subscr->expire_lu)); expire_time[sizeof(expire_time) - 1] = '\0'; vty_out(vty, " Expiration Time: %s%s", expire_time, VTY_NEWLINE); } if (subscr->flags) vty_out(vty, " Flags: %s%s%s%s%s%s", subscr->flags & GSM_SUBSCRIBER_FIRST_CONTACT ? "FIRST_CONTACT " : "", subscr->flags & GPRS_SUBSCRIBER_CANCELLED ? "CANCELLED " : "", subscr->flags & GPRS_SUBSCRIBER_UPDATE_LOCATION_PENDING ? "UPDATE_LOCATION_PENDING " : "", subscr->flags & GPRS_SUBSCRIBER_UPDATE_AUTH_INFO_PENDING ? "AUTH_INFO_PENDING " : "", subscr->flags & GPRS_SUBSCRIBER_ENABLE_PURGE ? "ENABLE_PURGE " : "", VTY_NEWLINE); vty_out(vty, " Use count: %u%s", subscr->use_count, VTY_NEWLINE); } DEFUN(show_subscr_cache, show_subscr_cache_cmd, "show subscriber cache", SHOW_STR "Show information about subscribers\n" "Display contents of subscriber cache\n") { struct gsm_subscriber *subscr; llist_for_each_entry(subscr, &active_subscribers, entry) { vty_out(vty, " Subscriber:%s", VTY_NEWLINE); subscr_dump_full_vty(vty, subscr, 0); } return CMD_SUCCESS; } #define UPDATE_SUBSCR_STR "update-subscriber imsi IMSI " #define UPDATE_SUBSCR_HELP "Update subscriber list\n" \ "Use the IMSI to select the subscriber\n" \ "The IMSI\n" #define UPDATE_SUBSCR_INSERT_HELP "Insert data into the subscriber record\n" DEFUN(update_subscr_insert_auth_triplet, update_subscr_insert_auth_triplet_cmd, UPDATE_SUBSCR_STR "insert auth-triplet <1-5> sres SRES rand RAND kc KC", UPDATE_SUBSCR_HELP UPDATE_SUBSCR_INSERT_HELP "Update authentication triplet\n" "Triplet index\n" "Set SRES value\nSRES value (4 byte) in hex\n" "Set RAND value\nRAND value (16 byte) in hex\n" "Set Kc value\nKc value (8 byte) in hex\n") { const char *imsi = argv[0]; const int cksn = atoi(argv[1]) - 1; const char *sres_str = argv[2]; const char *rand_str = argv[3]; const char *kc_str = argv[4]; struct gsm_auth_tuple at = {0,}; struct gsm_subscriber *subscr; subscr = gprs_subscr_get_by_imsi(imsi); if (!subscr) { vty_out(vty, "%% unable get subscriber record for %s%s", imsi, VTY_NEWLINE); return CMD_WARNING; } OSMO_ASSERT(subscr->sgsn_data); if (osmo_hexparse(sres_str, &at.sres[0], sizeof(at.sres)) < 0) { vty_out(vty, "%% invalid SRES value '%s'%s", sres_str, VTY_NEWLINE); goto failed; } if (osmo_hexparse(rand_str, &at.rand[0], sizeof(at.rand)) < 0) { vty_out(vty, "%% invalid RAND value '%s'%s", rand_str, VTY_NEWLINE); goto failed; } if (osmo_hexparse(kc_str, &at.kc[0], sizeof(at.kc)) < 0) { vty_out(vty, "%% invalid Kc value '%s'%s", kc_str, VTY_NEWLINE); goto failed; } at.key_seq = cksn; subscr->sgsn_data->auth_triplets[cksn] = at; subscr->sgsn_data->auth_triplets_updated = 1; subscr_put(subscr); return CMD_SUCCESS; failed: subscr_put(subscr); return CMD_SUCCESS; } DEFUN(update_subscr_cancel, update_subscr_cancel_cmd, UPDATE_SUBSCR_STR "cancel (update-procedure|subscription-withdraw)", UPDATE_SUBSCR_HELP "Cancel (remove) subscriber record\n" "The MS moved to another SGSN\n" "The subscription is no longer valid\n") { const char *imsi = argv[0]; const char *cancel_type = argv[1]; struct gsm_subscriber *subscr; subscr = gprs_subscr_get_by_imsi(imsi); if (!subscr) { vty_out(vty, "%% no subscriber record for %s%s", imsi, VTY_NEWLINE); return CMD_WARNING; } if (strcmp(cancel_type, "update-procedure") == 0) subscr->sgsn_data->error_cause = SGSN_ERROR_CAUSE_NONE; else subscr->sgsn_data->error_cause = GMM_CAUSE_IMPL_DETACHED; gprs_subscr_cancel(subscr); subscr_put(subscr); return CMD_SUCCESS; } DEFUN(update_subscr_create, update_subscr_create_cmd, UPDATE_SUBSCR_STR "create", UPDATE_SUBSCR_HELP "Create a subscriber entry\n") { const char *imsi = argv[0]; struct gsm_subscriber *subscr; subscr = gprs_subscr_get_by_imsi(imsi); if (subscr) { vty_out(vty, "%% subscriber record already exists for %s%s", imsi, VTY_NEWLINE); return CMD_WARNING; } subscr = gprs_subscr_get_or_create(imsi); subscr->keep_in_ram = 1; subscr_put(subscr); return CMD_SUCCESS; } DEFUN(update_subscr_destroy, update_subscr_destroy_cmd, UPDATE_SUBSCR_STR "destroy", UPDATE_SUBSCR_HELP "Destroy a subscriber entry\n") { const char *imsi = argv[0]; struct gsm_subscriber *subscr; subscr = gprs_subscr_get_by_imsi(imsi); if (!subscr) { vty_out(vty, "%% subscriber record does not exist for %s%s", imsi, VTY_NEWLINE); return CMD_WARNING; } subscr->keep_in_ram = 0; subscr->sgsn_data->error_cause = SGSN_ERROR_CAUSE_NONE; gprs_subscr_cancel(subscr); if (subscr->use_count > 1) vty_out(vty, "%% subscriber is still in use%s", VTY_NEWLINE); subscr_put(subscr); return CMD_SUCCESS; } #define UL_ERR_STR "system-failure|data-missing|unexpected-data-value|" \ "unknown-subscriber|roaming-not-allowed" #define UL_ERR_HELP \ "Force error code SystemFailure\n" \ "Force error code DataMissing\n" \ "Force error code UnexpectedDataValue\n" \ "Force error code UnknownSubscriber\n" \ "Force error code RoamingNotAllowed\n" DEFUN(update_subscr_update_location_result, update_subscr_update_location_result_cmd, UPDATE_SUBSCR_STR "update-location-result (ok|" UL_ERR_STR ")", UPDATE_SUBSCR_HELP "Complete the update location procedure\n" "The update location request succeeded\n" UL_ERR_HELP) { const char *imsi = argv[0]; const char *ret_code_str = argv[1]; struct gsm_subscriber *subscr; const struct value_string cause_mapping[] = { { GMM_CAUSE_NET_FAIL, "system-failure" }, { GMM_CAUSE_INV_MAND_INFO, "data-missing" }, { GMM_CAUSE_PROTO_ERR_UNSPEC, "unexpected-data-value" }, { GMM_CAUSE_IMSI_UNKNOWN, "unknown-subscriber" }, { GMM_CAUSE_GPRS_NOTALLOWED, "roaming-not-allowed" }, { 0, NULL } }; subscr = gprs_subscr_get_by_imsi(imsi); if (!subscr) { vty_out(vty, "%% unable to get subscriber record for %s%s", imsi, VTY_NEWLINE); return CMD_WARNING; } if (strcmp(ret_code_str, "ok") == 0) { subscr->sgsn_data->error_cause = SGSN_ERROR_CAUSE_NONE; subscr->authorized = 1; } else { subscr->sgsn_data->error_cause = get_string_value(cause_mapping, ret_code_str); subscr->authorized = 0; } gprs_subscr_update(subscr); subscr_put(subscr); return CMD_SUCCESS; } DEFUN(update_subscr_update_auth_info, update_subscr_update_auth_info_cmd, UPDATE_SUBSCR_STR "update-auth-info", UPDATE_SUBSCR_HELP "Complete the send authentication info procedure\n") { const char *imsi = argv[0]; struct gsm_subscriber *subscr; subscr = gprs_subscr_get_by_imsi(imsi); if (!subscr) { vty_out(vty, "%% unable to get subscriber record for %s%s", imsi, VTY_NEWLINE); return CMD_WARNING; } gprs_subscr_update_auth_info(subscr); subscr_put(subscr); return CMD_SUCCESS; } DEFUN(cfg_gsup_remote_ip, cfg_gsup_remote_ip_cmd, "gsup remote-ip A.B.C.D", "GSUP Parameters\n" "Set the IP address of the remote GSUP server\n" "IPv4 Address\n") { inet_aton(argv[0], &g_cfg->gsup_server_addr.sin_addr); return CMD_SUCCESS; } DEFUN(cfg_gsup_remote_port, cfg_gsup_remote_port_cmd, "gsup remote-port <0-65535>", "GSUP Parameters\n" "Set the TCP port of the remote GSUP server\n" "Remote TCP port\n") { g_cfg->gsup_server_port = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_apn_name, cfg_apn_name_cmd, "access-point-name NAME", "Configure a global list of allowed APNs\n" "Add this NAME to the list\n") { return add_apn_ggsn_mapping(vty, argv[0], "", 0); } DEFUN(cfg_no_apn_name, cfg_no_apn_name_cmd, "no access-point-name NAME", NO_STR "Configure a global list of allowed APNs\n" "Remove entry with NAME\n") { struct apn_ctx *apn_ctx = sgsn_apn_ctx_by_name(argv[0], ""); if (!apn_ctx) return CMD_SUCCESS; sgsn_apn_ctx_free(apn_ctx); return CMD_SUCCESS; } DEFUN(cfg_cdr_filename, cfg_cdr_filename_cmd, "cdr filename NAME", "CDR\nSet filename\nname\n") { talloc_free(g_cfg->cdr.filename); g_cfg->cdr.filename = talloc_strdup(tall_vty_ctx, argv[0]); return CMD_SUCCESS; } DEFUN(cfg_no_cdr_filename, cfg_no_cdr_filename_cmd, "no cdr filename", NO_STR "CDR\nDisable CDR generation\n") { talloc_free(g_cfg->cdr.filename); g_cfg->cdr.filename = NULL; return CMD_SUCCESS; } DEFUN(cfg_cdr_interval, cfg_cdr_interval_cmd, "cdr interval <1-2147483647>", "CDR\nPDP periodic log interval\nSeconds\n") { g_cfg->cdr.interval = atoi(argv[0]); return CMD_SUCCESS; } int sgsn_vty_init(void) { install_element_ve(&show_sgsn_cmd); //install_element_ve(&show_mmctx_tlli_cmd); install_element_ve(&show_mmctx_imsi_cmd); install_element_ve(&show_mmctx_all_cmd); install_element_ve(&show_pdpctx_all_cmd); install_element_ve(&show_subscr_cache_cmd); install_element(ENABLE_NODE, &update_subscr_insert_auth_triplet_cmd); install_element(ENABLE_NODE, &update_subscr_create_cmd); install_element(ENABLE_NODE, &update_subscr_destroy_cmd); install_element(ENABLE_NODE, &update_subscr_cancel_cmd); install_element(ENABLE_NODE, &update_subscr_update_location_result_cmd); install_element(ENABLE_NODE, &update_subscr_update_auth_info_cmd); install_element(CONFIG_NODE, &cfg_sgsn_cmd); install_node(&sgsn_node, config_write_sgsn); vty_install_default(SGSN_NODE); install_element(SGSN_NODE, &cfg_sgsn_bind_addr_cmd); install_element(SGSN_NODE, &cfg_ggsn_remote_ip_cmd); //install_element(SGSN_NODE, &cfg_ggsn_remote_port_cmd); install_element(SGSN_NODE, &cfg_ggsn_gtp_version_cmd); install_element(SGSN_NODE, &cfg_imsi_acl_cmd); install_element(SGSN_NODE, &cfg_auth_policy_cmd); install_element(SGSN_NODE, &cfg_gsup_remote_ip_cmd); install_element(SGSN_NODE, &cfg_gsup_remote_port_cmd); install_element(SGSN_NODE, &cfg_apn_ggsn_cmd); install_element(SGSN_NODE, &cfg_apn_imsi_ggsn_cmd); install_element(SGSN_NODE, &cfg_apn_name_cmd); install_element(SGSN_NODE, &cfg_no_apn_name_cmd); install_element(SGSN_NODE, &cfg_cdr_filename_cmd); install_element(SGSN_NODE, &cfg_no_cdr_filename_cmd); install_element(SGSN_NODE, &cfg_cdr_interval_cmd); install_element(SGSN_NODE, &cfg_ggsn_dynamic_lookup_cmd); install_element(SGSN_NODE, &cfg_grx_ggsn_cmd); install_element(SGSN_NODE, &cfg_sgsn_T3312_cmd); install_element(SGSN_NODE, &cfg_sgsn_T3322_cmd); install_element(SGSN_NODE, &cfg_sgsn_T3350_cmd); install_element(SGSN_NODE, &cfg_sgsn_T3360_cmd); install_element(SGSN_NODE, &cfg_sgsn_T3370_cmd); install_element(SGSN_NODE, &cfg_sgsn_T3313_cmd); install_element(SGSN_NODE, &cfg_sgsn_T3314_cmd); install_element(SGSN_NODE, &cfg_sgsn_T3316_cmd); install_element(SGSN_NODE, &cfg_sgsn_T3385_cmd); install_element(SGSN_NODE, &cfg_sgsn_T3386_cmd); install_element(SGSN_NODE, &cfg_sgsn_T3395_cmd); install_element(SGSN_NODE, &cfg_sgsn_T3397_cmd); return 0; } int sgsn_parse_config(const char *config_file, struct sgsn_config *cfg) { int rc; g_cfg = cfg; g_cfg->timers.T3312 = GSM0408_T3312_SECS; g_cfg->timers.T3322 = GSM0408_T3322_SECS; g_cfg->timers.T3350 = GSM0408_T3350_SECS; g_cfg->timers.T3360 = GSM0408_T3360_SECS; g_cfg->timers.T3370 = GSM0408_T3370_SECS; g_cfg->timers.T3313 = GSM0408_T3313_SECS; g_cfg->timers.T3314 = GSM0408_T3314_SECS; g_cfg->timers.T3316 = GSM0408_T3316_SECS; g_cfg->timers.T3385 = GSM0408_T3385_SECS; g_cfg->timers.T3386 = GSM0408_T3386_SECS; g_cfg->timers.T3395 = GSM0408_T3395_SECS; g_cfg->timers.T3397 = GSM0408_T3397_SECS; 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; } openbsc-0.15.0/openbsc/src/ipaccess/000077500000000000000000000000001265565154000172515ustar00rootroot00000000000000openbsc-0.15.0/openbsc/src/ipaccess/Makefile.am000066400000000000000000000024731265565154000213130ustar00rootroot00000000000000AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) AM_LDFLAGS = $(COVERAGE_LDFLAGS) OSMO_LIBS = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOABIS_LIBS) bin_PROGRAMS = ipaccess-find ipaccess-config ipaccess-proxy ipaccess_find_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ $(top_builddir)/src/libmsc/libmsc.a \ $(top_builddir)/src/libbsc/libbsc.a \ $(top_builddir)/src/libtrau/libtrau.a \ $(top_builddir)/src/libcommon/libcommon.a \ $(OSMO_LIBS) ipaccess_find_SOURCES = ipaccess-find.c ipaccess_config_SOURCES = ipaccess-config.c ipaccess-firmware.c network_listen.c # FIXME: resolve the bogus dependencies patched around here: ipaccess_config_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ $(top_builddir)/src/libbsc/libbsc.a \ $(top_builddir)/src/libtrau/libtrau.a \ $(top_builddir)/src/libcommon/libcommon.a \ $(LIBCRYPT) $(OSMO_LIBS) ipaccess_proxy_SOURCES = ipaccess-proxy.c ipaccess_proxy_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ $(top_builddir)/src/libmsc/libmsc.a \ $(top_builddir)/src/libbsc/libbsc.a \ $(top_builddir)/src/libtrau/libtrau.a \ $(top_builddir)/src/libcommon/libcommon.a \ $(OSMO_LIBS) openbsc-0.15.0/openbsc/src/ipaccess/ipaccess-config.c000066400000000000000000000647331265565154000224670ustar00rootroot00000000000000/* ip.access nanoBTS configuration tool */ /* (C) 2009-2010 by Harald Welte * (C) 2009-2011 by Holger Hans Peter Freyther * (C) 2009-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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct gsm_network *bsc_gsmnet; static int net_listen_testnr; static int restart; static char *prim_oml_ip; static char *bts_ip_addr, *bts_ip_mask, *bts_ip_gw; static char *unit_id; static uint16_t nv_flags; static uint16_t nv_mask; static char *software = NULL; static int sw_load_state = 0; static int oml_state = 0; static int dump_files = 0; static char *firmware_analysis = NULL; static int found_trx = 0; static int loop_tests = 0; struct sw_load { uint8_t file_id[255]; uint8_t file_id_len; uint8_t file_version[255]; uint8_t file_version_len; }; static void *tall_ctx_config = NULL; static struct sw_load *sw_load1 = NULL; static struct sw_load *sw_load2 = NULL; /* static uint8_t prim_oml_attr[] = { 0x95, 0x00, 7, 0x88, 192, 168, 100, 11, 0x00, 0x00 }; static uint8_t unit_id_attr[] = { 0x91, 0x00, 9, '2', '3', '4', '2', '/' , '0', '/', '0', 0x00 }; */ extern int ipaccess_fd_cb(struct osmo_fd *bfd, unsigned int what); extern struct e1inp_line_ops ipaccess_e1inp_line_ops; /* Actively connect to a BTS. Currently used by ipaccess-config.c */ static int ipaccess_connect(struct e1inp_line *line, struct sockaddr_in *sa) { struct e1inp_ts *e1i_ts = &line->ts[0]; struct osmo_fd *bfd = &e1i_ts->driver.ipaccess.fd; int ret, on = 1; bfd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); bfd->cb = ipaccess_fd_cb; bfd->when = BSC_FD_READ | BSC_FD_WRITE; bfd->data = line; bfd->priv_nr = E1INP_SIGN_OML; if (bfd->fd < 0) { LOGP(DLINP, LOGL_ERROR, "could not create TCP socket.\n"); return -EIO; } setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); ret = connect(bfd->fd, (struct sockaddr *) sa, sizeof(*sa)); if (ret < 0) { LOGP(DLINP, LOGL_ERROR, "could not connect socket\n"); close(bfd->fd); return ret; } ret = osmo_fd_register(bfd); if (ret < 0) { close(bfd->fd); return ret; } return ret; //return e1inp_line_register(line); } /* configure pseudo E1 line in ip.access style and connect to BTS */ static int ia_config_connect(struct gsm_bts *bts, struct sockaddr_in *sin) { struct e1inp_line *line; struct e1inp_ts *sign_ts, *rsl_ts; struct e1inp_sign_link *oml_link, *rsl_link; line = talloc_zero(tall_bsc_ctx, struct e1inp_line); if (!line) return -ENOMEM; line->driver = e1inp_driver_find("ipa"); if (!line->driver) { fprintf(stderr, "cannot `ipa' driver, giving up.\n"); return -EINVAL; } line->ops = &ipaccess_e1inp_line_ops; /* create E1 timeslots for signalling and TRAU frames */ e1inp_ts_config_sign(&line->ts[1-1], line); e1inp_ts_config_sign(&line->ts[2-1], line); /* create signalling links for TS1 */ sign_ts = &line->ts[1-1]; rsl_ts = &line->ts[2-1]; oml_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OML, bts->c0, 0xff, 0); rsl_link = e1inp_sign_link_create(rsl_ts, E1INP_SIGN_RSL, bts->c0, 0, 0); /* create back-links from bts/trx */ bts->oml_link = oml_link; bts->c0->rsl_link = rsl_link; /* default port at BTS for incoming connections is 3006 */ if (sin->sin_port == 0) sin->sin_port = htons(3006); return ipaccess_connect(line, sin); } /* * Callback function for NACK on the OML NM * * Currently we send the config requests but don't check the * result. The nanoBTS will send us a NACK when we did something the * BTS didn't like. */ static int ipacc_msg_nack(uint8_t mt) { fprintf(stderr, "Failure to set attribute. This seems fatal\n"); exit(-1); return 0; } static void check_restart_or_exit(struct gsm_bts_trx *trx) { if (restart) { abis_nm_ipaccess_restart(trx); } else { exit(0); } } static int ipacc_msg_ack(uint8_t mt, struct gsm_bts_trx *trx) { if (sw_load_state == 1) { fprintf(stderr, "The new software is activaed.\n"); check_restart_or_exit(trx); } else if (oml_state == 1) { fprintf(stderr, "Set the NV Attributes.\n"); check_restart_or_exit(trx); } return 0; } static const uint8_t phys_conf_min[] = { 0x02 }; static uint16_t build_physconf(uint8_t *physconf_buf, const struct rxlev_stats *st) { uint16_t *whitelist = (uint16_t *) (physconf_buf + 4); int num_arfcn; unsigned int arfcnlist_size; /* Create whitelist from rxlevels */ physconf_buf[0] = phys_conf_min[0]; physconf_buf[1] = NM_IPAC_EIE_ARFCN_WHITE; num_arfcn = ipac_rxlevstat2whitelist(whitelist, st, 0, 100); arfcnlist_size = num_arfcn * 2; *((uint16_t *) (physconf_buf+2)) = htons(arfcnlist_size); DEBUGP(DNM, "physconf_buf (%s)\n", osmo_hexdump(physconf_buf, arfcnlist_size+4)); return arfcnlist_size+4; } static int nwl_sig_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct gsm_bts_trx *trx; uint8_t physconf_buf[2*NUM_ARFCNS+16]; uint16_t physconf_len; switch (signal) { case S_IPAC_NWL_COMPLETE: trx = signal_data; DEBUGP(DNM, "received S_IPAC_NWL_COMPLETE signal\n"); switch (trx->ipaccess.test_nr) { case NM_IPACC_TESTNO_CHAN_USAGE: /* Dump RxLev results */ //rxlev_stat_dump(&trx->ipaccess.rxlev_stat); /* Create whitelist from results */ physconf_len = build_physconf(physconf_buf, &trx->ipaccess.rxlev_stat); /* Start next test abbout BCCH channel usage */ ipac_nwl_test_start(trx, NM_IPACC_TESTNO_BCCH_CHAN_USAGE, physconf_buf, physconf_len); break; case NM_IPACC_TESTNO_BCCH_CHAN_USAGE: /* Dump BCCH RxLev results */ //rxlev_stat_dump(&trx->ipaccess.rxlev_stat); /* Create whitelist from results */ physconf_len = build_physconf(physconf_buf, &trx->ipaccess.rxlev_stat); /* Start next test about BCCH info */ ipac_nwl_test_start(trx, NM_IPACC_TESTNO_BCCH_INFO, physconf_buf, physconf_len); break; case NM_IPACC_TESTNO_BCCH_INFO: /* re-start full process with CHAN_USAGE */ if (loop_tests) { DEBUGP(DNM, "starting next test cycle\n"); ipac_nwl_test_start(trx, net_listen_testnr, phys_conf_min, sizeof(phys_conf_min)); } else { exit(0); } break; } break; } return 0; } static int nm_state_event(int evt, uint8_t obj_class, void *obj, struct gsm_nm_state *old_state, struct gsm_nm_state *new_state, struct abis_om_obj_inst *obj_inst); static int nm_sig_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct ipacc_ack_signal_data *ipacc_data; struct nm_statechg_signal_data *nsd; switch (signal) { case S_NM_IPACC_NACK: ipacc_data = signal_data; return ipacc_msg_nack(ipacc_data->msg_type); case S_NM_IPACC_ACK: ipacc_data = signal_data; return ipacc_msg_ack(ipacc_data->msg_type, ipacc_data->trx); case S_NM_IPACC_RESTART_ACK: printf("The BTS has acked the restart. Exiting.\n"); exit(0); break; case S_NM_IPACC_RESTART_NACK: printf("The BTS has nacked the restart. Exiting.\n"); exit(0); break; case S_NM_STATECHG_OPER: case S_NM_STATECHG_ADM: nsd = signal_data; nm_state_event(signal, nsd->obj_class, nsd->obj, nsd->old_state, nsd->new_state, nsd->obj_inst); break; default: break; } return 0; } /* callback function passed to the ABIS OML code */ static int percent; static int percent_old; static int swload_cbfn(unsigned int hook, unsigned int event, struct msgb *_msg, void *data, void *param) { struct msgb *msg; struct gsm_bts_trx *trx; if (hook != GSM_HOOK_NM_SWLOAD) return 0; trx = (struct gsm_bts_trx *) data; switch (event) { case NM_MT_LOAD_INIT_ACK: fprintf(stdout, "Software Load Initiate ACK\n"); break; case NM_MT_LOAD_INIT_NACK: fprintf(stderr, "ERROR: Software Load Initiate NACK\n"); exit(5); break; case NM_MT_LOAD_END_ACK: fprintf(stderr, "LOAD END ACK..."); /* now make it the default */ sw_load_state = 1; msg = msgb_alloc(1024, "sw: nvattr"); msg->l2h = msgb_put(msg, 3); msg->l3h = &msg->l2h[3]; /* activate software */ if (sw_load1) { msgb_v_put(msg, NM_ATT_SW_DESCR); msgb_tl16v_put(msg, NM_ATT_FILE_ID, sw_load1->file_id_len, sw_load1->file_id); msgb_tl16v_put(msg, NM_ATT_FILE_VERSION, sw_load1->file_version_len, sw_load1->file_version); } if (sw_load2) { msgb_v_put(msg, NM_ATT_SW_DESCR); msgb_tl16v_put(msg, NM_ATT_FILE_ID, sw_load2->file_id_len, sw_load2->file_id); msgb_tl16v_put(msg, NM_ATT_FILE_VERSION, sw_load2->file_version_len, sw_load2->file_version); } /* fill in the data */ msg->l2h[0] = NM_ATT_IPACC_CUR_SW_CFG; msg->l2h[1] = msgb_l3len(msg) >> 8; msg->l2h[2] = msgb_l3len(msg) & 0xff; printf("Foo l2h: %p l3h: %p... length l2: %u l3: %u\n", msg->l2h, msg->l3h, msgb_l2len(msg), msgb_l3len(msg)); abis_nm_ipaccess_set_nvattr(trx, msg->l2h, msgb_l2len(msg)); msgb_free(msg); break; case NM_MT_LOAD_END_NACK: fprintf(stderr, "ERROR: Software Load End NACK\n"); exit(3); break; case NM_MT_ACTIVATE_SW_NACK: fprintf(stderr, "ERROR: Activate Software NACK\n"); exit(4); break; case NM_MT_ACTIVATE_SW_ACK: break; case NM_MT_LOAD_SEG_ACK: percent = abis_nm_software_load_status(trx->bts); if (percent > percent_old) printf("Software Download Progress: %d%%\n", percent); percent_old = percent; break; case NM_MT_LOAD_ABORT: fprintf(stderr, "ERROR: Load aborted by the BTS.\n"); exit(6); break; } return 0; } static void nv_put_ip_if_cfg(struct msgb *nmsg, uint32_t ip, uint32_t mask) { msgb_put_u8(nmsg, NM_ATT_IPACC_IP_IF_CFG); msgb_put_u32(nmsg, ip); msgb_put_u32(nmsg, mask); } static void nv_put_gw_cfg(struct msgb *nmsg, uint32_t addr, uint32_t mask, uint32_t gw) { msgb_put_u8(nmsg, NM_ATT_IPACC_IP_GW_CFG); msgb_put_u32(nmsg, addr); msgb_put_u32(nmsg, mask); msgb_put_u32(nmsg, gw); } static void nv_put_unit_id(struct msgb *nmsg, const char *unit_id) { msgb_tl16v_put(nmsg, NM_ATT_IPACC_UNIT_ID, strlen(unit_id)+1, (const uint8_t *)unit_id); } static void nv_put_prim_oml(struct msgb *nmsg, uint32_t ip, uint16_t port) { int len; /* 0x88 + IP + port */ len = 1 + sizeof(ip) + sizeof(port); msgb_put_u8(nmsg, NM_ATT_IPACC_PRIM_OML_CFG_LIST); msgb_put_u16(nmsg, len); msgb_put_u8(nmsg, 0x88); /* IP address */ msgb_put_u32(nmsg, ip); /* port number */ msgb_put_u16(nmsg, port); } static void nv_put_flags(struct msgb *nmsg, uint16_t nv_flags, uint16_t nv_mask) { msgb_put_u8(nmsg, NM_ATT_IPACC_NV_FLAGS); msgb_put_u16(nmsg, sizeof(nv_flags) + sizeof(nv_mask)); msgb_put_u8(nmsg, nv_flags & 0xff); msgb_put_u8(nmsg, nv_mask & 0xff); msgb_put_u8(nmsg, nv_flags >> 8); msgb_put_u8(nmsg, nv_mask >> 8); } /* human-readable test names for the ip.access tests */ static const struct value_string ipa_test_strs[] = { { 64, "ccch-usage" }, { 65, "bcch-usage" }, { 66, "freq-sync" }, { 67, "rtp-usage" }, { 68, "rtp-perf" }, { 69, "gprs-ccch" }, { 70, "pccch-usage" }, { 71, "gprs-usage" }, { 72, "esta-mf" }, { 73, "uplink-mf" }, { 74, "dolink-mf" }, { 75, "tbf-details" }, { 76, "tbf-usage" }, { 77, "llc-data" }, { 78, "pdch-usage" }, { 79, "power-control" }, { 80, "link-adaption" }, { 81, "tch-usage" }, { 82, "amr-mf" }, { 83, "rtp-multiplex-perf" }, { 84, "rtp-multiplex-usage" }, { 85, "srtp-multiplex-usage" }, { 86, "abis-traffic" }, { 89, "gprs-multiplex-perf" }, { 90, "gprs-multiplex-usage" }, { 0, NULL }, }; /* human-readable names for the ip.access nanoBTS NVRAM Flags */ static const struct value_string ipa_nvflag_strs[] = { { 0x0001, "static-ip" }, { 0x0002, "static-gw" }, { 0x0004, "no-dhcp-vsi" }, { 0x0008, "dhcp-enabled" }, { 0x0040, "led-disabled" }, { 0x0100, "secondary-oml-enabled" }, { 0x0200, "diag-enabled" }, { 0x0400, "cli-enabled" }, { 0x0800, "http-enabled" }, { 0x1000, "post-enabled" }, { 0x2000, "snmp-enabled" }, { 0, NULL } }; /* set the flags in flags/mask according to a string-identified flag and 'enable' */ static int ipa_nvflag_set(uint16_t *flags, uint16_t *mask, const char *name, int en) { int rc; rc = get_string_value(ipa_nvflag_strs, name); if (rc < 0) return rc; *mask |= rc; if (en) *flags |= rc; else *flags &= ~rc; return 0; } static void bootstrap_om(struct gsm_bts_trx *trx) { struct msgb *nmsg = msgb_alloc(1024, "nested msgb"); int need_to_set_attr = 0; int len; printf("OML link established using TRX %d\n", trx->nr); if (unit_id) { len = strlen(unit_id); if (len > nmsg->data_len-10) goto out_err; printf("setting Unit ID to '%s'\n", unit_id); nv_put_unit_id(nmsg, unit_id); need_to_set_attr = 1; } if (prim_oml_ip) { struct in_addr ia; if (!inet_aton(prim_oml_ip, &ia)) { fprintf(stderr, "invalid IP address: %s\n", prim_oml_ip); goto out_err; } printf("setting primary OML link IP to '%s'\n", inet_ntoa(ia)); nv_put_prim_oml(nmsg, ntohl(ia.s_addr), 0); need_to_set_attr = 1; } if (nv_mask) { printf("setting NV Flags/Mask to 0x%04x/0x%04x\n", nv_flags, nv_mask); nv_put_flags(nmsg, nv_flags, nv_mask); need_to_set_attr = 1; } if (bts_ip_addr && bts_ip_mask) { struct in_addr ia_addr, ia_mask; if (!inet_aton(bts_ip_addr, &ia_addr)) { fprintf(stderr, "invalid IP address: %s\n", bts_ip_addr); goto out_err; } if (!inet_aton(bts_ip_mask, &ia_mask)) { fprintf(stderr, "invalid IP address: %s\n", bts_ip_mask); goto out_err; } printf("setting static IP Address/Mask\n"); nv_put_ip_if_cfg(nmsg, ntohl(ia_addr.s_addr), ntohl(ia_mask.s_addr)); need_to_set_attr = 1; } if (bts_ip_gw) { struct in_addr ia_gw; if (!inet_aton(bts_ip_gw, &ia_gw)) { fprintf(stderr, "invalid IP address: %s\n", bts_ip_gw); goto out_err; } printf("setting static IP Gateway\n"); /* we only set the default gateway with zero addr/mask */ nv_put_gw_cfg(nmsg, 0, 0, ntohl(ia_gw.s_addr)); need_to_set_attr = 1; } if (need_to_set_attr) { abis_nm_ipaccess_set_nvattr(trx, nmsg->head, nmsg->len); oml_state = 1; } if (restart && !prim_oml_ip && !software) { printf("restarting BTS\n"); abis_nm_ipaccess_restart(trx); } out_err: msgb_free(nmsg); } static int nm_state_event(int evt, uint8_t obj_class, void *obj, struct gsm_nm_state *old_state, struct gsm_nm_state *new_state, struct abis_om_obj_inst *obj_inst) { if (obj_class == NM_OC_BASEB_TRANSC) { if (!found_trx && obj_inst->trx_nr != 0xff) { struct gsm_bts_trx *trx = container_of(obj, struct gsm_bts_trx, bb_transc); bootstrap_om(trx); found_trx = 1; } } else if (evt == S_NM_STATECHG_OPER && obj_class == NM_OC_RADIO_CARRIER && new_state->availability == 3) { struct gsm_bts_trx *trx = obj; if (net_listen_testnr) ipac_nwl_test_start(trx, net_listen_testnr, phys_conf_min, sizeof(phys_conf_min)); else if (software) { int rc; printf("Attempting software upload with '%s'\n", software); rc = abis_nm_software_load(trx->bts, trx->nr, software, 19, 0, swload_cbfn, trx); if (rc < 0) { fprintf(stderr, "Failed to start software load\n"); exit(-3); } } } return 0; } static struct sw_load *create_swload(struct sdp_header *header) { struct sw_load *load; load = talloc_zero(tall_ctx_config, struct sw_load); strncpy((char *)load->file_id, header->firmware_info.sw_part, 20); load->file_id_len = strlen(header->firmware_info.sw_part) + 1; strncpy((char *)load->file_version, header->firmware_info.version, 20); load->file_version_len = strlen(header->firmware_info.version) + 1; return load; } static int find_sw_load_params(const char *filename) { struct stat stat; struct sdp_header *header; struct llist_head *entry; int fd; void *tall_firm_ctx = 0; entry = talloc_zero(tall_firm_ctx, struct llist_head); INIT_LLIST_HEAD(entry); fd = open(filename, O_RDONLY); if (!fd) { perror("nada"); return -1; } /* verify the file */ if (fstat(fd, &stat) == -1) { perror("Can not stat the file"); close(fd); return -1; } ipaccess_analyze_file(fd, stat.st_size, 0, entry); if (close(fd) != 0) { perror("Close failed.\n"); return -1; } /* try to find what we are looking for */ llist_for_each_entry(header, entry, entry) { if (ntohs(header->firmware_info.more_more_magic) == 0x1000) { sw_load1 = create_swload(header); } else if (ntohs(header->firmware_info.more_more_magic) == 0x2001) { sw_load2 = create_swload(header); } } if (!sw_load1 || !sw_load2) { fprintf(stderr, "Did not find data.\n"); talloc_free(tall_firm_ctx); return -1; } talloc_free(tall_firm_ctx); return 0; } static void dump_entry(struct sdp_header_item *sub_entry, int part, int fd) { int out_fd; int copied; char filename[4096]; off_t target; if (!dump_files) return; if (sub_entry->header_entry.something1 == 0) return; snprintf(filename, sizeof(filename), "part.%d", part++); out_fd = open(filename, O_WRONLY | O_CREAT, 0660); if (out_fd < 0) { perror("Can not dump firmware"); return; } target = sub_entry->absolute_offset + ntohl(sub_entry->header_entry.start) + 4; if (lseek(fd, target, SEEK_SET) != target) { perror("seek failed"); close(out_fd); return; } for (copied = 0; copied < ntohl(sub_entry->header_entry.length); ++copied) { char c; if (read(fd, &c, sizeof(c)) != sizeof(c)) { perror("copy failed"); break; } if (write(out_fd, &c, sizeof(c)) != sizeof(c)) { perror("write failed"); break; } } close(out_fd); } static void analyze_firmware(const char *filename) { struct stat stat; struct sdp_header *header; struct sdp_header_item *sub_entry; struct llist_head *entry; int fd; void *tall_firm_ctx = 0; int part = 0; entry = talloc_zero(tall_firm_ctx, struct llist_head); INIT_LLIST_HEAD(entry); printf("Opening possible firmware '%s'\n", filename); fd = open(filename, O_RDONLY); if (!fd) { perror("nada"); return; } /* verify the file */ if (fstat(fd, &stat) == -1) { perror("Can not stat the file"); close(fd); return; } ipaccess_analyze_file(fd, stat.st_size, 0, entry); llist_for_each_entry(header, entry, entry) { printf("Printing header information:\n"); printf("more_more_magic: 0x%x\n", ntohs(header->firmware_info.more_more_magic)); printf("header_length: %u\n", ntohl(header->firmware_info.header_length)); printf("file_length: %u\n", ntohl(header->firmware_info.file_length)); printf("sw_part: %.20s\n", header->firmware_info.sw_part); printf("text1: %.64s\n", header->firmware_info.text1); printf("time: %.12s\n", header->firmware_info.time); printf("date: %.14s\n", header->firmware_info.date); printf("text2: %.10s\n", header->firmware_info.text2); printf("version: %.20s\n", header->firmware_info.version); printf("subitems...\n"); llist_for_each_entry(sub_entry, &header->header_list, entry) { printf("\tsomething1: %u\n", sub_entry->header_entry.something1); printf("\ttext1: %.64s\n", sub_entry->header_entry.text1); printf("\ttime: %.12s\n", sub_entry->header_entry.time); printf("\tdate: %.14s\n", sub_entry->header_entry.date); printf("\ttext2: %.10s\n", sub_entry->header_entry.text2); printf("\tversion: %.20s\n", sub_entry->header_entry.version); printf("\tlength: %u\n", ntohl(sub_entry->header_entry.length)); printf("\taddr1: 0x%x\n", ntohl(sub_entry->header_entry.addr1)); printf("\taddr2: 0x%x\n", ntohl(sub_entry->header_entry.addr2)); printf("\tstart: 0x%x\n", ntohl(sub_entry->header_entry.start)); printf("\tabs. offset: 0x%lx\n", sub_entry->absolute_offset); printf("\n\n"); dump_entry(sub_entry, part++, fd); } printf("\n\n"); } if (close(fd) != 0) { perror("Close failed.\n"); return; } talloc_free(tall_firm_ctx); } static void print_usage(void) { printf("Usage: ipaccess-config IP_OF_BTS\n"); } static void print_help(void) { #if 0 printf("Commmands for reading from the BTS:\n"); printf(" -D --dump\t\t\tDump the BTS configuration\n"); printf("\n"); #endif printf("Commmands for writing to the BTS:\n"); printf(" -u --unit-id UNIT_ID\t\tSet the Unit ID of the BTS\n"); printf(" -o --oml-ip IP\t\tSet primary OML IP (IP of your BSC)\n"); printf(" -i --ip-address IP/MASK\tSet static IP address + netmask of BTS\n"); printf(" -g --ip-gateway IP\t\tSet static IP gateway of BTS\n"); printf(" -r --restart\t\t\tRestart the BTS (after other operations)\n"); printf(" -n --nvram-flags FLAGS/MASK\tSet NVRAM attributes\n"); printf(" -S --nvattr-set FLAG\tSet one additional NVRAM attribute\n"); printf(" -U --nvattr-unset FLAG\tSet one additional NVRAM attribute\n"); printf(" -l --listen TESTNR\t\tPerform specified test number\n"); printf(" -L --Listen TEST_NAME\t\tPerform specified test\n"); printf(" -s --stream-id ID\t\tSet the IPA Stream Identifier for OML\n"); printf(" -d --software FIRMWARE\tDownload firmware into BTS\n"); printf("\n"); printf("Miscellaneous commands:\n"); printf(" -h --help\t\t\tthis text\n"); printf(" -H --HELP\t\t\tPrint parameter details.\n"); printf(" -f --firmware FIRMWARE\tProvide firmware information\n"); printf(" -w --write-firmware\t\tThis will dump the firmware parts to the filesystem. Use with -f.\n"); printf(" -p --loop\t\t\tLoop the tests executed with the --listen command.\n"); } static void print_value_string(const struct value_string *val, int size) { int i; for (i = 0; i < size - 1; ++i) { char sep = val[i + 1].str == NULL ? '.' : ','; printf("%s%c ", val[i].str, sep); } printf("\n"); } static void print_options(void) { printf("Options for NVRAM (-S,-U):\n "); print_value_string(&ipa_nvflag_strs[0], ARRAY_SIZE(ipa_nvflag_strs)); printf("Options for Tests (-L):\n "); print_value_string(&ipa_test_strs[0], ARRAY_SIZE(ipa_test_strs)); } extern void bts_model_nanobts_init(); int main(int argc, char **argv) { struct gsm_bts *bts; struct sockaddr_in sin; int rc, option_index = 0, stream_id = 0xff; osmo_init_logging(&log_info); log_parse_category_mask(osmo_stderr_target, "DNM,0"); bts_model_nanobts_init(); printf("ipaccess-config (C) 2009-2010 by Harald Welte and others\n"); printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n"); while (1) { int c; unsigned long ul; char *slash; static struct option long_options[] = { { "unit-id", 1, 0, 'u' }, { "oml-ip", 1, 0, 'o' }, { "ip-address", 1, 0, 'i' }, { "ip-gateway", 1, 0, 'g' }, { "restart", 0, 0, 'r' }, { "nvram-flags", 1, 0, 'n' }, { "nvattr-set", 1, 0, 'S' }, { "nvattr-unset", 1, 0, 'U' }, { "help", 0, 0, 'h' }, { "HELP", 0, 0, 'H' }, { "listen", 1, 0, 'l' }, { "Listen", 1, 0, 'L' }, { "stream-id", 1, 0, 's' }, { "software", 1, 0, 'd' }, { "firmware", 1, 0, 'f' }, { "write-firmware", 0, 0, 'w' }, { "disable-color", 0, 0, 'c'}, { "loop", 0, 0, 'p' }, { 0, 0, 0, 0 }, }; c = getopt_long(argc, argv, "u:o:i:g:rn:S:U:l:L:hs:d:f:wcpH", long_options, &option_index); if (c == -1) break; switch (c) { case 'u': unit_id = optarg; break; case 'o': prim_oml_ip = optarg; break; case 'i': slash = strchr(optarg, '/'); if (!slash) exit(2); bts_ip_addr = optarg; *slash = 0; bts_ip_mask = slash+1; break; case 'g': bts_ip_gw = optarg; break; case 'r': restart = 1; break; case 'n': slash = strchr(optarg, '/'); if (!slash) exit(2); ul = strtoul(optarg, NULL, 16); nv_flags = ul & 0xffff; ul = strtoul(slash+1, NULL, 16); nv_mask = ul & 0xffff; break; case 'S': if (ipa_nvflag_set(&nv_flags, &nv_mask, optarg, 1) < 0) exit(2); break; case 'U': if (ipa_nvflag_set(&nv_flags, &nv_mask, optarg, 0) < 0) exit(2); break; case 'l': net_listen_testnr = atoi(optarg); break; case 'L': net_listen_testnr = get_string_value(ipa_test_strs, optarg); if (net_listen_testnr < 0) { fprintf(stderr, "The test '%s' is not known. Use -H to" " see available tests.\n", optarg); exit(2); } break; case 's': stream_id = atoi(optarg); break; case 'd': software = strdup(optarg); if (find_sw_load_params(optarg) != 0) exit(0); break; case 'f': firmware_analysis = optarg; break; case 'w': dump_files = 1; break; case 'c': log_set_use_color(osmo_stderr_target, 0); break; case 'p': loop_tests = 1; break; case 'h': print_usage(); print_help(); exit(0); case 'H': print_options(); exit(0); } }; if (firmware_analysis) analyze_firmware(firmware_analysis); if (optind >= argc) { /* only warn if we have not done anything else */ if (!firmware_analysis) fprintf(stderr, "you have to specify the IP address of the BTS. Use --help for more information\n"); exit(2); } libosmo_abis_init(tall_ctx_config); bsc_gsmnet = gsm_network_init(1, 1, NULL); if (!bsc_gsmnet) exit(1); bts = gsm_bts_alloc_register(bsc_gsmnet, GSM_BTS_TYPE_NANOBTS, HARDCODED_TSC, HARDCODED_BSIC); /* ip.access supports up to 4 chained TRX */ gsm_bts_trx_alloc(bts); gsm_bts_trx_alloc(bts); gsm_bts_trx_alloc(bts); bts->oml_tei = stream_id; osmo_signal_register_handler(SS_NM, nm_sig_cb, NULL); osmo_signal_register_handler(SS_IPAC_NWL, nwl_sig_cb, NULL); ipac_nwl_init(); printf("Trying to connect to ip.access BTS ...\n"); memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; inet_aton(argv[optind], &sin.sin_addr); rc = ia_config_connect(bts, &sin); if (rc < 0) { perror("Error connecting to the BTS"); exit(1); } bts->oml_link->ts->sign.delay = 10; bts->c0->rsl_link->ts->sign.delay = 10; while (1) { rc = osmo_select_main(0); if (rc < 0) exit(3); } exit(0); } openbsc-0.15.0/openbsc/src/ipaccess/ipaccess-find.c000066400000000000000000000110331265565154000221230ustar00rootroot00000000000000/* ip.access nanoBTS configuration tool */ /* (C) 2009-2010 by Harald Welte * 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 . * */ #include #include #include #include #include #include #include #include #include #include #include static int udp_sock(const char *ifname) { int fd, rc, bc = 1; struct sockaddr_in sa; fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); if (fd < 0) return fd; if (ifname) { #ifdef __FreeBSD__ rc = setsockopt(fd, SOL_SOCKET, IP_RECVIF, ifname, strlen(ifname)); #else rc = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen(ifname)); #endif if (rc < 0) goto err; } memset(&sa, 0, sizeof(sa)); sa.sin_family = AF_INET; sa.sin_port = htons(3006); sa.sin_addr.s_addr = INADDR_ANY; rc = bind(fd, (struct sockaddr *)&sa, sizeof(sa)); if (rc < 0) goto err; rc = setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &bc, sizeof(bc)); if (rc < 0) goto err; #if 0 /* we cannot bind, since the response packets don't come from * the broadcast address */ sa.sin_family = AF_INET; sa.sin_port = htons(3006); inet_aton("255.255.255.255", &sa.sin_addr); rc = connect(fd, (struct sockaddr *)&sa, sizeof(sa)); if (rc < 0) goto err; #endif return fd; err: close(fd); return rc; } const unsigned char find_pkt[] = { 0x00, 0x0b+8, IPAC_PROTO_IPACCESS, 0x00, IPAC_MSGT_ID_GET, 0x01, IPAC_IDTAG_MACADDR, 0x01, IPAC_IDTAG_IPADDR, 0x01, IPAC_IDTAG_UNIT, 0x01, IPAC_IDTAG_LOCATION1, 0x01, IPAC_IDTAG_LOCATION2, 0x01, IPAC_IDTAG_EQUIPVERS, 0x01, IPAC_IDTAG_SWVERSION, 0x01, IPAC_IDTAG_UNITNAME, 0x01, IPAC_IDTAG_SERNR, }; static int bcast_find(int fd) { struct sockaddr_in sa; sa.sin_family = AF_INET; sa.sin_port = htons(3006); inet_aton("255.255.255.255", &sa.sin_addr); return sendto(fd, find_pkt, sizeof(find_pkt), 0, (struct sockaddr *) &sa, sizeof(sa)); } static int parse_response(unsigned char *buf, int len) { uint8_t t_len; uint8_t t_tag; uint8_t *cur = buf; while (cur < buf + len) { t_len = *cur++; t_tag = *cur++; printf("%s='%s' ", ipa_ccm_idtag_name(t_tag), cur); cur += t_len; } printf("\n"); return 0; } static int read_response(int fd) { unsigned char buf[255]; struct sockaddr_in sa; int len; socklen_t sa_len = sizeof(sa); len = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&sa, &sa_len); if (len < 0) return len; /* 2 bytes length, 1 byte protocol (0xfe) */ if (buf[2] != 0xfe) return 0; if (buf[4] != IPAC_MSGT_ID_RESP) return 0; return parse_response(buf+6, len-6); } static int bfd_cb(struct osmo_fd *bfd, unsigned int flags) { if (flags & BSC_FD_READ) return read_response(bfd->fd); if (flags & BSC_FD_WRITE) { bfd->when &= ~BSC_FD_WRITE; return bcast_find(bfd->fd); } return 0; } static struct osmo_timer_list timer; static void timer_cb(void *_data) { struct osmo_fd *bfd = _data; bfd->when |= BSC_FD_WRITE; osmo_timer_schedule(&timer, 5, 0); } int main(int argc, char **argv) { struct osmo_fd bfd; char *ifname = NULL; int rc; printf("ipaccess-find (C) 2009 by Harald Welte\n"); printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n"); if (argc < 2) { fprintf(stdout, "you might need to specify the outgoing\n" " network interface, e.g. ``%s eth0''\n", argv[0]); } else { ifname = argv[1]; } bfd.cb = bfd_cb; bfd.when = BSC_FD_READ | BSC_FD_WRITE; bfd.fd = udp_sock(ifname); if (bfd.fd < 0) { perror("Cannot create local socket for broadcast udp"); exit(1); } osmo_fd_register(&bfd); timer.cb = timer_cb; timer.data = &bfd; osmo_timer_schedule(&timer, 5, 0); printf("Trying to find ip.access BTS by broadcast UDP...\n"); while (1) { rc = osmo_select_main(0); if (rc < 0) exit(3); } exit(0); } openbsc-0.15.0/openbsc/src/ipaccess/ipaccess-firmware.c000066400000000000000000000075441265565154000230330ustar00rootroot00000000000000/* Routines for parsing an ipacces SDP firmware file */ /* (C) 2009 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 . * */ #include #include #include #include #include #include #include #define PART_LENGTH 138 osmo_static_assert(sizeof(struct sdp_header_entry) == 138, right_entry); osmo_static_assert(sizeof(struct sdp_firmware) == 158, _right_header_length); /* more magic, the second "int" in the header */ static char more_magic[] = { 0x10, 0x02 }; int ipaccess_analyze_file(int fd, const unsigned int st_size, const unsigned int base_offset, struct llist_head *list) { struct sdp_firmware *firmware_header = 0; struct sdp_header *header; char buf[4096]; int rc, i; uint16_t table_size; uint16_t table_offset; off_t table_start; rc = read(fd, buf, sizeof(*firmware_header)); if (rc < 0) { perror("Can not read header start."); return -1; } firmware_header = (struct sdp_firmware *) &buf[0]; if (strncmp(firmware_header->magic, " SDP", 4) != 0) { fprintf(stderr, "Wrong magic.\n"); return -1; } if (memcmp(firmware_header->more_magic, more_magic, 2) != 0) { fprintf(stderr, "Wrong more magic. Got: 0x%x 0x%x vs. 0x%x 0x%x\n", firmware_header->more_magic[0] & 0xff, firmware_header->more_magic[1] & 0xff, more_magic[0], more_magic[1]); return -1; } if (ntohl(firmware_header->file_length) != st_size) { fprintf(stderr, "The filesize and the header do not match.\n"); return -1; } /* add the firmware */ header = talloc_zero(list, struct sdp_header); header->firmware_info = *firmware_header; INIT_LLIST_HEAD(&header->header_list); llist_add(&header->entry, list); table_offset = ntohs(firmware_header->table_offset); table_start = lseek(fd, table_offset, SEEK_CUR); if (table_start == -1) { fprintf(stderr, "Failed to seek to the rel position: 0x%x\n", table_offset); return -1; } if (read(fd, &table_size, sizeof(table_size)) != sizeof(table_size)) { fprintf(stderr, "The table size could not be read.\n"); return -1; } table_size = ntohs(table_size); if (table_size % PART_LENGTH != 0) { fprintf(stderr, "The part length seems to be wrong: 0x%x\n", table_size); return -1; } /* look into each firmware now */ for (i = 0; i < table_size / PART_LENGTH; ++i) { struct sdp_header_entry entry; struct sdp_header_item *header_entry; unsigned int offset = table_start + 2; offset += i * 138; if (lseek(fd, offset, SEEK_SET) != offset) { fprintf(stderr, "Can not seek to the offset: %u.\n", offset); return -1; } rc = read(fd, &entry, sizeof(entry)); if (rc != sizeof(entry)) { fprintf(stderr, "Can not read the header entry.\n"); return -1; } header_entry = talloc_zero(header, struct sdp_header_item); header_entry->header_entry = entry; header_entry->absolute_offset = base_offset; llist_add(&header_entry->entry, &header->header_list); /* now we need to find the SDP file... */ offset = ntohl(entry.start) + 4 + base_offset; if (lseek(fd, offset, SEEK_SET) != offset) { perror("can't seek to sdp"); return -1; } ipaccess_analyze_file(fd, ntohl(entry.length), offset, list); } return 0; } openbsc-0.15.0/openbsc/src/ipaccess/ipaccess-proxy.c000066400000000000000000000753521265565154000224020ustar00rootroot00000000000000/* OpenBSC Abis/IP proxy ip.access nanoBTS */ /* (C) 2009 by Harald Welte * (C) 2010 by On-Waves * (C) 2010 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include /* one instance of an ip.access protocol proxy */ struct ipa_proxy { /* socket where we listen for incoming OML from BTS */ struct osmo_fd oml_listen_fd; /* socket where we listen for incoming RSL from BTS */ struct osmo_fd rsl_listen_fd; /* list of BTS's (struct ipa_bts_conn */ struct llist_head bts_list; /* the BSC reconnect timer */ struct osmo_timer_list reconn_timer; /* global GPRS NS data */ struct in_addr gprs_addr; struct in_addr listen_addr; }; /* global pointer to the proxy structure */ static struct ipa_proxy *ipp; struct ipa_proxy_conn { struct osmo_fd fd; struct llist_head tx_queue; struct ipa_bts_conn *bts_conn; }; #define MAX_TRX 4 /* represents a particular BTS in our proxy */ struct ipa_bts_conn { /* list of BTS's (ipa_proxy->bts_list) */ struct llist_head list; /* back pointer to the proxy which we belong to */ struct ipa_proxy *ipp; /* the unit ID as determined by CCM */ struct { uint16_t site_id; uint16_t bts_id; } unit_id; /* incoming connections from BTS */ struct ipa_proxy_conn *oml_conn; struct ipa_proxy_conn *rsl_conn[MAX_TRX]; /* outgoing connections to BSC */ struct ipa_proxy_conn *bsc_oml_conn; struct ipa_proxy_conn *bsc_rsl_conn[MAX_TRX]; /* UDP sockets for BTS and BSC injection */ struct osmo_fd udp_bts_fd; struct osmo_fd udp_bsc_fd; /* NS data */ struct in_addr bts_addr; struct osmo_fd gprs_ns_fd; int gprs_local_port; uint16_t gprs_orig_port; uint32_t gprs_orig_ip; char *id_tags[256]; uint8_t *id_resp; unsigned int id_resp_len; }; enum ipp_fd_type { OML_FROM_BTS = 1, RSL_FROM_BTS = 2, OML_TO_BSC = 3, RSL_TO_BSC = 4, UDP_TO_BTS = 5, UDP_TO_BSC = 6, }; /* some of the code against we link from OpenBSC needs this */ void *tall_bsc_ctx; static char *listen_ipaddr; static char *bsc_ipaddr; static char *gprs_ns_ipaddr; static int gprs_ns_cb(struct osmo_fd *bfd, unsigned int what); #define PROXY_ALLOC_SIZE 1200 static struct ipa_bts_conn *find_bts_by_unitid(struct ipa_proxy *ipp, uint16_t site_id, uint16_t bts_id) { struct ipa_bts_conn *ipbc; llist_for_each_entry(ipbc, &ipp->bts_list, list) { if (ipbc->unit_id.site_id == site_id && ipbc->unit_id.bts_id == bts_id) return ipbc; } return NULL; } struct ipa_proxy_conn *alloc_conn(void) { struct ipa_proxy_conn *ipc; ipc = talloc_zero(tall_bsc_ctx, struct ipa_proxy_conn); if (!ipc) return NULL; INIT_LLIST_HEAD(&ipc->tx_queue); return ipc; } static int store_idtags(struct ipa_bts_conn *ipbc, struct tlv_parsed *tlvp) { unsigned int i, len; for (i = 0; i <= 0xff; i++) { if (!TLVP_PRESENT(tlvp, i)) continue; len = TLVP_LEN(tlvp, i); #if 0 if (!ipbc->id_tags[i]) ipbc->id_tags[i] = talloc_size(tall_bsc_ctx, len); else #endif ipbc->id_tags[i] = talloc_realloc_size(ipbc, ipbc->id_tags[i], len); if (!ipbc->id_tags[i]) return -ENOMEM; memset(ipbc->id_tags[i], 0, len); //memcpy(ipbc->id_tags[i], TLVP_VAL(tlvp, i), len); } return 0; } static struct ipa_proxy_conn *connect_bsc(struct sockaddr_in *sa, int priv_nr, void *data); #define logp_ipbc_uid(ss, lvl, ipbc, trx_id) _logp_ipbc_uid(ss, lvl, __FILE__, __LINE__, ipbc, trx_id) static void _logp_ipbc_uid(unsigned int ss, unsigned int lvl, char *file, int line, struct ipa_bts_conn *ipbc, uint8_t trx_id) { if (ipbc) logp2(ss, lvl, file, line, 0, "(%u/%u/%u) ", ipbc->unit_id.site_id, ipbc->unit_id.bts_id, trx_id); else logp2(ss, lvl, file, line, 0, "unknown "); } static int handle_udp_read(struct osmo_fd *bfd) { struct ipa_bts_conn *ipbc = bfd->data; struct ipa_proxy_conn *other_conn = NULL; struct msgb *msg = msgb_alloc(PROXY_ALLOC_SIZE, "Abis/IP UDP"); struct ipaccess_head *hh; int ret; /* with UDP sockets, we cannot read partial packets but have to read * all of it in one go */ hh = (struct ipaccess_head *) msg->data; ret = recv(bfd->fd, msg->data, msg->data_len, 0); if (ret < 0) { if (errno != EAGAIN) LOGP(DLINP, LOGL_ERROR, "recv error %s\n", strerror(errno)); msgb_free(msg); return ret; } if (ret == 0) { DEBUGP(DLINP, "UDP peer disappeared, dead socket\n"); osmo_fd_unregister(bfd); close(bfd->fd); bfd->fd = -1; msgb_free(msg); return -EIO; } if (ret < sizeof(*hh)) { DEBUGP(DLINP, "could not even read header!?!\n"); msgb_free(msg); return -EIO; } msgb_put(msg, ret); msg->l2h = msg->data + sizeof(*hh); DEBUGP(DLMI, "UDP RX: %s\n", osmo_hexdump(msg->data, msg->len)); if (hh->len != msg->len - sizeof(*hh)) { DEBUGP(DLINP, "length (%u/%u) disagrees with header(%u)\n", msg->len, msg->len - 3, hh->len); msgb_free(msg); return -EIO; } switch (bfd->priv_nr & 0xff) { case UDP_TO_BTS: /* injection towards BTS */ switch (hh->proto) { case IPAC_PROTO_RSL: /* FIXME: what to do about TRX > 0 */ other_conn = ipbc->rsl_conn[0]; break; default: DEBUGP(DLINP, "Unknown protocol 0x%02x, sending to " "OML FD\n", hh->proto); /* fall through */ case IPAC_PROTO_IPACCESS: case IPAC_PROTO_OML: other_conn = ipbc->oml_conn; break; } break; case UDP_TO_BSC: /* injection towards BSC */ switch (hh->proto) { case IPAC_PROTO_RSL: /* FIXME: what to do about TRX > 0 */ other_conn = ipbc->bsc_rsl_conn[0]; break; default: DEBUGP(DLINP, "Unknown protocol 0x%02x, sending to " "OML FD\n", hh->proto); /* fall through */ case IPAC_PROTO_IPACCESS: case IPAC_PROTO_OML: other_conn = ipbc->bsc_oml_conn; break; } break; default: DEBUGP(DLINP, "Unknown filedescriptor priv_nr=%04x\n", bfd->priv_nr); break; } if (other_conn) { /* enqueue the message for TX on the respective FD */ msgb_enqueue(&other_conn->tx_queue, msg); other_conn->fd.when |= BSC_FD_WRITE; } else msgb_free(msg); return 0; } static int handle_udp_write(struct osmo_fd *bfd) { /* not implemented yet */ bfd->when &= ~BSC_FD_WRITE; return -EIO; } /* callback from select.c in case one of the fd's can be read/written */ static int udp_fd_cb(struct osmo_fd *bfd, unsigned int what) { int rc = 0; if (what & BSC_FD_READ) rc = handle_udp_read(bfd); if (what & BSC_FD_WRITE) rc = handle_udp_write(bfd); return rc; } static int ipbc_alloc_connect(struct ipa_proxy_conn *ipc, struct osmo_fd *bfd, uint16_t site_id, uint16_t bts_id, uint16_t trx_id, struct tlv_parsed *tlvp, struct msgb *msg) { struct ipa_bts_conn *ipbc; uint16_t udp_port; int ret = 0; struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; inet_aton(bsc_ipaddr, &sin.sin_addr); DEBUGP(DLINP, "(%u/%u/%u) New BTS connection: ", site_id, bts_id, trx_id); /* OML needs to be established before RSL */ if ((bfd->priv_nr & 0xff) != OML_FROM_BTS) { DEBUGPC(DLINP, "Not a OML connection ?!?\n"); return -EIO; } /* allocate new BTS connection data structure */ ipbc = talloc_zero(tall_bsc_ctx, struct ipa_bts_conn); if (!ipbc) { ret = -ENOMEM; goto err_out; } DEBUGPC(DLINP, "Created BTS Conn data structure\n"); ipbc->ipp = ipp; ipbc->unit_id.site_id = site_id; ipbc->unit_id.bts_id = bts_id; ipbc->oml_conn = ipc; ipc->bts_conn = ipbc; /* store the content of the ID TAGS for later reference */ store_idtags(ipbc, tlvp); ipbc->id_resp_len = msg->len; ipbc->id_resp = talloc_size(tall_bsc_ctx, ipbc->id_resp_len); memcpy(ipbc->id_resp, msg->data, ipbc->id_resp_len); /* Create OML TCP connection towards BSC */ sin.sin_port = htons(IPA_TCP_PORT_OML); ipbc->bsc_oml_conn = connect_bsc(&sin, OML_TO_BSC, ipbc); if (!ipbc->bsc_oml_conn) { ret = -EIO; goto err_bsc_conn; } DEBUGP(DLINP, "(%u/%u/%u) OML Connected to BSC\n", site_id, bts_id, trx_id); /* Create UDP socket for BTS packet injection */ udp_port = 10000 + (site_id % 1000)*100 + (bts_id % 100); ret = make_sock(&ipbc->udp_bts_fd, IPPROTO_UDP, INADDR_ANY, udp_port, UDP_TO_BTS, udp_fd_cb, ipbc); if (ret < 0) goto err_udp_bts; DEBUGP(DLINP, "(%u/%u/%u) Created UDP socket for injection " "towards BTS at port %u\n", site_id, bts_id, trx_id, udp_port); /* Create UDP socket for BSC packet injection */ udp_port = 20000 + (site_id % 1000)*100 + (bts_id % 100); ret = make_sock(&ipbc->udp_bsc_fd, IPPROTO_UDP, INADDR_ANY, udp_port, UDP_TO_BSC, udp_fd_cb, ipbc); if (ret < 0) goto err_udp_bsc; DEBUGP(DLINP, "(%u/%u/%u) Created UDP socket for injection " "towards BSC at port %u\n", site_id, bts_id, trx_id, udp_port); /* GPRS NS related code */ if (gprs_ns_ipaddr) { struct sockaddr_in sock; socklen_t len = sizeof(sock); struct in_addr addr; uint32_t ip; inet_aton(listen_ipaddr, &addr); ip = ntohl(addr.s_addr); /* make_sock() needs host byte order */ ret = make_sock(&ipbc->gprs_ns_fd, IPPROTO_UDP, ip, 0, 0, gprs_ns_cb, ipbc); if (ret < 0) { LOGP(DLINP, LOGL_ERROR, "Creating the GPRS socket failed.\n"); goto err_udp_bsc; } ret = getsockname(ipbc->gprs_ns_fd.fd, (struct sockaddr* ) &sock, &len); ipbc->gprs_local_port = ntohs(sock.sin_port); LOGP(DLINP, LOGL_NOTICE, "Created GPRS NS Socket. Listening on: %s:%d\n", inet_ntoa(sock.sin_addr), ipbc->gprs_local_port); ret = getpeername(bfd->fd, (struct sockaddr* ) &sock, &len); ipbc->bts_addr = sock.sin_addr; } llist_add(&ipbc->list, &ipp->bts_list); return 0; err_udp_bsc: osmo_fd_unregister(&ipbc->udp_bts_fd); err_udp_bts: osmo_fd_unregister(&ipbc->bsc_oml_conn->fd); close(ipbc->bsc_oml_conn->fd.fd); talloc_free(ipbc->bsc_oml_conn); ipbc->bsc_oml_conn = NULL; err_bsc_conn: talloc_free(ipbc->id_resp); talloc_free(ipbc); #if 0 osmo_fd_unregister(bfd); close(bfd->fd); talloc_free(bfd); #endif err_out: return ret; } static int ipaccess_rcvmsg(struct ipa_proxy_conn *ipc, struct msgb *msg, struct osmo_fd *bfd) { struct tlv_parsed tlvp; uint8_t msg_type = *(msg->l2h); struct ipaccess_unit unit_data; struct ipa_bts_conn *ipbc; int ret = 0; switch (msg_type) { case IPAC_MSGT_PING: ret = ipa_ccm_send_pong(bfd->fd); break; case IPAC_MSGT_PONG: DEBUGP(DLMI, "PONG!\n"); break; case IPAC_MSGT_ID_RESP: DEBUGP(DLMI, "ID_RESP "); /* parse tags, search for Unit ID */ ipa_ccm_idtag_parse(&tlvp, (uint8_t *)msg->l2h + 2, msgb_l2len(msg)-2); DEBUGP(DLMI, "\n"); if (!TLVP_PRESENT(&tlvp, IPAC_IDTAG_UNIT)) { LOGP(DLINP, LOGL_ERROR, "No Unit ID in ID RESPONSE !?!\n"); return -EIO; } /* lookup BTS, create sign_link, ... */ memset(&unit_data, 0, sizeof(unit_data)); ipa_parse_unitid((char *)TLVP_VAL(&tlvp, IPAC_IDTAG_UNIT), &unit_data); ipbc = find_bts_by_unitid(ipp, unit_data.site_id, unit_data.bts_id); if (!ipbc) { /* We have not found an ipbc (per-bts proxy instance) * for this BTS yet. The first connection of a new BTS must * be a OML connection. We allocate the associated data structures, * and try to connect to the remote end */ return ipbc_alloc_connect(ipc, bfd, unit_data.site_id, unit_data.bts_id, unit_data.trx_id, &tlvp, msg); /* if this fails, the caller will clean up bfd */ } else { struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; inet_aton(bsc_ipaddr, &sin.sin_addr); DEBUGP(DLINP, "Identified BTS %u/%u/%u\n", unit_data.site_id, unit_data.bts_id, unit_data.trx_id); if ((bfd->priv_nr & 0xff) != RSL_FROM_BTS) { LOGP(DLINP, LOGL_ERROR, "Second OML connection from " "same BTS ?!?\n"); return 0; } if (unit_data.trx_id >= MAX_TRX) { LOGP(DLINP, LOGL_ERROR, "We don't support more " "than %u TRX\n", MAX_TRX); return -EINVAL; } ipc->bts_conn = ipbc; /* store TRX number in higher 8 bit of the bfd private number */ bfd->priv_nr |= unit_data.trx_id << 8; ipbc->rsl_conn[unit_data.trx_id] = ipc; /* Create RSL TCP connection towards BSC */ sin.sin_port = htons(IPA_TCP_PORT_RSL); ipbc->bsc_rsl_conn[unit_data.trx_id] = connect_bsc(&sin, RSL_TO_BSC | (unit_data.trx_id << 8), ipbc); if (!ipbc->bsc_oml_conn) return -EIO; DEBUGP(DLINP, "(%u/%u/%u) Connected RSL to BSC\n", unit_data.site_id, unit_data.bts_id, unit_data.trx_id); } break; case IPAC_MSGT_ID_GET: DEBUGP(DLMI, "ID_GET\n"); if ((bfd->priv_nr & 0xff) != OML_TO_BSC && (bfd->priv_nr & 0xff) != RSL_TO_BSC) { DEBUGP(DLINP, "IDentity REQuest from BTS ?!?\n"); return -EIO; } ipbc = ipc->bts_conn; if (!ipbc) { DEBUGP(DLINP, "ID_GET from BSC before we have ID_RESP from BTS\n"); return -EIO; } ret = write(bfd->fd, ipbc->id_resp, ipbc->id_resp_len); break; case IPAC_MSGT_ID_ACK: DEBUGP(DLMI, "ID_ACK? -> ACK!\n"); ret = ipa_ccm_send_id_ack(bfd->fd); break; default: LOGP(DLMI, LOGL_ERROR, "Unhandled IPA type; %d\n", msg_type); return 1; break; } return 0; } struct msgb *ipaccess_proxy_read_msg(struct osmo_fd *bfd, int *error) { struct msgb *msg = msgb_alloc(PROXY_ALLOC_SIZE, "Abis/IP"); struct ipaccess_head *hh; int len, ret = 0; if (!msg) { *error = -ENOMEM; return NULL; } /* first read our 3-byte header */ hh = (struct ipaccess_head *) msg->data; ret = recv(bfd->fd, msg->data, 3, 0); if (ret < 0) { if (errno != EAGAIN) LOGP(DLINP, LOGL_ERROR, "recv error: %s\n", strerror(errno)); msgb_free(msg); *error = ret; return NULL; } else if (ret == 0) { msgb_free(msg); *error = ret; return NULL; } msgb_put(msg, ret); /* then read te length as specified in header */ msg->l2h = msg->data + sizeof(*hh); len = ntohs(hh->len); ret = recv(bfd->fd, msg->l2h, len, 0); if (ret < len) { LOGP(DLINP, LOGL_ERROR, "short read!\n"); msgb_free(msg); *error = -EIO; return NULL; } msgb_put(msg, ret); return msg; } static struct ipa_proxy_conn *ipc_by_priv_nr(struct ipa_bts_conn *ipbc, unsigned int priv_nr) { struct ipa_proxy_conn *bsc_conn; unsigned int trx_id = priv_nr >> 8; switch (priv_nr & 0xff) { case OML_FROM_BTS: /* incoming OML data from BTS, forward to BSC OML */ bsc_conn = ipbc->bsc_oml_conn; break; case RSL_FROM_BTS: /* incoming RSL data from BTS, forward to BSC RSL */ bsc_conn = ipbc->bsc_rsl_conn[trx_id]; break; case OML_TO_BSC: /* incoming OML data from BSC, forward to BTS OML */ bsc_conn = ipbc->oml_conn; break; case RSL_TO_BSC: /* incoming RSL data from BSC, forward to BTS RSL */ bsc_conn = ipbc->rsl_conn[trx_id]; break; default: bsc_conn = NULL; break; } return bsc_conn; } static void reconn_tmr_cb(void *data) { struct ipa_proxy *ipp = data; struct ipa_bts_conn *ipbc; struct sockaddr_in sin; int i; DEBUGP(DLINP, "Running reconnect timer\n"); memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; inet_aton(bsc_ipaddr, &sin.sin_addr); llist_for_each_entry(ipbc, &ipp->bts_list, list) { /* if OML to BSC is dead, try to restore it */ if (ipbc->oml_conn && !ipbc->bsc_oml_conn) { sin.sin_port = htons(IPA_TCP_PORT_OML); logp_ipbc_uid(DLINP, LOGL_NOTICE, ipbc, 0); LOGPC(DLINP, LOGL_NOTICE, "OML Trying to reconnect\n"); ipbc->bsc_oml_conn = connect_bsc(&sin, OML_TO_BSC, ipbc); if (!ipbc->bsc_oml_conn) goto reschedule; logp_ipbc_uid(DLINP, LOGL_NOTICE, ipbc, 0); LOGPC(DLINP, LOGL_NOTICE, "OML Reconnected\n"); } /* if we (still) don't have a OML connection, skip RSL */ if (!ipbc->oml_conn || !ipbc->bsc_oml_conn) continue; for (i = 0; i < ARRAY_SIZE(ipbc->rsl_conn); i++) { unsigned int priv_nr; /* don't establish RSL links which we don't have */ if (!ipbc->rsl_conn[i]) continue; if (ipbc->bsc_rsl_conn[i]) continue; priv_nr = ipbc->rsl_conn[i]->fd.priv_nr; priv_nr &= ~0xff; priv_nr |= RSL_TO_BSC; sin.sin_port = htons(IPA_TCP_PORT_RSL); logp_ipbc_uid(DLINP, LOGL_NOTICE, ipbc, priv_nr >> 8); LOGPC(DLINP, LOGL_NOTICE, "RSL Trying to reconnect\n"); ipbc->bsc_rsl_conn[i] = connect_bsc(&sin, priv_nr, ipbc); if (!ipbc->bsc_rsl_conn[i]) goto reschedule; logp_ipbc_uid(DLINP, LOGL_NOTICE, ipbc, priv_nr >> 8); LOGPC(DLINP, LOGL_NOTICE, "RSL Reconnected\n"); } } return; reschedule: osmo_timer_schedule(&ipp->reconn_timer, 5, 0); } static void handle_dead_socket(struct osmo_fd *bfd) { struct ipa_proxy_conn *ipc = bfd->data; /* local conn */ struct ipa_proxy_conn *bsc_conn; /* remote conn */ struct ipa_bts_conn *ipbc = ipc->bts_conn; unsigned int trx_id = bfd->priv_nr >> 8; struct msgb *msg, *msg2; osmo_fd_unregister(bfd); close(bfd->fd); bfd->fd = -1; /* FIXME: clear tx_queue, remove all references, etc. */ llist_for_each_entry_safe(msg, msg2, &ipc->tx_queue, list) msgb_free(msg); switch (bfd->priv_nr & 0xff) { case OML_FROM_BTS: /* incoming OML data from BTS, forward to BSC OML */ /* The BTS started a connection with us but we got no * IPAC_MSGT_ID_RESP message yet, in that scenario we did not * allocate the ipa_bts_conn structure. */ if (ipbc == NULL) break; ipbc->oml_conn = NULL; bsc_conn = ipbc->bsc_oml_conn; /* close the connection to the BSC */ osmo_fd_unregister(&bsc_conn->fd); close(bsc_conn->fd.fd); llist_for_each_entry_safe(msg, msg2, &bsc_conn->tx_queue, list) msgb_free(msg); talloc_free(bsc_conn); ipbc->bsc_oml_conn = NULL; /* FIXME: do we need to delete the entire ipbc ? */ break; case RSL_FROM_BTS: /* incoming RSL data from BTS, forward to BSC RSL */ ipbc->rsl_conn[trx_id] = NULL; bsc_conn = ipbc->bsc_rsl_conn[trx_id]; /* close the connection to the BSC */ osmo_fd_unregister(&bsc_conn->fd); close(bsc_conn->fd.fd); llist_for_each_entry_safe(msg, msg2, &bsc_conn->tx_queue, list) msgb_free(msg); talloc_free(bsc_conn); ipbc->bsc_rsl_conn[trx_id] = NULL; break; case OML_TO_BSC: /* incoming OML data from BSC, forward to BTS OML */ ipbc->bsc_oml_conn = NULL; bsc_conn = ipbc->oml_conn; /* start reconnect timer */ osmo_timer_schedule(&ipp->reconn_timer, 5, 0); break; case RSL_TO_BSC: /* incoming RSL data from BSC, forward to BTS RSL */ ipbc->bsc_rsl_conn[trx_id] = NULL; bsc_conn = ipbc->rsl_conn[trx_id]; /* start reconnect timer */ osmo_timer_schedule(&ipp->reconn_timer, 5, 0); break; default: bsc_conn = NULL; break; } talloc_free(ipc); } static void patch_gprs_msg(struct ipa_bts_conn *ipbc, int priv_nr, struct msgb *msg) { uint8_t *nsvci; if ((priv_nr & 0xff) != OML_FROM_BTS && (priv_nr & 0xff) != OML_TO_BSC) return; if (msgb_l2len(msg) != 39) return; /* * Check if this is a IPA Set Attribute or IPA Set Attribute ACK * and if the FOM Class is GPRS NSVC0 and then we will patch it. * * The patch assumes the message looks like the one from the trace * but we only match messages with a specific size anyway... So * this hack should work just fine. */ if (msg->l2h[0] == 0x10 && msg->l2h[1] == 0x80 && msg->l2h[2] == 0x00 && msg->l2h[3] == 0x15 && msg->l2h[18] == 0xf5 && msg->l2h[19] == 0xf2) { nsvci = &msg->l2h[23]; ipbc->gprs_orig_port = *(uint16_t *)(nsvci+8); ipbc->gprs_orig_ip = *(uint32_t *)(nsvci+10); *(uint16_t *)(nsvci+8) = htons(ipbc->gprs_local_port); *(uint32_t *)(nsvci+10) = ipbc->ipp->listen_addr.s_addr; } else if (msg->l2h[0] == 0x10 && msg->l2h[1] == 0x80 && msg->l2h[2] == 0x00 && msg->l2h[3] == 0x15 && msg->l2h[18] == 0xf6 && msg->l2h[19] == 0xf2) { nsvci = &msg->l2h[23]; *(uint16_t *)(nsvci+8) = ipbc->gprs_orig_port; *(uint32_t *)(nsvci+10) = ipbc->gprs_orig_ip; } } static int handle_tcp_read(struct osmo_fd *bfd) { struct ipa_proxy_conn *ipc = bfd->data; struct ipa_bts_conn *ipbc = ipc->bts_conn; struct ipa_proxy_conn *bsc_conn; struct msgb *msg; struct ipaccess_head *hh; int ret = 0; char *btsbsc; if ((bfd->priv_nr & 0xff) <= 2) btsbsc = "BTS"; else btsbsc = "BSC"; msg = ipaccess_proxy_read_msg(bfd, &ret); if (!msg) { if (ret == 0) { logp_ipbc_uid(DLINP, LOGL_NOTICE, ipbc, bfd->priv_nr >> 8); LOGPC(DLINP, LOGL_NOTICE, "%s disappeared, " "dead socket\n", btsbsc); handle_dead_socket(bfd); } return ret; } msgb_put(msg, ret); logp_ipbc_uid(DLMI, LOGL_DEBUG, ipbc, bfd->priv_nr >> 8); DEBUGPC(DLMI, "RX<-%s: %s\n", btsbsc, osmo_hexdump(msg->data, msg->len)); hh = (struct ipaccess_head *) msg->data; if (hh->proto == IPAC_PROTO_IPACCESS) { ret = ipaccess_rcvmsg(ipc, msg, bfd); if (ret < 0) { osmo_fd_unregister(bfd); close(bfd->fd); bfd->fd = -1; talloc_free(bfd); msgb_free(msg); return ret; } else if (ret == 0) { /* we do not forward parts of the CCM protocol * through the proxy but rather terminate it ourselves. */ msgb_free(msg); return ret; } } if (!ipbc) { LOGP(DLINP, LOGL_ERROR, "received %s packet but no ipc->bts_conn?!?\n", btsbsc); msgb_free(msg); return -EIO; } bsc_conn = ipc_by_priv_nr(ipbc, bfd->priv_nr); if (bsc_conn) { if (gprs_ns_ipaddr) patch_gprs_msg(ipbc, bfd->priv_nr, msg); /* enqueue packet towards BSC */ msgb_enqueue(&bsc_conn->tx_queue, msg); /* mark respective filedescriptor as 'we want to write' */ bsc_conn->fd.when |= BSC_FD_WRITE; } else { logp_ipbc_uid(DLINP, LOGL_INFO, ipbc, bfd->priv_nr >> 8); LOGPC(DLINP, LOGL_INFO, "Dropping packet from %s, " "since remote connection is dead\n", btsbsc); msgb_free(msg); } return ret; } /* a TCP socket is ready to be written to */ static int handle_tcp_write(struct osmo_fd *bfd) { struct ipa_proxy_conn *ipc = bfd->data; struct ipa_bts_conn *ipbc = ipc->bts_conn; struct llist_head *lh; struct msgb *msg; char *btsbsc; int ret; if ((bfd->priv_nr & 0xff) <= 2) btsbsc = "BTS"; else btsbsc = "BSC"; /* get the next msg for this timeslot */ if (llist_empty(&ipc->tx_queue)) { bfd->when &= ~BSC_FD_WRITE; return 0; } lh = ipc->tx_queue.next; llist_del(lh); msg = llist_entry(lh, struct msgb, list); logp_ipbc_uid(DLMI, LOGL_DEBUG, ipbc, bfd->priv_nr >> 8); DEBUGPC(DLMI, "TX %04x: %s\n", bfd->priv_nr, osmo_hexdump(msg->data, msg->len)); ret = send(bfd->fd, msg->data, msg->len, 0); msgb_free(msg); if (ret == 0) { logp_ipbc_uid(DLINP, LOGL_NOTICE, ipbc, bfd->priv_nr >> 8); LOGP(DLINP, LOGL_NOTICE, "%s disappeared, dead socket\n", btsbsc); handle_dead_socket(bfd); } return ret; } /* callback from select.c in case one of the fd's can be read/written */ static int proxy_ipaccess_fd_cb(struct osmo_fd *bfd, unsigned int what) { int rc = 0; if (what & BSC_FD_READ) { rc = handle_tcp_read(bfd); if (rc < 0) return rc; } if (what & BSC_FD_WRITE) rc = handle_tcp_write(bfd); return rc; } /* callback of the listening filedescriptor */ static int listen_fd_cb(struct osmo_fd *listen_bfd, unsigned int what) { int ret; struct ipa_proxy_conn *ipc; struct osmo_fd *bfd; struct sockaddr_in sa; socklen_t sa_len = sizeof(sa); if (!(what & BSC_FD_READ)) return 0; ret = accept(listen_bfd->fd, (struct sockaddr *) &sa, &sa_len); if (ret < 0) { perror("accept"); return ret; } DEBUGP(DLINP, "accept()ed new %s link from %s\n", (listen_bfd->priv_nr & 0xff) == OML_FROM_BTS ? "OML" : "RSL", inet_ntoa(sa.sin_addr)); ipc = alloc_conn(); if (!ipc) { close(ret); return -ENOMEM; } bfd = &ipc->fd; bfd->fd = ret; bfd->data = ipc; bfd->priv_nr = listen_bfd->priv_nr; bfd->cb = proxy_ipaccess_fd_cb; bfd->when = BSC_FD_READ; ret = osmo_fd_register(bfd); if (ret < 0) { LOGP(DLINP, LOGL_ERROR, "could not register FD\n"); close(bfd->fd); talloc_free(ipc); return ret; } /* Request ID. FIXME: request LOCATION, HW/SW VErsion, Unit Name, Serno */ ret = ipa_ccm_send_id_req(bfd->fd); return 0; } static void send_ns(int fd, const char *buf, int size, struct in_addr ip, int port) { int ret; struct sockaddr_in addr; socklen_t len = sizeof(addr); memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr = ip; ret = sendto(fd, buf, size, 0, (struct sockaddr *) &addr, len); if (ret < 0) { LOGP(DLINP, LOGL_ERROR, "Failed to forward GPRS message.\n"); } } static int gprs_ns_cb(struct osmo_fd *bfd, unsigned int what) { struct ipa_bts_conn *bts; char buf[4096]; int ret; struct sockaddr_in sock; socklen_t len = sizeof(sock); /* 1. get the data... */ ret = recvfrom(bfd->fd, buf, sizeof(buf), 0, (struct sockaddr *) &sock, &len); if (ret < 0) { LOGP(DLINP, LOGL_ERROR, "Failed to recv GPRS NS msg: %s.\n", strerror(errno)); return -1; } bts = bfd->data; /* 2. figure out where to send it to */ if (memcmp(&sock.sin_addr, &ipp->gprs_addr, sizeof(sock.sin_addr)) == 0) { LOGP(DLINP, LOGL_DEBUG, "GPRS NS msg from network.\n"); send_ns(bfd->fd, buf, ret, bts->bts_addr, 23000); } else if (memcmp(&sock.sin_addr, &bts->bts_addr, sizeof(sock.sin_addr)) == 0) { LOGP(DLINP, LOGL_DEBUG, "GPRS NS msg from BTS.\n"); send_ns(bfd->fd, buf, ret, ipp->gprs_addr, 23000); } else { LOGP(DLINP, LOGL_ERROR, "Unknown GPRS source: %s\n", inet_ntoa(sock.sin_addr)); } return 0; } /* Actively connect to a BSC. */ static struct ipa_proxy_conn *connect_bsc(struct sockaddr_in *sa, int priv_nr, void *data) { struct ipa_proxy_conn *ipc; struct osmo_fd *bfd; int ret, on = 1; ipc = alloc_conn(); if (!ipc) return NULL; ipc->bts_conn = data; bfd = &ipc->fd; bfd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); bfd->cb = ipaccess_fd_cb; bfd->when = BSC_FD_READ | BSC_FD_WRITE; bfd->data = ipc; bfd->priv_nr = priv_nr; if (bfd->fd < 0) { LOGP(DLINP, LOGL_ERROR, "Could not create socket: %s\n", strerror(errno)); talloc_free(ipc); return NULL; } setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); ret = connect(bfd->fd, (struct sockaddr *) sa, sizeof(*sa)); if (ret < 0) { LOGP(DLINP, LOGL_ERROR, "Could not connect socket: %s\n", inet_ntoa(sa->sin_addr)); close(bfd->fd); talloc_free(ipc); return NULL; } /* pre-fill tx_queue with identity request */ ret = osmo_fd_register(bfd); if (ret < 0) { close(bfd->fd); talloc_free(ipc); return NULL; } return ipc; } static int ipaccess_proxy_setup(void) { int ret; ipp = talloc_zero(tall_bsc_ctx, struct ipa_proxy); if (!ipp) return -ENOMEM; INIT_LLIST_HEAD(&ipp->bts_list); ipp->reconn_timer.cb = reconn_tmr_cb; ipp->reconn_timer.data = ipp; /* Listen for OML connections */ ret = make_sock(&ipp->oml_listen_fd, IPPROTO_TCP, INADDR_ANY, IPA_TCP_PORT_OML, OML_FROM_BTS, listen_fd_cb, NULL); if (ret < 0) return ret; /* Listen for RSL connections */ ret = make_sock(&ipp->rsl_listen_fd, IPPROTO_TCP, INADDR_ANY, IPA_TCP_PORT_RSL, RSL_FROM_BTS, listen_fd_cb, NULL); if (ret < 0) return ret; /* Connect the GPRS NS Socket */ if (gprs_ns_ipaddr) { inet_aton(gprs_ns_ipaddr, &ipp->gprs_addr); inet_aton(listen_ipaddr, &ipp->listen_addr); } return ret; } static void signal_handler(int signal) { fprintf(stdout, "signal %u received\n", signal); switch (signal) { 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_full(tall_bsc_ctx, stderr); break; default: break; } } static void print_help(void) { printf(" ipaccess-proxy is a proxy BTS.\n"); printf(" -h --help. This help text.\n"); printf(" -l --listen IP. The ip to listen to.\n"); printf(" -b --bsc IP. The BSC IP address.\n"); printf(" -g --gprs IP. Take GPRS NS from that IP.\n"); printf("\n"); printf(" -s --disable-color. Disable the color inside the logging message.\n"); printf(" -e --log-level number. Set the global loglevel.\n"); printf(" -T --timestamp. Prefix every log message with a timestamp.\n"); printf(" -V --version. Print the version of OpenBSC.\n"); } static void print_usage(void) { printf("Usage: ipaccess-proxy [options]\n"); } enum { IPA_PROXY_OPT_LISTEN_NONE = 0, IPA_PROXY_OPT_LISTEN_IP = (1 << 0), IPA_PROXY_OPT_BSC_IP = (1 << 1), }; static void handle_options(int argc, char** argv) { int options_mask = 0; /* disable explicit missing arguments error output from getopt_long */ opterr = 0; while (1) { int option_index = 0, c; static struct option long_options[] = { {"help", 0, 0, 'h'}, {"disable-color", 0, 0, 's'}, {"timestamp", 0, 0, 'T'}, {"log-level", 1, 0, 'e'}, {"listen", 1, 0, 'l'}, {"bsc", 1, 0, 'b'}, {0, 0, 0, 0} }; c = getopt_long(argc, argv, "hsTe:l:b:g:", long_options, &option_index); if (c == -1) break; switch (c) { case 'h': print_usage(); print_help(); exit(0); case 'l': listen_ipaddr = optarg; options_mask |= IPA_PROXY_OPT_LISTEN_IP; break; case 'b': bsc_ipaddr = optarg; options_mask |= IPA_PROXY_OPT_BSC_IP; break; case 'g': gprs_ns_ipaddr = optarg; break; case 's': log_set_use_color(osmo_stderr_target, 0); 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 '?': if (optopt) { printf("ERROR: missing mandatory argument " "for `%s' option\n", argv[optind-1]); } else { printf("ERROR: unknown option `%s'\n", argv[optind-1]); } print_usage(); print_help(); exit(EXIT_FAILURE); break; default: /* ignore */ break; } } if ((options_mask & (IPA_PROXY_OPT_LISTEN_IP | IPA_PROXY_OPT_BSC_IP)) != (IPA_PROXY_OPT_LISTEN_IP | IPA_PROXY_OPT_BSC_IP)) { printf("ERROR: You have to specify `--listen' and `--bsc' " "options at least.\n"); print_usage(); print_help(); exit(EXIT_FAILURE); } } int main(int argc, char **argv) { int rc; tall_bsc_ctx = talloc_named_const(NULL, 1, "ipaccess-proxy"); osmo_init_logging(&log_info); log_parse_category_mask(osmo_stderr_target, "DLINP:DLMI"); handle_options(argc, argv); rc = ipaccess_proxy_setup(); if (rc < 0) exit(1); signal(SIGUSR1, &signal_handler); signal(SIGABRT, &signal_handler); osmo_init_ignore_signals(); while (1) { osmo_select_main(0); } } openbsc-0.15.0/openbsc/src/ipaccess/network_listen.c000066400000000000000000000164711265565154000224750ustar00rootroot00000000000000/* ip.access nanoBTS network listen mode */ /* (C) 2009-2010 by Harald Welte * * 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define WHITELIST_MAX_SIZE ((NUM_ARFCNS*2)+2+1) int ipac_rxlevstat2whitelist(uint16_t *buf, const struct rxlev_stats *st, uint8_t min_rxlev, uint16_t max_num_arfcns) { int i; unsigned int num_arfcn = 0; for (i = NUM_RXLEVS-1; i >= min_rxlev; i--) { int16_t arfcn = -1; while ((arfcn = rxlev_stat_get_next(st, i, arfcn)) >= 0) { *buf++ = htons(arfcn); num_arfcn++; } if (num_arfcn > max_num_arfcns) break; } return num_arfcn; } enum ipac_test_state { IPAC_TEST_S_IDLE, IPAC_TEST_S_RQD, IPAC_TEST_S_EXEC, IPAC_TEST_S_PARTIAL, }; int ipac_nwl_test_start(struct gsm_bts_trx *trx, uint8_t testnr, const uint8_t *phys_conf, unsigned int phys_conf_len) { struct msgb *msg; if (trx->ipaccess.test_state != IPAC_TEST_S_IDLE) { fprintf(stderr, "Cannot start test in state %u\n", trx->ipaccess.test_state); return -EINVAL; } switch (testnr) { case NM_IPACC_TESTNO_CHAN_USAGE: case NM_IPACC_TESTNO_BCCH_CHAN_USAGE: rxlev_stat_reset(&trx->ipaccess.rxlev_stat); break; } msg = msgb_alloc_headroom(phys_conf_len+256, 128, "OML"); if (phys_conf && phys_conf_len) { uint8_t *payload; /* first put the phys conf header */ msgb_tv16_put(msg, NM_ATT_PHYS_CONF, phys_conf_len); payload = msgb_put(msg, phys_conf_len); memcpy(payload, phys_conf, phys_conf_len); } abis_nm_perform_test(trx->bts, NM_OC_RADIO_CARRIER, 0, trx->nr, 0xff, testnr, 1, msg); trx->ipaccess.test_nr = testnr; /* FIXME: start safety timer until when test is supposed to complete */ return 0; } static uint16_t last_arfcn; static struct gsm_sysinfo_freq nwl_si_freq[1024]; #define FREQ_TYPE_NCELL_2 0x04 /* sub channel of SI 2 */ #define FREQ_TYPE_NCELL_2bis 0x08 /* sub channel of SI 2bis */ #define FREQ_TYPE_NCELL_2ter 0x10 /* sub channel of SI 2ter */ struct ipacc_ferr_elem { int16_t freq_err; uint8_t freq_qual; uint8_t arfcn; } __attribute__((packed)); struct ipacc_cusage_elem { uint16_t arfcn:10, rxlev:6; } __attribute__ ((packed)); static int test_rep(void *_msg) { struct msgb *msg = _msg; struct abis_om_fom_hdr *foh = msgb_l3(msg); uint16_t test_rep_len, ferr_list_len; struct ipacc_ferr_elem *ife; struct ipac_bcch_info binfo; struct e1inp_sign_link *sign_link = (struct e1inp_sign_link *)msg->dst; int i, rc; DEBUGP(DNM, "TEST REPORT: "); if (foh->data[0] != NM_ATT_TEST_NO || foh->data[2] != NM_ATT_TEST_REPORT) return -EINVAL; DEBUGPC(DNM, "test_no=0x%02x ", foh->data[1]); /* data[2] == NM_ATT_TEST_REPORT */ /* data[3..4]: test_rep_len */ memcpy(&test_rep_len, &foh->data[3], sizeof(uint16_t)); test_rep_len = ntohs(test_rep_len); /* data[5]: ip.access test result */ DEBUGPC(DNM, "tst_res=%s\n", ipacc_testres_name(foh->data[5])); /* data[6]: ip.access nested IE. 3 == freq_err_list */ switch (foh->data[6]) { case NM_IPAC_EIE_FREQ_ERR_LIST: /* data[7..8]: length of ferr_list */ memcpy(&ferr_list_len, &foh->data[7], sizeof(uint16_t)); ferr_list_len = ntohs(ferr_list_len); /* data[9...]: frequency error list elements */ for (i = 0; i < ferr_list_len; i+= sizeof(*ife)) { ife = (struct ipacc_ferr_elem *) (foh->data + 9 + i); DEBUGP(DNM, "==> ARFCN %4u, Frequency Error %6hd\n", ife->arfcn, ntohs(ife->freq_err)); } break; case NM_IPAC_EIE_CHAN_USE_LIST: /* data[7..8]: length of ferr_list */ memcpy(&ferr_list_len, &foh->data[7], sizeof(uint16_t)); ferr_list_len = ntohs(ferr_list_len); /* data[9...]: channel usage list elements */ for (i = 0; i < ferr_list_len; i+= 2) { uint16_t *cu_ptr = (uint16_t *)(foh->data + 9 + i); uint16_t cu = ntohs(*cu_ptr); uint16_t arfcn = cu & 0x3ff; uint8_t rxlev = cu >> 10; DEBUGP(DNM, "==> ARFCN %4u, RxLev %2u\n", arfcn, rxlev); rxlev_stat_input(&sign_link->trx->ipaccess.rxlev_stat, arfcn, rxlev); } break; case NM_IPAC_EIE_BCCH_INFO_TYPE: break; case NM_IPAC_EIE_BCCH_INFO: rc = ipac_parse_bcch_info(&binfo, foh->data+6); if (rc < 0) { DEBUGP(DNM, "BCCH Info parsing failed\n"); break; } DEBUGP(DNM, "==> ARFCN %u, RxLev %2u, RxQual %2u: %3d-%d, LAC %d CI %d BSIC %u\n", binfo.arfcn, binfo.rx_lev, binfo.rx_qual, binfo.cgi.mcc, binfo.cgi.mnc, binfo.cgi.lac, binfo.cgi.ci, binfo.bsic); if (binfo.arfcn != last_arfcn) { /* report is on a new arfcn, need to clear channel list */ memset(nwl_si_freq, 0, sizeof(nwl_si_freq)); last_arfcn = binfo.arfcn; } if (binfo.info_type & IPAC_BINF_NEIGH_BA_SI2) { DEBUGP(DNM, "BA SI2: %s\n", osmo_hexdump(binfo.ba_list_si2, sizeof(binfo.ba_list_si2))); gsm48_decode_freq_list(nwl_si_freq, binfo.ba_list_si2, sizeof(binfo.ba_list_si2), 0x8c, FREQ_TYPE_NCELL_2); } if (binfo.info_type & IPAC_BINF_NEIGH_BA_SI2bis) { DEBUGP(DNM, "BA SI2bis: %s\n", osmo_hexdump(binfo.ba_list_si2bis, sizeof(binfo.ba_list_si2bis))); gsm48_decode_freq_list(nwl_si_freq, binfo.ba_list_si2bis, sizeof(binfo.ba_list_si2bis), 0x8e, FREQ_TYPE_NCELL_2bis); } if (binfo.info_type & IPAC_BINF_NEIGH_BA_SI2ter) { DEBUGP(DNM, "BA SI2ter: %s\n", osmo_hexdump(binfo.ba_list_si2ter, sizeof(binfo.ba_list_si2ter))); gsm48_decode_freq_list(nwl_si_freq, binfo.ba_list_si2ter, sizeof(binfo.ba_list_si2ter), 0x8e, FREQ_TYPE_NCELL_2ter); } for (i = 0; i < ARRAY_SIZE(nwl_si_freq); i++) { if (nwl_si_freq[i].mask) DEBUGP(DNM, "Neighbor Cell on ARFCN %u\n", i); } break; default: break; } switch (foh->data[5]) { case NM_IPACC_TESTRES_SUCCESS: case NM_IPACC_TESTRES_STOPPED: case NM_IPACC_TESTRES_TIMEOUT: case NM_IPACC_TESTRES_NO_CHANS: sign_link->trx->ipaccess.test_state = IPAC_TEST_S_IDLE; /* Send signal to notify higher layers of test completion */ DEBUGP(DNM, "dispatching S_IPAC_NWL_COMPLETE signal\n"); osmo_signal_dispatch(SS_IPAC_NWL, S_IPAC_NWL_COMPLETE, sign_link->trx); break; case NM_IPACC_TESTRES_PARTIAL: sign_link->trx->ipaccess.test_state = IPAC_TEST_S_PARTIAL; break; } return 0; } static int nwl_sig_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { switch (signal) { case S_NM_TEST_REP: return test_rep(signal_data); default: break; } return 0; } void ipac_nwl_init(void) { osmo_signal_register_handler(SS_NM, nwl_sig_cb, NULL); } openbsc-0.15.0/openbsc/src/libbsc/000077500000000000000000000000001265565154000167155ustar00rootroot00000000000000openbsc-0.15.0/openbsc/src/libbsc/Makefile.am000066400000000000000000000014401265565154000207500ustar00rootroot00000000000000AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) \ $(LIBOSMOVTY_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) noinst_LIBRARIES = libbsc.a libbsc_a_SOURCES = abis_nm.c abis_nm_vty.c \ abis_om2000.c abis_om2000_vty.c \ abis_rsl.c bsc_rll.c \ paging.c \ bts_ericsson_rbs2000.c \ bts_ipaccess_nanobts.c \ bts_siemens_bs11.c \ bts_nokia_site.c \ bts_unknown.c \ bts_sysmobts.c \ chan_alloc.c \ handover_decision.c handover_logic.c meas_rep.c \ rest_octets.c system_information.c \ e1_config.c \ bsc_api.c bsc_msc.c bsc_vty.c \ gsm_04_08_utils.c \ bsc_init.c bts_init.c bsc_rf_ctrl.c \ arfcn_range_encode.c bsc_ctrl_commands.c \ bsc_ctrl_lookup.c \ net_init.c openbsc-0.15.0/openbsc/src/libbsc/abis_nm.c000066400000000000000000002161211265565154000204740ustar00rootroot00000000000000/* GSM Network Management (OML) messages on the A-bis interface * 3GPP TS 12.21 version 8.0.0 Release 1999 / ETSI TS 100 623 V8.0.0 */ /* (C) 2008-2009 by Harald Welte * * 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define OM_ALLOC_SIZE 1024 #define OM_HEADROOM_SIZE 128 #define IPACC_SEGMENT_SIZE 245 int abis_nm_tlv_parse(struct tlv_parsed *tp, struct gsm_bts *bts, const uint8_t *buf, int len) { if (!bts->model) return -EIO; return tlv_parse(tp, &bts->model->nm_att_tlvdef, buf, len, 0, 0); } static int is_in_arr(enum abis_nm_msgtype mt, const enum abis_nm_msgtype *arr, int size) { int i; for (i = 0; i < size; i++) { if (arr[i] == mt) return 1; } return 0; } #if 0 /* is this msgtype the usual ACK/NACK type ? */ static int is_ack_nack(enum abis_nm_msgtype mt) { return !is_in_arr(mt, no_ack_nack, ARRAY_SIZE(no_ack_nack)); } #endif /* is this msgtype a report ? */ static int is_report(enum abis_nm_msgtype mt) { return is_in_arr(mt, abis_nm_reports, ARRAY_SIZE(abis_nm_reports)); } #define MT_ACK(x) (x+1) #define MT_NACK(x) (x+2) static void fill_om_hdr(struct abis_om_hdr *oh, uint8_t len) { oh->mdisc = ABIS_OM_MDISC_FOM; oh->placement = ABIS_OM_PLACEMENT_ONLY; oh->sequence = 0; oh->length = len; } static struct abis_om_fom_hdr *fill_om_fom_hdr(struct abis_om_hdr *oh, uint8_t len, uint8_t msg_type, uint8_t obj_class, uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr) { struct abis_om_fom_hdr *foh = (struct abis_om_fom_hdr *) oh->data; fill_om_hdr(oh, len+sizeof(*foh)); foh->msg_type = msg_type; foh->obj_class = obj_class; foh->obj_inst.bts_nr = bts_nr; foh->obj_inst.trx_nr = trx_nr; foh->obj_inst.ts_nr = ts_nr; return foh; } static struct msgb *nm_msgb_alloc(void) { return msgb_alloc_headroom(OM_ALLOC_SIZE, OM_HEADROOM_SIZE, "OML"); } int _abis_nm_sendmsg(struct msgb *msg) { msg->l2h = msg->data; if (!msg->dst) { LOGP(DNM, LOGL_ERROR, "%s: msg->dst == NULL\n", __func__); return -EINVAL; } return abis_sendmsg(msg); } /* Send a OML NM Message from BSC to BTS */ static int abis_nm_queue_msg(struct gsm_bts *bts, struct msgb *msg) { msg->dst = bts->oml_link; /* queue OML messages */ if (llist_empty(&bts->abis_queue) && !bts->abis_nm_pend) { bts->abis_nm_pend = OBSC_NM_W_ACK_CB(msg); return _abis_nm_sendmsg(msg); } else { msgb_enqueue(&bts->abis_queue, msg); return 0; } } int abis_nm_sendmsg(struct gsm_bts *bts, struct msgb *msg) { OBSC_NM_W_ACK_CB(msg) = 1; return abis_nm_queue_msg(bts, msg); } static int abis_nm_sendmsg_direct(struct gsm_bts *bts, struct msgb *msg) { OBSC_NM_W_ACK_CB(msg) = 0; return abis_nm_queue_msg(bts, msg); } static int abis_nm_rcvmsg_sw(struct msgb *mb); int nm_is_running(struct gsm_nm_state *s) { return (s->operational == NM_OPSTATE_ENABLED) && ( (s->availability == NM_AVSTATE_OK) || (s->availability == 0xff) ); } /* Update the administrative state of a given object in our in-memory data * structures and send an event to the higher layer */ static int update_admstate(struct gsm_bts *bts, uint8_t obj_class, struct abis_om_obj_inst *obj_inst, uint8_t adm_state) { struct gsm_nm_state *nm_state, new_state; struct nm_statechg_signal_data nsd; memset(&nsd, 0, sizeof(nsd)); nsd.obj = gsm_objclass2obj(bts, obj_class, obj_inst); if (!nsd.obj) return -EINVAL; nm_state = gsm_objclass2nmstate(bts, obj_class, obj_inst); if (!nm_state) return -1; new_state = *nm_state; new_state.administrative = adm_state; nsd.bts = bts; nsd.obj_class = obj_class; nsd.old_state = nm_state; nsd.new_state = &new_state; nsd.obj_inst = obj_inst; osmo_signal_dispatch(SS_NM, S_NM_STATECHG_ADM, &nsd); nm_state->administrative = adm_state; return 0; } static int abis_nm_rx_statechg_rep(struct msgb *mb) { struct abis_om_hdr *oh = msgb_l2(mb); struct abis_om_fom_hdr *foh = msgb_l3(mb); struct e1inp_sign_link *sign_link = mb->dst; struct gsm_bts *bts = sign_link->trx->bts; struct tlv_parsed tp; struct gsm_nm_state *nm_state, new_state; DEBUGPC(DNM, "STATE CHG: "); memset(&new_state, 0, sizeof(new_state)); nm_state = gsm_objclass2nmstate(bts, foh->obj_class, &foh->obj_inst); if (!nm_state) { DEBUGPC(DNM, "unknown object class\n"); return -EINVAL; } new_state = *nm_state; abis_nm_tlv_parse(&tp, bts, foh->data, oh->length-sizeof(*foh)); if (TLVP_PRESENT(&tp, NM_ATT_OPER_STATE)) { new_state.operational = *TLVP_VAL(&tp, NM_ATT_OPER_STATE); DEBUGPC(DNM, "OP_STATE=%s ", abis_nm_opstate_name(new_state.operational)); } if (TLVP_PRESENT(&tp, NM_ATT_AVAIL_STATUS)) { if (TLVP_LEN(&tp, NM_ATT_AVAIL_STATUS) == 0) new_state.availability = 0xff; else new_state.availability = *TLVP_VAL(&tp, NM_ATT_AVAIL_STATUS); DEBUGPC(DNM, "AVAIL=%s(%02x) ", abis_nm_avail_name(new_state.availability), new_state.availability); } else new_state.availability = 0xff; if (TLVP_PRESENT(&tp, NM_ATT_ADM_STATE)) { new_state.administrative = *TLVP_VAL(&tp, NM_ATT_ADM_STATE); DEBUGPC(DNM, "ADM=%2s ", get_value_string(abis_nm_adm_state_names, new_state.administrative)); } DEBUGPC(DNM, "\n"); if ((new_state.administrative != 0 && nm_state->administrative == 0) || new_state.operational != nm_state->operational || new_state.availability != nm_state->availability) { /* Update the operational state of a given object in our in-memory data * structures and send an event to the higher layer */ struct nm_statechg_signal_data nsd; nsd.obj = gsm_objclass2obj(bts, foh->obj_class, &foh->obj_inst); nsd.obj_class = foh->obj_class; nsd.old_state = nm_state; nsd.new_state = &new_state; nsd.obj_inst = &foh->obj_inst; nsd.bts = bts; osmo_signal_dispatch(SS_NM, S_NM_STATECHG_OPER, &nsd); nm_state->operational = new_state.operational; nm_state->availability = new_state.availability; if (nm_state->administrative == 0) nm_state->administrative = new_state.administrative; } #if 0 if (op_state == 1) { /* try to enable objects that are disabled */ abis_nm_opstart(bts, foh->obj_class, foh->obj_inst.bts_nr, foh->obj_inst.trx_nr, foh->obj_inst.ts_nr); } #endif return 0; } static int rx_fail_evt_rep(struct msgb *mb) { struct abis_om_hdr *oh = msgb_l2(mb); struct abis_om_fom_hdr *foh = msgb_l3(mb); struct e1inp_sign_link *sign_link = mb->dst; struct tlv_parsed tp; const uint8_t *p_val; char *p_text; LOGPC(DNM, LOGL_ERROR, "Failure Event Report "); abis_nm_tlv_parse(&tp, sign_link->trx->bts, foh->data, oh->length-sizeof(*foh)); if (TLVP_PRESENT(&tp, NM_ATT_EVENT_TYPE)) LOGPC(DNM, LOGL_ERROR, "Type=%s ", abis_nm_event_type_name(*TLVP_VAL(&tp, NM_ATT_EVENT_TYPE))); if (TLVP_PRESENT(&tp, NM_ATT_SEVERITY)) LOGPC(DNM, LOGL_ERROR, "Severity=%s ", abis_nm_severity_name(*TLVP_VAL(&tp, NM_ATT_SEVERITY))); if (TLVP_PRESENT(&tp, NM_ATT_PROB_CAUSE)) { p_val = TLVP_VAL(&tp, NM_ATT_PROB_CAUSE); LOGPC(DNM, LOGL_ERROR, "Probable cause= %02X %02X %02X ", p_val[0], p_val[1], p_val[2]); } if (TLVP_PRESENT(&tp, NM_ATT_ADD_TEXT)) { p_val = TLVP_VAL(&tp, NM_ATT_ADD_TEXT); p_text = talloc_strndup(tall_bsc_ctx, (const char *) p_val, TLVP_LEN(&tp, NM_ATT_ADD_TEXT)); if (p_text) { LOGPC(DNM, LOGL_ERROR, "Additional Text=%s ", p_text); talloc_free(p_text); } } LOGPC(DNM, LOGL_ERROR, "\n"); return 0; } static int abis_nm_rcvmsg_report(struct msgb *mb) { struct abis_om_fom_hdr *foh = msgb_l3(mb); uint8_t mt = foh->msg_type; abis_nm_debugp_foh(DNM, foh); //nmh->cfg->report_cb(mb, foh); switch (mt) { case NM_MT_STATECHG_EVENT_REP: return abis_nm_rx_statechg_rep(mb); break; case NM_MT_SW_ACTIVATED_REP: DEBUGPC(DNM, "Software Activated Report\n"); osmo_signal_dispatch(SS_NM, S_NM_SW_ACTIV_REP, mb); break; case NM_MT_FAILURE_EVENT_REP: rx_fail_evt_rep(mb); osmo_signal_dispatch(SS_NM, S_NM_FAIL_REP, mb); break; case NM_MT_TEST_REP: DEBUGPC(DNM, "Test Report\n"); osmo_signal_dispatch(SS_NM, S_NM_TEST_REP, mb); break; default: DEBUGPC(DNM, "reporting NM MT 0x%02x\n", mt); break; }; return 0; } /* Activate the specified software into the BTS */ static int ipacc_sw_activate(struct gsm_bts *bts, uint8_t obj_class, uint8_t i0, uint8_t i1, uint8_t i2, const uint8_t *sw_desc, uint8_t swdesc_len) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); uint8_t len = swdesc_len; uint8_t *trailer; oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, len, NM_MT_ACTIVATE_SW, obj_class, i0, i1, i2); trailer = msgb_put(msg, swdesc_len); memcpy(trailer, sw_desc, swdesc_len); return abis_nm_sendmsg(bts, msg); } int abis_nm_parse_sw_config(const uint8_t *sw_descr, const size_t sw_descr_len, struct abis_nm_sw_descr *desc, const int res_len) { static const struct tlv_definition sw_descr_def = { .def = { [NM_ATT_FILE_ID] = { TLV_TYPE_TL16V, }, [NM_ATT_FILE_VERSION] = { TLV_TYPE_TL16V, }, }, }; size_t pos = 0; int desc_pos = 0; for (pos = 0; pos < sw_descr_len && desc_pos < res_len; ++desc_pos) { uint8_t tag; uint16_t tag_len; const uint8_t *val; int len; memset(&desc[desc_pos], 0, sizeof(desc[desc_pos])); desc[desc_pos].start = &sw_descr[pos]; /* Classic TLV parsing doesn't work well with SW_DESCR because of it's * nested nature and the fact you have to assume it contains only two sub * tags NM_ATT_FILE_VERSION & NM_ATT_FILE_ID to parse it */ if (sw_descr[pos] != NM_ATT_SW_DESCR) { LOGP(DNM, LOGL_ERROR, "SW_DESCR attribute identifier not found!\n"); return -1; } pos += 1; len = tlv_parse_one(&tag, &tag_len, &val, &sw_descr_def, &sw_descr[pos], sw_descr_len - pos); if (len < 0 || (tag != NM_ATT_FILE_ID)) { LOGP(DNM, LOGL_ERROR, "FILE_ID attribute identifier not found!\n"); return -2; } desc[desc_pos].file_id = val; desc[desc_pos].file_id_len = tag_len; pos += len; len = tlv_parse_one(&tag, &tag_len, &val, &sw_descr_def, &sw_descr[pos], sw_descr_len - pos); if (len < 0 || (tag != NM_ATT_FILE_VERSION)) { LOGP(DNM, LOGL_ERROR, "FILE_VERSION attribute identifier not found!\n"); return -3; } desc[desc_pos].file_ver = val; desc[desc_pos].file_ver_len = tag_len; pos += len; /* final size */ desc[desc_pos].len = &sw_descr[pos] - desc[desc_pos].start; } return desc_pos; } int abis_nm_select_newest_sw(const struct abis_nm_sw_descr *sw_descr, const size_t size) { int res = 0; int i; for (i = 1; i < size; ++i) { if (memcmp(sw_descr[res].file_ver, sw_descr[i].file_ver, OSMO_MIN(sw_descr[i].file_ver_len, sw_descr[res].file_ver_len)) < 0) { res = i; } } return res; } static int abis_nm_rx_sw_act_req(struct msgb *mb) { struct abis_om_hdr *oh = msgb_l2(mb); struct abis_om_fom_hdr *foh = msgb_l3(mb); struct e1inp_sign_link *sign_link = mb->dst; struct tlv_parsed tp; const uint8_t *sw_config; int ret, sw_config_len, len; struct abis_nm_sw_descr sw_descr[5]; abis_nm_debugp_foh(DNM, foh); DEBUGPC(DNM, "SW Activate Request: "); DEBUGP(DNM, "Software Activate Request, ACKing and Activating\n"); ret = abis_nm_sw_act_req_ack(sign_link->trx->bts, foh->obj_class, foh->obj_inst.bts_nr, foh->obj_inst.trx_nr, foh->obj_inst.ts_nr, 0, foh->data, oh->length-sizeof(*foh)); if (ret != 0) { LOGP(DNM, LOGL_ERROR, "Sending SW ActReq ACK failed: %d\n", ret); return ret; } abis_nm_tlv_parse(&tp, sign_link->trx->bts, foh->data, oh->length-sizeof(*foh)); sw_config = TLVP_VAL(&tp, NM_ATT_SW_CONFIG); sw_config_len = TLVP_LEN(&tp, NM_ATT_SW_CONFIG); if (!TLVP_PRESENT(&tp, NM_ATT_SW_CONFIG)) { LOGP(DNM, LOGL_ERROR, "SW config not found! Can't continue.\n"); return -EINVAL; } else { DEBUGP(DNM, "Found SW config: %s\n", osmo_hexdump(sw_config, sw_config_len)); } /* Parse up to two sw descriptions from the data */ len = abis_nm_parse_sw_config(sw_config, sw_config_len, &sw_descr[0], ARRAY_SIZE(sw_descr)); if (len <= 0) { LOGP(DNM, LOGL_ERROR, "Failed to parse SW Config.\n"); return -EINVAL; } ret = abis_nm_select_newest_sw(&sw_descr[0], len); DEBUGP(DNM, "Selected sw description %d of %d\n", ret, len); return ipacc_sw_activate(sign_link->trx->bts, foh->obj_class, foh->obj_inst.bts_nr, foh->obj_inst.trx_nr, foh->obj_inst.ts_nr, sw_descr[ret].start, sw_descr[ret].len); } /* Receive a CHANGE_ADM_STATE_ACK, parse the TLV and update local state */ static int abis_nm_rx_chg_adm_state_ack(struct msgb *mb) { struct abis_om_hdr *oh = msgb_l2(mb); struct abis_om_fom_hdr *foh = msgb_l3(mb); struct e1inp_sign_link *sign_link = mb->dst; struct tlv_parsed tp; uint8_t adm_state; abis_nm_tlv_parse(&tp, sign_link->trx->bts, foh->data, oh->length-sizeof(*foh)); if (!TLVP_PRESENT(&tp, NM_ATT_ADM_STATE)) return -EINVAL; adm_state = *TLVP_VAL(&tp, NM_ATT_ADM_STATE); return update_admstate(sign_link->trx->bts, foh->obj_class, &foh->obj_inst, adm_state); } static int abis_nm_rx_lmt_event(struct msgb *mb) { struct abis_om_hdr *oh = msgb_l2(mb); struct abis_om_fom_hdr *foh = msgb_l3(mb); struct e1inp_sign_link *sign_link = mb->dst; struct tlv_parsed tp; DEBUGP(DNM, "LMT Event "); abis_nm_tlv_parse(&tp, sign_link->trx->bts, foh->data, oh->length-sizeof(*foh)); if (TLVP_PRESENT(&tp, NM_ATT_BS11_LMT_LOGON_SESSION) && TLVP_LEN(&tp, NM_ATT_BS11_LMT_LOGON_SESSION) >= 1) { uint8_t onoff = *TLVP_VAL(&tp, NM_ATT_BS11_LMT_LOGON_SESSION); DEBUGPC(DNM, "LOG%s ", onoff ? "ON" : "OFF"); } if (TLVP_PRESENT(&tp, NM_ATT_BS11_LMT_USER_ACC_LEV) && TLVP_LEN(&tp, NM_ATT_BS11_LMT_USER_ACC_LEV) >= 1) { uint8_t level = *TLVP_VAL(&tp, NM_ATT_BS11_LMT_USER_ACC_LEV); DEBUGPC(DNM, "Level=%u ", level); } if (TLVP_PRESENT(&tp, NM_ATT_BS11_LMT_USER_NAME) && TLVP_LEN(&tp, NM_ATT_BS11_LMT_USER_NAME) >= 1) { char *name = (char *) TLVP_VAL(&tp, NM_ATT_BS11_LMT_USER_NAME); DEBUGPC(DNM, "Username=%s ", name); } DEBUGPC(DNM, "\n"); /* FIXME: parse LMT LOGON TIME */ return 0; } void abis_nm_queue_send_next(struct gsm_bts *bts) { int wait = 0; struct msgb *msg; /* the queue is empty */ while (!llist_empty(&bts->abis_queue)) { msg = msgb_dequeue(&bts->abis_queue); wait = OBSC_NM_W_ACK_CB(msg); _abis_nm_sendmsg(msg); if (wait) break; } bts->abis_nm_pend = wait; } /* Receive a OML NM Message from BTS */ static int abis_nm_rcvmsg_fom(struct msgb *mb) { struct abis_om_hdr *oh = msgb_l2(mb); struct abis_om_fom_hdr *foh = msgb_l3(mb); struct e1inp_sign_link *sign_link = mb->dst; uint8_t mt = foh->msg_type; /* sign_link might get deleted via osmo_signal_dispatch -> save bts */ struct gsm_bts *bts = sign_link->trx->bts; int ret = 0; /* check for unsolicited message */ if (is_report(mt)) return abis_nm_rcvmsg_report(mb); if (is_in_arr(mt, abis_nm_sw_load_msgs, ARRAY_SIZE(abis_nm_sw_load_msgs))) return abis_nm_rcvmsg_sw(mb); if (is_in_arr(mt, abis_nm_nacks, ARRAY_SIZE(abis_nm_nacks))) { struct nm_nack_signal_data nack_data; struct tlv_parsed tp; abis_nm_debugp_foh(DNM, foh); DEBUGPC(DNM, "%s NACK ", abis_nm_nack_name(mt)); abis_nm_tlv_parse(&tp, bts, foh->data, oh->length-sizeof(*foh)); if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES)) DEBUGPC(DNM, "CAUSE=%s\n", abis_nm_nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES))); else DEBUGPC(DNM, "\n"); nack_data.msg = mb; nack_data.mt = mt; nack_data.bts = bts; osmo_signal_dispatch(SS_NM, S_NM_NACK, &nack_data); abis_nm_queue_send_next(bts); return 0; } #if 0 /* check if last message is to be acked */ if (is_ack_nack(nmh->last_msgtype)) { if (mt == MT_ACK(nmh->last_msgtype)) { DEBUGP(DNM, "received ACK (0x%x)\n", foh->msg_type); /* we got our ACK, continue sending the next msg */ } else if (mt == MT_NACK(nmh->last_msgtype)) { /* we got a NACK, signal this to the caller */ DEBUGP(DNM, "received NACK (0x%x)\n", foh->msg_type); /* FIXME: somehow signal this to the caller */ } else { /* really strange things happen */ return -EINVAL; } } #endif switch (mt) { case NM_MT_CHG_ADM_STATE_ACK: ret = abis_nm_rx_chg_adm_state_ack(mb); break; case NM_MT_SW_ACT_REQ: ret = abis_nm_rx_sw_act_req(mb); break; case NM_MT_BS11_LMT_SESSION: ret = abis_nm_rx_lmt_event(mb); break; case NM_MT_CONN_MDROP_LINK_ACK: DEBUGP(DNM, "CONN MDROP LINK ACK\n"); break; case NM_MT_IPACC_RESTART_ACK: osmo_signal_dispatch(SS_NM, S_NM_IPACC_RESTART_ACK, NULL); break; case NM_MT_IPACC_RESTART_NACK: osmo_signal_dispatch(SS_NM, S_NM_IPACC_RESTART_NACK, NULL); break; case NM_MT_SET_BTS_ATTR_ACK: break; } abis_nm_queue_send_next(bts); return ret; } static int abis_nm_rx_ipacc(struct msgb *mb); static int abis_nm_rcvmsg_manuf(struct msgb *mb) { int rc; struct e1inp_sign_link *sign_link = mb->dst; int bts_type = sign_link->trx->bts->type; switch (bts_type) { case GSM_BTS_TYPE_NANOBTS: case GSM_BTS_TYPE_OSMO_SYSMO: rc = abis_nm_rx_ipacc(mb); abis_nm_queue_send_next(sign_link->trx->bts); break; default: LOGP(DNM, LOGL_ERROR, "don't know how to parse OML for this " "BTS type (%u)\n", bts_type); rc = 0; break; } return rc; } /* High-Level API */ /* Entry-point where L2 OML from BTS enters the NM code */ int abis_nm_rcvmsg(struct msgb *msg) { struct abis_om_hdr *oh = msgb_l2(msg); int rc = 0; /* Various consistency checks */ if (oh->placement != ABIS_OM_PLACEMENT_ONLY) { LOGP(DNM, LOGL_ERROR, "ABIS OML placement 0x%x not supported\n", oh->placement); if (oh->placement != ABIS_OM_PLACEMENT_FIRST) { rc = -EINVAL; goto err; } } if (oh->sequence != 0) { LOGP(DNM, LOGL_ERROR, "ABIS OML sequence 0x%x != 0x00\n", oh->sequence); rc = -EINVAL; goto err; } #if 0 unsigned int l2_len = msg->tail - (uint8_t *)msgb_l2(msg); unsigned int hlen = sizeof(*oh) + sizeof(struct abis_om_fom_hdr); if (oh->length + hlen > l2_len) { LOGP(DNM, LOGL_ERROR, "ABIS OML truncated message (%u > %u)\n", oh->length + sizeof(*oh), l2_len); return -EINVAL; } if (oh->length + hlen < l2_len) LOGP(DNM, LOGL_ERROR, "ABIS OML message with extra trailer?!? (oh->len=%d, sizeof_oh=%d l2_len=%d\n", oh->length, sizeof(*oh), l2_len); #endif msg->l3h = (unsigned char *)oh + sizeof(*oh); switch (oh->mdisc) { case ABIS_OM_MDISC_FOM: rc = abis_nm_rcvmsg_fom(msg); break; case ABIS_OM_MDISC_MANUF: rc = abis_nm_rcvmsg_manuf(msg); break; case ABIS_OM_MDISC_MMI: case ABIS_OM_MDISC_TRAU: LOGP(DNM, LOGL_ERROR, "unimplemented ABIS OML message discriminator 0x%x\n", oh->mdisc); break; default: LOGP(DNM, LOGL_ERROR, "unknown ABIS OML message discriminator 0x%x\n", oh->mdisc); rc = -EINVAL; break; } err: msgb_free(msg); return rc; } #if 0 /* initialized all resources */ struct abis_nm_h *abis_nm_init(struct abis_nm_cfg *cfg) { struct abis_nm_h *nmh; nmh = malloc(sizeof(*nmh)); if (!nmh) return NULL; nmh->cfg = cfg; return nmh; } /* free all resources */ void abis_nm_fini(struct abis_nm_h *nmh) { free(nmh); } #endif /* Here we are trying to define a high-level API that can be used by * the actual BSC implementation. However, the architecture is currently * still under design. Ideally the calls to this API would be synchronous, * while the underlying stack behind the APi runs in a traditional select * based state machine. */ /* 6.2 Software Load: */ enum sw_state { SW_STATE_NONE, SW_STATE_WAIT_INITACK, SW_STATE_WAIT_SEGACK, SW_STATE_WAIT_ENDACK, SW_STATE_WAIT_ACTACK, SW_STATE_ERROR, }; struct abis_nm_sw { struct gsm_bts *bts; int trx_nr; gsm_cbfn *cbfn; void *cb_data; int forced; /* this will become part of the SW LOAD INITIATE */ uint8_t obj_class; uint8_t obj_instance[3]; uint8_t file_id[255]; uint8_t file_id_len; uint8_t file_version[255]; uint8_t file_version_len; uint8_t window_size; uint8_t seg_in_window; int fd; FILE *stream; enum sw_state state; int last_seg; }; static struct abis_nm_sw g_sw; static void sw_add_file_id_and_ver(struct abis_nm_sw *sw, struct msgb *msg) { if (sw->bts->type == GSM_BTS_TYPE_NANOBTS) { msgb_v_put(msg, NM_ATT_SW_DESCR); msgb_tl16v_put(msg, NM_ATT_FILE_ID, sw->file_id_len, sw->file_id); msgb_tl16v_put(msg, NM_ATT_FILE_VERSION, sw->file_version_len, sw->file_version); } else if (sw->bts->type == GSM_BTS_TYPE_BS11) { msgb_tlv_put(msg, NM_ATT_FILE_ID, sw->file_id_len, sw->file_id); msgb_tlv_put(msg, NM_ATT_FILE_VERSION, sw->file_version_len, sw->file_version); } else { LOGP(DNM, LOGL_ERROR, "Please implement this for the BTS.\n"); } } /* 6.2.1 / 8.3.1: Load Data Initiate */ static int sw_load_init(struct abis_nm_sw *sw) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); uint8_t len = 3*2 + sw->file_id_len + sw->file_version_len; oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, len, NM_MT_LOAD_INIT, sw->obj_class, sw->obj_instance[0], sw->obj_instance[1], sw->obj_instance[2]); sw_add_file_id_and_ver(sw, msg); msgb_tv_put(msg, NM_ATT_WINDOW_SIZE, sw->window_size); return abis_nm_sendmsg(sw->bts, msg); } static int is_last_line(FILE *stream) { char next_seg_buf[256]; long pos; /* check if we're sending the last line */ pos = ftell(stream); /* Did ftell fail? Then we are at the end for sure */ if (pos < 0) return 1; if (!fgets(next_seg_buf, sizeof(next_seg_buf)-2, stream)) { fseek(stream, pos, SEEK_SET); return 1; } fseek(stream, pos, SEEK_SET); return 0; } /* 6.2.2 / 8.3.2 Load Data Segment */ static int sw_load_segment(struct abis_nm_sw *sw) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); char seg_buf[256]; char *line_buf = seg_buf+2; unsigned char *tlv; int len; oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); switch (sw->bts->type) { case GSM_BTS_TYPE_BS11: if (fgets(line_buf, sizeof(seg_buf)-2, sw->stream) == NULL) { perror("fgets reading segment"); return -EINVAL; } seg_buf[0] = 0x00; /* check if we're sending the last line */ sw->last_seg = is_last_line(sw->stream); if (sw->last_seg) seg_buf[1] = 0; else seg_buf[1] = 1 + sw->seg_in_window++; len = strlen(line_buf) + 2; tlv = msgb_put(msg, TLV_GROSS_LEN(len)); tlv_put(tlv, NM_ATT_BS11_FILE_DATA, len, (uint8_t *)seg_buf); /* BS11 wants CR + LF in excess of the TLV length !?! */ tlv[1] -= 2; /* we only now know the exact length for the OM hdr */ len = strlen(line_buf)+2; break; case GSM_BTS_TYPE_NANOBTS: { osmo_static_assert(sizeof(seg_buf) >= IPACC_SEGMENT_SIZE, buffer_big_enough); len = read(sw->fd, &seg_buf, IPACC_SEGMENT_SIZE); if (len < 0) { perror("read failed"); return -EINVAL; } if (len != IPACC_SEGMENT_SIZE) sw->last_seg = 1; ++sw->seg_in_window; msgb_tl16v_put(msg, NM_ATT_IPACC_FILE_DATA, len, (const uint8_t *) seg_buf); len += 3; break; } default: LOGP(DNM, LOGL_ERROR, "sw_load_segment needs implementation for the BTS.\n"); /* FIXME: Other BTS types */ return -1; } fill_om_fom_hdr(oh, len, NM_MT_LOAD_SEG, sw->obj_class, sw->obj_instance[0], sw->obj_instance[1], sw->obj_instance[2]); return abis_nm_sendmsg_direct(sw->bts, msg); } /* 6.2.4 / 8.3.4 Load Data End */ static int sw_load_end(struct abis_nm_sw *sw) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); uint8_t len = 2*2 + sw->file_id_len + sw->file_version_len; oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, len, NM_MT_LOAD_END, sw->obj_class, sw->obj_instance[0], sw->obj_instance[1], sw->obj_instance[2]); sw_add_file_id_and_ver(sw, msg); return abis_nm_sendmsg(sw->bts, msg); } /* Activate the specified software into the BTS */ static int sw_activate(struct abis_nm_sw *sw) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); uint8_t len = 2*2 + sw->file_id_len + sw->file_version_len; oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, len, NM_MT_ACTIVATE_SW, sw->obj_class, sw->obj_instance[0], sw->obj_instance[1], sw->obj_instance[2]); /* FIXME: this is BS11 specific format */ msgb_tlv_put(msg, NM_ATT_FILE_ID, sw->file_id_len, sw->file_id); msgb_tlv_put(msg, NM_ATT_FILE_VERSION, sw->file_version_len, sw->file_version); return abis_nm_sendmsg(sw->bts, msg); } struct sdp_firmware { char magic[4]; char more_magic[4]; unsigned int header_length; unsigned int file_length; } __attribute__ ((packed)); static int parse_sdp_header(struct abis_nm_sw *sw) { struct sdp_firmware firmware_header; int rc; struct stat stat; rc = read(sw->fd, &firmware_header, sizeof(firmware_header)); if (rc != sizeof(firmware_header)) { LOGP(DNM, LOGL_ERROR, "Could not read SDP file header.\n"); return -1; } if (strncmp(firmware_header.magic, " SDP", 4) != 0) { LOGP(DNM, LOGL_ERROR, "The magic number1 is wrong.\n"); return -1; } if (firmware_header.more_magic[0] != 0x10 || firmware_header.more_magic[1] != 0x02 || firmware_header.more_magic[2] != 0x00 || firmware_header.more_magic[3] != 0x00) { LOGP(DNM, LOGL_ERROR, "The more magic number is wrong.\n"); return -1; } if (fstat(sw->fd, &stat) == -1) { LOGP(DNM, LOGL_ERROR, "Could not stat the file.\n"); return -1; } if (ntohl(firmware_header.file_length) != stat.st_size) { LOGP(DNM, LOGL_ERROR, "The filesizes do not match.\n"); return -1; } /* go back to the start as we checked the whole filesize.. */ lseek(sw->fd, 0l, SEEK_SET); LOGP(DNM, LOGL_NOTICE, "The ipaccess SDP header is not fully understood.\n" "There might be checksums in the file that are not\n" "verified and incomplete firmware might be flashed.\n" "There is absolutely no WARRANTY that flashing will\n" "work.\n"); return 0; } static int sw_open_file(struct abis_nm_sw *sw, const char *fname) { char file_id[12+1]; char file_version[80+1]; int rc; sw->fd = open(fname, O_RDONLY); if (sw->fd < 0) return sw->fd; switch (sw->bts->type) { case GSM_BTS_TYPE_BS11: sw->stream = fdopen(sw->fd, "r"); if (!sw->stream) { perror("fdopen"); return -1; } /* read first line and parse file ID and VERSION */ rc = fscanf(sw->stream, "@(#)%12s:%80s\r\n", file_id, file_version); if (rc != 2) { perror("parsing header line of software file"); return -1; } strcpy((char *)sw->file_id, file_id); sw->file_id_len = strlen(file_id); strcpy((char *)sw->file_version, file_version); sw->file_version_len = strlen(file_version); /* rewind to start of file */ rewind(sw->stream); break; case GSM_BTS_TYPE_NANOBTS: /* TODO: extract that from the filename or content */ rc = parse_sdp_header(sw); if (rc < 0) { fprintf(stderr, "Could not parse the ipaccess SDP header\n"); return -1; } strcpy((char *)sw->file_id, "id"); sw->file_id_len = 3; strcpy((char *)sw->file_version, "version"); sw->file_version_len = 8; break; default: /* We don't know how to treat them yet */ close(sw->fd); return -EINVAL; } return 0; } static void sw_close_file(struct abis_nm_sw *sw) { switch (sw->bts->type) { case GSM_BTS_TYPE_BS11: fclose(sw->stream); break; default: close(sw->fd); break; } } /* Fill the window */ static int sw_fill_window(struct abis_nm_sw *sw) { int rc; while (sw->seg_in_window < sw->window_size) { rc = sw_load_segment(sw); if (rc < 0) return rc; if (sw->last_seg) break; } return 0; } /* callback function from abis_nm_rcvmsg() handler */ static int abis_nm_rcvmsg_sw(struct msgb *mb) { struct abis_om_fom_hdr *foh = msgb_l3(mb); struct e1inp_sign_link *sign_link = mb->dst; int rc = -1; struct abis_nm_sw *sw = &g_sw; enum sw_state old_state = sw->state; //DEBUGP(DNM, "state %u, NM MT 0x%02x\n", sw->state, foh->msg_type); switch (sw->state) { case SW_STATE_WAIT_INITACK: switch (foh->msg_type) { case NM_MT_LOAD_INIT_ACK: /* fill window with segments */ if (sw->cbfn) sw->cbfn(GSM_HOOK_NM_SWLOAD, NM_MT_LOAD_INIT_ACK, mb, sw->cb_data, NULL); rc = sw_fill_window(sw); sw->state = SW_STATE_WAIT_SEGACK; abis_nm_queue_send_next(sign_link->trx->bts); break; case NM_MT_LOAD_INIT_NACK: if (sw->forced) { DEBUGP(DNM, "FORCED: Ignoring Software Load " "Init NACK\n"); if (sw->cbfn) sw->cbfn(GSM_HOOK_NM_SWLOAD, NM_MT_LOAD_INIT_ACK, mb, sw->cb_data, NULL); rc = sw_fill_window(sw); sw->state = SW_STATE_WAIT_SEGACK; } else { DEBUGP(DNM, "Software Load Init NACK\n"); /* FIXME: cause */ if (sw->cbfn) sw->cbfn(GSM_HOOK_NM_SWLOAD, NM_MT_LOAD_INIT_NACK, mb, sw->cb_data, NULL); sw->state = SW_STATE_ERROR; } abis_nm_queue_send_next(sign_link->trx->bts); break; } break; case SW_STATE_WAIT_SEGACK: switch (foh->msg_type) { case NM_MT_LOAD_SEG_ACK: if (sw->cbfn) sw->cbfn(GSM_HOOK_NM_SWLOAD, NM_MT_LOAD_SEG_ACK, mb, sw->cb_data, NULL); sw->seg_in_window = 0; if (!sw->last_seg) { /* fill window with more segments */ rc = sw_fill_window(sw); sw->state = SW_STATE_WAIT_SEGACK; } else { /* end the transfer */ sw->state = SW_STATE_WAIT_ENDACK; rc = sw_load_end(sw); } abis_nm_queue_send_next(sign_link->trx->bts); break; case NM_MT_LOAD_ABORT: if (sw->cbfn) sw->cbfn(GSM_HOOK_NM_SWLOAD, NM_MT_LOAD_ABORT, mb, sw->cb_data, NULL); break; } break; case SW_STATE_WAIT_ENDACK: switch (foh->msg_type) { case NM_MT_LOAD_END_ACK: sw_close_file(sw); DEBUGP(DNM, "Software Load End (BTS %u)\n", sw->bts->nr); sw->state = SW_STATE_NONE; if (sw->cbfn) sw->cbfn(GSM_HOOK_NM_SWLOAD, NM_MT_LOAD_END_ACK, mb, sw->cb_data, NULL); rc = 0; abis_nm_queue_send_next(sign_link->trx->bts); break; case NM_MT_LOAD_END_NACK: if (sw->forced) { DEBUGP(DNM, "FORCED: Ignoring Software Load" "End NACK\n"); sw->state = SW_STATE_NONE; if (sw->cbfn) sw->cbfn(GSM_HOOK_NM_SWLOAD, NM_MT_LOAD_END_ACK, mb, sw->cb_data, NULL); } else { DEBUGP(DNM, "Software Load End NACK\n"); /* FIXME: cause */ sw->state = SW_STATE_ERROR; if (sw->cbfn) sw->cbfn(GSM_HOOK_NM_SWLOAD, NM_MT_LOAD_END_NACK, mb, sw->cb_data, NULL); } abis_nm_queue_send_next(sign_link->trx->bts); break; } case SW_STATE_WAIT_ACTACK: switch (foh->msg_type) { case NM_MT_ACTIVATE_SW_ACK: /* we're done */ DEBUGP(DNM, "Activate Software DONE!\n"); sw->state = SW_STATE_NONE; rc = 0; if (sw->cbfn) sw->cbfn(GSM_HOOK_NM_SWLOAD, NM_MT_ACTIVATE_SW_ACK, mb, sw->cb_data, NULL); abis_nm_queue_send_next(sign_link->trx->bts); break; case NM_MT_ACTIVATE_SW_NACK: DEBUGP(DNM, "Activate Software NACK\n"); /* FIXME: cause */ sw->state = SW_STATE_ERROR; if (sw->cbfn) sw->cbfn(GSM_HOOK_NM_SWLOAD, NM_MT_ACTIVATE_SW_NACK, mb, sw->cb_data, NULL); abis_nm_queue_send_next(sign_link->trx->bts); break; } case SW_STATE_NONE: switch (foh->msg_type) { case NM_MT_ACTIVATE_SW_ACK: rc = 0; break; } break; case SW_STATE_ERROR: break; } if (rc) DEBUGP(DNM, "unexpected NM MT 0x%02x in state %u -> %u\n", foh->msg_type, old_state, sw->state); return rc; } /* Load the specified software into the BTS */ int abis_nm_software_load(struct gsm_bts *bts, int trx_nr, const char *fname, uint8_t win_size, int forced, gsm_cbfn *cbfn, void *cb_data) { struct abis_nm_sw *sw = &g_sw; int rc; DEBUGP(DNM, "Software Load (BTS %u, File \"%s\")\n", bts->nr, fname); if (sw->state != SW_STATE_NONE) return -EBUSY; sw->bts = bts; sw->trx_nr = trx_nr; switch (bts->type) { case GSM_BTS_TYPE_BS11: sw->obj_class = NM_OC_SITE_MANAGER; sw->obj_instance[0] = 0xff; sw->obj_instance[1] = 0xff; sw->obj_instance[2] = 0xff; break; case GSM_BTS_TYPE_NANOBTS: sw->obj_class = NM_OC_BASEB_TRANSC; sw->obj_instance[0] = sw->bts->nr; sw->obj_instance[1] = sw->trx_nr; sw->obj_instance[2] = 0xff; break; case GSM_BTS_TYPE_UNKNOWN: default: LOGPC(DNM, LOGL_ERROR, "Software Load not properly implemented.\n"); return -1; break; } sw->window_size = win_size; sw->state = SW_STATE_WAIT_INITACK; sw->cbfn = cbfn; sw->cb_data = cb_data; sw->forced = forced; rc = sw_open_file(sw, fname); if (rc < 0) { sw->state = SW_STATE_NONE; return rc; } return sw_load_init(sw); } int abis_nm_software_load_status(struct gsm_bts *bts) { struct abis_nm_sw *sw = &g_sw; struct stat st; int rc, percent; rc = fstat(sw->fd, &st); if (rc < 0) { perror("ERROR during stat"); return rc; } if (sw->stream) percent = (ftell(sw->stream) * 100) / st.st_size; else percent = (lseek(sw->fd, 0, SEEK_CUR) * 100) / st.st_size; return percent; } /* Activate the specified software into the BTS */ int abis_nm_software_activate(struct gsm_bts *bts, const char *fname, gsm_cbfn *cbfn, void *cb_data) { struct abis_nm_sw *sw = &g_sw; int rc; DEBUGP(DNM, "Activating Software (BTS %u, File \"%s\")\n", bts->nr, fname); if (sw->state != SW_STATE_NONE) return -EBUSY; sw->bts = bts; sw->obj_class = NM_OC_SITE_MANAGER; sw->obj_instance[0] = 0xff; sw->obj_instance[1] = 0xff; sw->obj_instance[2] = 0xff; sw->state = SW_STATE_WAIT_ACTACK; sw->cbfn = cbfn; sw->cb_data = cb_data; /* Open the file in order to fill some sw struct members */ rc = sw_open_file(sw, fname); if (rc < 0) { sw->state = SW_STATE_NONE; return rc; } sw_close_file(sw); return sw_activate(sw); } static void fill_nm_channel(struct abis_nm_channel *ch, uint8_t bts_port, uint8_t ts_nr, uint8_t subslot_nr) { ch->attrib = NM_ATT_ABIS_CHANNEL; ch->bts_port = bts_port; ch->timeslot = ts_nr; ch->subslot = subslot_nr; } int abis_nm_establish_tei(struct gsm_bts *bts, uint8_t trx_nr, uint8_t e1_port, uint8_t e1_timeslot, uint8_t e1_subslot, uint8_t tei) { struct abis_om_hdr *oh; struct abis_nm_channel *ch; uint8_t len = sizeof(*ch) + 2; struct msgb *msg = nm_msgb_alloc(); oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, len, NM_MT_ESTABLISH_TEI, NM_OC_RADIO_CARRIER, bts->bts_nr, trx_nr, 0xff); msgb_tv_put(msg, NM_ATT_TEI, tei); ch = (struct abis_nm_channel *) msgb_put(msg, sizeof(*ch)); fill_nm_channel(ch, e1_port, e1_timeslot, e1_subslot); return abis_nm_sendmsg(bts, msg); } /* connect signalling of one (BTS,TRX) to a particular timeslot on the E1 */ int abis_nm_conn_terr_sign(struct gsm_bts_trx *trx, uint8_t e1_port, uint8_t e1_timeslot, uint8_t e1_subslot) { struct gsm_bts *bts = trx->bts; struct abis_om_hdr *oh; struct abis_nm_channel *ch; struct msgb *msg = nm_msgb_alloc(); oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, sizeof(*ch), NM_MT_CONN_TERR_SIGN, NM_OC_RADIO_CARRIER, bts->bts_nr, trx->nr, 0xff); ch = (struct abis_nm_channel *) msgb_put(msg, sizeof(*ch)); fill_nm_channel(ch, e1_port, e1_timeslot, e1_subslot); return abis_nm_sendmsg(bts, msg); } #if 0 int abis_nm_disc_terr_sign(struct abis_nm_h *h, struct abis_om_obj_inst *inst, struct abis_nm_abis_channel *chan) { } #endif int abis_nm_conn_terr_traf(struct gsm_bts_trx_ts *ts, uint8_t e1_port, uint8_t e1_timeslot, uint8_t e1_subslot) { struct gsm_bts *bts = ts->trx->bts; struct abis_om_hdr *oh; struct abis_nm_channel *ch; struct msgb *msg = nm_msgb_alloc(); oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, sizeof(*ch), NM_MT_CONN_TERR_TRAF, NM_OC_CHANNEL, bts->bts_nr, ts->trx->nr, ts->nr); ch = (struct abis_nm_channel *) msgb_put(msg, sizeof(*ch)); fill_nm_channel(ch, e1_port, e1_timeslot, e1_subslot); DEBUGP(DNM, "CONNECT TERR TRAF Um=%s E1=(%u,%u,%u)\n", gsm_ts_name(ts), e1_port, e1_timeslot, e1_subslot); return abis_nm_sendmsg(bts, msg); } #if 0 int abis_nm_disc_terr_traf(struct abis_nm_h *h, struct abis_om_obj_inst *inst, struct abis_nm_abis_channel *chan, uint8_t subchan) { } #endif /* Chapter 8.11.1 */ int abis_nm_get_attr(struct gsm_bts *bts, uint8_t obj_class, uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr, uint8_t *attr, uint8_t attr_len) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); DEBUGP(DNM, "Get Attr (bts=%d)\n", bts->nr); oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, attr_len, NM_MT_GET_ATTR, obj_class, bts_nr, trx_nr, ts_nr); msgb_tl16v_put(msg, NM_ATT_LIST_REQ_ATTR, attr_len, attr); return abis_nm_sendmsg(bts, msg); } /* Chapter 8.6.1 */ int abis_nm_set_bts_attr(struct gsm_bts *bts, uint8_t *attr, int attr_len) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); uint8_t *cur; DEBUGP(DNM, "Set BTS Attr (bts=%d)\n", bts->nr); oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, attr_len, NM_MT_SET_BTS_ATTR, NM_OC_BTS, bts->bts_nr, 0xff, 0xff); cur = msgb_put(msg, attr_len); memcpy(cur, attr, attr_len); return abis_nm_sendmsg(bts, msg); } /* Chapter 8.6.2 */ int abis_nm_set_radio_attr(struct gsm_bts_trx *trx, uint8_t *attr, int attr_len) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); uint8_t *cur; DEBUGP(DNM, "Set TRX Attr (bts=%d,trx=%d)\n", trx->bts->nr, trx->nr); oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, attr_len, NM_MT_SET_RADIO_ATTR, NM_OC_RADIO_CARRIER, trx->bts->bts_nr, trx->nr, 0xff); cur = msgb_put(msg, attr_len); memcpy(cur, attr, attr_len); return abis_nm_sendmsg(trx->bts, msg); } int abis_nm_update_max_power_red(struct gsm_bts_trx *trx) { uint8_t attr[] = { NM_ATT_RF_MAXPOWR_R, trx->max_power_red / 2 }; return abis_nm_set_radio_attr(trx, attr, ARRAY_SIZE(attr)); } static int verify_chan_comb(struct gsm_bts_trx_ts *ts, uint8_t chan_comb, const char **reason) { int i; *reason = "Reason unknown"; /* As it turns out, the BS-11 has some very peculiar restrictions * on the channel combinations it allows */ switch (ts->trx->bts->type) { case GSM_BTS_TYPE_BS11: switch (chan_comb) { case NM_CHANC_TCHHalf: case NM_CHANC_TCHHalf2: /* not supported */ *reason = "TCH/H is not supported."; return -EINVAL; case NM_CHANC_SDCCH: /* only one SDCCH/8 per TRX */ for (i = 0; i < TRX_NR_TS; i++) { if (i == ts->nr) continue; if (ts->trx->ts[i].nm_chan_comb == NM_CHANC_SDCCH) { *reason = "Only one SDCCH/8 per TRX allowed."; return -EINVAL; } } /* not allowed for TS0 of BCCH-TRX */ if (ts->trx == ts->trx->bts->c0 && ts->nr == 0) { *reason = "SDCCH/8 must be on TS0."; return -EINVAL; } /* not on the same TRX that has a BCCH+SDCCH4 * combination */ if (ts->trx != ts->trx->bts->c0 && (ts->trx->ts[0].nm_chan_comb == 5 || ts->trx->ts[0].nm_chan_comb == 8)) { *reason = "SDCCH/8 and BCCH must be on the same TRX."; return -EINVAL; } break; case NM_CHANC_mainBCCH: case NM_CHANC_BCCHComb: /* allowed only for TS0 of C0 */ if (ts->trx != ts->trx->bts->c0 || ts->nr != 0) { *reason = "Main BCCH must be on TS0."; return -EINVAL; } break; case NM_CHANC_BCCH: /* allowed only for TS 2/4/6 of C0 */ if (ts->trx != ts->trx->bts->c0) { *reason = "BCCH must be on C0."; return -EINVAL; } if (ts->nr != 2 && ts->nr != 4 && ts->nr != 6) { *reason = "BCCH must be on TS 2/4/6."; return -EINVAL; } break; case 8: /* this is not like 08.58, but in fact * FCCH+SCH+BCCH+CCCH+SDCCH/4+SACCH/C4+CBCH */ /* FIXME: only one CBCH allowed per cell */ break; } break; case GSM_BTS_TYPE_NANOBTS: switch (ts->nr) { case 0: if (ts->trx->nr == 0) { /* only on TRX0 */ switch (chan_comb) { case NM_CHANC_BCCH: case NM_CHANC_mainBCCH: case NM_CHANC_BCCHComb: return 0; break; default: *reason = "TS0 of TRX0 must carry a BCCH."; return -EINVAL; } } else { switch (chan_comb) { case NM_CHANC_TCHFull: case NM_CHANC_TCHHalf: case NM_CHANC_IPAC_TCHFull_TCHHalf: return 0; default: *reason = "TS0 must carry a TCH/F or TCH/H."; return -EINVAL; } } break; case 1: if (ts->trx->nr == 0) { switch (chan_comb) { case NM_CHANC_SDCCH_CBCH: if (ts->trx->ts[0].nm_chan_comb == NM_CHANC_mainBCCH) return 0; *reason = "TS0 must be the main BCCH for CBCH."; return -EINVAL; case NM_CHANC_SDCCH: case NM_CHANC_TCHFull: case NM_CHANC_TCHHalf: case NM_CHANC_IPAC_TCHFull_TCHHalf: case NM_CHANC_IPAC_TCHFull_PDCH: return 0; default: *reason = "TS1 must carry a CBCH, SDCCH or TCH."; return -EINVAL; } } else { switch (chan_comb) { case NM_CHANC_SDCCH: case NM_CHANC_TCHFull: case NM_CHANC_TCHHalf: case NM_CHANC_IPAC_TCHFull_TCHHalf: return 0; default: *reason = "TS1 must carry a SDCCH or TCH."; return -EINVAL; } } break; case 2: case 3: case 4: case 5: case 6: case 7: switch (chan_comb) { case NM_CHANC_TCHFull: case NM_CHANC_TCHHalf: case NM_CHANC_IPAC_TCHFull_TCHHalf: return 0; case NM_CHANC_IPAC_PDCH: case NM_CHANC_IPAC_TCHFull_PDCH: if (ts->trx->nr == 0) return 0; else { *reason = "PDCH must be on TRX0."; return -EINVAL; } } break; } *reason = "Unknown combination"; return -EINVAL; case GSM_BTS_TYPE_OSMO_SYSMO: /* no known restrictions */ return 0; default: /* unknown BTS type */ return 0; } return 0; } /* Chapter 8.6.3 */ int abis_nm_set_channel_attr(struct gsm_bts_trx_ts *ts, uint8_t chan_comb) { struct gsm_bts *bts = ts->trx->bts; struct abis_om_hdr *oh; uint8_t zero = 0x00; struct msgb *msg = nm_msgb_alloc(); uint8_t len = 2 + 2; const char *reason = NULL; if (bts->type == GSM_BTS_TYPE_BS11) len += 4 + 2 + 2 + 3; DEBUGP(DNM, "Set Chan Attr %s\n", gsm_ts_name(ts)); if (verify_chan_comb(ts, chan_comb, &reason) < 0) { msgb_free(msg); LOGP(DNM, LOGL_ERROR, "Invalid Channel Combination %d on %s. Reason: %s\n", chan_comb, gsm_ts_name(ts), reason); return -EINVAL; } ts->nm_chan_comb = chan_comb; oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, len, NM_MT_SET_CHAN_ATTR, NM_OC_CHANNEL, bts->bts_nr, ts->trx->nr, ts->nr); msgb_tv_put(msg, NM_ATT_CHAN_COMB, chan_comb); if (ts->hopping.enabled) { unsigned int i; uint8_t *len; msgb_tv_put(msg, NM_ATT_HSN, ts->hopping.hsn); msgb_tv_put(msg, NM_ATT_MAIO, ts->hopping.maio); /* build the ARFCN list */ msgb_put_u8(msg, NM_ATT_ARFCN_LIST); len = msgb_put(msg, 1); *len = 0; for (i = 0; i < ts->hopping.arfcns.data_len*8; i++) { if (bitvec_get_bit_pos(&ts->hopping.arfcns, i)) { msgb_put_u16(msg, i); /* At least BS-11 wants a TLV16 here */ if (bts->type == GSM_BTS_TYPE_BS11) *len += 1; else *len += sizeof(uint16_t); } } } msgb_tv_put(msg, NM_ATT_TSC, gsm_ts_tsc(ts)); /* training sequence */ if (bts->type == GSM_BTS_TYPE_BS11) msgb_tlv_put(msg, 0x59, 1, &zero); return abis_nm_sendmsg(bts, msg); } int abis_nm_sw_act_req_ack(struct gsm_bts *bts, uint8_t obj_class, uint8_t i1, uint8_t i2, uint8_t i3, int nack, uint8_t *attr, int att_len) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); uint8_t msgtype = NM_MT_SW_ACT_REQ_ACK; uint8_t len = att_len; if (nack) { len += 2; msgtype = NM_MT_SW_ACT_REQ_NACK; } oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, att_len, msgtype, obj_class, i1, i2, i3); if (attr) { uint8_t *ptr = msgb_put(msg, att_len); memcpy(ptr, attr, att_len); } if (nack) msgb_tv_put(msg, NM_ATT_NACK_CAUSES, NM_NACK_OBJCLASS_NOTSUPP); return abis_nm_sendmsg_direct(bts, msg); } int abis_nm_raw_msg(struct gsm_bts *bts, int len, uint8_t *rawmsg) { struct msgb *msg = nm_msgb_alloc(); struct abis_om_hdr *oh; uint8_t *data; oh = (struct abis_om_hdr *) msgb_put(msg, sizeof(*oh)); fill_om_hdr(oh, len); data = msgb_put(msg, len); memcpy(data, rawmsg, len); return abis_nm_sendmsg(bts, msg); } /* Siemens specific commands */ static int __simple_cmd(struct gsm_bts *bts, uint8_t msg_type) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, 0, msg_type, NM_OC_SITE_MANAGER, 0xff, 0xff, 0xff); return abis_nm_sendmsg(bts, msg); } /* Chapter 8.9.2 */ int abis_nm_opstart(struct gsm_bts *bts, uint8_t obj_class, uint8_t i0, uint8_t i1, uint8_t i2) { struct abis_om_hdr *oh; struct abis_om_fom_hdr *foh; struct msgb *msg = nm_msgb_alloc(); oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); foh = fill_om_fom_hdr(oh, 0, NM_MT_OPSTART, obj_class, i0, i1, i2); abis_nm_debugp_foh(DNM, foh); DEBUGPC(DNM, "Sending OPSTART\n"); return abis_nm_sendmsg(bts, msg); } /* Chapter 8.8.5 */ int abis_nm_chg_adm_state(struct gsm_bts *bts, uint8_t obj_class, uint8_t i0, uint8_t i1, uint8_t i2, enum abis_nm_adm_state adm_state) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, 2, NM_MT_CHG_ADM_STATE, obj_class, i0, i1, i2); msgb_tv_put(msg, NM_ATT_ADM_STATE, adm_state); return abis_nm_sendmsg(bts, msg); } int abis_nm_conn_mdrop_link(struct gsm_bts *bts, uint8_t e1_port0, uint8_t ts0, uint8_t e1_port1, uint8_t ts1) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); uint8_t *attr; DEBUGP(DNM, "CONNECT MDROP LINK E1=(%u,%u) -> E1=(%u, %u)\n", e1_port0, ts0, e1_port1, ts1); oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, 6, NM_MT_CONN_MDROP_LINK, NM_OC_SITE_MANAGER, 0x00, 0x00, 0x00); attr = msgb_put(msg, 3); attr[0] = NM_ATT_MDROP_LINK; attr[1] = e1_port0; attr[2] = ts0; attr = msgb_put(msg, 3); attr[0] = NM_ATT_MDROP_NEXT; attr[1] = e1_port1; attr[2] = ts1; return abis_nm_sendmsg(bts, msg); } /* Chapter 8.7.1 */ int abis_nm_perform_test(struct gsm_bts *bts, uint8_t obj_class, uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr, uint8_t test_nr, uint8_t auton_report, struct msgb *msg) { struct abis_om_hdr *oh; DEBUGP(DNM, "PEFORM TEST %s\n", abis_nm_test_name(test_nr)); if (!msg) msg = nm_msgb_alloc(); msgb_tv_push(msg, NM_ATT_AUTON_REPORT, auton_report); msgb_tv_push(msg, NM_ATT_TEST_NO, test_nr); oh = (struct abis_om_hdr *) msgb_push(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, msgb_l3len(msg), NM_MT_PERF_TEST, obj_class, bts_nr, trx_nr, ts_nr); return abis_nm_sendmsg(bts, msg); } int abis_nm_event_reports(struct gsm_bts *bts, int on) { if (on == 0) return __simple_cmd(bts, NM_MT_STOP_EVENT_REP); else return __simple_cmd(bts, NM_MT_REST_EVENT_REP); } /* Siemens (or BS-11) specific commands */ int abis_nm_bs11_bsc_disconnect(struct gsm_bts *bts, int reconnect) { if (reconnect == 0) return __simple_cmd(bts, NM_MT_BS11_DISCONNECT); else return __simple_cmd(bts, NM_MT_BS11_RECONNECT); } int abis_nm_bs11_restart(struct gsm_bts *bts) { return __simple_cmd(bts, NM_MT_BS11_RESTART); } struct bs11_date_time { uint16_t year; uint8_t month; uint8_t day; uint8_t hour; uint8_t min; uint8_t sec; } __attribute__((packed)); void get_bs11_date_time(struct bs11_date_time *aet) { time_t t; struct tm *tm; t = time(NULL); tm = localtime(&t); aet->sec = tm->tm_sec; aet->min = tm->tm_min; aet->hour = tm->tm_hour; aet->day = tm->tm_mday; aet->month = tm->tm_mon; aet->year = htons(1900 + tm->tm_year); } int abis_nm_bs11_reset_resource(struct gsm_bts *bts) { return __simple_cmd(bts, NM_MT_BS11_RESET_RESOURCE); } int abis_nm_bs11_db_transmission(struct gsm_bts *bts, int begin) { if (begin) return __simple_cmd(bts, NM_MT_BS11_BEGIN_DB_TX); else return __simple_cmd(bts, NM_MT_BS11_END_DB_TX); } int abis_nm_bs11_create_object(struct gsm_bts *bts, enum abis_bs11_objtype type, uint8_t idx, uint8_t attr_len, const uint8_t *attr) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); uint8_t *cur; oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, attr_len, NM_MT_BS11_CREATE_OBJ, NM_OC_BS11, type, 0, idx); cur = msgb_put(msg, attr_len); memcpy(cur, attr, attr_len); return abis_nm_sendmsg(bts, msg); } int abis_nm_bs11_delete_object(struct gsm_bts *bts, enum abis_bs11_objtype type, uint8_t idx) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, 0, NM_MT_BS11_DELETE_OBJ, NM_OC_BS11, type, 0, idx); return abis_nm_sendmsg(bts, msg); } int abis_nm_bs11_create_envaBTSE(struct gsm_bts *bts, uint8_t idx) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); uint8_t zero = 0x00; oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, 3, NM_MT_BS11_CREATE_OBJ, NM_OC_BS11_ENVABTSE, 0, idx, 0xff); msgb_tlv_put(msg, 0x99, 1, &zero); return abis_nm_sendmsg(bts, msg); } int abis_nm_bs11_create_bport(struct gsm_bts *bts, uint8_t idx) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, 0, NM_MT_BS11_CREATE_OBJ, NM_OC_BS11_BPORT, idx, 0xff, 0xff); return abis_nm_sendmsg(bts, msg); } int abis_nm_bs11_delete_bport(struct gsm_bts *bts, uint8_t idx) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, 0, NM_MT_BS11_DELETE_OBJ, NM_OC_BS11_BPORT, idx, 0xff, 0xff); return abis_nm_sendmsg(bts, msg); } static const uint8_t sm_attr[] = { NM_ATT_TEI, NM_ATT_ABIS_CHANNEL }; int abis_nm_bs11_get_oml_tei_ts(struct gsm_bts *bts) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, 2+sizeof(sm_attr), NM_MT_GET_ATTR, NM_OC_SITE_MANAGER, 0xff, 0xff, 0xff); msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(sm_attr), sm_attr); return abis_nm_sendmsg(bts, msg); } /* like abis_nm_conn_terr_traf + set_tei */ int abis_nm_bs11_conn_oml_tei(struct gsm_bts *bts, uint8_t e1_port, uint8_t e1_timeslot, uint8_t e1_subslot, uint8_t tei) { struct abis_om_hdr *oh; struct abis_nm_channel *ch; struct msgb *msg = nm_msgb_alloc(); oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, sizeof(*ch)+2, NM_MT_BS11_SET_ATTR, NM_OC_SITE_MANAGER, 0xff, 0xff, 0xff); ch = (struct abis_nm_channel *) msgb_put(msg, sizeof(*ch)); fill_nm_channel(ch, e1_port, e1_timeslot, e1_subslot); msgb_tv_put(msg, NM_ATT_TEI, tei); return abis_nm_sendmsg(bts, msg); } int abis_nm_bs11_set_trx_power(struct gsm_bts_trx *trx, uint8_t level) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, 3, NM_MT_BS11_SET_ATTR, NM_OC_BS11, BS11_OBJ_PA, 0x00, trx->nr); msgb_tlv_put(msg, NM_ATT_BS11_TXPWR, 1, &level); return abis_nm_sendmsg(trx->bts, msg); } int abis_nm_bs11_get_trx_power(struct gsm_bts_trx *trx) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); uint8_t attr = NM_ATT_BS11_TXPWR; oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, 2+sizeof(attr), NM_MT_GET_ATTR, NM_OC_BS11, BS11_OBJ_PA, 0x00, trx->nr); msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(attr), &attr); return abis_nm_sendmsg(trx->bts, msg); } int abis_nm_bs11_get_pll_mode(struct gsm_bts *bts) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); uint8_t attr[] = { NM_ATT_BS11_PLL_MODE }; oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, 2+sizeof(attr), NM_MT_GET_ATTR, NM_OC_BS11, BS11_OBJ_LI, 0x00, 0x00); msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(attr), attr); return abis_nm_sendmsg(bts, msg); } int abis_nm_bs11_get_cclk(struct gsm_bts *bts) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); uint8_t attr[] = { NM_ATT_BS11_CCLK_ACCURACY, NM_ATT_BS11_CCLK_TYPE }; oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, 2+sizeof(attr), NM_MT_GET_ATTR, NM_OC_BS11, BS11_OBJ_CCLK, 0x00, 0x00); msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(attr), attr); return abis_nm_sendmsg(bts, msg); } //static const uint8_t bs11_logon_c7[] = { 0x07, 0xd9, 0x01, 0x11, 0x0d, 0x10, 0x20 }; int abis_nm_bs11_factory_logon(struct gsm_bts *bts, int on) { return abis_nm_bs11_logon(bts, 0x02, "FACTORY", on); } int abis_nm_bs11_infield_logon(struct gsm_bts *bts, int on) { return abis_nm_bs11_logon(bts, 0x03, "FIELD ", on); } int abis_nm_bs11_logon(struct gsm_bts *bts, uint8_t level, const char *name, int on) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); struct bs11_date_time bdt; get_bs11_date_time(&bdt); oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); if (on) { uint8_t len = 3*2 + sizeof(bdt) + 1 + strlen(name); fill_om_fom_hdr(oh, len, NM_MT_BS11_LMT_LOGON, NM_OC_BS11_BTSE, 0xff, 0xff, 0xff); msgb_tlv_put(msg, NM_ATT_BS11_LMT_LOGIN_TIME, sizeof(bdt), (uint8_t *) &bdt); msgb_tlv_put(msg, NM_ATT_BS11_LMT_USER_ACC_LEV, 1, &level); msgb_tlv_put(msg, NM_ATT_BS11_LMT_USER_NAME, strlen(name), (uint8_t *)name); } else { fill_om_fom_hdr(oh, 0, NM_MT_BS11_LMT_LOGOFF, NM_OC_BS11_BTSE, 0xff, 0xff, 0xff); } return abis_nm_sendmsg(bts, msg); } int abis_nm_bs11_set_trx1_pw(struct gsm_bts *bts, const char *password) { struct abis_om_hdr *oh; struct msgb *msg; if (strlen(password) != 10) return -EINVAL; msg = nm_msgb_alloc(); oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, 2+strlen(password), NM_MT_BS11_SET_ATTR, NM_OC_BS11, BS11_OBJ_TRX1, 0x00, 0x00); msgb_tlv_put(msg, NM_ATT_BS11_PASSWORD, 10, (const uint8_t *)password); return abis_nm_sendmsg(bts, msg); } /* change the BS-11 PLL Mode to either locked (E1 derived) or standalone */ int abis_nm_bs11_set_pll_locked(struct gsm_bts *bts, int locked) { struct abis_om_hdr *oh; struct msgb *msg; uint8_t tlv_value; msg = nm_msgb_alloc(); oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, 3, NM_MT_BS11_SET_ATTR, NM_OC_BS11, BS11_OBJ_LI, 0x00, 0x00); if (locked) tlv_value = BS11_LI_PLL_LOCKED; else tlv_value = BS11_LI_PLL_STANDALONE; msgb_tlv_put(msg, NM_ATT_BS11_PLL_MODE, 1, &tlv_value); return abis_nm_sendmsg(bts, msg); } /* Set the calibration value of the PLL (work value/set value) * It depends on the login which one is changed */ int abis_nm_bs11_set_pll(struct gsm_bts *bts, int value) { struct abis_om_hdr *oh; struct msgb *msg; uint8_t tlv_value[2]; msg = nm_msgb_alloc(); oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, 3, NM_MT_BS11_SET_ATTR, NM_OC_BS11, BS11_OBJ_TRX1, 0x00, 0x00); tlv_value[0] = value>>8; tlv_value[1] = value&0xff; msgb_tlv_put(msg, NM_ATT_BS11_PLL, 2, tlv_value); return abis_nm_sendmsg(bts, msg); } int abis_nm_bs11_get_state(struct gsm_bts *bts) { return __simple_cmd(bts, NM_MT_BS11_GET_STATE); } /* BS11 SWL */ void *tall_fle_ctx; struct abis_nm_bs11_sw { struct gsm_bts *bts; char swl_fname[PATH_MAX]; uint8_t win_size; int forced; struct llist_head file_list; gsm_cbfn *user_cb; /* specified by the user */ }; static struct abis_nm_bs11_sw _g_bs11_sw, *g_bs11_sw = &_g_bs11_sw; struct file_list_entry { struct llist_head list; char fname[PATH_MAX]; }; struct file_list_entry *fl_dequeue(struct llist_head *queue) { struct llist_head *lh; if (llist_empty(queue)) return NULL; lh = queue->next; llist_del(lh); return llist_entry(lh, struct file_list_entry, list); } static int bs11_read_swl_file(struct abis_nm_bs11_sw *bs11_sw) { char linebuf[255]; struct llist_head *lh, *lh2; FILE *swl; int rc = 0; swl = fopen(bs11_sw->swl_fname, "r"); if (!swl) return -ENODEV; /* zero the stale file list, if any */ llist_for_each_safe(lh, lh2, &bs11_sw->file_list) { llist_del(lh); talloc_free(lh); } while (fgets(linebuf, sizeof(linebuf), swl)) { char file_id[12+1]; char file_version[80+1]; struct file_list_entry *fle; static char dir[PATH_MAX]; if (strlen(linebuf) < 4) continue; rc = sscanf(linebuf+4, "%12s:%80s\r\n", file_id, file_version); if (rc < 0) { perror("ERR parsing SWL file"); rc = -EINVAL; goto out; } if (rc < 2) continue; fle = talloc_zero(tall_fle_ctx, struct file_list_entry); if (!fle) { rc = -ENOMEM; goto out; } /* construct new filename */ strncpy(dir, bs11_sw->swl_fname, sizeof(dir)); strncat(fle->fname, dirname(dir), sizeof(fle->fname) - 1); strcat(fle->fname, "/"); strncat(fle->fname, file_id, sizeof(fle->fname) - 1 -strlen(fle->fname)); llist_add_tail(&fle->list, &bs11_sw->file_list); } out: fclose(swl); return rc; } /* bs11 swload specific callback, passed to abis_nm core swload */ static int bs11_swload_cbfn(unsigned int hook, unsigned int event, struct msgb *msg, void *data, void *param) { struct abis_nm_bs11_sw *bs11_sw = data; struct file_list_entry *fle; int rc = 0; switch (event) { case NM_MT_LOAD_END_ACK: fle = fl_dequeue(&bs11_sw->file_list); if (fle) { /* start download the next file of our file list */ rc = abis_nm_software_load(bs11_sw->bts, 0xff, fle->fname, bs11_sw->win_size, bs11_sw->forced, &bs11_swload_cbfn, bs11_sw); talloc_free(fle); } else { /* activate the SWL */ rc = abis_nm_software_activate(bs11_sw->bts, bs11_sw->swl_fname, bs11_swload_cbfn, bs11_sw); } break; case NM_MT_LOAD_SEG_ACK: case NM_MT_LOAD_END_NACK: case NM_MT_LOAD_INIT_ACK: case NM_MT_LOAD_INIT_NACK: case NM_MT_ACTIVATE_SW_NACK: case NM_MT_ACTIVATE_SW_ACK: default: /* fallthrough to the user callback */ if (bs11_sw->user_cb) rc = bs11_sw->user_cb(hook, event, msg, NULL, NULL); break; } return rc; } /* Siemens provides a SWL file that is a mere listing of all the other * files that are part of a software release. We need to upload first * the list file, and then each file that is listed in the list file */ int abis_nm_bs11_load_swl(struct gsm_bts *bts, const char *fname, uint8_t win_size, int forced, gsm_cbfn *cbfn) { struct abis_nm_bs11_sw *bs11_sw = g_bs11_sw; struct file_list_entry *fle; int rc = 0; INIT_LLIST_HEAD(&bs11_sw->file_list); bs11_sw->bts = bts; bs11_sw->win_size = win_size; bs11_sw->user_cb = cbfn; bs11_sw->forced = forced; strncpy(bs11_sw->swl_fname, fname, sizeof(bs11_sw->swl_fname)); rc = bs11_read_swl_file(bs11_sw); if (rc < 0) return rc; /* dequeue next item in file list */ fle = fl_dequeue(&bs11_sw->file_list); if (!fle) return -EINVAL; /* start download the next file of our file list */ rc = abis_nm_software_load(bts, 0xff, fle->fname, win_size, forced, bs11_swload_cbfn, bs11_sw); talloc_free(fle); return rc; } #if 0 static uint8_t req_attr_btse[] = { NM_ATT_ADM_STATE, NM_ATT_BS11_LMT_LOGON_SESSION, NM_ATT_BS11_LMT_LOGIN_TIME, NM_ATT_BS11_LMT_USER_ACC_LEV, NM_ATT_BS11_LMT_USER_NAME, 0xaf, NM_ATT_BS11_RX_OFFSET, NM_ATT_BS11_VENDOR_NAME, NM_ATT_BS11_SW_LOAD_INTENDED, NM_ATT_BS11_SW_LOAD_SAFETY, NM_ATT_BS11_SW_LOAD_STORED }; static uint8_t req_attr_btsm[] = { NM_ATT_ABIS_CHANNEL, NM_ATT_TEI, NM_ATT_BS11_ABIS_EXT_TIME, NM_ATT_ADM_STATE, NM_ATT_AVAIL_STATUS, 0xce, NM_ATT_FILE_ID, NM_ATT_FILE_VERSION, NM_ATT_OPER_STATE, 0xe8, NM_ATT_BS11_ALL_TEST_CATG, NM_ATT_SW_DESCR, NM_ATT_GET_ARI }; #endif static uint8_t req_attr[] = { NM_ATT_ADM_STATE, NM_ATT_AVAIL_STATUS, 0xa8, NM_ATT_OPER_STATE, 0xd5, 0xa1, NM_ATT_BS11_ESN_FW_CODE_NO, NM_ATT_BS11_ESN_HW_CODE_NO, 0x42, NM_ATT_BS11_ESN_PCB_SERIAL, NM_ATT_BS11_PLL }; int abis_nm_bs11_get_serno(struct gsm_bts *bts) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); /* SiemensHW CCTRL object */ fill_om_fom_hdr(oh, 2+sizeof(req_attr), NM_MT_GET_ATTR, NM_OC_BS11, 0x03, 0x00, 0x00); msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(req_attr), req_attr); return abis_nm_sendmsg(bts, msg); } int abis_nm_bs11_set_ext_time(struct gsm_bts *bts) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); struct bs11_date_time aet; get_bs11_date_time(&aet); oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); /* SiemensHW CCTRL object */ fill_om_fom_hdr(oh, 2+sizeof(aet), NM_MT_BS11_SET_ATTR, NM_OC_SITE_MANAGER, 0xff, 0xff, 0xff); msgb_tlv_put(msg, NM_ATT_BS11_ABIS_EXT_TIME, sizeof(aet), (uint8_t *) &aet); return abis_nm_sendmsg(bts, msg); } int abis_nm_bs11_get_bport_line_cfg(struct gsm_bts *bts, uint8_t bport) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); uint8_t attr = NM_ATT_BS11_LINE_CFG; oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, 2+sizeof(attr), NM_MT_GET_ATTR, NM_OC_BS11_BPORT, bport, 0xff, 0x02); msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(attr), &attr); return abis_nm_sendmsg(bts, msg); } int abis_nm_bs11_set_bport_line_cfg(struct gsm_bts *bts, uint8_t bport, enum abis_bs11_line_cfg line_cfg) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); struct bs11_date_time aet; get_bs11_date_time(&aet); oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, 2, NM_MT_BS11_SET_ATTR, NM_OC_BS11_BPORT, bport, 0xff, 0x02); msgb_tv_put(msg, NM_ATT_BS11_LINE_CFG, line_cfg); return abis_nm_sendmsg(bts, msg); } /* ip.access nanoBTS specific commands */ static const char ipaccess_magic[] = "com.ipaccess"; static int abis_nm_rx_ipacc(struct msgb *msg) { struct in_addr addr; struct abis_om_hdr *oh = msgb_l2(msg); struct abis_om_fom_hdr *foh; uint8_t idstrlen = oh->data[0]; struct tlv_parsed tp; struct ipacc_ack_signal_data signal; struct e1inp_sign_link *sign_link = msg->dst; if (strncmp((char *)&oh->data[1], ipaccess_magic, idstrlen)) { LOGP(DNM, LOGL_ERROR, "id string is not com.ipaccess !?!\n"); return -EINVAL; } foh = (struct abis_om_fom_hdr *) (oh->data + 1 + idstrlen); abis_nm_tlv_parse(&tp, sign_link->trx->bts, foh->data, oh->length-sizeof(*foh)); abis_nm_debugp_foh(DNM, foh); DEBUGPC(DNM, "IPACCESS(0x%02x): ", foh->msg_type); switch (foh->msg_type) { case NM_MT_IPACC_RSL_CONNECT_ACK: DEBUGPC(DNM, "RSL CONNECT ACK "); if (TLVP_PRESENT(&tp, NM_ATT_IPACC_DST_IP)) { memcpy(&addr, TLVP_VAL(&tp, NM_ATT_IPACC_DST_IP), sizeof(addr)); DEBUGPC(DNM, "IP=%s ", inet_ntoa(addr)); } if (TLVP_PRESENT(&tp, NM_ATT_IPACC_DST_IP_PORT)) DEBUGPC(DNM, "PORT=%u ", ntohs(*((uint16_t *) TLVP_VAL(&tp, NM_ATT_IPACC_DST_IP_PORT)))); if (TLVP_PRESENT(&tp, NM_ATT_IPACC_STREAM_ID)) DEBUGPC(DNM, "STREAM=0x%02x ", *TLVP_VAL(&tp, NM_ATT_IPACC_STREAM_ID)); DEBUGPC(DNM, "\n"); break; case NM_MT_IPACC_RSL_CONNECT_NACK: LOGP(DNM, LOGL_ERROR, "RSL CONNECT NACK "); if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES)) LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n", abis_nm_nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES))); else LOGPC(DNM, LOGL_ERROR, "\n"); break; case NM_MT_IPACC_SET_NVATTR_ACK: DEBUGPC(DNM, "SET NVATTR ACK\n"); /* FIXME: decode and show the actual attributes */ break; case NM_MT_IPACC_SET_NVATTR_NACK: LOGP(DNM, LOGL_ERROR, "SET NVATTR NACK "); if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES)) LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n", abis_nm_nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES))); else LOGPC(DNM, LOGL_ERROR, "\n"); break; case NM_MT_IPACC_GET_NVATTR_ACK: DEBUGPC(DNM, "GET NVATTR ACK\n"); /* FIXME: decode and show the actual attributes */ break; case NM_MT_IPACC_GET_NVATTR_NACK: LOGPC(DNM, LOGL_ERROR, "GET NVATTR NACK "); if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES)) LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n", abis_nm_nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES))); else LOGPC(DNM, LOGL_ERROR, "\n"); break; case NM_MT_IPACC_SET_ATTR_ACK: DEBUGPC(DNM, "SET ATTR ACK\n"); break; case NM_MT_IPACC_SET_ATTR_NACK: LOGPC(DNM, LOGL_ERROR, "SET ATTR NACK "); if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES)) LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n", abis_nm_nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES))); else LOGPC(DNM, LOGL_ERROR, "\n"); break; default: DEBUGPC(DNM, "unknown\n"); break; } /* signal handling */ switch (foh->msg_type) { case NM_MT_IPACC_RSL_CONNECT_NACK: case NM_MT_IPACC_SET_NVATTR_NACK: case NM_MT_IPACC_GET_NVATTR_NACK: signal.trx = gsm_bts_trx_by_nr(sign_link->trx->bts, foh->obj_inst.trx_nr); signal.msg_type = foh->msg_type; osmo_signal_dispatch(SS_NM, S_NM_IPACC_NACK, &signal); break; case NM_MT_IPACC_SET_NVATTR_ACK: signal.trx = gsm_bts_trx_by_nr(sign_link->trx->bts, foh->obj_inst.trx_nr); signal.msg_type = foh->msg_type; osmo_signal_dispatch(SS_NM, S_NM_IPACC_ACK, &signal); break; default: break; } return 0; } /* send an ip-access manufacturer specific message */ int abis_nm_ipaccess_msg(struct gsm_bts *bts, uint8_t msg_type, uint8_t obj_class, uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr, uint8_t *attr, int attr_len) { struct msgb *msg = nm_msgb_alloc(); struct abis_om_hdr *oh; struct abis_om_fom_hdr *foh; uint8_t *data; /* construct the 12.21 OM header, observe the erroneous length */ oh = (struct abis_om_hdr *) msgb_put(msg, sizeof(*oh)); fill_om_hdr(oh, sizeof(*foh) + attr_len); oh->mdisc = ABIS_OM_MDISC_MANUF; /* add the ip.access magic */ data = msgb_put(msg, sizeof(ipaccess_magic)+1); *data++ = sizeof(ipaccess_magic); memcpy(data, ipaccess_magic, sizeof(ipaccess_magic)); /* fill the 12.21 FOM header */ foh = (struct abis_om_fom_hdr *) msgb_put(msg, sizeof(*foh)); foh->msg_type = msg_type; foh->obj_class = obj_class; foh->obj_inst.bts_nr = bts_nr; foh->obj_inst.trx_nr = trx_nr; foh->obj_inst.ts_nr = ts_nr; if (attr && attr_len) { data = msgb_put(msg, attr_len); memcpy(data, attr, attr_len); } return abis_nm_sendmsg(bts, msg); } /* set some attributes in NVRAM */ int abis_nm_ipaccess_set_nvattr(struct gsm_bts_trx *trx, uint8_t *attr, int attr_len) { return abis_nm_ipaccess_msg(trx->bts, NM_MT_IPACC_SET_NVATTR, NM_OC_BASEB_TRANSC, 0, trx->nr, 0xff, attr, attr_len); } int abis_nm_ipaccess_rsl_connect(struct gsm_bts_trx *trx, uint32_t ip, uint16_t port, uint8_t stream) { struct in_addr ia; uint8_t attr[] = { NM_ATT_IPACC_STREAM_ID, 0, NM_ATT_IPACC_DST_IP_PORT, 0, 0, NM_ATT_IPACC_DST_IP, 0, 0, 0, 0 }; int attr_len = sizeof(attr); ia.s_addr = htonl(ip); attr[1] = stream; attr[3] = port >> 8; attr[4] = port & 0xff; *(uint32_t *)(attr+6) = ia.s_addr; /* if ip == 0, we use the default IP */ if (ip == 0) attr_len -= 5; DEBUGP(DNM, "ip.access RSL CONNECT IP=%s PORT=%u STREAM=0x%02x\n", inet_ntoa(ia), port, stream); return abis_nm_ipaccess_msg(trx->bts, NM_MT_IPACC_RSL_CONNECT, NM_OC_BASEB_TRANSC, trx->bts->bts_nr, trx->nr, 0xff, attr, attr_len); } /* restart / reboot an ip.access nanoBTS */ int abis_nm_ipaccess_restart(struct gsm_bts_trx *trx) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, 0, NM_MT_IPACC_RESTART, NM_OC_BASEB_TRANSC, trx->bts->nr, trx->nr, 0xff); return abis_nm_sendmsg(trx->bts, msg); } int abis_nm_ipaccess_set_attr(struct gsm_bts *bts, uint8_t obj_class, uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr, uint8_t *attr, uint8_t attr_len) { return abis_nm_ipaccess_msg(bts, NM_MT_IPACC_SET_ATTR, obj_class, bts_nr, trx_nr, ts_nr, attr, attr_len); } void abis_nm_ipaccess_cgi(uint8_t *buf, struct gsm_bts *bts) { /* we simply reuse the GSM48 function and overwrite the RAC * with the Cell ID */ gsm48_ra_id_by_bts(buf, bts); *((uint16_t *)(buf + 5)) = htons(bts->cell_identity); } void gsm_trx_lock_rf(struct gsm_bts_trx *trx, int locked) { int new_state = locked ? NM_STATE_LOCKED : NM_STATE_UNLOCKED; trx->mo.nm_state.administrative = new_state; if (!trx->bts || !trx->bts->oml_link) return; abis_nm_chg_adm_state(trx->bts, NM_OC_RADIO_CARRIER, trx->bts->bts_nr, trx->nr, 0xff, new_state); } static const struct value_string ipacc_testres_names[] = { { NM_IPACC_TESTRES_SUCCESS, "SUCCESS" }, { NM_IPACC_TESTRES_TIMEOUT, "TIMEOUT" }, { NM_IPACC_TESTRES_NO_CHANS, "NO CHANNELS" }, { NM_IPACC_TESTRES_PARTIAL, "PARTIAL" }, { NM_IPACC_TESTRES_STOPPED, "STOPPED" }, { 0, NULL } }; const char *ipacc_testres_name(uint8_t res) { return get_value_string(ipacc_testres_names, res); } void ipac_parse_cgi(struct cell_global_id *cid, const uint8_t *buf) { cid->mcc = (buf[0] & 0xf) * 100; cid->mcc += (buf[0] >> 4) * 10; cid->mcc += (buf[1] & 0xf) * 1; if (buf[1] >> 4 == 0xf) { cid->mnc = (buf[2] & 0xf) * 10; cid->mnc += (buf[2] >> 4) * 1; } else { cid->mnc = (buf[2] & 0xf) * 100; cid->mnc += (buf[2] >> 4) * 10; cid->mnc += (buf[1] >> 4) * 1; } cid->lac = ntohs(*((uint16_t *)&buf[3])); cid->ci = ntohs(*((uint16_t *)&buf[5])); } /* parse BCCH information IEI from wire format to struct ipac_bcch_info */ int ipac_parse_bcch_info(struct ipac_bcch_info *binf, uint8_t *buf) { uint8_t *cur = buf; uint16_t len __attribute__((unused)); memset(binf, 0, sizeof(*binf)); if (cur[0] != NM_IPAC_EIE_BCCH_INFO) return -EINVAL; cur++; len = ntohs(*(uint16_t *)cur); cur += 2; binf->info_type = ntohs(*(uint16_t *)cur); cur += 2; if (binf->info_type & IPAC_BINF_FREQ_ERR_QUAL) binf->freq_qual = *cur >> 2; binf->arfcn = (*cur++ & 3) << 8; binf->arfcn |= *cur++; if (binf->info_type & IPAC_BINF_RXLEV) binf->rx_lev = *cur & 0x3f; cur++; if (binf->info_type & IPAC_BINF_RXQUAL) binf->rx_qual = *cur & 0x7; cur++; if (binf->info_type & IPAC_BINF_FREQ_ERR_QUAL) binf->freq_err = ntohs(*(uint16_t *)cur); cur += 2; if (binf->info_type & IPAC_BINF_FRAME_OFFSET) binf->frame_offset = ntohs(*(uint16_t *)cur); cur += 2; if (binf->info_type & IPAC_BINF_FRAME_NR_OFFSET) binf->frame_nr_offset = ntohl(*(uint32_t *)cur); cur += 4; #if 0 /* Somehow this is not set correctly */ if (binf->info_type & IPAC_BINF_BSIC) #endif binf->bsic = *cur & 0x3f; cur++; ipac_parse_cgi(&binf->cgi, cur); cur += 7; if (binf->info_type & IPAC_BINF_NEIGH_BA_SI2) { memcpy(binf->ba_list_si2, cur, sizeof(binf->ba_list_si2)); cur += sizeof(binf->ba_list_si2); } if (binf->info_type & IPAC_BINF_NEIGH_BA_SI2bis) { memcpy(binf->ba_list_si2bis, cur, sizeof(binf->ba_list_si2bis)); cur += sizeof(binf->ba_list_si2bis); } if (binf->info_type & IPAC_BINF_NEIGH_BA_SI2ter) { memcpy(binf->ba_list_si2ter, cur, sizeof(binf->ba_list_si2ter)); cur += sizeof(binf->ba_list_si2ter); } return 0; } void abis_nm_clear_queue(struct gsm_bts *bts) { struct msgb *msg; while (!llist_empty(&bts->abis_queue)) { msg = msgb_dequeue(&bts->abis_queue); msgb_free(msg); } bts->abis_nm_pend = 0; } openbsc-0.15.0/openbsc/src/libbsc/abis_nm_ipaccess.c000066400000000000000000000050301265565154000223410ustar00rootroot00000000000000/* GSM Network Management (OML) messages on the A-bis interface * Extensions for the ip.access A-bis over IP protocol*/ /* (C) 2008-2009 by Harald Welte * * 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 . * */ /* A list of all the 'embedded' attributes of ip.access */ enum ipa_embedded_att { IPA_ATT_ARFCN_WHITELIST = 0x01, IPA_ATT_ARFCN_BLACKLIST = 0x02, IPA_ATT_FREQ_ERR_LIST = 0x03, IPA_ATT_CHAN_USAGE_LIST = 0x04, IPA_ATT_BCCH_INF_TYPE = 0x05, IPA_ATT_BCCH_INF = 0x06, IPA_ATT_CONFIG = 0x07, IPA_ATT_RESULT_DETAILS = 0x08, IPA_ATT_RXLEV_THRESH = 0x09, IPA_ATT_FREQ_SYNC_OPT = 0x0a, IPA_ATT_MAC_ADDR = 0x0b, IPA_ATT_HW_SW_COMPAT_NR = 0x0c, IPA_ATT_MANUF_SER_NR = 0x0d, IPA_ATT_OEM_ID = 0x0e, IPA_ATT_DATETIME_MANUF = 0x0f, IPA_ATT_DATETIME_CALIB = 0x10, IPA_ATT_BEACON_INF = 0x11, IPA_ATT_FREQ_ERR = 0x12, IPA_ATT_SNMP_COMM_STRING = 0x13, IPA_ATT_SNMP_TRAP_ADDR = 0x14, IPA_ATT_SNMP_TRAP_PORT = 0x15, IPA_ATT_SNMP_MAN_ADDR = 0x16, IPA_ATT_SNMP_SYS_CONTACT = 0x17, IPA_ATT_FACTORY_ID = 0x18, IPA_ATT_FACTORY_SERIAL = 0x19, IPA_ATT_LOGGED_EVT_IND = 0x1a, IPA_ATT_LOCAL_ADD_TEXT = 0x1b, IPA_ATT_FREQ_BANDS = 0x1c, IPA_ATT_MAX_TA = 0x1d, IPA_ATT_CIPH_ALG = 0x1e, IPA_ATT_CHAN_TYPES = 0x1f, IPA_ATT_CHAN_MODES = 0x20, IPA_ATT_GPRS_CODING_SCHEMES = 0x21, IPA_ATT_RTP_FEATURES = 0x22, IPA_ATT_RSL_FEATURES = 0x23, IPA_ATT_BTS_HW_CLASS = 0x24, IPA_ATT_BTS_ID = 0x25, IPA_ATT_BCAST_L2_MSG = 0x26, }; /* append an ip.access channel list to the given msgb */ static int ipa_chan_list_append(struct msgb *msg, uint8_t ie, uint16_t *arfcns, int arfcn_count) { int i; uint8_t *u8; uint16_t *u16; /* tag */ u8 = msgb_push(msg, 1); *u8 = ie; /* length in octets */ u16 = msgb_push(msg, 2); *u16 = htons(arfcn_count * 2); for (i = 0; i < arfcn_count; i++) { u16 = msgb_push(msg, 2); *u16 = htons(arfcns[i]); } return 0; } openbsc-0.15.0/openbsc/src/libbsc/abis_nm_vty.c000066400000000000000000000122451265565154000213770ustar00rootroot00000000000000/* VTY interface for A-bis OML (Netowrk Management) */ /* (C) 2009-2010 by Harald Welte * * 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern struct gsm_network *bsc_gsmnet; static struct cmd_node oml_node = { OML_NODE, "%s(oml)# ", 1, }; struct oml_node_state { struct gsm_bts *bts; uint8_t obj_class; uint8_t obj_inst[3]; }; static int dummy_config_write(struct vty *v) { return CMD_SUCCESS; } /* FIXME: auto-generate those strings from the value_string lists */ #define NM_OBJCLASS_VTY "(site-manager|bts|radio-carrier|baseband-transceiver|channel|adjc|handover|power-contorl|btse|rack|test|envabtse|bport|gprs-nse|gprs-cell|gprs-nsvc|siemenshw)" #define NM_OBJCLASS_VTY_HELP "Site Manager Object\n" \ "BTS Object\n" \ "Radio Carrier Object\n" \ "Baseband Transceiver Object\n" \ "Channel (Timeslot) Object\n" \ "Adjacent Object (Siemens)\n" \ "Handover Object (Siemens)\n" \ "Power Control Object (Siemens)\n" \ "BTSE Object (Siemens)\n" \ "Rack Object (Siemens)\n" \ "Test Object (Siemens)\n" \ "ENVABTSE Object (Siemens)\n" \ "BPORT Object (Siemens)\n" \ "GPRS NSE Object (ip.access/osmo-bts)\n" \ "GPRS Cell Object (ip.acecss/osmo-bts)\n" \ "GPRS NSVC Object (ip.acecss/osmo-bts)\n" \ "SIEMENSHW Object (Siemens)\n" DEFUN(oml_class_inst, oml_class_inst_cmd, "bts <0-255> oml class " NM_OBJCLASS_VTY " instance <0-255> <0-255> <0-255>", "BTS related commands\n" "BTS Number\n" "Manipulate the OML managed objects\n" "Object Class\n" NM_OBJCLASS_VTY_HELP "Object Instance\n" "BTS Number\n" "TRX Number\n" "TS Number\n") { struct gsm_bts *bts; struct oml_node_state *oms; int bts_nr = atoi(argv[0]); bts = gsm_bts_num(bsc_gsmnet, bts_nr); if (!bts) { vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE); return CMD_WARNING; } oms = talloc_zero(tall_bsc_ctx, struct oml_node_state); if (!oms) return CMD_WARNING; oms->bts = bts; oms->obj_class = get_string_value(abis_nm_obj_class_names, argv[1]); oms->obj_inst[0] = atoi(argv[2]); oms->obj_inst[1] = atoi(argv[3]); oms->obj_inst[2] = atoi(argv[4]); vty->index = oms; vty->node = OML_NODE; return CMD_SUCCESS; } DEFUN(oml_classnum_inst, oml_classnum_inst_cmd, "bts <0-255> oml class <0-255> instance <0-255> <0-255> <0-255>", "BTS related commands\n" "BTS Number\n" "Manipulate the OML managed objects\n" "Object Class\n" "Object Class\n" "Object Instance\n" "BTS Number\n" "TRX Number\n" "TS Number\n") { struct gsm_bts *bts; struct oml_node_state *oms; int bts_nr = atoi(argv[0]); bts = gsm_bts_num(bsc_gsmnet, bts_nr); if (!bts) { vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE); return CMD_WARNING; } oms = talloc_zero(tall_bsc_ctx, struct oml_node_state); if (!oms) return CMD_WARNING; oms->bts = bts; oms->obj_class = atoi(argv[1]); oms->obj_inst[0] = atoi(argv[2]); oms->obj_inst[1] = atoi(argv[3]); oms->obj_inst[2] = atoi(argv[4]); vty->index = oms; vty->node = OML_NODE; return CMD_SUCCESS; } DEFUN(oml_chg_adm_state, oml_chg_adm_state_cmd, "change-adm-state (locked|unlocked|shutdown|null)", "Change the Administrative State\n" "Locked\n" "Unlocked\n" "Shutdown\n" "NULL\n") { struct oml_node_state *oms = vty->index; enum abis_nm_adm_state state; state = get_string_value(abis_nm_adm_state_names, argv[0]); abis_nm_chg_adm_state(oms->bts, oms->obj_class, oms->obj_inst[0], oms->obj_inst[1], oms->obj_inst[2], state); return CMD_SUCCESS; } DEFUN(oml_opstart, oml_opstart_cmd, "opstart", "Send an OPSTART message to the object") { struct oml_node_state *oms = vty->index; abis_nm_opstart(oms->bts, oms->obj_class, oms->obj_inst[0], oms->obj_inst[1], oms->obj_inst[2]); return CMD_SUCCESS; } int abis_nm_vty_init(void) { install_element(ENABLE_NODE, &oml_class_inst_cmd); install_element(ENABLE_NODE, &oml_classnum_inst_cmd); install_node(&oml_node, dummy_config_write); vty_install_default(OML_NODE); install_element(OML_NODE, &oml_chg_adm_state_cmd); install_element(OML_NODE, &oml_opstart_cmd); return 0; } openbsc-0.15.0/openbsc/src/libbsc/abis_om2000.c000066400000000000000000001365261265565154000210110ustar00rootroot00000000000000/* Ericsson RBS 2xxx GSM O&M (OM2000) messages on the A-bis interface * implemented based on protocol trace analysis, no formal documentation */ /* (C) 2010-2011 by Harald Welte * * 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define OM_ALLOC_SIZE 1024 #define OM_HEADROOM_SIZE 128 /* use following functions from abis_nm.c: * om2k_msgb_alloc() * abis_om2k_sendmsg() */ struct abis_om2k_hdr { struct abis_om_hdr om; uint16_t msg_type; struct abis_om2k_mo mo; uint8_t data[0]; } __attribute__ ((packed)); enum abis_om2k_msgtype { OM2K_MSGT_ABORT_SP_CMD = 0x0000, OM2K_MSGT_ABORT_SP_COMPL = 0x0002, OM2K_MSGT_ALARM_REP_ACK = 0x0004, OM2K_MSGT_ALARM_REP_NACK = 0x0005, OM2K_MSGT_ALARM_REP = 0x0006, OM2K_MSGT_ALARM_STATUS_REQ = 0x0008, OM2K_MSGT_ALARM_STATUS_REQ_ACK = 0x000a, OM2K_MSGT_ALARM_STATUS_REQ_REJ = 0x000b, OM2K_MSGT_ALARM_STATUS_RES_ACK = 0x000c, OM2K_MSGT_ALARM_STATUS_RES_NACK = 0x000d, OM2K_MSGT_ALARM_STATUS_RES = 0x000e, OM2K_MSGT_CAL_TIME_RESP = 0x0010, OM2K_MSGT_CAL_TIME_REJ = 0x0011, OM2K_MSGT_CAL_TIME_REQ = 0x0012, OM2K_MSGT_CON_CONF_REQ = 0x0014, OM2K_MSGT_CON_CONF_REQ_ACK = 0x0016, OM2K_MSGT_CON_CONF_REQ_REJ = 0x0017, OM2K_MSGT_CON_CONF_RES_ACK = 0x0018, OM2K_MSGT_CON_CONF_RES_NACK = 0x0019, OM2K_MSGT_CON_CONF_RES = 0x001a, OM2K_MSGT_CONNECT_CMD = 0x001c, OM2K_MSGT_CONNECT_COMPL = 0x001e, OM2K_MSGT_CONNECT_REJ = 0x001f, OM2K_MSGT_DISABLE_REQ = 0x0028, OM2K_MSGT_DISABLE_REQ_ACK = 0x002a, OM2K_MSGT_DISABLE_REQ_REJ = 0x002b, OM2K_MSGT_DISABLE_RES_ACK = 0x002c, OM2K_MSGT_DISABLE_RES_NACK = 0x002d, OM2K_MSGT_DISABLE_RES = 0x002e, OM2K_MSGT_DISCONNECT_CMD = 0x0030, OM2K_MSGT_DISCONNECT_COMPL = 0x0032, OM2K_MSGT_DISCONNECT_REJ = 0x0033, OM2K_MSGT_ENABLE_REQ = 0x0034, OM2K_MSGT_ENABLE_REQ_ACK = 0x0036, OM2K_MSGT_ENABLE_REQ_REJ = 0x0037, OM2K_MSGT_ENABLE_RES_ACK = 0x0038, OM2K_MSGT_ENABLE_RES_NACK = 0x0039, OM2K_MSGT_ENABLE_RES = 0x003a, OM2K_MSGT_FAULT_REP_ACK = 0x0040, OM2K_MSGT_FAULT_REP_NACK = 0x0041, OM2K_MSGT_FAULT_REP = 0x0042, OM2K_MSGT_IS_CONF_REQ = 0x0060, OM2K_MSGT_IS_CONF_REQ_ACK = 0x0062, OM2K_MSGT_IS_CONF_REQ_REJ = 0x0063, OM2K_MSGT_IS_CONF_RES_ACK = 0x0064, OM2K_MSGT_IS_CONF_RES_NACK = 0x0065, OM2K_MSGT_IS_CONF_RES = 0x0066, OM2K_MSGT_OP_INFO = 0x0074, OM2K_MSGT_OP_INFO_ACK = 0x0076, OM2K_MSGT_OP_INFO_REJ = 0x0077, OM2K_MSGT_RESET_CMD = 0x0078, OM2K_MSGT_RESET_COMPL = 0x007a, OM2K_MSGT_RESET_REJ = 0x007b, OM2K_MSGT_RX_CONF_REQ = 0x007c, OM2K_MSGT_RX_CONF_REQ_ACK = 0x007e, OM2K_MSGT_RX_CONF_REQ_REJ = 0x007f, OM2K_MSGT_RX_CONF_RES_ACK = 0x0080, OM2K_MSGT_RX_CONF_RES_NACK = 0x0081, OM2K_MSGT_RX_CONF_RES = 0x0082, OM2K_MSGT_START_REQ = 0x0084, OM2K_MSGT_START_REQ_ACK = 0x0086, OM2K_MSGT_START_REQ_REJ = 0x0087, OM2K_MSGT_START_RES_ACK = 0x0088, OM2K_MSGT_START_RES_NACK = 0x0089, OM2K_MSGT_START_RES = 0x008a, OM2K_MSGT_STATUS_REQ = 0x008c, OM2K_MSGT_STATUS_RESP = 0x008e, OM2K_MSGT_STATUS_REJ = 0x008f, OM2K_MSGT_TEST_REQ = 0x0094, OM2K_MSGT_TEST_REQ_ACK = 0x0096, OM2K_MSGT_TEST_REQ_REJ = 0x0097, OM2K_MSGT_TEST_RES_ACK = 0x0098, OM2K_MSGT_TEST_RES_NACK = 0x0099, OM2K_MSGT_TEST_RES = 0x009a, OM2K_MSGT_TF_CONF_REQ = 0x00a0, OM2K_MSGT_TF_CONF_REQ_ACK = 0x00a2, OM2K_MSGT_TF_CONF_REQ_REJ = 0x00a3, OM2K_MSGT_TF_CONF_RES_ACK = 0x00a4, OM2K_MSGT_TF_CONF_RES_NACK = 0x00a5, OM2K_MSGT_TF_CONF_RES = 0x00a6, OM2K_MSGT_TS_CONF_REQ = 0x00a8, OM2K_MSGT_TS_CONF_REQ_ACK = 0x00aa, OM2K_MSGT_TS_CONF_REQ_REJ = 0x00ab, OM2K_MSGT_TS_CONF_RES_ACK = 0x00ac, OM2K_MSGT_TS_CONF_RES_NACK = 0x00ad, OM2K_MSGT_TS_CONF_RES = 0x00ae, OM2K_MSGT_TX_CONF_REQ = 0x00b0, OM2K_MSGT_TX_CONF_REQ_ACK = 0x00b2, OM2K_MSGT_TX_CONF_REQ_REJ = 0x00b3, OM2K_MSGT_TX_CONF_RES_ACK = 0x00b4, OM2K_MSGT_TX_CONF_RES_NACK = 0x00b5, OM2K_MSGT_TX_CONF_RES = 0x00b6, OM2K_MSGT_NEGOT_REQ_ACK = 0x0104, OM2K_MSGT_NEGOT_REQ_NACK = 0x0105, OM2K_MSGT_NEGOT_REQ = 0x0106, }; enum abis_om2k_dei { OM2K_DEI_ACCORDANCE_IND = 0x00, OM2K_DEI_BCC = 0x06, OM2K_DEI_BS_AG_BKS_RES = 0x07, OM2K_DEI_BSIC = 0x09, OM2K_DEI_BA_PA_MFRMS = 0x0a, OM2K_DEI_CBCH_INDICATOR = 0x0b, OM2K_DEI_CCCH_OPTIONS = 0x0c, OM2K_DEI_CAL_TIME = 0x0d, OM2K_DEI_COMBINATION = 0x0f, OM2K_DEI_CON_CONN_LIST = 0x10, OM2K_DEI_DRX_DEV_MAX = 0x12, OM2K_DEI_END_LIST_NR = 0x13, OM2K_DEI_EXT_COND_MAP_1 = 0x14, OM2K_DEI_EXT_COND_MAP_2 = 0x15, OM2K_DEI_FILLING_MARKER = 0x1c, OM2K_DEI_FN_OFFSET = 0x1d, OM2K_DEI_FREQ_LIST = 0x1e, OM2K_DEI_FREQ_SPEC_RX = 0x1f, OM2K_DEI_FREQ_SPEC_TX = 0x20, OM2K_DEI_HSN = 0x21, OM2K_DEI_ICM_INDICATOR = 0x22, OM2K_DEI_INT_FAULT_MAP_1A = 0x23, OM2K_DEI_INT_FAULT_MAP_1B = 0x24, OM2K_DEI_INT_FAULT_MAP_2A = 0x25, OM2K_DEI_INT_FAULT_MAP_2A_EXT = 0x26, OM2K_DEI_IS_CONN_LIST = 0x27, OM2K_DEI_LIST_NR = 0x28, OM2K_DEI_LOCAL_ACCESS = 0x2a, OM2K_DEI_MAIO = 0x2b, OM2K_DEI_MO_STATE = 0x2c, OM2K_DEI_NY1 = 0x2d, OM2K_DEI_OP_INFO = 0x2e, OM2K_DEI_POWER = 0x2f, OM2K_DEI_REASON_CODE = 0x32, OM2K_DEI_RX_DIVERSITY = 0x33, OM2K_DEI_RESULT_CODE = 0x35, OM2K_DEI_T3105 = 0x38, OM2K_DEI_TF_MODE = 0x3a, OM2K_DEI_TS_NR = 0x3c, OM2K_DEI_TSC = 0x3d, OM2K_DEI_BTS_VERSION = 0x40, OM2K_DEI_OML_IWD_VERSION = 0x41, OM2K_DEI_RSL_IWD_VERSION = 0x42, OM2K_DEI_OML_FUNC_MAP_1 = 0x43, OM2K_DEI_OML_FUNC_MAP_2 = 0x44, OM2K_DEI_RSL_FUNC_MAP_1 = 0x45, OM2K_DEI_RSL_FUNC_MAP_2 = 0x46, OM2K_DEI_EXT_RANGE = 0x47, OM2K_DEI_REQ_IND = 0x48, OM2K_DEI_REPL_UNIT_MAP = 0x50, OM2K_DEI_ICM_BOUND_PARAMS = 0x74, OM2K_DEI_LSC = 0x79, OM2K_DEI_LSC_FILT_TIME = 0x7a, OM2K_DEI_CALL_SUPV_TIME = 0x7b, OM2K_DEI_ICM_CHAN_RATE = 0x7e, OM2K_DEI_HW_INFO_SIG = 0x84, OM2K_DEI_TF_SYNC_SRC = 0x86, OM2K_DEI_TTA = 0x87, OM2K_DEI_CAPA_SIG = 0x8a, OM2K_DEI_NEGOT_REC1 = 0x90, OM2K_DEI_NEGOT_REC2 = 0x91, OM2K_DEI_ENCR_ALG = 0x92, OM2K_DEI_INTERF_REJ_COMB = 0x94, OM2K_DEI_FS_OFFSET = 0x98, OM2K_DEI_EXT_COND_MAP_2_EXT = 0x9c, }; const struct tlv_definition om2k_att_tlvdef = { .def = { [OM2K_DEI_ACCORDANCE_IND] = { TLV_TYPE_TV }, [OM2K_DEI_BCC] = { TLV_TYPE_TV }, [OM2K_DEI_BS_AG_BKS_RES] = { TLV_TYPE_TV }, [OM2K_DEI_BSIC] = { TLV_TYPE_TV }, [OM2K_DEI_BA_PA_MFRMS] = { TLV_TYPE_TV }, [OM2K_DEI_CBCH_INDICATOR] = { TLV_TYPE_TV }, [OM2K_DEI_INT_FAULT_MAP_1A] = { TLV_TYPE_FIXED, 6 }, [OM2K_DEI_INT_FAULT_MAP_1B] = { TLV_TYPE_FIXED, 6 }, [OM2K_DEI_INT_FAULT_MAP_2A] = { TLV_TYPE_FIXED, 6 }, [OM2K_DEI_INT_FAULT_MAP_2A_EXT]={ TLV_TYPE_FIXED, 6 }, [OM2K_DEI_CCCH_OPTIONS] = { TLV_TYPE_TV }, [OM2K_DEI_CAL_TIME] = { TLV_TYPE_FIXED, 6 }, [OM2K_DEI_COMBINATION] = { TLV_TYPE_TV }, [OM2K_DEI_CON_CONN_LIST] = { TLV_TYPE_TLV }, [OM2K_DEI_DRX_DEV_MAX] = { TLV_TYPE_TV }, [OM2K_DEI_END_LIST_NR] = { TLV_TYPE_TV }, [OM2K_DEI_EXT_COND_MAP_1] = { TLV_TYPE_FIXED, 2 }, [OM2K_DEI_EXT_COND_MAP_2] = { TLV_TYPE_FIXED, 2 }, [OM2K_DEI_FILLING_MARKER] = { TLV_TYPE_TV }, [OM2K_DEI_FN_OFFSET] = { TLV_TYPE_FIXED, 2 }, [OM2K_DEI_FREQ_LIST] = { TLV_TYPE_TLV }, [OM2K_DEI_FREQ_SPEC_RX] = { TLV_TYPE_FIXED, 2 }, [OM2K_DEI_FREQ_SPEC_TX] = { TLV_TYPE_FIXED, 2 }, [OM2K_DEI_HSN] = { TLV_TYPE_TV }, [OM2K_DEI_ICM_INDICATOR] = { TLV_TYPE_TV }, [OM2K_DEI_IS_CONN_LIST] = { TLV_TYPE_TLV }, [OM2K_DEI_LIST_NR] = { TLV_TYPE_TV }, [OM2K_DEI_LOCAL_ACCESS] = { TLV_TYPE_TV }, [OM2K_DEI_MAIO] = { TLV_TYPE_TV }, [OM2K_DEI_MO_STATE] = { TLV_TYPE_TV }, [OM2K_DEI_NY1] = { TLV_TYPE_TV }, [OM2K_DEI_OP_INFO] = { TLV_TYPE_TV }, [OM2K_DEI_POWER] = { TLV_TYPE_TV }, [OM2K_DEI_REASON_CODE] = { TLV_TYPE_TV }, [OM2K_DEI_RX_DIVERSITY] = { TLV_TYPE_TV }, [OM2K_DEI_RESULT_CODE] = { TLV_TYPE_TV }, [OM2K_DEI_T3105] = { TLV_TYPE_TV }, [OM2K_DEI_TF_MODE] = { TLV_TYPE_TV }, [OM2K_DEI_TS_NR] = { TLV_TYPE_TV }, [OM2K_DEI_TSC] = { TLV_TYPE_TV }, [OM2K_DEI_BTS_VERSION] = { TLV_TYPE_FIXED, 12 }, [OM2K_DEI_OML_IWD_VERSION] = { TLV_TYPE_FIXED, 6 }, [OM2K_DEI_RSL_IWD_VERSION] = { TLV_TYPE_FIXED, 6 }, [OM2K_DEI_OML_FUNC_MAP_1] = { TLV_TYPE_TLV }, [OM2K_DEI_OML_FUNC_MAP_2] = { TLV_TYPE_TLV }, [OM2K_DEI_RSL_FUNC_MAP_1] = { TLV_TYPE_TLV }, [OM2K_DEI_RSL_FUNC_MAP_2] = { TLV_TYPE_TLV }, [OM2K_DEI_EXT_RANGE] = { TLV_TYPE_TV }, [OM2K_DEI_REQ_IND] = { TLV_TYPE_TV }, [OM2K_DEI_REPL_UNIT_MAP] = { TLV_TYPE_FIXED, 6 }, [OM2K_DEI_ICM_BOUND_PARAMS] = { TLV_TYPE_FIXED, 5 }, [OM2K_DEI_LSC] = { TLV_TYPE_TV }, [OM2K_DEI_LSC_FILT_TIME] = { TLV_TYPE_TV }, [OM2K_DEI_CALL_SUPV_TIME] = { TLV_TYPE_TV }, [OM2K_DEI_ICM_CHAN_RATE] = { TLV_TYPE_TV }, [OM2K_DEI_HW_INFO_SIG] = { TLV_TYPE_FIXED, 2 }, [OM2K_DEI_TF_SYNC_SRC] = { TLV_TYPE_TV }, [OM2K_DEI_TTA] = { TLV_TYPE_TV }, [OM2K_DEI_CAPA_SIG] = { TLV_TYPE_FIXED, 2 }, [OM2K_DEI_NEGOT_REC1] = { TLV_TYPE_TLV }, [OM2K_DEI_NEGOT_REC2] = { TLV_TYPE_TLV }, [OM2K_DEI_ENCR_ALG] = { TLV_TYPE_TV }, [OM2K_DEI_INTERF_REJ_COMB] = { TLV_TYPE_TV }, [OM2K_DEI_FS_OFFSET] = { TLV_TYPE_FIXED, 5 }, [OM2K_DEI_EXT_COND_MAP_2_EXT] = { TLV_TYPE_FIXED, 4 }, }, }; static const struct value_string om2k_msgcode_vals[] = { { 0x0000, "Abort SP Command" }, { 0x0002, "Abort SP Complete" }, { 0x0004, "Alarm Report ACK" }, { 0x0005, "Alarm Report NACK" }, { 0x0006, "Alarm Report" }, { 0x0008, "Alarm Status Request" }, { 0x000a, "Alarm Status Request Accept" }, { 0x000b, "Alarm Status Request Reject" }, { 0x000c, "Alarm Status Result ACK" }, { 0x000d, "Alarm Status Result NACK" }, { 0x000e, "Alarm Status Result" }, { 0x0010, "Calendar Time Response" }, { 0x0011, "Calendar Time Reject" }, { 0x0012, "Calendar Time Request" }, { 0x0014, "CON Configuration Request" }, { 0x0016, "CON Configuration Request Accept" }, { 0x0017, "CON Configuration Request Reject" }, { 0x0018, "CON Configuration Result ACK" }, { 0x0019, "CON Configuration Result NACK" }, { 0x001a, "CON Configuration Result" }, { 0x001c, "Connect Command" }, { 0x001e, "Connect Complete" }, { 0x001f, "Connect Reject" }, { 0x0028, "Disable Request" }, { 0x002a, "Disable Request Accept" }, { 0x002b, "Disable Request Reject" }, { 0x002c, "Disable Result ACK" }, { 0x002d, "Disable Result NACK" }, { 0x002e, "Disable Result" }, { 0x0030, "Disconnect Command" }, { 0x0032, "Disconnect Complete" }, { 0x0033, "Disconnect Reject" }, { 0x0034, "Enable Request" }, { 0x0036, "Enable Request Accept" }, { 0x0037, "Enable Request Reject" }, { 0x0038, "Enable Result ACK" }, { 0x0039, "Enable Result NACK" }, { 0x003a, "Enable Result" }, { 0x003c, "Escape Downlink Normal" }, { 0x003d, "Escape Downlink NACK" }, { 0x003e, "Escape Uplink Normal" }, { 0x003f, "Escape Uplink NACK" }, { 0x0040, "Fault Report ACK" }, { 0x0041, "Fault Report NACK" }, { 0x0042, "Fault Report" }, { 0x0044, "File Package End Command" }, { 0x0046, "File Package End Result" }, { 0x0047, "File Package End Reject" }, { 0x0048, "File Relation Request" }, { 0x004a, "File Relation Response" }, { 0x004b, "File Relation Request Reject" }, { 0x004c, "File Segment Transfer" }, { 0x004e, "File Segment Transfer Complete" }, { 0x004f, "File Segment Transfer Reject" }, { 0x0050, "HW Information Request" }, { 0x0052, "HW Information Request Accept" }, { 0x0053, "HW Information Request Reject" }, { 0x0054, "HW Information Result ACK" }, { 0x0055, "HW Information Result NACK" }, { 0x0056, "HW Information Result" }, { 0x0060, "IS Configuration Request" }, { 0x0062, "IS Configuration Request Accept" }, { 0x0063, "IS Configuration Request Reject" }, { 0x0064, "IS Configuration Result ACK" }, { 0x0065, "IS Configuration Result NACK" }, { 0x0066, "IS Configuration Result" }, { 0x0068, "Load Data End" }, { 0x006a, "Load Data End Result" }, { 0x006b, "Load Data End Reject" }, { 0x006c, "Load Data Init" }, { 0x006e, "Load Data Init Accept" }, { 0x006f, "Load Data Init Reject" }, { 0x0070, "Loop Control Command" }, { 0x0072, "Loop Control Complete" }, { 0x0073, "Loop Control Reject" }, { 0x0074, "Operational Information" }, { 0x0076, "Operational Information Accept" }, { 0x0077, "Operational Information Reject" }, { 0x0078, "Reset Command" }, { 0x007a, "Reset Complete" }, { 0x007b, "Reset Reject" }, { 0x007c, "RX Configuration Request" }, { 0x007e, "RX Configuration Request Accept" }, { 0x007f, "RX Configuration Request Reject" }, { 0x0080, "RX Configuration Result ACK" }, { 0x0081, "RX Configuration Result NACK" }, { 0x0082, "RX Configuration Result" }, { 0x0084, "Start Request" }, { 0x0086, "Start Request Accept" }, { 0x0087, "Start Request Reject" }, { 0x0088, "Start Result ACK" }, { 0x0089, "Start Result NACK" }, { 0x008a, "Start Result" }, { 0x008c, "Status Request" }, { 0x008e, "Status Response" }, { 0x008f, "Status Reject" }, { 0x0094, "Test Request" }, { 0x0096, "Test Request Accept" }, { 0x0097, "Test Request Reject" }, { 0x0098, "Test Result ACK" }, { 0x0099, "Test Result NACK" }, { 0x009a, "Test Result" }, { 0x00a0, "TF Configuration Request" }, { 0x00a2, "TF Configuration Request Accept" }, { 0x00a3, "TF Configuration Request Reject" }, { 0x00a4, "TF Configuration Result ACK" }, { 0x00a5, "TF Configuration Result NACK" }, { 0x00a6, "TF Configuration Result" }, { 0x00a8, "TS Configuration Request" }, { 0x00aa, "TS Configuration Request Accept" }, { 0x00ab, "TS Configuration Request Reject" }, { 0x00ac, "TS Configuration Result ACK" }, { 0x00ad, "TS Configuration Result NACK" }, { 0x00ae, "TS Configuration Result" }, { 0x00b0, "TX Configuration Request" }, { 0x00b2, "TX Configuration Request Accept" }, { 0x00b3, "TX Configuration Request Reject" }, { 0x00b4, "TX Configuration Result ACK" }, { 0x00b5, "TX Configuration Result NACK" }, { 0x00b6, "TX Configuration Result" }, { 0x00bc, "DIP Alarm Report ACK" }, { 0x00bd, "DIP Alarm Report NACK" }, { 0x00be, "DIP Alarm Report" }, { 0x00c0, "DIP Alarm Status Request" }, { 0x00c2, "DIP Alarm Status Response" }, { 0x00c3, "DIP Alarm Status Reject" }, { 0x00c4, "DIP Quality Report I ACK" }, { 0x00c5, "DIP Quality Report I NACK" }, { 0x00c6, "DIP Quality Report I" }, { 0x00c8, "DIP Quality Report II ACK" }, { 0x00c9, "DIP Quality Report II NACK" }, { 0x00ca, "DIP Quality Report II" }, { 0x00dc, "DP Configuration Request" }, { 0x00de, "DP Configuration Request Accept" }, { 0x00df, "DP Configuration Request Reject" }, { 0x00e0, "DP Configuration Result ACK" }, { 0x00e1, "DP Configuration Result NACK" }, { 0x00e2, "DP Configuration Result" }, { 0x00e4, "Capabilities HW Info Report ACK" }, { 0x00e5, "Capabilities HW Info Report NACK" }, { 0x00e6, "Capabilities HW Info Report" }, { 0x00e8, "Capabilities Request" }, { 0x00ea, "Capabilities Request Accept" }, { 0x00eb, "Capabilities Request Reject" }, { 0x00ec, "Capabilities Result ACK" }, { 0x00ed, "Capabilities Result NACK" }, { 0x00ee, "Capabilities Result" }, { 0x00f0, "FM Configuration Request" }, { 0x00f2, "FM Configuration Request Accept" }, { 0x00f3, "FM Configuration Request Reject" }, { 0x00f4, "FM Configuration Result ACK" }, { 0x00f5, "FM Configuration Result NACK" }, { 0x00f6, "FM Configuration Result" }, { 0x00f8, "FM Report Request" }, { 0x00fa, "FM Report Response" }, { 0x00fb, "FM Report Reject" }, { 0x00fc, "FM Start Command" }, { 0x00fe, "FM Start Complete" }, { 0x00ff, "FM Start Reject" }, { 0x0100, "FM Stop Command" }, { 0x0102, "FM Stop Complete" }, { 0x0103, "FM Stop Reject" }, { 0x0104, "Negotiation Request ACK" }, { 0x0105, "Negotiation Request NACK" }, { 0x0106, "Negotiation Request" }, { 0x0108, "BTS Initiated Request ACK" }, { 0x0109, "BTS Initiated Request NACK" }, { 0x010a, "BTS Initiated Request" }, { 0x010c, "Radio Channels Release Command" }, { 0x010e, "Radio Channels Release Complete" }, { 0x010f, "Radio Channels Release Reject" }, { 0x0118, "Feature Control Command" }, { 0x011a, "Feature Control Complete" }, { 0x011b, "Feature Control Reject" }, { 0, NULL } }; /* TS 12.21 Section 9.4: Attributes */ static const struct value_string om2k_attr_vals[] = { { 0x00, "Accordance indication" }, { 0x01, "Alarm Id" }, { 0x02, "Alarm Data" }, { 0x03, "Alarm Severity" }, { 0x04, "Alarm Status" }, { 0x05, "Alarm Status Type" }, { 0x06, "BCC" }, { 0x07, "BS_AG_BKS_RES" }, { 0x09, "BSIC" }, { 0x0a, "BA_PA_MFRMS" }, { 0x0b, "CBCH Indicator" }, { 0x0c, "CCCH Options" }, { 0x0d, "Calendar Time" }, { 0x0f, "Channel Combination" }, { 0x10, "CON Connection List" }, { 0x11, "Data End Indication" }, { 0x12, "DRX_DEV_MAX" }, { 0x13, "End List Number" }, { 0x14, "External Condition Map Class 1" }, { 0x15, "External Condition Map Class 2" }, { 0x16, "File Relation Indication" }, { 0x17, "File Revision" }, { 0x18, "File Segment Data" }, { 0x19, "File Segment Length" }, { 0x1a, "File Segment Sequence Number" }, { 0x1b, "File Size" }, { 0x1c, "Filling Marker" }, { 0x1d, "FN Offset" }, { 0x1e, "Frequency List" }, { 0x1f, "Frequency Specifier RX" }, { 0x20, "Frequency Specifier TX" }, { 0x21, "HSN" }, { 0x22, "ICM Indicator" }, { 0x23, "Internal Fault Map Class 1A" }, { 0x24, "Internal Fault Map Class 1B" }, { 0x25, "Internal Fault Map Class 2A" }, { 0x26, "Internal Fault Map Class 2A Extension" }, { 0x27, "IS Connection List" }, { 0x28, "List Number" }, { 0x29, "File Package State Indication" }, { 0x2a, "Local Access State" }, { 0x2b, "MAIO" }, { 0x2c, "MO State" }, { 0x2d, "Ny1" }, { 0x2e, "Operational Information" }, { 0x2f, "Power" }, { 0x30, "RU Position Data" }, { 0x31, "Protocol Error" }, { 0x32, "Reason Code" }, { 0x33, "Receiver Diversity" }, { 0x34, "Replacement Unit Map" }, { 0x35, "Result Code" }, { 0x36, "RU Revision Data" }, { 0x38, "T3105" }, { 0x39, "Test Loop Setting" }, { 0x3a, "TF Mode" }, { 0x3b, "TF Compensation Value" }, { 0x3c, "Time Slot Number" }, { 0x3d, "TSC" }, { 0x3e, "RU Logical Id" }, { 0x3f, "RU Serial Number Data" }, { 0x40, "BTS Version" }, { 0x41, "OML IWD Version" }, { 0x42, "RWL IWD Version" }, { 0x43, "OML Function Map 1" }, { 0x44, "OML Function Map 2" }, { 0x45, "RSL Function Map 1" }, { 0x46, "RSL Function Map 2" }, { 0x47, "Extended Range Indicator" }, { 0x48, "Request Indicators" }, { 0x49, "DIP Alarm Condition Map" }, { 0x4a, "ES Incoming" }, { 0x4b, "ES Outgoing" }, { 0x4e, "SES Incoming" }, { 0x4f, "SES Outgoing" }, { 0x50, "Replacement Unit Map Extension" }, { 0x52, "UAS Incoming" }, { 0x53, "UAS Outgoing" }, { 0x58, "DF Incoming" }, { 0x5a, "DF Outgoing" }, { 0x5c, "SF" }, { 0x60, "S Bits Setting" }, { 0x61, "CRC-4 Use Option" }, { 0x62, "T Parameter" }, { 0x63, "N Parameter" }, { 0x64, "N1 Parameter" }, { 0x65, "N3 Parameter" }, { 0x66, "N4 Parameter" }, { 0x67, "P Parameter" }, { 0x68, "Q Parameter" }, { 0x69, "BI_Q1" }, { 0x6a, "BI_Q2" }, { 0x74, "ICM Boundary Parameters" }, { 0x77, "AFT" }, { 0x78, "AFT RAI" }, { 0x79, "Link Supervision Control" }, { 0x7a, "Link Supervision Filtering Time" }, { 0x7b, "Call Supervision Time" }, { 0x7c, "Interval Length UAS Incoming" }, { 0x7d, "Interval Length UAS Outgoing" }, { 0x7e, "ICM Channel Rate" }, { 0x7f, "Attribute Identifier" }, { 0x80, "FM Frequency List" }, { 0x81, "FM Frequency Report" }, { 0x82, "FM Percentile" }, { 0x83, "FM Clear Indication" }, { 0x84, "HW Info Signature" }, { 0x85, "MO Record" }, { 0x86, "TF Synchronisation Source" }, { 0x87, "TTA" }, { 0x88, "End Segment Number" }, { 0x89, "Segment Number" }, { 0x8a, "Capabilities Signature" }, { 0x8c, "File Relation List" }, { 0x90, "Negotiation Record I" }, { 0x91, "Negotiation Record II" }, { 0x92, "Encryption Algorithm" }, { 0x94, "Interference Rejection Combining" }, { 0x95, "Dedication Information" }, { 0x97, "Feature Code" }, { 0x98, "FS Offset" }, { 0x99, "ESB Timeslot" }, { 0x9a, "Master TG Instance" }, { 0x9b, "Master TX Chain Delay" }, { 0x9c, "External Condition Class 2 Extension" }, { 0x9d, "TSs MO State" }, { 0, NULL } }; const struct value_string om2k_mo_class_short_vals[] = { { 0x01, "TRXC" }, { 0x03, "TS" }, { 0x04, "TF" }, { 0x05, "IS" }, { 0x06, "CON" }, { 0x07, "DP" }, { 0x0a, "CF" }, { 0x0b, "TX" }, { 0x0c, "RX" }, { 0, NULL } }; static struct msgb *om2k_msgb_alloc(void) { return msgb_alloc_headroom(OM_ALLOC_SIZE, OM_HEADROOM_SIZE, "OM2000"); } static int abis_om2k_tlv_parse(struct tlv_parsed *tp, const uint8_t *buf, int len) { return tlv_parse(tp, &om2k_att_tlvdef, buf, len, 0, 0); } static int abis_om2k_msg_tlv_parse(struct tlv_parsed *tp, struct abis_om2k_hdr *oh) { return abis_om2k_tlv_parse(tp, oh->data, oh->om.length - 6); } static char *om2k_mo_name(const struct abis_om2k_mo *mo) { static char mo_buf[64]; memset(mo_buf, 0, sizeof(mo_buf)); snprintf(mo_buf, sizeof(mo_buf), "%s/%02x/%02x/%02x", get_value_string(om2k_mo_class_short_vals, mo->class), mo->bts, mo->assoc_so, mo->inst); return mo_buf; } /* resolve the gsm_nm_state data structure for a given MO */ static struct gsm_nm_state * mo2nm_state(struct gsm_bts *bts, const struct abis_om2k_mo *mo) { struct gsm_bts_trx *trx; struct gsm_nm_state *nm_state = NULL; switch (mo->class) { case OM2K_MO_CLS_TRXC: trx = gsm_bts_trx_num(bts, mo->assoc_so); if (!trx) return NULL; nm_state = &trx->mo.nm_state; break; case OM2K_MO_CLS_TS: trx = gsm_bts_trx_num(bts, mo->assoc_so); if (!trx) return NULL; if (mo->inst >= ARRAY_SIZE(trx->ts)) return NULL; nm_state = &trx->ts[mo->inst].mo.nm_state; break; case OM2K_MO_CLS_TF: nm_state = &bts->rbs2000.tf.mo.nm_state; break; case OM2K_MO_CLS_IS: nm_state = &bts->rbs2000.is.mo.nm_state; break; case OM2K_MO_CLS_CON: nm_state = &bts->rbs2000.con.mo.nm_state; break; case OM2K_MO_CLS_DP: nm_state = &bts->rbs2000.con.mo.nm_state; break; case OM2K_MO_CLS_CF: nm_state = &bts->mo.nm_state; break; case OM2K_MO_CLS_TX: trx = gsm_bts_trx_num(bts, mo->assoc_so); if (!trx) return NULL; break; case OM2K_MO_CLS_RX: trx = gsm_bts_trx_num(bts, mo->assoc_so); if (!trx) return NULL; break; } return nm_state; } static void *mo2obj(struct gsm_bts *bts, struct abis_om2k_mo *mo) { struct gsm_bts_trx *trx; switch (mo->class) { case OM2K_MO_CLS_TX: case OM2K_MO_CLS_RX: case OM2K_MO_CLS_TRXC: return gsm_bts_trx_num(bts, mo->assoc_so); case OM2K_MO_CLS_TS: trx = gsm_bts_trx_num(bts, mo->assoc_so); if (!trx) return NULL; if (mo->inst >= ARRAY_SIZE(trx->ts)) return NULL; return &trx->ts[mo->inst]; case OM2K_MO_CLS_TF: case OM2K_MO_CLS_IS: case OM2K_MO_CLS_CON: case OM2K_MO_CLS_DP: case OM2K_MO_CLS_CF: return bts; } return NULL; } static void update_mo_state(struct gsm_bts *bts, struct abis_om2k_mo *mo, uint8_t mo_state) { struct gsm_nm_state *nm_state = mo2nm_state(bts, mo); struct gsm_nm_state new_state; struct nm_statechg_signal_data nsd; if (!nm_state) return; new_state = *nm_state; /* NOTICE: 12.21 Availability state values != OM2000 */ new_state.availability = mo_state; memset(&nsd, 0, sizeof(nsd)); nsd.bts = bts; nsd.obj = mo2obj(bts, mo); nsd.old_state = nm_state; nsd.new_state = &new_state; nsd.om2k_mo = mo; osmo_signal_dispatch(SS_NM, S_NM_STATECHG_ADM, &nsd); nm_state->availability = new_state.availability; } static void update_op_state(struct gsm_bts *bts, const struct abis_om2k_mo *mo, uint8_t op_state) { struct gsm_nm_state *nm_state = mo2nm_state(bts, mo); struct gsm_nm_state new_state; if (!nm_state) return; new_state = *nm_state; switch (op_state) { case 1: new_state.operational = NM_OPSTATE_ENABLED; break; case 0: new_state.operational = NM_OPSTATE_DISABLED; break; default: new_state.operational = NM_OPSTATE_NULL; break; } nm_state->operational = new_state.operational; } static void signal_op_state(struct gsm_bts *bts, struct abis_om2k_mo *mo) { struct gsm_nm_state *nm_state = mo2nm_state(bts, mo); struct nm_statechg_signal_data nsd; if (!nm_state) return; nsd.bts = bts; nsd.obj = mo2obj(bts, mo); nsd.old_state = nm_state; nsd.new_state = nm_state; nsd.om2k_mo = mo; osmo_signal_dispatch(SS_NM, S_NM_STATECHG_OPER, &nsd); } static int abis_om2k_sendmsg(struct gsm_bts *bts, struct msgb *msg) { struct abis_om2k_hdr *o2h; struct gsm_bts_trx *trx; msg->l2h = msg->data; o2h = (struct abis_om2k_hdr *) msg->l2h; /* Compute the length in the OML header */ o2h->om.length = 6 + msgb_l2len(msg)-sizeof(*o2h); switch (o2h->mo.class) { case OM2K_MO_CLS_TRXC: case OM2K_MO_CLS_TX: case OM2K_MO_CLS_RX: /* Route through per-TRX OML Link to the appropriate TRX */ trx = gsm_bts_trx_by_nr(bts, o2h->mo.inst); if (!trx) { LOGP(DNM, LOGL_ERROR, "MO=%s Tx Dropping msg to " "non-existing TRX\n", om2k_mo_name(&o2h->mo)); return -ENODEV; } msg->dst = trx->oml_link; break; case OM2K_MO_CLS_TS: /* Route through per-TRX OML Link to the appropriate TRX */ trx = gsm_bts_trx_by_nr(bts, o2h->mo.assoc_so); if (!trx) { LOGP(DNM, LOGL_ERROR, "MO=%s Tx Dropping msg to " "non-existing TRX\n", om2k_mo_name(&o2h->mo)); return -ENODEV; } msg->dst = trx->oml_link; break; default: /* Route through the IXU/DXU OML Link */ msg->dst = bts->oml_link; break; } return _abis_nm_sendmsg(msg); } static void fill_om2k_hdr(struct abis_om2k_hdr *o2h, const struct abis_om2k_mo *mo, uint16_t msg_type) { o2h->om.mdisc = ABIS_OM_MDISC_FOM; o2h->om.placement = ABIS_OM_PLACEMENT_ONLY; o2h->om.sequence = 0; /* We fill o2h->om.length later during om2k_sendmsg() */ o2h->msg_type = htons(msg_type); memcpy(&o2h->mo, mo, sizeof(o2h->mo)); } const struct abis_om2k_mo om2k_mo_cf = { OM2K_MO_CLS_CF, 0, 0xFF, 0 }; const struct abis_om2k_mo om2k_mo_is = { OM2K_MO_CLS_IS, 0, 0xFF, 0 }; const struct abis_om2k_mo om2k_mo_con = { OM2K_MO_CLS_CON, 0, 0xFF, 0 }; const struct abis_om2k_mo om2k_mo_tf = { OM2K_MO_CLS_TF, 0, 0xFF, 0 }; static int abis_om2k_cal_time_resp(struct gsm_bts *bts) { struct msgb *msg = om2k_msgb_alloc(); struct abis_om2k_hdr *o2k; time_t tm_t; struct tm *tm; o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); fill_om2k_hdr(o2k, &om2k_mo_cf, OM2K_MSGT_CAL_TIME_RESP); tm_t = time(NULL); tm = localtime(&tm_t); msgb_put_u8(msg, OM2K_DEI_CAL_TIME); msgb_put_u8(msg, tm->tm_year % 100); msgb_put_u8(msg, tm->tm_mon + 1); msgb_put_u8(msg, tm->tm_mday); msgb_put_u8(msg, tm->tm_hour); msgb_put_u8(msg, tm->tm_min); msgb_put_u8(msg, tm->tm_sec); return abis_om2k_sendmsg(bts, msg); } static int abis_om2k_tx_simple(struct gsm_bts *bts, const struct abis_om2k_mo *mo, uint8_t msg_type) { struct msgb *msg = om2k_msgb_alloc(); struct abis_om2k_hdr *o2k; o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); fill_om2k_hdr(o2k, mo, msg_type); DEBUGP(DNM, "Tx MO=%s %s\n", om2k_mo_name(mo), get_value_string(om2k_msgcode_vals, msg_type)); return abis_om2k_sendmsg(bts, msg); } int abis_om2k_tx_reset_cmd(struct gsm_bts *bts, const struct abis_om2k_mo *mo) { return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_RESET_CMD); } int abis_om2k_tx_start_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo) { return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_START_REQ); } int abis_om2k_tx_status_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo) { return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_STATUS_REQ); } int abis_om2k_tx_connect_cmd(struct gsm_bts *bts, const struct abis_om2k_mo *mo) { return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_CONNECT_CMD); } int abis_om2k_tx_disconnect_cmd(struct gsm_bts *bts, const struct abis_om2k_mo *mo) { return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_DISCONNECT_CMD); } int abis_om2k_tx_test_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo) { return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_TEST_REQ); } int abis_om2k_tx_enable_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo) { return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_ENABLE_REQ); } int abis_om2k_tx_disable_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo) { return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_DISABLE_REQ); } int abis_om2k_tx_op_info(struct gsm_bts *bts, const struct abis_om2k_mo *mo, uint8_t operational) { struct msgb *msg = om2k_msgb_alloc(); struct abis_om2k_hdr *o2k; o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); fill_om2k_hdr(o2k, mo, OM2K_MSGT_OP_INFO); msgb_tv_put(msg, OM2K_DEI_OP_INFO, operational); DEBUGP(DNM, "Tx MO=%s %s\n", om2k_mo_name(mo), get_value_string(om2k_msgcode_vals, OM2K_MSGT_OP_INFO)); /* we update the state here... and send the signal at ACK */ update_op_state(bts, mo, operational); return abis_om2k_sendmsg(bts, msg); } static void om2k_fill_is_conn_grp(struct om2k_is_conn_grp *grp, uint16_t icp1, uint16_t icp2, uint8_t cont_idx) { grp->icp1 = htons(icp1); grp->icp2 = htons(icp2); grp->cont_idx = cont_idx; } int abis_om2k_tx_is_conf_req(struct gsm_bts *bts) { struct msgb *msg = om2k_msgb_alloc(); struct abis_om2k_hdr *o2k; struct is_conn_group *grp; unsigned int num_grps = 0, i = 0; struct om2k_is_conn_grp *cg; /* count number of groups in linked list */ llist_for_each_entry(grp, &bts->rbs2000.is.conn_groups, list) num_grps++; if (!num_grps) return -EINVAL; /* allocate buffer for oml group array */ cg = talloc_zero_array(bts, struct om2k_is_conn_grp, num_grps); /* fill array with data from linked list */ llist_for_each_entry(grp, &bts->rbs2000.is.conn_groups, list) om2k_fill_is_conn_grp(&cg[i++], grp->icp1, grp->icp2, grp->ci); o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); fill_om2k_hdr(o2k, &om2k_mo_is, OM2K_MSGT_IS_CONF_REQ); msgb_tv_put(msg, OM2K_DEI_LIST_NR, 1); msgb_tv_put(msg, OM2K_DEI_END_LIST_NR, 1); msgb_tlv_put(msg, OM2K_DEI_IS_CONN_LIST, num_grps * sizeof(*cg), (uint8_t *)cg); talloc_free(cg); DEBUGP(DNM, "Tx MO=%s %s\n", om2k_mo_name(&om2k_mo_is), get_value_string(om2k_msgcode_vals, OM2K_MSGT_IS_CONF_REQ)); return abis_om2k_sendmsg(bts, msg); } int abis_om2k_tx_con_conf_req(struct gsm_bts *bts, uint8_t *data, unsigned int len) { struct msgb *msg = om2k_msgb_alloc(); struct abis_om2k_hdr *o2k; o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); fill_om2k_hdr(o2k, &om2k_mo_con, OM2K_MSGT_CON_CONF_REQ); msgb_tv_put(msg, OM2K_DEI_LIST_NR, 1); msgb_tv_put(msg, OM2K_DEI_END_LIST_NR, 1); msgb_tlv_put(msg, OM2K_DEI_CON_CONN_LIST, len, data); DEBUGP(DNM, "Tx MO=%s %s\n", om2k_mo_name(&om2k_mo_con), get_value_string(om2k_msgcode_vals, OM2K_MSGT_CON_CONF_REQ)); return abis_om2k_sendmsg(bts, msg); } static void om2k_trx_to_mo(struct abis_om2k_mo *mo, const struct gsm_bts_trx *trx, enum abis_om2k_mo_cls cls) { mo->class = cls; mo->bts = 0; mo->inst = trx->nr; mo->assoc_so = 255; } static void om2k_ts_to_mo(struct abis_om2k_mo *mo, const struct gsm_bts_trx_ts *ts) { mo->class = OM2K_MO_CLS_TS; mo->bts = 0; mo->inst = ts->nr; mo->assoc_so = ts->trx->nr; } /* Configure a Receiver MO */ int abis_om2k_tx_rx_conf_req(struct gsm_bts_trx *trx) { struct msgb *msg = om2k_msgb_alloc(); struct abis_om2k_hdr *o2k; struct abis_om2k_mo mo; om2k_trx_to_mo(&mo, trx, OM2K_MO_CLS_RX); o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); fill_om2k_hdr(o2k, &mo, OM2K_MSGT_RX_CONF_REQ); msgb_tv16_put(msg, OM2K_DEI_FREQ_SPEC_RX, trx->arfcn); msgb_tv_put(msg, OM2K_DEI_RX_DIVERSITY, 0x02); /* A */ return abis_om2k_sendmsg(trx->bts, msg); } /* Configure a Transmitter MO */ int abis_om2k_tx_tx_conf_req(struct gsm_bts_trx *trx) { struct msgb *msg = om2k_msgb_alloc(); struct abis_om2k_hdr *o2k; struct abis_om2k_mo mo; om2k_trx_to_mo(&mo, trx, OM2K_MO_CLS_TX); o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); fill_om2k_hdr(o2k, &mo, OM2K_MSGT_TX_CONF_REQ); msgb_tv16_put(msg, OM2K_DEI_FREQ_SPEC_TX, trx->arfcn); msgb_tv_put(msg, OM2K_DEI_POWER, trx->nominal_power-trx->max_power_red); msgb_tv_put(msg, OM2K_DEI_FILLING_MARKER, 0); /* Filling enabled */ msgb_tv_put(msg, OM2K_DEI_BCC, trx->bts->bsic & 0x7); /* Dedication Information is optional */ return abis_om2k_sendmsg(trx->bts, msg); } enum abis_om2k_tf_mode { OM2K_TF_MODE_MASTER = 0x00, OM2K_TF_MODE_STANDALONE = 0x01, OM2K_TF_MODE_SLAVE = 0x02, OM2K_TF_MODE_UNDEFINED = 0xff, }; static const uint8_t fs_offset_undef[5] = { 0xff, 0xff, 0xff, 0xff, 0xff }; int abis_om2k_tx_tf_conf_req(struct gsm_bts *bts) { struct msgb *msg = om2k_msgb_alloc(); struct abis_om2k_hdr *o2k; o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); fill_om2k_hdr(o2k, &om2k_mo_tf, OM2K_MSGT_TF_CONF_REQ); msgb_tv_put(msg, OM2K_DEI_TF_MODE, OM2K_TF_MODE_STANDALONE); msgb_tv_put(msg, OM2K_DEI_TF_SYNC_SRC, 0x00); msgb_tv_fixed_put(msg, OM2K_DEI_FS_OFFSET, sizeof(fs_offset_undef), fs_offset_undef); DEBUGP(DNM, "Tx MO=%s %s\n", om2k_mo_name(&om2k_mo_tf), get_value_string(om2k_msgcode_vals, OM2K_MSGT_TF_CONF_REQ)); return abis_om2k_sendmsg(bts, msg); } static uint8_t pchan2comb(enum gsm_phys_chan_config pchan) { switch (pchan) { case GSM_PCHAN_CCCH: return 4; case GSM_PCHAN_CCCH_SDCCH4: return 5; case GSM_PCHAN_SDCCH8_SACCH8C: return 3; case GSM_PCHAN_TCH_F: case GSM_PCHAN_TCH_H: case GSM_PCHAN_PDCH: case GSM_PCHAN_TCH_F_PDCH: return 8; default: return 0; } } static int put_freq_list(uint8_t *buf, uint16_t arfcn) { buf[0] = 0x00; /* TX/RX address */ buf[1] = (arfcn >> 8); buf[2] = (arfcn & 0xff); return 3; } /* Compute a frequency list in OM2000 fomrmat */ static int om2k_gen_freq_list(uint8_t *list, struct gsm_bts_trx_ts *ts) { uint8_t *cur = list; int len; if (ts->hopping.enabled) { unsigned int i; for (i = 0; i < ts->hopping.arfcns.data_len*8; i++) { if (bitvec_get_bit_pos(&ts->hopping.arfcns, i)) cur += put_freq_list(cur, i); } } else cur += put_freq_list(cur, ts->trx->arfcn); len = cur - list; return len; } const uint8_t icm_bound_params[] = { 0x02, 0x06, 0x0c, 0x16, 0x06 }; int abis_om2k_tx_ts_conf_req(struct gsm_bts_trx_ts *ts) { struct msgb *msg = om2k_msgb_alloc(); struct abis_om2k_hdr *o2k; struct abis_om2k_mo mo; uint8_t freq_list[64*3]; /* BA max size: 64 ARFCN */ int freq_list_len; om2k_ts_to_mo(&mo, ts); memset(freq_list, 0, sizeof(freq_list)); freq_list_len = om2k_gen_freq_list(freq_list, ts); if (freq_list_len < 0) return freq_list_len; o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); fill_om2k_hdr(o2k, &mo, OM2K_MSGT_TS_CONF_REQ); msgb_tv_put(msg, OM2K_DEI_COMBINATION, pchan2comb(ts->pchan)); msgb_tv_put(msg, OM2K_DEI_TS_NR, ts->nr); msgb_tlv_put(msg, OM2K_DEI_FREQ_LIST, freq_list_len, freq_list); msgb_tv_put(msg, OM2K_DEI_HSN, ts->hopping.hsn); msgb_tv_put(msg, OM2K_DEI_MAIO, ts->hopping.maio); msgb_tv_put(msg, OM2K_DEI_BSIC, ts->trx->bts->bsic); msgb_tv_put(msg, OM2K_DEI_RX_DIVERSITY, 0x02); /* A */ msgb_tv16_put(msg, OM2K_DEI_FN_OFFSET, 0); msgb_tv_put(msg, OM2K_DEI_EXT_RANGE, 0); /* Off */ /* Optional: Interference Rejection Combining */ msgb_tv_put(msg, OM2K_DEI_INTERF_REJ_COMB, 0x00); switch (ts->pchan) { case GSM_PCHAN_CCCH: msgb_tv_put(msg, OM2K_DEI_BA_PA_MFRMS, 0x06); msgb_tv_put(msg, OM2K_DEI_BS_AG_BKS_RES, 0x01); msgb_tv_put(msg, OM2K_DEI_DRX_DEV_MAX, 0x05); /* Repeat Paging/IMM.ASS: True, Allow Paging Type 3: Yes, Page for 5 seconds (default) */ msgb_tv_put(msg, OM2K_DEI_CCCH_OPTIONS, 0x01); break; case GSM_PCHAN_CCCH_SDCCH4: msgb_tv_put(msg, OM2K_DEI_T3105, ts->trx->bts->network->T3105 / 10); msgb_tv_put(msg, OM2K_DEI_NY1, 35); msgb_tv_put(msg, OM2K_DEI_BA_PA_MFRMS, 0x06); msgb_tv_put(msg, OM2K_DEI_CBCH_INDICATOR, 0); msgb_tv_put(msg, OM2K_DEI_TSC, gsm_ts_tsc(ts)); msgb_tv_put(msg, OM2K_DEI_BS_AG_BKS_RES, 0x01); msgb_tv_put(msg, OM2K_DEI_ICM_INDICATOR, 0); msgb_tv_put(msg, OM2K_DEI_DRX_DEV_MAX, 0x05); /* Repeat Paging/IMM.ASS: True, Allow Paging Type 3: Yes, Page for 5 seconds (default) */ msgb_tv_put(msg, OM2K_DEI_CCCH_OPTIONS, 0x01); msgb_tv_fixed_put(msg, OM2K_DEI_ICM_BOUND_PARAMS, sizeof(icm_bound_params), icm_bound_params); break; case GSM_PCHAN_SDCCH8_SACCH8C: msgb_tv_put(msg, OM2K_DEI_T3105, ts->trx->bts->network->T3105 / 10); msgb_tv_put(msg, OM2K_DEI_NY1, 35); msgb_tv_put(msg, OM2K_DEI_CBCH_INDICATOR, 0); msgb_tv_put(msg, OM2K_DEI_TSC, gsm_ts_tsc(ts)); /* Disable RF RESOURCE INDICATION on idle channels */ msgb_tv_put(msg, OM2K_DEI_ICM_INDICATOR, 0); msgb_tv_fixed_put(msg, OM2K_DEI_ICM_BOUND_PARAMS, sizeof(icm_bound_params), icm_bound_params); break; default: msgb_tv_put(msg, OM2K_DEI_T3105, ts->trx->bts->network->T3105 / 10); msgb_tv_put(msg, OM2K_DEI_NY1, 35); msgb_tv_put(msg, OM2K_DEI_TSC, gsm_ts_tsc(ts)); /* Disable RF RESOURCE INDICATION on idle channels */ msgb_tv_put(msg, OM2K_DEI_ICM_INDICATOR, 0); msgb_tv_fixed_put(msg, OM2K_DEI_ICM_BOUND_PARAMS, sizeof(icm_bound_params), icm_bound_params); msgb_tv_put(msg, OM2K_DEI_TTA, 10); /* Timer for Time Alignment */ if (ts->pchan == GSM_PCHAN_TCH_H) msgb_tv_put(msg, OM2K_DEI_ICM_CHAN_RATE, 1); /* TCH/H */ else msgb_tv_put(msg, OM2K_DEI_ICM_CHAN_RATE, 0); /* TCH/F */ msgb_tv_put(msg, OM2K_DEI_LSC, 1); /* enabled */ msgb_tv_put(msg, OM2K_DEI_LSC_FILT_TIME, 10); /* units of 100ms */ msgb_tv_put(msg, OM2K_DEI_CALL_SUPV_TIME, 8); msgb_tv_put(msg, OM2K_DEI_ENCR_ALG, 0x00); break; } return abis_om2k_sendmsg(ts->trx->bts, msg); } static int abis_om2k_tx_negot_req_ack(struct gsm_bts *bts, const struct abis_om2k_mo *mo, uint8_t *data, unsigned int len) { struct msgb *msg = om2k_msgb_alloc(); struct abis_om2k_hdr *o2k; o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); fill_om2k_hdr(o2k, mo, OM2K_MSGT_NEGOT_REQ_ACK); msgb_tlv_put(msg, OM2K_DEI_NEGOT_REC2, len, data); DEBUGP(DNM, "Tx MO=%s %s\n", om2k_mo_name(mo), get_value_string(om2k_msgcode_vals, OM2K_MSGT_NEGOT_REQ_ACK)); return abis_om2k_sendmsg(bts, msg); } struct iwd_version { uint8_t gen_char[3+1]; uint8_t rev_char[3+1]; }; struct iwd_type { uint8_t num_vers; struct iwd_version v[8]; }; static int om2k_rx_negot_req(struct msgb *msg) { struct e1inp_sign_link *sign_link = (struct e1inp_sign_link *)msg->dst; struct abis_om2k_hdr *o2h = msgb_l2(msg); struct iwd_type iwd_types[16]; uint8_t num_iwd_types = o2h->data[2]; uint8_t *cur = o2h->data+3; unsigned int i, v; uint8_t out_buf[1024]; uint8_t *out_cur = out_buf+1; uint8_t out_num_types = 0; memset(iwd_types, 0, sizeof(iwd_types)); /* Parse the RBS-supported IWD versions into iwd_types array */ for (i = 0; i < num_iwd_types; i++) { uint8_t num_versions = *cur++; uint8_t iwd_type = *cur++; iwd_types[iwd_type].num_vers = num_versions; for (v = 0; v < num_versions; v++) { struct iwd_version *iwd_v = &iwd_types[iwd_type].v[v]; memcpy(iwd_v->gen_char, cur, 3); cur += 3; memcpy(iwd_v->rev_char, cur, 3); cur += 3; DEBUGP(DNM, "\tIWD Type %u Gen %s Rev %s\n", iwd_type, iwd_v->gen_char, iwd_v->rev_char); } } /* Select the last version for each IWD type */ for (i = 0; i < ARRAY_SIZE(iwd_types); i++) { struct iwd_type *type = &iwd_types[i]; struct iwd_version *last_v; if (type->num_vers == 0) continue; out_num_types++; last_v = &type->v[type->num_vers-1]; *out_cur++ = i; memcpy(out_cur, last_v->gen_char, 3); out_cur += 3; memcpy(out_cur, last_v->rev_char, 3); out_cur += 3; } out_buf[0] = out_num_types; return abis_om2k_tx_negot_req_ack(sign_link->trx->bts, &o2h->mo, out_buf, out_cur - out_buf); } static int om2k_rx_start_res(struct msgb *msg) { struct e1inp_sign_link *sign_link = (struct e1inp_sign_link *)msg->dst; struct abis_om2k_hdr *o2h = msgb_l2(msg); int rc; rc = abis_om2k_tx_simple(sign_link->trx->bts, &o2h->mo, OM2K_MSGT_START_RES_ACK); rc = abis_om2k_tx_op_info(sign_link->trx->bts, &o2h->mo, 1); return rc; } static int om2k_rx_op_info_ack(struct msgb *msg) { struct e1inp_sign_link *sign_link = (struct e1inp_sign_link *)msg->dst; struct abis_om2k_hdr *o2h = msgb_l2(msg); /* This Acknowledgement does not contain the actual operational state, * so we signal whatever state we saved when we sent the Op Info * request */ signal_op_state(sign_link->trx->bts, &o2h->mo); return 0; } const struct value_string om2k_result_strings[] = { { 0x02, "Wrong state or out of sequence" }, { 0x03, "File error" }, { 0x04, "Fault, unspecified" }, { 0x05, "Tuning fault" }, { 0x06, "Protocol error" }, { 0x07, "MO not connected" }, { 0x08, "Parameter error" }, { 0x09, "Optional function not supported" }, { 0x0a, "Local access state LOCALLY DISCONNECTED" }, { 0, NULL } }; const struct value_string om2k_accordance_strings[] = { { 0x00, "Data according to request" }, { 0x01, "Data not according to request" }, { 0x02, "Inconsistent MO data" }, { 0x03, "Capability constraint violation" }, { 0, NULL } }; const struct value_string om2k_mostate_vals[] = { { 0x00, "RESET" }, { 0x01, "STARTED" }, { 0x02, "ENABLED" }, { 0x03, "DISABLED" }, { 0, NULL } }; static int om2k_rx_nack(struct msgb *msg) { struct abis_om2k_hdr *o2h = msgb_l2(msg); uint16_t msg_type = ntohs(o2h->msg_type); struct tlv_parsed tp; LOGP(DNM, LOGL_ERROR, "Rx MO=%s %s", om2k_mo_name(&o2h->mo), get_value_string(om2k_msgcode_vals, msg_type)); abis_om2k_msg_tlv_parse(&tp, o2h); if (TLVP_PRESENT(&tp, OM2K_DEI_REASON_CODE)) LOGPC(DNM, LOGL_ERROR, ", Reason 0x%02x", *TLVP_VAL(&tp, OM2K_DEI_REASON_CODE)); if (TLVP_PRESENT(&tp, OM2K_DEI_RESULT_CODE)) LOGPC(DNM, LOGL_ERROR, ", Result %s", get_value_string(om2k_result_strings, *TLVP_VAL(&tp, OM2K_DEI_RESULT_CODE))); LOGPC(DNM, LOGL_ERROR, "\n"); return 0; } /* Process a Configuration Result message */ static int process_conf_res(struct gsm_bts *bts, struct msgb *msg) { struct abis_om2k_hdr *o2h = msgb_l2(msg); uint16_t msg_type = ntohs(o2h->msg_type); struct nm_om2k_signal_data nsd; struct tlv_parsed tp; uint8_t acc; unsigned int log_level; int ret; memset(&nsd, 0, sizeof(nsd)); abis_om2k_msg_tlv_parse(&tp, o2h); if (!TLVP_PRESENT(&tp, OM2K_DEI_ACCORDANCE_IND)) return -EIO; acc = *TLVP_VAL(&tp, OM2K_DEI_ACCORDANCE_IND); switch (acc) { case 0: log_level = LOGL_DEBUG; ret = 0; break; default: log_level = LOGL_ERROR; ret = -EINVAL; break; } LOGP(DNM, log_level, "Rx MO=%s %s, Accordance: %s\n", om2k_mo_name(&o2h->mo), get_value_string(om2k_msgcode_vals, msg_type), get_value_string(om2k_accordance_strings, acc)); nsd.bts = bts; nsd.obj = mo2obj(bts, &o2h->mo); nsd.om2k_mo = &o2h->mo; nsd.accordance_ind = acc; osmo_signal_dispatch(SS_NM, S_NM_OM2K_CONF_RES, &nsd); return ret; } static int process_mo_state(struct gsm_bts *bts, struct msgb *msg) { struct abis_om2k_hdr *o2h = msgb_l2(msg); uint16_t msg_type = ntohs(o2h->msg_type); struct tlv_parsed tp; uint8_t mo_state; abis_om2k_msg_tlv_parse(&tp, o2h); if (!TLVP_PRESENT(&tp, OM2K_DEI_MO_STATE)) return -EIO; mo_state = *TLVP_VAL(&tp, OM2K_DEI_MO_STATE); LOGP(DNM, LOGL_DEBUG, "Rx MO=%s %s, MO State: %s\n", om2k_mo_name(&o2h->mo), get_value_string(om2k_msgcode_vals, msg_type), get_value_string(om2k_mostate_vals, mo_state)); update_mo_state(bts, &o2h->mo, mo_state); return 0; } int abis_om2k_rcvmsg(struct msgb *msg) { struct e1inp_sign_link *sign_link = (struct e1inp_sign_link *)msg->dst; struct gsm_bts *bts = sign_link->trx->bts; struct abis_om2k_hdr *o2h = msgb_l2(msg); struct abis_om_hdr *oh = &o2h->om; uint16_t msg_type = ntohs(o2h->msg_type); int rc = 0; /* Various consistency checks */ if (oh->placement != ABIS_OM_PLACEMENT_ONLY) { LOGP(DNM, LOGL_ERROR, "ABIS OML placement 0x%x not supported\n", oh->placement); if (oh->placement != ABIS_OM_PLACEMENT_FIRST) return -EINVAL; } if (oh->sequence != 0) { LOGP(DNM, LOGL_ERROR, "ABIS OML sequence 0x%x != 0x00\n", oh->sequence); return -EINVAL; } msg->l3h = (unsigned char *)o2h + sizeof(*o2h); if (oh->mdisc != ABIS_OM_MDISC_FOM) { LOGP(DNM, LOGL_ERROR, "unknown ABIS OM2000 message discriminator 0x%x\n", oh->mdisc); return -EINVAL; } DEBUGP(DNM, "Rx MO=%s %s (%s)\n", om2k_mo_name(&o2h->mo), get_value_string(om2k_msgcode_vals, msg_type), osmo_hexdump(msg->l2h, msgb_l2len(msg))); switch (msg_type) { case OM2K_MSGT_CAL_TIME_REQ: rc = abis_om2k_cal_time_resp(bts); break; case OM2K_MSGT_FAULT_REP: rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_FAULT_REP_ACK); process_mo_state(bts, msg); break; case OM2K_MSGT_NEGOT_REQ: rc = om2k_rx_negot_req(msg); break; case OM2K_MSGT_START_RES: rc = om2k_rx_start_res(msg); process_mo_state(bts, msg); break; case OM2K_MSGT_OP_INFO_ACK: rc = om2k_rx_op_info_ack(msg); break; case OM2K_MSGT_IS_CONF_RES: rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_IS_CONF_RES_ACK); process_conf_res(bts, msg); break; case OM2K_MSGT_CON_CONF_RES: rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_CON_CONF_RES_ACK); process_conf_res(bts, msg); break; case OM2K_MSGT_TX_CONF_RES: rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_TX_CONF_RES_ACK); process_conf_res(bts, msg); break; case OM2K_MSGT_RX_CONF_RES: rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_RX_CONF_RES_ACK); process_conf_res(bts, msg); break; case OM2K_MSGT_TS_CONF_RES: rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_TS_CONF_RES_ACK); process_conf_res(bts, msg); break; case OM2K_MSGT_TF_CONF_RES: rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_TF_CONF_RES_ACK); process_conf_res(bts, msg); break; case OM2K_MSGT_CONNECT_COMPL: rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_RESET_CMD); break; case OM2K_MSGT_RESET_COMPL: rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_START_REQ); break; case OM2K_MSGT_ENABLE_RES: rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_ENABLE_RES_ACK); process_mo_state(bts, msg); break; case OM2K_MSGT_DISABLE_RES: rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_DISABLE_RES_ACK); process_mo_state(bts, msg); break; case OM2K_MSGT_TEST_RES: rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_TEST_RES_ACK); process_mo_state(bts, msg); break; case OM2K_MSGT_STATUS_RESP: process_mo_state(bts, msg); break; case OM2K_MSGT_START_REQ_ACK: case OM2K_MSGT_CON_CONF_REQ_ACK: case OM2K_MSGT_IS_CONF_REQ_ACK: case OM2K_MSGT_TX_CONF_REQ_ACK: case OM2K_MSGT_RX_CONF_REQ_ACK: case OM2K_MSGT_TS_CONF_REQ_ACK: case OM2K_MSGT_TF_CONF_REQ_ACK: case OM2K_MSGT_ENABLE_REQ_ACK: case OM2K_MSGT_ALARM_STATUS_REQ_ACK: case OM2K_MSGT_DISABLE_REQ_ACK: break; case OM2K_MSGT_START_REQ_REJ: case OM2K_MSGT_CONNECT_REJ: case OM2K_MSGT_OP_INFO_REJ: case OM2K_MSGT_DISCONNECT_REJ: case OM2K_MSGT_TEST_REQ_REJ: case OM2K_MSGT_CON_CONF_REQ_REJ: case OM2K_MSGT_IS_CONF_REQ_REJ: case OM2K_MSGT_TX_CONF_REQ_REJ: case OM2K_MSGT_RX_CONF_REQ_REJ: case OM2K_MSGT_TS_CONF_REQ_REJ: case OM2K_MSGT_TF_CONF_REQ_REJ: case OM2K_MSGT_ENABLE_REQ_REJ: case OM2K_MSGT_ALARM_STATUS_REQ_REJ: case OM2K_MSGT_DISABLE_REQ_REJ: rc = om2k_rx_nack(msg); break; default: LOGP(DNM, LOGL_NOTICE, "Rx unhandled OM2000 msg %s\n", get_value_string(om2k_msgcode_vals, msg_type)); } msgb_free(msg); return rc; } openbsc-0.15.0/openbsc/src/libbsc/abis_om2000_vty.c000066400000000000000000000263731265565154000217110ustar00rootroot00000000000000/* VTY interface for A-bis OM2000 */ /* (C) 2010-2011 by Harald Welte * * 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern struct gsm_network *bsc_gsmnet; static struct cmd_node om2k_node = { OM2K_NODE, "%s(om2k)# ", 1, }; struct oml_node_state { struct gsm_bts *bts; struct abis_om2k_mo mo; }; static int dummy_config_write(struct vty *v) { return CMD_SUCCESS; } /* FIXME: auto-generate those strings from the value_string lists */ #define OM2K_OBJCLASS_VTY "(trxc|ts|tf|is|con|dp|cf|tx|rx)" #define OM2K_OBJCLASS_VTY_HELP "TRX Controller\n" \ "Timeslot\n" \ "Timing Function\n" \ "Interface Switch\n" \ "Abis Concentrator\n" \ "Digital Path\n" \ "Central Function\n" \ "Transmitter\n" \ "Receiver\n" DEFUN(om2k_class_inst, om2k_class_inst_cmd, "bts <0-255> om2000 class " OM2K_OBJCLASS_VTY " <0-255> <0-255> <0-255>", "BTS related commands\n" "BTS Number\n" "Manipulate the OM2000 managed objects\n" "Object Class\n" OM2K_OBJCLASS_VTY_HELP "BTS Number\n" "Associated SO Instance\n" "Instance Number\n") { struct gsm_bts *bts; struct oml_node_state *oms; int bts_nr = atoi(argv[0]); bts = gsm_bts_num(bsc_gsmnet, bts_nr); if (!bts) { vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE); return CMD_WARNING; } if (bts->type != GSM_BTS_TYPE_RBS2000) { vty_out(vty, "%% BTS %d not an Ericsson RBS%s", bts_nr, VTY_NEWLINE); return CMD_WARNING; } oms = talloc_zero(tall_bsc_ctx, struct oml_node_state); if (!oms) return CMD_WARNING; oms->bts = bts; oms->mo.class = get_string_value(om2k_mo_class_short_vals, argv[1]); oms->mo.bts = atoi(argv[2]); oms->mo.assoc_so = atoi(argv[3]); oms->mo.inst = atoi(argv[4]); vty->index = oms; vty->node = OM2K_NODE; return CMD_SUCCESS; } DEFUN(om2k_classnum_inst, om2k_classnum_inst_cmd, "bts <0-255> om2000 class <0-255> <0-255> <0-255> <0-255>", "BTS related commands\n" "BTS Number\n" "Manipulate the OML managed objects\n" "Object Class\n" "Object Class\n" "BTS Number\n" "Associated SO Instance\n" "Instance Number\n") { struct gsm_bts *bts; struct oml_node_state *oms; int bts_nr = atoi(argv[0]); bts = gsm_bts_num(bsc_gsmnet, bts_nr); if (!bts) { vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE); return CMD_WARNING; } oms = talloc_zero(tall_bsc_ctx, struct oml_node_state); if (!oms) return CMD_WARNING; oms->bts = bts; oms->mo.class = atoi(argv[1]); oms->mo.bts = atoi(argv[2]); oms->mo.assoc_so = atoi(argv[3]); oms->mo.inst = atoi(argv[4]); vty->index = oms; vty->node = OM2K_NODE; return CMD_SUCCESS; } DEFUN(om2k_reset, om2k_reset_cmd, "reset-command", "Reset the MO\n") { struct oml_node_state *oms = vty->index; abis_om2k_tx_reset_cmd(oms->bts, &oms->mo); return CMD_SUCCESS; } DEFUN(om2k_start, om2k_start_cmd, "start-request", "Start the MO\n") { struct oml_node_state *oms = vty->index; abis_om2k_tx_start_req(oms->bts, &oms->mo); return CMD_SUCCESS; } DEFUN(om2k_status, om2k_status_cmd, "status-request", "Get the MO Status\n") { struct oml_node_state *oms = vty->index; abis_om2k_tx_status_req(oms->bts, &oms->mo); return CMD_SUCCESS; } DEFUN(om2k_connect, om2k_connect_cmd, "connect-command", "Connect the MO\n") { struct oml_node_state *oms = vty->index; abis_om2k_tx_connect_cmd(oms->bts, &oms->mo); return CMD_SUCCESS; } DEFUN(om2k_disconnect, om2k_disconnect_cmd, "disconnect-command", "Disconnect the MO\n") { struct oml_node_state *oms = vty->index; abis_om2k_tx_disconnect_cmd(oms->bts, &oms->mo); return CMD_SUCCESS; } DEFUN(om2k_enable, om2k_enable_cmd, "enable-request", "Enable the MO\n") { struct oml_node_state *oms = vty->index; abis_om2k_tx_enable_req(oms->bts, &oms->mo); return CMD_SUCCESS; } DEFUN(om2k_disable, om2k_disable_cmd, "disable-request", "Disable the MO\n") { struct oml_node_state *oms = vty->index; abis_om2k_tx_disable_req(oms->bts, &oms->mo); return CMD_SUCCESS; } DEFUN(om2k_op_info, om2k_op_info_cmd, "operational-info <0-1>", "Set operational information\n" "Set operational info to 0 or 1\n") { struct oml_node_state *oms = vty->index; int oper = atoi(argv[0]); abis_om2k_tx_op_info(oms->bts, &oms->mo, oper); return CMD_SUCCESS; } DEFUN(om2k_test, om2k_test_cmd, "test-request", "Test the MO\n") { struct oml_node_state *oms = vty->index; abis_om2k_tx_test_req(oms->bts, &oms->mo); return CMD_SUCCESS; } struct con_conn_group { struct llist_head list; uint8_t cg; uint16_t ccp; uint8_t tag; uint8_t tei; }; static void add_con_list(struct gsm_bts *bts, uint8_t cg, uint16_t ccp, uint8_t tag, uint8_t tei) { struct con_conn_group *ent = talloc_zero(bts, struct con_conn_group); ent->cg = cg; ent->ccp = ccp; ent->tag = tag; ent->tei = tei; llist_add_tail(&ent->list, &bts->rbs2000.con.conn_groups); } static int del_con_list(struct gsm_bts *bts, uint8_t cg, uint16_t ccp, uint8_t tag, uint8_t tei) { struct con_conn_group *grp, *grp2; llist_for_each_entry_safe(grp, grp2, &bts->rbs2000.con.conn_groups, list) { if (grp->cg == cg && grp->ccp == ccp && grp->tag == tag && grp->tei == tei) { llist_del(&grp->list); talloc_free(grp); return 0; } } return -ENOENT; } #define CON_LIST_HELP "CON connetiton list\n" \ "Add entry to CON list\n" \ "Delete entry from CON list\n" \ "Connection Group Number\n" \ "CON Connection Point\n" \ DEFUN(om2k_con_list_dec, om2k_con_list_dec_cmd, "con-connection-list (add|del) <1-255> <0-1023> deconcentrated", CON_LIST_HELP "De-concentrated in/outlet\n") { struct oml_node_state *oms = vty->index; struct gsm_bts *bts = oms->bts; uint8_t cg = atoi(argv[1]); uint16_t ccp = atoi(argv[2]); if (!strcmp(argv[0], "add")) add_con_list(bts, cg, ccp, 0, 0xff); else { if (del_con_list(bts, cg, ccp, 0, 0xff) < 0) { vty_out(vty, "%% No matching CON list entry%s", VTY_NEWLINE); return CMD_WARNING; } } return CMD_SUCCESS; } DEFUN(om2k_con_list_tei, om2k_con_list_tei_cmd, "con-connection-list (add|del) <1-255> <0-1023> tei <0-63>", CON_LIST_HELP "Concentrated in/outlet with TEI\n" "TEI Number\n") { struct oml_node_state *oms = vty->index; struct gsm_bts *bts = oms->bts; uint8_t cg = atoi(argv[1]); uint16_t ccp = atoi(argv[2]); uint8_t tei = atoi(argv[3]); if (!strcmp(argv[0], "add")) add_con_list(bts, cg, ccp, cg, tei); else { if (del_con_list(bts, cg, ccp, cg, tei) < 0) { vty_out(vty, "%% No matching CON list entry%s", VTY_NEWLINE); return CMD_WARNING; } } return CMD_SUCCESS; } DEFUN(cfg_bts_is_conn_list, cfg_bts_is_conn_list_cmd, "is-connection-list (add|del) <0-2047> <0-2047> <0-255>", "Interface Switch Connnection List\n" "Add to IS list\n" "Delete from IS list\n" "ICP1\n" "ICP2\n" "Contiguity Index\n") { struct gsm_bts *bts = vty->index; uint16_t icp1 = atoi(argv[1]); uint16_t icp2 = atoi(argv[2]); uint8_t ci = atoi(argv[3]); struct is_conn_group *grp, *grp2; if (!strcmp(argv[0], "add")) { grp = talloc_zero(bts, struct is_conn_group); grp->icp1 = icp1; grp->icp2 = icp2; grp->ci = ci; llist_add_tail(&grp->list, &bts->rbs2000.is.conn_groups); } else { llist_for_each_entry_safe(grp, grp2, &bts->rbs2000.is.conn_groups, list) { if (grp->icp1 == icp1 && grp->icp2 == icp2 && grp->ci == ci) { llist_del(&grp->list); talloc_free(grp); return CMD_SUCCESS; } } vty_out(vty, "%% No matching IS Conn Group found!%s", VTY_NEWLINE); return CMD_WARNING; } return CMD_SUCCESS; } DEFUN(om2k_conf_req, om2k_conf_req_cmd, "configuration-request", "Send the configuration request for current MO\n") { struct oml_node_state *oms = vty->index; struct gsm_bts *bts = oms->bts; struct gsm_bts_trx *trx = NULL; struct gsm_bts_trx_ts *ts = NULL; switch (oms->mo.class) { case OM2K_MO_CLS_IS: abis_om2k_tx_is_conf_req(bts); break; case OM2K_MO_CLS_TS: trx = gsm_bts_trx_by_nr(bts, oms->mo.assoc_so); if (!trx) { vty_out(vty, "%% BTS %u has no TRX %u%s", bts->nr, oms->mo.assoc_so, VTY_NEWLINE); return CMD_WARNING; } if (oms->mo.inst >= ARRAY_SIZE(trx->ts)) { vty_out(vty, "%% Timeslot %u out of range%s", oms->mo.inst, VTY_NEWLINE); return CMD_WARNING; } ts = &trx->ts[oms->mo.inst]; abis_om2k_tx_ts_conf_req(ts); break; case OM2K_MO_CLS_RX: case OM2K_MO_CLS_TX: case OM2K_MO_CLS_TRXC: trx = gsm_bts_trx_by_nr(bts, oms->mo.inst); if (!trx) { vty_out(vty, "%% BTS %u has no TRX %u%s", bts->nr, oms->mo.inst, VTY_NEWLINE); return CMD_WARNING; } switch (oms->mo.class) { case OM2K_MO_CLS_RX: abis_om2k_tx_rx_conf_req(trx); break; case OM2K_MO_CLS_TX: abis_om2k_tx_tx_conf_req(trx); break; default: break; } break; case OM2K_MO_CLS_TF: abis_om2k_tx_tf_conf_req(bts); break; default: vty_out(vty, "%% Don't know how to configure MO%s", VTY_NEWLINE); } return CMD_SUCCESS; } void abis_om2k_config_write_bts(struct vty *vty, struct gsm_bts *bts) { struct is_conn_group *igrp; struct con_conn_group *cgrp; llist_for_each_entry(igrp, &bts->rbs2000.is.conn_groups, list) vty_out(vty, " is-connection-list add %u %u %u%s", igrp->icp1, igrp->icp2, igrp->ci, VTY_NEWLINE); llist_for_each_entry(cgrp, &bts->rbs2000.con.conn_groups, list) { vty_out(vty, " con-connection-list add %u %u ", cgrp->cg, cgrp->ccp); if (cgrp->tei == 0xff) vty_out(vty, "deconcentrated%s", VTY_NEWLINE); else vty_out(vty, "tei %u%s", cgrp->tei, VTY_NEWLINE); } } int abis_om2k_vty_init(void) { install_element(ENABLE_NODE, &om2k_class_inst_cmd); install_element(ENABLE_NODE, &om2k_classnum_inst_cmd); install_node(&om2k_node, dummy_config_write); vty_install_default(OM2K_NODE); install_element(OM2K_NODE, &om2k_reset_cmd); install_element(OM2K_NODE, &om2k_start_cmd); install_element(OM2K_NODE, &om2k_status_cmd); install_element(OM2K_NODE, &om2k_connect_cmd); install_element(OM2K_NODE, &om2k_disconnect_cmd); install_element(OM2K_NODE, &om2k_enable_cmd); install_element(OM2K_NODE, &om2k_disable_cmd); install_element(OM2K_NODE, &om2k_op_info_cmd); install_element(OM2K_NODE, &om2k_test_cmd); install_element(OM2K_NODE, &om2k_conf_req_cmd); install_element(OM2K_NODE, &om2k_con_list_dec_cmd); install_element(OM2K_NODE, &om2k_con_list_tei_cmd); install_element(BTS_NODE, &cfg_bts_is_conn_list_cmd); return 0; } openbsc-0.15.0/openbsc/src/libbsc/abis_rsl.c000066400000000000000000001710271265565154000206670ustar00rootroot00000000000000/* GSM Radio Signalling Link messages on the A-bis interface * 3GPP TS 08.58 version 8.6.0 Release 1999 / ETSI TS 100 596 V8.6.0 */ /* (C) 2008-2010 by Harald Welte * (C) 2012 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define RSL_ALLOC_SIZE 1024 #define RSL_ALLOC_HEADROOM 128 enum sacch_deact { SACCH_NONE, SACCH_DEACTIVATE, }; static int rsl_send_imm_assignment(struct gsm_lchan *lchan); static void error_timeout_cb(void *data); static void send_lchan_signal(int sig_no, struct gsm_lchan *lchan, struct gsm_meas_rep *resp) { struct lchan_signal_data sig; sig.lchan = lchan; sig.mr = resp; osmo_signal_dispatch(SS_LCHAN, sig_no, &sig); } static void do_lchan_free(struct gsm_lchan *lchan) { /* We start the error timer to make the channel available again */ if (lchan->state == LCHAN_S_REL_ERR) { lchan->error_timer.data = lchan; lchan->error_timer.cb = error_timeout_cb; osmo_timer_schedule(&lchan->error_timer, lchan->ts->trx->bts->network->T3111 + 2, 0); } else { rsl_lchan_set_state(lchan, LCHAN_S_NONE); } lchan_free(lchan); } static uint8_t mdisc_by_msgtype(uint8_t msg_type) { /* mask off the transparent bit ? */ msg_type &= 0xfe; if ((msg_type & 0xf0) == 0x00) return ABIS_RSL_MDISC_RLL; if ((msg_type & 0xf0) == 0x10) { if (msg_type >= 0x19 && msg_type <= 0x22) return ABIS_RSL_MDISC_TRX; else return ABIS_RSL_MDISC_COM_CHAN; } if ((msg_type & 0xe0) == 0x20) return ABIS_RSL_MDISC_DED_CHAN; return ABIS_RSL_MDISC_LOC; } static inline void init_dchan_hdr(struct abis_rsl_dchan_hdr *dh, uint8_t msg_type) { dh->c.msg_discr = mdisc_by_msgtype(msg_type); dh->c.msg_type = msg_type; dh->ie_chan = RSL_IE_CHAN_NR; } /* determine logical channel based on TRX and channel number IE */ struct gsm_lchan *lchan_lookup(struct gsm_bts_trx *trx, uint8_t chan_nr) { struct gsm_lchan *lchan; uint8_t ts_nr = chan_nr & 0x07; uint8_t cbits = chan_nr >> 3; uint8_t lch_idx; struct gsm_bts_trx_ts *ts = &trx->ts[ts_nr]; if (cbits == 0x01) { lch_idx = 0; /* TCH/F */ if (ts->pchan != GSM_PCHAN_TCH_F && ts->pchan != GSM_PCHAN_PDCH && ts->pchan != GSM_PCHAN_TCH_F_PDCH) LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n", chan_nr, ts->pchan); } else if ((cbits & 0x1e) == 0x02) { lch_idx = cbits & 0x1; /* TCH/H */ if (ts->pchan != GSM_PCHAN_TCH_H) LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n", chan_nr, ts->pchan); } else if ((cbits & 0x1c) == 0x04) { lch_idx = cbits & 0x3; /* SDCCH/4 */ if (ts->pchan != GSM_PCHAN_CCCH_SDCCH4 && ts->pchan != GSM_PCHAN_CCCH_SDCCH4_CBCH) LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n", chan_nr, ts->pchan); } else if ((cbits & 0x18) == 0x08) { lch_idx = cbits & 0x7; /* SDCCH/8 */ if (ts->pchan != GSM_PCHAN_SDCCH8_SACCH8C && ts->pchan != GSM_PCHAN_SDCCH8_SACCH8C_CBCH) LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n", chan_nr, ts->pchan); } else if (cbits == 0x10 || cbits == 0x11 || cbits == 0x12) { lch_idx = 0; if (ts->pchan != GSM_PCHAN_CCCH && ts->pchan != GSM_PCHAN_CCCH_SDCCH4 && ts->pchan != GSM_PCHAN_CCCH_SDCCH4_CBCH) LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n", chan_nr, ts->pchan); /* FIXME: we should not return first sdcch4 !!! */ } else { LOGP(DRSL, LOGL_ERROR, "unknown chan_nr=0x%02x\n", chan_nr); return NULL; } lchan = &ts->lchan[lch_idx]; log_set_context(BSC_CTX_LCHAN, lchan); if (lchan->conn) log_set_context(BSC_CTX_SUBSCR, lchan->conn->subscr); return lchan; } /* As per TS 03.03 Section 2.2, the IMSI has 'not more than 15 digits' */ uint64_t str_to_imsi(const char *imsi_str) { uint64_t ret; ret = strtoull(imsi_str, NULL, 10); return ret; } static struct msgb *rsl_msgb_alloc(void) { return msgb_alloc_headroom(RSL_ALLOC_SIZE, RSL_ALLOC_HEADROOM, "RSL"); } #define MACBLOCK_SIZE 23 static void pad_macblock(uint8_t *out, const uint8_t *in, int len) { memcpy(out, in, len); if (len < MACBLOCK_SIZE) memset(out+len, 0x2b, MACBLOCK_SIZE-len); } /* Chapter 9.3.7: Encryption Information */ static int build_encr_info(uint8_t *out, struct gsm_lchan *lchan) { *out++ = lchan->encr.alg_id & 0xff; if (lchan->encr.key_len) memcpy(out, lchan->encr.key, lchan->encr.key_len); return lchan->encr.key_len + 1; } static void print_rsl_cause(int lvl, const uint8_t *cause_v, uint8_t cause_len) { int i; LOGPC(DRSL, lvl, "CAUSE=0x%02x(%s) ", cause_v[0], rsl_err_name(cause_v[0])); for (i = 1; i < cause_len-1; i++) LOGPC(DRSL, lvl, "%02x ", cause_v[i]); } static void lchan_act_tmr_cb(void *data) { struct gsm_lchan *lchan = data; LOGP(DRSL, LOGL_ERROR, "%s Timeout during activation. Marked as broken.\n", gsm_lchan_name(lchan)); rsl_lchan_mark_broken(lchan, "activation timeout"); lchan_free(lchan); } static void lchan_deact_tmr_cb(void *data) { struct gsm_lchan *lchan = data; LOGP(DRSL, LOGL_ERROR, "%s Timeout during deactivation! Marked as broken.\n", gsm_lchan_name(lchan)); rsl_lchan_mark_broken(lchan, "de-activation timeout"); lchan_free(lchan); } /* Send a BCCH_INFO message as per Chapter 8.5.1 */ int rsl_bcch_info(struct gsm_bts_trx *trx, uint8_t type, const uint8_t *data, int len) { struct abis_rsl_dchan_hdr *dh; struct msgb *msg = rsl_msgb_alloc(); dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof*dh); init_dchan_hdr(dh, RSL_MT_BCCH_INFO); dh->chan_nr = RSL_CHAN_BCCH; msgb_tv_put(msg, RSL_IE_SYSINFO_TYPE, type); msgb_tlv_put(msg, RSL_IE_FULL_BCCH_INFO, len, data); msg->dst = trx->rsl_link; return abis_rsl_sendmsg(msg); } int rsl_sacch_filling(struct gsm_bts_trx *trx, uint8_t type, const uint8_t *data, int len) { struct abis_rsl_common_hdr *ch; struct msgb *msg = rsl_msgb_alloc(); ch = (struct abis_rsl_common_hdr *) msgb_put(msg, sizeof(*ch)); ch->msg_discr = ABIS_RSL_MDISC_TRX; ch->msg_type = RSL_MT_SACCH_FILL; msgb_tv_put(msg, RSL_IE_SYSINFO_TYPE, type); msgb_tl16v_put(msg, RSL_IE_L3_INFO, len, data); msg->dst = trx->rsl_link; return abis_rsl_sendmsg(msg); } int rsl_sacch_info_modify(struct gsm_lchan *lchan, uint8_t type, const uint8_t *data, int len) { struct abis_rsl_dchan_hdr *dh; struct msgb *msg = rsl_msgb_alloc(); uint8_t chan_nr = gsm_lchan2chan_nr(lchan); dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); init_dchan_hdr(dh, RSL_MT_SACCH_INFO_MODIFY); dh->chan_nr = chan_nr; msgb_tv_put(msg, RSL_IE_SYSINFO_TYPE, type); msgb_tl16v_put(msg, RSL_IE_L3_INFO, len, data); msg->dst = lchan->ts->trx->rsl_link; return abis_rsl_sendmsg(msg); } int rsl_chan_bs_power_ctrl(struct gsm_lchan *lchan, unsigned int fpc, int db) { struct abis_rsl_dchan_hdr *dh; struct msgb *msg; uint8_t chan_nr = gsm_lchan2chan_nr(lchan); db = abs(db); if (db > 30) return -EINVAL; msg = rsl_msgb_alloc(); lchan->bs_power = db/2; if (fpc) lchan->bs_power |= 0x10; dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); init_dchan_hdr(dh, RSL_MT_BS_POWER_CONTROL); dh->chan_nr = chan_nr; msgb_tv_put(msg, RSL_IE_BS_POWER, lchan->bs_power); msg->dst = lchan->ts->trx->rsl_link; return abis_rsl_sendmsg(msg); } int rsl_chan_ms_power_ctrl(struct gsm_lchan *lchan, unsigned int fpc, int dbm) { struct abis_rsl_dchan_hdr *dh; struct msgb *msg; uint8_t chan_nr = gsm_lchan2chan_nr(lchan); int ctl_lvl; ctl_lvl = ms_pwr_ctl_lvl(lchan->ts->trx->bts->band, dbm); if (ctl_lvl < 0) return ctl_lvl; msg = rsl_msgb_alloc(); lchan->ms_power = ctl_lvl; if (fpc) lchan->ms_power |= 0x20; dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); init_dchan_hdr(dh, RSL_MT_MS_POWER_CONTROL); dh->chan_nr = chan_nr; msgb_tv_put(msg, RSL_IE_MS_POWER, lchan->ms_power); msg->dst = lchan->ts->trx->rsl_link; return abis_rsl_sendmsg(msg); } static int channel_mode_from_lchan(struct rsl_ie_chan_mode *cm, struct gsm_lchan *lchan) { memset(cm, 0, sizeof(*cm)); /* FIXME: what to do with data calls ? */ if (lchan->ts->trx->bts->network->dtx_enabled) cm->dtx_dtu = 0x03; else cm->dtx_dtu = 0x00; /* set TCH Speech/Data */ cm->spd_ind = lchan->rsl_cmode; if (lchan->rsl_cmode == RSL_CMOD_SPD_SIGN && lchan->tch_mode != GSM48_CMODE_SIGN) LOGP(DRSL, LOGL_ERROR, "unsupported: rsl_mode == signalling, " "but tch_mode != signalling\n"); switch (lchan->type) { case GSM_LCHAN_SDCCH: cm->chan_rt = RSL_CMOD_CRT_SDCCH; break; case GSM_LCHAN_TCH_F: cm->chan_rt = RSL_CMOD_CRT_TCH_Bm; break; case GSM_LCHAN_TCH_H: cm->chan_rt = RSL_CMOD_CRT_TCH_Lm; break; case GSM_LCHAN_NONE: case GSM_LCHAN_UNKNOWN: default: return -EINVAL; } switch (lchan->tch_mode) { case GSM48_CMODE_SIGN: cm->chan_rate = 0; break; case GSM48_CMODE_SPEECH_V1: cm->chan_rate = RSL_CMOD_SP_GSM1; break; case GSM48_CMODE_SPEECH_EFR: cm->chan_rate = RSL_CMOD_SP_GSM2; break; case GSM48_CMODE_SPEECH_AMR: cm->chan_rate = RSL_CMOD_SP_GSM3; break; case GSM48_CMODE_DATA_14k5: case GSM48_CMODE_DATA_12k0: case GSM48_CMODE_DATA_6k0: switch (lchan->csd_mode) { case LCHAN_CSD_M_NT: /* non-transparent CSD with RLP */ switch (lchan->tch_mode) { case GSM48_CMODE_DATA_14k5: cm->chan_rate = RSL_CMOD_SP_NT_14k5; break; case GSM48_CMODE_DATA_12k0: cm->chan_rate = RSL_CMOD_SP_NT_12k0; break; case GSM48_CMODE_DATA_6k0: cm->chan_rate = RSL_CMOD_SP_NT_6k0; break; default: return -EINVAL; } break; /* transparent data services below */ case LCHAN_CSD_M_T_1200_75: cm->chan_rate = RSL_CMOD_CSD_T_1200_75; break; case LCHAN_CSD_M_T_600: cm->chan_rate = RSL_CMOD_CSD_T_600; break; case LCHAN_CSD_M_T_1200: cm->chan_rate = RSL_CMOD_CSD_T_1200; break; case LCHAN_CSD_M_T_2400: cm->chan_rate = RSL_CMOD_CSD_T_2400; break; case LCHAN_CSD_M_T_9600: cm->chan_rate = RSL_CMOD_CSD_T_9600; break; case LCHAN_CSD_M_T_14400: cm->chan_rate = RSL_CMOD_CSD_T_14400; break; case LCHAN_CSD_M_T_29000: cm->chan_rate = RSL_CMOD_CSD_T_29000; break; case LCHAN_CSD_M_T_32000: cm->chan_rate = RSL_CMOD_CSD_T_32000; break; default: return -EINVAL; } default: return -EINVAL; } return 0; } static void mr_config_for_bts(struct gsm_lchan *lchan, struct msgb *msg) { if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) msgb_tlv_put(msg, RSL_IE_MR_CONFIG, lchan->mr_bts_lv[0], lchan->mr_bts_lv + 1); } /* Chapter 8.4.1 */ int rsl_chan_activate_lchan(struct gsm_lchan *lchan, uint8_t act_type, uint8_t ho_ref) { struct abis_rsl_dchan_hdr *dh; struct msgb *msg; int rc; uint8_t *len; uint8_t ta; uint8_t chan_nr = gsm_lchan2chan_nr(lchan); struct rsl_ie_chan_mode cm; struct gsm48_chan_desc cd; rc = channel_mode_from_lchan(&cm, lchan); if (rc < 0) return rc; ta = lchan->rqd_ta; /* BS11 requires TA shifted by 2 bits */ if (lchan->ts->trx->bts->type == GSM_BTS_TYPE_BS11) ta <<= 2; memset(&cd, 0, sizeof(cd)); gsm48_lchan2chan_desc(&cd, lchan); msg = rsl_msgb_alloc(); dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); init_dchan_hdr(dh, RSL_MT_CHAN_ACTIV); dh->chan_nr = chan_nr; msgb_tv_put(msg, RSL_IE_ACT_TYPE, act_type); msgb_tlv_put(msg, RSL_IE_CHAN_MODE, sizeof(cm), (uint8_t *) &cm); /* * The Channel Identification is needed for Phase1 phones * and it contains the GSM48 Channel Description and the * Mobile Allocation. The GSM 08.58 asks for the Mobile * Allocation to have a length of zero. We are using the * msgb_l3len to calculate the length of both messages. */ msgb_v_put(msg, RSL_IE_CHAN_IDENT); len = msgb_put(msg, 1); msgb_tv_fixed_put(msg, GSM48_IE_CHANDESC_2, sizeof(cd), (const uint8_t *) &cd); if (lchan->ts->hopping.enabled) msgb_tlv_put(msg, GSM48_IE_MA_AFTER, lchan->ts->hopping.ma_len, lchan->ts->hopping.ma_data); else msgb_tlv_put(msg, GSM48_IE_MA_AFTER, 0, NULL); /* update the calculated size */ msg->l3h = len + 1; *len = msgb_l3len(msg); if (lchan->encr.alg_id > RSL_ENC_ALG_A5(0)) { uint8_t encr_info[MAX_A5_KEY_LEN+2]; rc = build_encr_info(encr_info, lchan); if (rc > 0) msgb_tlv_put(msg, RSL_IE_ENCR_INFO, rc, encr_info); } switch (act_type) { case RSL_ACT_INTER_ASYNC: case RSL_ACT_INTER_SYNC: msgb_tv_put(msg, RSL_IE_HANDO_REF, ho_ref); break; default: break; } msgb_tv_put(msg, RSL_IE_BS_POWER, lchan->bs_power); msgb_tv_put(msg, RSL_IE_MS_POWER, lchan->ms_power); msgb_tv_put(msg, RSL_IE_TIMING_ADVANCE, ta); mr_config_for_bts(lchan, msg); msg->dst = lchan->ts->trx->rsl_link; return abis_rsl_sendmsg(msg); } /* Chapter 8.4.9: Modify channel mode on BTS side */ int rsl_chan_mode_modify_req(struct gsm_lchan *lchan) { struct abis_rsl_dchan_hdr *dh; struct msgb *msg; int rc; uint8_t chan_nr = gsm_lchan2chan_nr(lchan); struct rsl_ie_chan_mode cm; rc = channel_mode_from_lchan(&cm, lchan); if (rc < 0) return rc; msg = rsl_msgb_alloc(); dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); init_dchan_hdr(dh, RSL_MT_MODE_MODIFY_REQ); dh->chan_nr = chan_nr; msgb_tlv_put(msg, RSL_IE_CHAN_MODE, sizeof(cm), (uint8_t *) &cm); if (lchan->encr.alg_id > RSL_ENC_ALG_A5(0)) { uint8_t encr_info[MAX_A5_KEY_LEN+2]; rc = build_encr_info(encr_info, lchan); if (rc > 0) msgb_tlv_put(msg, RSL_IE_ENCR_INFO, rc, encr_info); } mr_config_for_bts(lchan, msg); msg->dst = lchan->ts->trx->rsl_link; return abis_rsl_sendmsg(msg); } /* Chapter 8.4.6: Send the encryption command with given L3 info */ int rsl_encryption_cmd(struct msgb *msg) { struct abis_rsl_dchan_hdr *dh; struct gsm_lchan *lchan = msg->lchan; uint8_t chan_nr = gsm_lchan2chan_nr(lchan); uint8_t encr_info[MAX_A5_KEY_LEN+2]; uint8_t l3_len = msg->len; int rc; /* First push the L3 IE tag and length */ msgb_tv16_push(msg, RSL_IE_L3_INFO, l3_len); /* then the link identifier (SAPI0, main sign link) */ msgb_tv_push(msg, RSL_IE_LINK_IDENT, 0); /* then encryption information */ rc = build_encr_info(encr_info, lchan); if (rc <= 0) return rc; msgb_tlv_push(msg, RSL_IE_ENCR_INFO, rc, encr_info); /* and finally the DCHAN header */ dh = (struct abis_rsl_dchan_hdr *) msgb_push(msg, sizeof(*dh)); init_dchan_hdr(dh, RSL_MT_ENCR_CMD); dh->chan_nr = chan_nr; msg->dst = lchan->ts->trx->rsl_link; return abis_rsl_sendmsg(msg); } /* Chapter 8.4.5 / 4.6: Deactivate the SACCH after 04.08 RR CHAN RELEASE */ int rsl_deact_sacch(struct gsm_lchan *lchan) { struct abis_rsl_dchan_hdr *dh; struct msgb *msg = rsl_msgb_alloc(); dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); init_dchan_hdr(dh, RSL_MT_DEACTIVATE_SACCH); dh->chan_nr = gsm_lchan2chan_nr(lchan); msg->lchan = lchan; msg->dst = lchan->ts->trx->rsl_link; DEBUGP(DRSL, "%s DEACTivate SACCH CMD\n", gsm_lchan_name(lchan)); return abis_rsl_sendmsg(msg); } static void error_timeout_cb(void *data) { struct gsm_lchan *lchan = data; if (lchan->state != LCHAN_S_REL_ERR) { LOGP(DRSL, LOGL_ERROR, "%s error timeout but not in error state: %d\n", gsm_lchan_name(lchan), lchan->state); return; } /* go back to the none state */ LOGP(DRSL, LOGL_INFO, "%s is back in operation.\n", gsm_lchan_name(lchan)); rsl_lchan_set_state(lchan, LCHAN_S_NONE); } static int rsl_rx_rf_chan_rel_ack(struct gsm_lchan *lchan); /* Chapter 8.4.14 / 4.7: Tell BTS to release the radio channel */ static int rsl_rf_chan_release(struct gsm_lchan *lchan, int error, enum sacch_deact deact_sacch) { struct abis_rsl_dchan_hdr *dh; struct msgb *msg; int rc; /* Stop timers that should lead to a channel release */ osmo_timer_del(&lchan->T3109); if (lchan->state == LCHAN_S_REL_ERR) { LOGP(DRSL, LOGL_NOTICE, "%s is in error state not sending release.\n", gsm_lchan_name(lchan)); return -1; } msg = rsl_msgb_alloc(); dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); init_dchan_hdr(dh, RSL_MT_RF_CHAN_REL); dh->chan_nr = gsm_lchan2chan_nr(lchan); msg->lchan = lchan; msg->dst = lchan->ts->trx->rsl_link; DEBUGP(DRSL, "%s RF Channel Release CMD due error %d\n", gsm_lchan_name(lchan), error); if (error) { /* * FIXME: GSM 04.08 gives us two options for the abnormal * chanel release. This can be either like in the non-existent * sub-lcuase 3.5.1 or for the main signalling link deactivate * the SACCH, start timer T3109 and consider the channel as * released. * * This code is doing the later for all raido links and not * only the main link. Right now all SAPIs are released on the * local end, the SACCH will be de-activated and right now the * T3111 will be started. First T3109 should be started and then * the T3111. * * TODO: Move this out of the function. */ /* * sacch de-activate and "local end release" */ if (deact_sacch == SACCH_DEACTIVATE) rsl_deact_sacch(lchan); rsl_release_sapis_from(lchan, 0, RSL_REL_LOCAL_END); /* * TODO: start T3109 now. */ rsl_lchan_set_state(lchan, LCHAN_S_REL_ERR); } /* Start another timer or assume the BTS sends a ACK/NACK? */ lchan->act_timer.cb = lchan_deact_tmr_cb; lchan->act_timer.data = lchan; osmo_timer_schedule(&lchan->act_timer, 4, 0); rc = abis_rsl_sendmsg(msg); /* BTS will respond by RF CHAN REL ACK */ return rc; } /* * Special handling for channel releases in the error case. */ static int rsl_rf_chan_release_err(struct gsm_lchan *lchan) { if (lchan->state != LCHAN_S_ACTIVE) return 0; return rsl_rf_chan_release(lchan, 1, SACCH_DEACTIVATE); } static int rsl_rx_rf_chan_rel_ack(struct gsm_lchan *lchan) { DEBUGP(DRSL, "%s RF CHANNEL RELEASE ACK\n", gsm_lchan_name(lchan)); /* Stop all pending timers */ osmo_timer_del(&lchan->act_timer); osmo_timer_del(&lchan->T3111); /* * The BTS didn't respond within the timeout to our channel * release request and we have marked the channel as broken. * Now we do receive an ACK and let's be conservative. If it * is a sysmoBTS we know that only one RF Channel Release ACK * will be sent. So let's "repair" the channel. */ if (lchan->state == LCHAN_S_BROKEN) { int do_free = is_sysmobts_v2(lchan->ts->trx->bts); LOGP(DRSL, LOGL_NOTICE, "%s CHAN REL ACK for broken channel. %s.\n", gsm_lchan_name(lchan), do_free ? "Releasing it" : "Keeping it broken"); if (do_free) do_lchan_free(lchan); return 0; } if (lchan->state != LCHAN_S_REL_REQ && lchan->state != LCHAN_S_REL_ERR) LOGP(DRSL, LOGL_NOTICE, "%s CHAN REL ACK but state %s\n", gsm_lchan_name(lchan), gsm_lchans_name(lchan->state)); do_lchan_free(lchan); return 0; } int rsl_paging_cmd(struct gsm_bts *bts, uint8_t paging_group, uint8_t len, uint8_t *ms_ident, uint8_t chan_needed) { struct abis_rsl_dchan_hdr *dh; struct msgb *msg = rsl_msgb_alloc(); dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); init_dchan_hdr(dh, RSL_MT_PAGING_CMD); dh->chan_nr = RSL_CHAN_PCH_AGCH; msgb_tv_put(msg, RSL_IE_PAGING_GROUP, paging_group); msgb_tlv_put(msg, RSL_IE_MS_IDENTITY, len-2, ms_ident+2); msgb_tv_put(msg, RSL_IE_CHAN_NEEDED, chan_needed); msg->dst = bts->c0->rsl_link; return abis_rsl_sendmsg(msg); } int imsi_str2bcd(uint8_t *bcd_out, const char *str_in) { int i, len = strlen(str_in); for (i = 0; i < len; i++) { int num = str_in[i] - 0x30; if (num < 0 || num > 9) return -1; if (i % 2 == 0) bcd_out[i/2] = num; else bcd_out[i/2] |= (num << 4); } return 0; } /* Chapter 8.5.6 */ int rsl_imm_assign_cmd(struct gsm_bts *bts, uint8_t len, uint8_t *val) { struct msgb *msg = rsl_msgb_alloc(); struct abis_rsl_dchan_hdr *dh; uint8_t buf[MACBLOCK_SIZE]; dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); init_dchan_hdr(dh, RSL_MT_IMMEDIATE_ASSIGN_CMD); dh->chan_nr = RSL_CHAN_PCH_AGCH; switch (bts->type) { case GSM_BTS_TYPE_BS11: msgb_tlv_put(msg, RSL_IE_IMM_ASS_INFO, len, val); break; default: /* If phase 2, construct a FULL_IMM_ASS_INFO */ pad_macblock(buf, val, len); msgb_tlv_put(msg, RSL_IE_FULL_IMM_ASS_INFO, MACBLOCK_SIZE, buf); break; } msg->dst = bts->c0->rsl_link; return abis_rsl_sendmsg(msg); } /* Send Siemens specific MS RF Power Capability Indication */ int rsl_siemens_mrpci(struct gsm_lchan *lchan, struct rsl_mrpci *mrpci) { struct msgb *msg = rsl_msgb_alloc(); struct abis_rsl_dchan_hdr *dh; dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); init_dchan_hdr(dh, RSL_MT_SIEMENS_MRPCI); dh->c.msg_discr = ABIS_RSL_MDISC_DED_CHAN; dh->chan_nr = gsm_lchan2chan_nr(lchan); msgb_tv_put(msg, RSL_IE_SIEMENS_MRPCI, *(uint8_t *)mrpci); DEBUGP(DRSL, "%s TX Siemens MRPCI 0x%02x\n", gsm_lchan_name(lchan), *(uint8_t *)mrpci); msg->dst = lchan->ts->trx->rsl_link; return abis_rsl_sendmsg(msg); } /* Send "DATA REQUEST" message with given L3 Info payload */ /* Chapter 8.3.1 */ int rsl_data_request(struct msgb *msg, uint8_t link_id) { if (msg->lchan == NULL) { LOGP(DRSL, LOGL_ERROR, "cannot send DATA REQUEST to unknown lchan\n"); return -EINVAL; } rsl_rll_push_l3(msg, RSL_MT_DATA_REQ, gsm_lchan2chan_nr(msg->lchan), link_id, 1); msg->dst = msg->lchan->ts->trx->rsl_link; return abis_rsl_sendmsg(msg); } /* Send "ESTABLISH REQUEST" message with given L3 Info payload */ /* Chapter 8.3.1 */ int rsl_establish_request(struct gsm_lchan *lchan, uint8_t link_id) { struct msgb *msg; msg = rsl_rll_simple(RSL_MT_EST_REQ, gsm_lchan2chan_nr(lchan), link_id, 0); msg->dst = lchan->ts->trx->rsl_link; DEBUGP(DRLL, "%s RSL RLL ESTABLISH REQ (link_id=0x%02x)\n", gsm_lchan_name(lchan), link_id); return abis_rsl_sendmsg(msg); } static void rsl_handle_release(struct gsm_lchan *lchan); /* Special work handler to handle missing RSL_MT_REL_CONF message from * Nokia InSite BTS */ static void lchan_rel_work_cb(void *data) { struct gsm_lchan *lchan = data; int sapi; for (sapi = 0; sapi < ARRAY_SIZE(lchan->sapis); ++sapi) { if (lchan->sapis[sapi] == LCHAN_SAPI_REL) lchan->sapis[sapi] = LCHAN_SAPI_UNUSED; } rsl_handle_release(lchan); } /* Chapter 8.3.7 Request the release of multiframe mode of RLL connection. This is what higher layers should call. The BTS then responds with RELEASE CONFIRM, which we in turn use to trigger RSL CHANNEL RELEASE, which in turn is acknowledged by RSL CHANNEL RELEASE ACK, which calls lchan_free() */ int rsl_release_request(struct gsm_lchan *lchan, uint8_t link_id, enum rsl_rel_mode release_mode) { struct msgb *msg; msg = rsl_rll_simple(RSL_MT_REL_REQ, gsm_lchan2chan_nr(lchan), link_id, 0); /* 0 is normal release, 1 is local end */ msgb_tv_put(msg, RSL_IE_RELEASE_MODE, release_mode); /* FIXME: start some timer in case we don't receive a REL ACK ? */ msg->dst = lchan->ts->trx->rsl_link; DEBUGP(DRLL, "%s RSL RLL RELEASE REQ (link_id=0x%02x, reason=%u)\n", gsm_lchan_name(lchan), link_id, release_mode); abis_rsl_sendmsg(msg); /* Do not wait for Nokia BTS to send the confirm. */ if (is_nokia_bts(lchan->ts->trx->bts) && lchan->ts->trx->bts->nokia.no_loc_rel_cnf && release_mode == RSL_REL_LOCAL_END) { DEBUGP(DRLL, "Scheduling release, becasuse Nokia InSite BTS does not send a RELease CONFirm.\n"); lchan->sapis[link_id & 0x7] = LCHAN_SAPI_REL; lchan->rel_work.cb = lchan_rel_work_cb; lchan->rel_work.data = lchan; osmo_timer_schedule(&lchan->rel_work, 0, 0); } return 0; } int rsl_lchan_mark_broken(struct gsm_lchan *lchan, const char *reason) { lchan->state = LCHAN_S_BROKEN; lchan->broken_reason = reason; return 0; } int rsl_lchan_set_state(struct gsm_lchan *lchan, int state) { lchan->state = state; return 0; } /* Chapter 8.4.2: Channel Activate Acknowledge */ static int rsl_rx_chan_act_ack(struct msgb *msg) { struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg); /* BTS has confirmed channel activation, we now need * to assign the activated channel to the MS */ if (rslh->ie_chan != RSL_IE_CHAN_NR) return -EINVAL; osmo_timer_del(&msg->lchan->act_timer); if (msg->lchan->state == LCHAN_S_BROKEN) { LOGP(DRSL, LOGL_NOTICE, "%s CHAN ACT ACK for broken channel.\n", gsm_lchan_name(msg->lchan)); return 0; } if (msg->lchan->state != LCHAN_S_ACT_REQ) LOGP(DRSL, LOGL_NOTICE, "%s CHAN ACT ACK, but state %s\n", gsm_lchan_name(msg->lchan), gsm_lchans_name(msg->lchan->state)); rsl_lchan_set_state(msg->lchan, LCHAN_S_ACTIVE); if (msg->lchan->rqd_ref) { rsl_send_imm_assignment(msg->lchan); talloc_free(msg->lchan->rqd_ref); msg->lchan->rqd_ref = NULL; msg->lchan->rqd_ta = 0; } send_lchan_signal(S_LCHAN_ACTIVATE_ACK, msg->lchan, NULL); return 0; } /* Chapter 8.4.3: Channel Activate NACK */ static int rsl_rx_chan_act_nack(struct msgb *msg) { struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); struct tlv_parsed tp; osmo_timer_del(&msg->lchan->act_timer); if (msg->lchan->state == LCHAN_S_BROKEN) { LOGP(DRSL, LOGL_ERROR, "%s CHANNEL ACTIVATE NACK for broken channel.\n", gsm_lchan_name(msg->lchan)); return -1; } LOGP(DRSL, LOGL_ERROR, "%s CHANNEL ACTIVATE NACK ", gsm_lchan_name(msg->lchan)); /* BTS has rejected channel activation ?!? */ if (dh->ie_chan != RSL_IE_CHAN_NR) return -EINVAL; rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh)); if (TLVP_PRESENT(&tp, RSL_IE_CAUSE)) { const uint8_t *cause = TLVP_VAL(&tp, RSL_IE_CAUSE); print_rsl_cause(LOGL_ERROR, cause, TLVP_LEN(&tp, RSL_IE_CAUSE)); msg->lchan->error_cause = *cause; if (*cause != RSL_ERR_RCH_ALR_ACTV_ALLOC) { rsl_lchan_mark_broken(msg->lchan, "NACK on activation"); } else rsl_rf_chan_release(msg->lchan, 1, SACCH_DEACTIVATE); } else { rsl_lchan_mark_broken(msg->lchan, "NACK on activation no IE"); } LOGPC(DRSL, LOGL_ERROR, "\n"); send_lchan_signal(S_LCHAN_ACTIVATE_NACK, msg->lchan, NULL); return 0; } /* Chapter 8.4.4: Connection Failure Indication */ static int rsl_rx_conn_fail(struct msgb *msg) { struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); struct tlv_parsed tp; LOGP(DRSL, LOGL_NOTICE, "%s CONNECTION FAIL: RELEASING state %s ", gsm_lchan_name(msg->lchan), gsm_lchans_name(msg->lchan->state)); rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh)); if (TLVP_PRESENT(&tp, RSL_IE_CAUSE)) print_rsl_cause(LOGL_NOTICE, TLVP_VAL(&tp, RSL_IE_CAUSE), TLVP_LEN(&tp, RSL_IE_CAUSE)); LOGPC(DRSL, LOGL_NOTICE, "\n"); osmo_counter_inc(msg->lchan->ts->trx->bts->network->stats.chan.rf_fail); return rsl_rf_chan_release_err(msg->lchan); } static void print_meas_rep_uni(struct gsm_meas_rep_unidir *mru, const char *prefix) { DEBUGPC(DMEAS, "RXL-FULL-%s=%3ddBm RXL-SUB-%s=%3ddBm ", prefix, rxlev2dbm(mru->full.rx_lev), prefix, rxlev2dbm(mru->sub.rx_lev)); DEBUGPC(DMEAS, "RXQ-FULL-%s=%d RXQ-SUB-%s=%d ", prefix, mru->full.rx_qual, prefix, mru->sub.rx_qual); } static void print_meas_rep(struct gsm_lchan *lchan, struct gsm_meas_rep *mr) { int i; char *name = ""; if (lchan && lchan->conn && lchan->conn->subscr) name = subscr_name(lchan->conn->subscr); DEBUGP(DMEAS, "[%s] MEASUREMENT RESULT NR=%d ", name, mr->nr); if (mr->flags & MEAS_REP_F_DL_DTX) DEBUGPC(DMEAS, "DTXd "); print_meas_rep_uni(&mr->ul, "ul"); DEBUGPC(DMEAS, "BS_POWER=%d ", mr->bs_power); if (mr->flags & MEAS_REP_F_MS_TO) DEBUGPC(DMEAS, "MS_TO=%d ", mr->ms_timing_offset); if (mr->flags & MEAS_REP_F_MS_L1) { DEBUGPC(DMEAS, "L1_MS_PWR=%3ddBm ", mr->ms_l1.pwr); DEBUGPC(DMEAS, "L1_FPC=%u ", mr->flags & MEAS_REP_F_FPC ? 1 : 0); DEBUGPC(DMEAS, "L1_TA=%u ", mr->ms_l1.ta); } if (mr->flags & MEAS_REP_F_UL_DTX) DEBUGPC(DMEAS, "DTXu "); if (mr->flags & MEAS_REP_F_BA1) DEBUGPC(DMEAS, "BA1 "); if (!(mr->flags & MEAS_REP_F_DL_VALID)) DEBUGPC(DMEAS, "NOT VALID "); else print_meas_rep_uni(&mr->dl, "dl"); DEBUGPC(DMEAS, "NUM_NEIGH=%u\n", mr->num_cell); if (mr->num_cell == 7) return; for (i = 0; i < mr->num_cell; i++) { struct gsm_meas_rep_cell *mrc = &mr->cell[i]; DEBUGP(DMEAS, "IDX=%u ARFCN=%u BSIC=%u => %d dBm\n", mrc->neigh_idx, mrc->arfcn, mrc->bsic, rxlev2dbm(mrc->rxlev)); } } static int rsl_rx_meas_res(struct msgb *msg) { struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); struct tlv_parsed tp; struct gsm_meas_rep *mr = lchan_next_meas_rep(msg->lchan); uint8_t len; const uint8_t *val; int rc; /* check if this channel is actually active */ /* FIXME: maybe this check should be way more generic/centralized */ if (msg->lchan->state != LCHAN_S_ACTIVE) { LOGP(DRSL, LOGL_DEBUG, "%s: MEAS RES for inactive channel\n", gsm_lchan_name(msg->lchan)); return 0; } memset(mr, 0, sizeof(*mr)); mr->lchan = msg->lchan; rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh)); if (!TLVP_PRESENT(&tp, RSL_IE_MEAS_RES_NR) || !TLVP_PRESENT(&tp, RSL_IE_UPLINK_MEAS) || !TLVP_PRESENT(&tp, RSL_IE_BS_POWER)) return -EIO; /* Mandatory Parts */ mr->nr = *TLVP_VAL(&tp, RSL_IE_MEAS_RES_NR); len = TLVP_LEN(&tp, RSL_IE_UPLINK_MEAS); val = TLVP_VAL(&tp, RSL_IE_UPLINK_MEAS); if (len >= 3) { if (val[0] & 0x40) mr->flags |= MEAS_REP_F_DL_DTX; mr->ul.full.rx_lev = val[0] & 0x3f; mr->ul.sub.rx_lev = val[1] & 0x3f; mr->ul.full.rx_qual = val[2]>>3 & 0x7; mr->ul.sub.rx_qual = val[2] & 0x7; } mr->bs_power = *TLVP_VAL(&tp, RSL_IE_BS_POWER); /* Optional Parts */ if (TLVP_PRESENT(&tp, RSL_IE_MS_TIMING_OFFSET)) mr->ms_timing_offset = *TLVP_VAL(&tp, RSL_IE_MS_TIMING_OFFSET); if (TLVP_PRESENT(&tp, RSL_IE_L1_INFO)) { struct e1inp_sign_link *sign_link = msg->dst; val = TLVP_VAL(&tp, RSL_IE_L1_INFO); mr->flags |= MEAS_REP_F_MS_L1; mr->ms_l1.pwr = ms_pwr_dbm(sign_link->trx->bts->band, val[0] >> 3); if (val[0] & 0x04) mr->flags |= MEAS_REP_F_FPC; mr->ms_l1.ta = val[1]; /* BS11 and Nokia reports TA shifted by 2 bits */ if (msg->lchan->ts->trx->bts->type == GSM_BTS_TYPE_BS11 || msg->lchan->ts->trx->bts->type == GSM_BTS_TYPE_NOKIA_SITE) mr->ms_l1.ta >>= 2; } if (TLVP_PRESENT(&tp, RSL_IE_L3_INFO)) { msg->l3h = (uint8_t *) TLVP_VAL(&tp, RSL_IE_L3_INFO); rc = gsm48_parse_meas_rep(mr, msg); if (rc < 0) return rc; } print_meas_rep(msg->lchan, mr); send_lchan_signal(S_LCHAN_MEAS_REP, msg->lchan, mr); return 0; } /* Chapter 8.4.7 */ static int rsl_rx_hando_det(struct msgb *msg) { struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); struct tlv_parsed tp; DEBUGP(DRSL, "%s HANDOVER DETECT ", gsm_lchan_name(msg->lchan)); rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh)); if (TLVP_PRESENT(&tp, RSL_IE_ACCESS_DELAY)) DEBUGPC(DRSL, "access delay = %u\n", *TLVP_VAL(&tp, RSL_IE_ACCESS_DELAY)); else DEBUGPC(DRSL, "\n"); send_lchan_signal(S_LCHAN_HANDOVER_DETECT, msg->lchan, NULL); return 0; } static int abis_rsl_rx_dchan(struct msgb *msg) { struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg); int rc = 0; char *ts_name; struct e1inp_sign_link *sign_link = msg->dst; msg->lchan = lchan_lookup(sign_link->trx, rslh->chan_nr); ts_name = gsm_lchan_name(msg->lchan); switch (rslh->c.msg_type) { case RSL_MT_CHAN_ACTIV_ACK: DEBUGP(DRSL, "%s CHANNEL ACTIVATE ACK\n", ts_name); rc = rsl_rx_chan_act_ack(msg); break; case RSL_MT_CHAN_ACTIV_NACK: rc = rsl_rx_chan_act_nack(msg); break; case RSL_MT_CONN_FAIL: rc = rsl_rx_conn_fail(msg); break; case RSL_MT_MEAS_RES: rc = rsl_rx_meas_res(msg); break; case RSL_MT_HANDO_DET: rc = rsl_rx_hando_det(msg); break; case RSL_MT_RF_CHAN_REL_ACK: rc = rsl_rx_rf_chan_rel_ack(msg->lchan); break; case RSL_MT_MODE_MODIFY_ACK: DEBUGP(DRSL, "%s CHANNEL MODE MODIFY ACK\n", ts_name); break; case RSL_MT_MODE_MODIFY_NACK: LOGP(DRSL, LOGL_ERROR, "%s CHANNEL MODE MODIFY NACK\n", ts_name); break; case RSL_MT_IPAC_PDCH_ACT_ACK: DEBUGPC(DRSL, "%s IPAC PDCH ACT ACK\n", ts_name); msg->lchan->ts->flags |= TS_F_PDCH_MODE; break; case RSL_MT_IPAC_PDCH_ACT_NACK: LOGP(DRSL, LOGL_ERROR, "%s IPAC PDCH ACT NACK\n", ts_name); break; case RSL_MT_IPAC_PDCH_DEACT_ACK: DEBUGP(DRSL, "%s IPAC PDCH DEACT ACK\n", ts_name); msg->lchan->ts->flags &= ~TS_F_PDCH_MODE; break; case RSL_MT_IPAC_PDCH_DEACT_NACK: LOGP(DRSL, LOGL_ERROR, "%s IPAC PDCH DEACT NACK\n", ts_name); break; case RSL_MT_PHY_CONTEXT_CONF: case RSL_MT_PREPROC_MEAS_RES: case RSL_MT_TALKER_DET: case RSL_MT_LISTENER_DET: case RSL_MT_REMOTE_CODEC_CONF_REP: case RSL_MT_MR_CODEC_MOD_ACK: case RSL_MT_MR_CODEC_MOD_NACK: case RSL_MT_MR_CODEC_MOD_PER: LOGP(DRSL, LOGL_NOTICE, "%s Unimplemented Abis RSL DChan " "msg 0x%02x\n", ts_name, rslh->c.msg_type); break; default: LOGP(DRSL, LOGL_NOTICE, "%s unknown Abis RSL DChan msg 0x%02x\n", ts_name, rslh->c.msg_type); return -EINVAL; } return rc; } static int rsl_rx_error_rep(struct msgb *msg) { struct abis_rsl_common_hdr *rslh = msgb_l2(msg); struct tlv_parsed tp; struct e1inp_sign_link *sign_link = msg->dst; LOGP(DRSL, LOGL_ERROR, "%s ERROR REPORT ", gsm_trx_name(sign_link->trx)); rsl_tlv_parse(&tp, rslh->data, msgb_l2len(msg)-sizeof(*rslh)); if (TLVP_PRESENT(&tp, RSL_IE_CAUSE)) print_rsl_cause(LOGL_ERROR, TLVP_VAL(&tp, RSL_IE_CAUSE), TLVP_LEN(&tp, RSL_IE_CAUSE)); LOGPC(DRSL, LOGL_ERROR, "\n"); return 0; } static int abis_rsl_rx_trx(struct msgb *msg) { struct abis_rsl_common_hdr *rslh = msgb_l2(msg); struct e1inp_sign_link *sign_link = msg->dst; int rc = 0; switch (rslh->msg_type) { case RSL_MT_ERROR_REPORT: rc = rsl_rx_error_rep(msg); break; case RSL_MT_RF_RES_IND: /* interference on idle channels of TRX */ //DEBUGP(DRSL, "%s RF Resource Indication\n", gsm_trx_name(sign_link->trx)); break; case RSL_MT_OVERLOAD: /* indicate CCCH / ACCH / processor overload */ LOGP(DRSL, LOGL_ERROR, "%s CCCH/ACCH/CPU Overload\n", gsm_trx_name(sign_link->trx)); break; case 0x42: /* Nokia specific: SI End ACK */ LOGP(DRSL, LOGL_INFO, "Nokia SI End ACK\n"); break; case 0x43: /* Nokia specific: SI End NACK */ LOGP(DRSL, LOGL_INFO, "Nokia SI End NACK\n"); break; default: LOGP(DRSL, LOGL_NOTICE, "%s Unknown Abis RSL TRX message " "type 0x%02x\n", gsm_trx_name(sign_link->trx), rslh->msg_type); return -EINVAL; } return rc; } /* If T3101 expires, we never received a response to IMMEDIATE ASSIGN */ static void t3101_expired(void *data) { struct gsm_lchan *lchan = data; rsl_rf_chan_release(lchan, 1, SACCH_DEACTIVATE); } /* If T3111 expires, we will send the RF Channel Request */ static void t3111_expired(void *data) { struct gsm_lchan *lchan = data; rsl_rf_chan_release(lchan, 0, SACCH_NONE); } /* If T3109 expires the MS has not send a UA/UM do the error release */ static void t3109_expired(void *data) { struct gsm_lchan *lchan = data; LOGP(DRSL, LOGL_ERROR, "%s SACCH deactivation timeout.\n", gsm_lchan_name(lchan)); rsl_rf_chan_release(lchan, 1, SACCH_NONE); } #define GSM48_LEN2PLEN(a) (((a) << 2) | 1) /* Format an IMM ASS REJ according to 04.08 Chapter 9.1.20 */ static int rsl_send_imm_ass_rej(struct gsm_bts *bts, unsigned int num_req_refs, struct gsm48_req_ref *rqd_refs, uint8_t wait_ind) { uint8_t buf[GSM_MACBLOCK_LEN]; struct gsm48_imm_ass_rej *iar = (struct gsm48_imm_ass_rej *)buf; /* create IMMEDIATE ASSIGN REJECT 04.08 message */ memset(iar, 0, sizeof(*iar)); iar->proto_discr = GSM48_PDISC_RR; iar->msg_type = GSM48_MT_RR_IMM_ASS_REJ; iar->page_mode = GSM48_PM_SAME; memcpy(&iar->req_ref1, &rqd_refs[0], sizeof(iar->req_ref1)); iar->wait_ind1 = wait_ind; if (num_req_refs >= 2) memcpy(&iar->req_ref2, &rqd_refs[1], sizeof(iar->req_ref2)); else memcpy(&iar->req_ref2, &rqd_refs[0], sizeof(iar->req_ref2)); iar->wait_ind2 = wait_ind; if (num_req_refs >= 3) memcpy(&iar->req_ref3, &rqd_refs[2], sizeof(iar->req_ref3)); else memcpy(&iar->req_ref3, &rqd_refs[0], sizeof(iar->req_ref3)); iar->wait_ind3 = wait_ind; if (num_req_refs >= 4) memcpy(&iar->req_ref4, &rqd_refs[3], sizeof(iar->req_ref4)); else memcpy(&iar->req_ref4, &rqd_refs[0], sizeof(iar->req_ref4)); iar->wait_ind4 = wait_ind; /* we need to subtract 1 byte from sizeof(*iar) since ia includes the l2_plen field */ iar->l2_plen = GSM48_LEN2PLEN((sizeof(*iar)-1)); return rsl_imm_assign_cmd(bts, sizeof(*iar), (uint8_t *) iar); } /* MS has requested a channel on the RACH */ static int rsl_rx_chan_rqd(struct msgb *msg) { struct e1inp_sign_link *sign_link = msg->dst; struct gsm_bts *bts = sign_link->trx->bts; struct abis_rsl_dchan_hdr *rqd_hdr = msgb_l2(msg); struct gsm48_req_ref *rqd_ref; enum gsm_chan_t lctype; enum gsm_chreq_reason_t chreq_reason; struct gsm_lchan *lchan; uint8_t rqd_ta; int is_lu; uint16_t arfcn; uint8_t subch; /* parse request reference to be used in immediate assign */ if (rqd_hdr->data[0] != RSL_IE_REQ_REFERENCE) return -EINVAL; rqd_ref = (struct gsm48_req_ref *) &rqd_hdr->data[1]; /* parse access delay and use as TA */ if (rqd_hdr->data[sizeof(struct gsm48_req_ref)+1] != RSL_IE_ACCESS_DELAY) return -EINVAL; rqd_ta = rqd_hdr->data[sizeof(struct gsm48_req_ref)+2]; /* determine channel type (SDCCH/TCH_F/TCH_H) based on * request reference RA */ lctype = get_ctype_by_chreq(bts->network, rqd_ref->ra); chreq_reason = get_reason_by_chreq(rqd_ref->ra, bts->network->neci); osmo_counter_inc(bts->network->stats.chreq.total); /* * We want LOCATION UPDATES to succeed and will assign a TCH * if we have no SDCCH available. */ is_lu = !!(chreq_reason == GSM_CHREQ_REASON_LOCATION_UPD); /* check availability / allocate channel */ lchan = lchan_alloc(bts, lctype, is_lu); if (!lchan) { LOGP(DRSL, LOGL_NOTICE, "BTS %d CHAN RQD: no resources for %s 0x%x\n", msg->lchan->ts->trx->bts->nr, gsm_lchant_name(lctype), rqd_ref->ra); osmo_counter_inc(bts->network->stats.chreq.no_channel); /* FIXME gather multiple CHAN RQD and reject up to 4 at the same time */ if (bts->network->T3122) rsl_send_imm_ass_rej(bts, 1, rqd_ref, bts->network->T3122 & 0xff); return 0; } if (lchan->state != LCHAN_S_NONE) LOGP(DRSL, LOGL_NOTICE, "%s lchan_alloc() returned channel " "in state %s\n", gsm_lchan_name(lchan), gsm_lchans_name(lchan->state)); /* save the RACH data as we need it after the CHAN ACT ACK */ lchan->rqd_ref = talloc_zero(bts, struct gsm48_req_ref); if (!lchan->rqd_ref) { LOGP(DRSL, LOGL_ERROR, "Failed to allocate gsm48_req_ref.\n"); lchan_free(lchan); return -ENOMEM; } rsl_lchan_set_state(lchan, LCHAN_S_ACT_REQ); memcpy(lchan->rqd_ref, rqd_ref, sizeof(*rqd_ref)); lchan->rqd_ta = rqd_ta; arfcn = lchan->ts->trx->arfcn; subch = lchan->nr; lchan->encr.alg_id = RSL_ENC_ALG_A5(0); /* no encryption */ lchan->ms_power = ms_pwr_ctl_lvl(bts->band, bts->ms_max_power); lchan->bs_power = 0; /* 0dB reduction, output power = Pn */ lchan->rsl_cmode = RSL_CMOD_SPD_SIGN; lchan->tch_mode = GSM48_CMODE_SIGN; /* Start another timer or assume the BTS sends a ACK/NACK? */ lchan->act_timer.cb = lchan_act_tmr_cb; lchan->act_timer.data = lchan; osmo_timer_schedule(&lchan->act_timer, 4, 0); DEBUGP(DRSL, "%s Activating ARFCN(%u) SS(%u) lctype %s " "r=%s ra=0x%02x ta=%d\n", gsm_lchan_name(lchan), arfcn, subch, gsm_lchant_name(lchan->type), gsm_chreq_name(chreq_reason), rqd_ref->ra, rqd_ta); rsl_chan_activate_lchan(lchan, 0x00, 0); return 0; } static int rsl_send_imm_assignment(struct gsm_lchan *lchan) { struct gsm_bts *bts = lchan->ts->trx->bts; uint8_t buf[GSM_MACBLOCK_LEN]; struct gsm48_imm_ass *ia = (struct gsm48_imm_ass *) buf; /* create IMMEDIATE ASSIGN 04.08 messge */ memset(ia, 0, sizeof(*ia)); /* we set ia->l2_plen once we know the length of the MA below */ ia->proto_discr = GSM48_PDISC_RR; ia->msg_type = GSM48_MT_RR_IMM_ASS; ia->page_mode = GSM48_PM_SAME; gsm48_lchan2chan_desc(&ia->chan_desc, lchan); /* use request reference extracted from CHAN_RQD */ memcpy(&ia->req_ref, lchan->rqd_ref, sizeof(ia->req_ref)); ia->timing_advance = lchan->rqd_ta; if (!lchan->ts->hopping.enabled) { ia->mob_alloc_len = 0; } else { ia->mob_alloc_len = lchan->ts->hopping.ma_len; memcpy(ia->mob_alloc, lchan->ts->hopping.ma_data, ia->mob_alloc_len); } /* we need to subtract 1 byte from sizeof(*ia) since ia includes the l2_plen field */ ia->l2_plen = GSM48_LEN2PLEN((sizeof(*ia)-1) + ia->mob_alloc_len); /* Start timer T3101 to wait for GSM48_MT_RR_PAG_RESP */ lchan->T3101.cb = t3101_expired; lchan->T3101.data = lchan; osmo_timer_schedule(&lchan->T3101, bts->network->T3101, 0); /* send IMMEDIATE ASSIGN CMD on RSL to BTS (to send on CCCH to MS) */ return rsl_imm_assign_cmd(bts, sizeof(*ia)+ia->mob_alloc_len, (uint8_t *) ia); } /* current load on the CCCH */ static int rsl_rx_ccch_load(struct msgb *msg) { struct e1inp_sign_link *sign_link = msg->dst; struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg); struct ccch_signal_data sd; sd.bts = sign_link->trx->bts; sd.rach_slot_count = -1; sd.rach_busy_count = -1; sd.rach_access_count = -1; switch (rslh->data[0]) { case RSL_IE_PAGING_LOAD: sd.pg_buf_space = rslh->data[1] << 8 | rslh->data[2]; if (is_ipaccess_bts(sign_link->trx->bts) && sd.pg_buf_space == 0xffff) { /* paging load below configured threshold, use 50 as default */ sd.pg_buf_space = 50; } paging_update_buffer_space(sign_link->trx->bts, sd.pg_buf_space); osmo_signal_dispatch(SS_CCCH, S_CCCH_PAGING_LOAD, &sd); break; case RSL_IE_RACH_LOAD: if (msg->data_len >= 7) { sd.rach_slot_count = rslh->data[2] << 8 | rslh->data[3]; sd.rach_busy_count = rslh->data[4] << 8 | rslh->data[5]; sd.rach_access_count = rslh->data[6] << 8 | rslh->data[7]; osmo_signal_dispatch(SS_CCCH, S_CCCH_RACH_LOAD, &sd); } break; default: break; } return 0; } static int abis_rsl_rx_cchan(struct msgb *msg) { struct e1inp_sign_link *sign_link = msg->dst; struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg); int rc = 0; msg->lchan = lchan_lookup(sign_link->trx, rslh->chan_nr); switch (rslh->c.msg_type) { case RSL_MT_CHAN_RQD: /* MS has requested a channel on the RACH */ rc = rsl_rx_chan_rqd(msg); break; case RSL_MT_CCCH_LOAD_IND: /* current load on the CCCH */ rc = rsl_rx_ccch_load(msg); break; case RSL_MT_DELETE_IND: /* CCCH overloaded, IMM_ASSIGN was dropped */ case RSL_MT_CBCH_LOAD_IND: /* current load on the CBCH */ LOGP(DRSL, LOGL_NOTICE, "Unimplemented Abis RSL TRX message " "type 0x%02x\n", rslh->c.msg_type); break; default: LOGP(DRSL, LOGL_NOTICE, "Unknown Abis RSL TRX message type " "0x%02x\n", rslh->c.msg_type); return -EINVAL; } return rc; } static int rsl_rx_rll_err_ind(struct msgb *msg) { struct tlv_parsed tp; struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); uint8_t rlm_cause; rsl_tlv_parse(&tp, rllh->data, msgb_l2len(msg) - sizeof(*rllh)); if (!TLVP_PRESENT(&tp, RSL_IE_RLM_CAUSE)) { LOGP(DRLL, LOGL_ERROR, "%s ERROR INDICATION without mandantory cause.\n", gsm_lchan_name(msg->lchan)); return -1; } rlm_cause = *TLVP_VAL(&tp, RSL_IE_RLM_CAUSE); LOGP(DRLL, LOGL_ERROR, "%s ERROR INDICATION cause=%s in state=%s\n", gsm_lchan_name(msg->lchan), rsl_rlm_cause_name(rlm_cause), gsm_lchans_name(msg->lchan->state)); rll_indication(msg->lchan, rllh->link_id, BSC_RLLR_IND_ERR_IND); if (rlm_cause == RLL_CAUSE_T200_EXPIRED) { osmo_counter_inc(msg->lchan->ts->trx->bts->network->stats.chan.rll_err); return rsl_rf_chan_release_err(msg->lchan); } return 0; } static void rsl_handle_release(struct gsm_lchan *lchan) { int sapi; struct gsm_bts *bts; /* * Maybe only one link/SAPI was releasd or the error handling * was activated. Just return now and let the other code handle * it. */ if (lchan->state != LCHAN_S_REL_REQ) return; for (sapi = 0; sapi < ARRAY_SIZE(lchan->sapis); ++sapi) { if (lchan->sapis[sapi] == LCHAN_SAPI_UNUSED) continue; LOGP(DRSL, LOGL_DEBUG, "%s waiting for SAPI=%d to be released.\n", gsm_lchan_name(lchan), sapi); return; } /* Stop T3109 and wait for T3111 before re-using the channel */ osmo_timer_del(&lchan->T3109); lchan->T3111.cb = t3111_expired; lchan->T3111.data = lchan; bts = lchan->ts->trx->bts; osmo_timer_schedule(&lchan->T3111, bts->network->T3111, 0); } /* ESTABLISH INDICATION, LOCATION AREA UPDATE REQUEST 0x02, 0x06, 0x01, 0x20, 0x02, 0x00, 0x0b, 0x00, 0x0f, 0x05, 0x08, ... */ static int abis_rsl_rx_rll(struct msgb *msg) { struct e1inp_sign_link *sign_link = msg->dst; struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); int rc = 0; char *ts_name; uint8_t sapi = rllh->link_id & 7; msg->lchan = lchan_lookup(sign_link->trx, rllh->chan_nr); ts_name = gsm_lchan_name(msg->lchan); DEBUGP(DRLL, "%s SAPI=%u ", ts_name, sapi); switch (rllh->c.msg_type) { case RSL_MT_DATA_IND: DEBUGPC(DRLL, "DATA INDICATION\n"); if (msgb_l2len(msg) > sizeof(struct abis_rsl_common_hdr) + sizeof(*rllh) && rllh->data[0] == RSL_IE_L3_INFO) { msg->l3h = &rllh->data[3]; return gsm0408_rcvmsg(msg, rllh->link_id); } break; case RSL_MT_EST_IND: DEBUGPC(DRLL, "ESTABLISH INDICATION\n"); /* lchan is established, stop T3101 */ msg->lchan->sapis[rllh->link_id & 0x7] = LCHAN_SAPI_MS; osmo_timer_del(&msg->lchan->T3101); if (msgb_l2len(msg) > sizeof(struct abis_rsl_common_hdr) + sizeof(*rllh) && rllh->data[0] == RSL_IE_L3_INFO) { msg->l3h = &rllh->data[3]; return gsm0408_rcvmsg(msg, rllh->link_id); } break; case RSL_MT_EST_CONF: DEBUGPC(DRLL, "ESTABLISH CONFIRM\n"); msg->lchan->sapis[rllh->link_id & 0x7] = LCHAN_SAPI_NET; rll_indication(msg->lchan, rllh->link_id, BSC_RLLR_IND_EST_CONF); break; case RSL_MT_REL_IND: /* BTS informs us of having received DISC from MS */ DEBUGPC(DRLL, "RELEASE INDICATION\n"); msg->lchan->sapis[rllh->link_id & 0x7] = LCHAN_SAPI_UNUSED; rll_indication(msg->lchan, rllh->link_id, BSC_RLLR_IND_REL_IND); rsl_handle_release(msg->lchan); break; case RSL_MT_REL_CONF: /* BTS informs us of having received UA from MS, * in response to DISC that we've sent earlier */ DEBUGPC(DRLL, "RELEASE CONFIRMATION\n"); msg->lchan->sapis[rllh->link_id & 0x7] = LCHAN_SAPI_UNUSED; rsl_handle_release(msg->lchan); break; case RSL_MT_ERROR_IND: rc = rsl_rx_rll_err_ind(msg); break; case RSL_MT_UNIT_DATA_IND: LOGP(DRLL, LOGL_NOTICE, "unimplemented Abis RLL message " "type 0x%02x\n", rllh->c.msg_type); break; default: LOGP(DRLL, LOGL_NOTICE, "unknown Abis RLL message " "type 0x%02x\n", rllh->c.msg_type); } return rc; } static uint8_t ipa_smod_s_for_lchan(struct gsm_lchan *lchan) { switch (lchan->tch_mode) { case GSM48_CMODE_SPEECH_V1: switch (lchan->type) { case GSM_LCHAN_TCH_F: return 0x00; case GSM_LCHAN_TCH_H: return 0x03; default: break; } break; case GSM48_CMODE_SPEECH_EFR: switch (lchan->type) { case GSM_LCHAN_TCH_F: return 0x01; /* there's no half-rate EFR */ default: break; } break; case GSM48_CMODE_SPEECH_AMR: switch (lchan->type) { case GSM_LCHAN_TCH_F: return 0x02; case GSM_LCHAN_TCH_H: return 0x05; default: break; } break; default: break; } LOGP(DRSL, LOGL_ERROR, "Cannot determine ip.access speech mode for " "tch_mode == 0x%02x\n", lchan->tch_mode); return 0; } static uint8_t ipa_rtp_pt_for_lchan(struct gsm_lchan *lchan) { switch (lchan->tch_mode) { case GSM48_CMODE_SPEECH_V1: switch (lchan->type) { case GSM_LCHAN_TCH_F: return RTP_PT_GSM_FULL; case GSM_LCHAN_TCH_H: return RTP_PT_GSM_HALF; default: break; } break; case GSM48_CMODE_SPEECH_EFR: switch (lchan->type) { case GSM_LCHAN_TCH_F: return RTP_PT_GSM_EFR; /* there's no half-rate EFR */ default: break; } break; case GSM48_CMODE_SPEECH_AMR: switch (lchan->type) { case GSM_LCHAN_TCH_F: case GSM_LCHAN_TCH_H: return RTP_PT_AMR; default: break; } break; default: break; } LOGP(DRSL, LOGL_ERROR, "Cannot determine ip.access rtp payload type for " "tch_mode == 0x%02x\n & lchan_type == %d", lchan->tch_mode, lchan->type); return 0; } /* ip.access specific RSL extensions */ static void ipac_parse_rtp(struct gsm_lchan *lchan, struct tlv_parsed *tv) { struct in_addr ip; uint16_t port, conn_id; if (TLVP_PRESENT(tv, RSL_IE_IPAC_LOCAL_IP)) { ip.s_addr = tlvp_val32_unal(tv, RSL_IE_IPAC_LOCAL_IP); DEBUGPC(DRSL, "LOCAL_IP=%s ", inet_ntoa(ip)); lchan->abis_ip.bound_ip = ntohl(ip.s_addr); } if (TLVP_PRESENT(tv, RSL_IE_IPAC_LOCAL_PORT)) { port = tlvp_val16_unal(tv, RSL_IE_IPAC_LOCAL_PORT); port = ntohs(port); DEBUGPC(DRSL, "LOCAL_PORT=%u ", port); lchan->abis_ip.bound_port = port; } if (TLVP_PRESENT(tv, RSL_IE_IPAC_CONN_ID)) { conn_id = tlvp_val16_unal(tv, RSL_IE_IPAC_CONN_ID); conn_id = ntohs(conn_id); DEBUGPC(DRSL, "CON_ID=%u ", conn_id); lchan->abis_ip.conn_id = conn_id; } if (TLVP_PRESENT(tv, RSL_IE_IPAC_RTP_PAYLOAD2)) { lchan->abis_ip.rtp_payload2 = *TLVP_VAL(tv, RSL_IE_IPAC_RTP_PAYLOAD2); DEBUGPC(DRSL, "RTP_PAYLOAD2=0x%02x ", lchan->abis_ip.rtp_payload2); } if (TLVP_PRESENT(tv, RSL_IE_IPAC_SPEECH_MODE)) { lchan->abis_ip.speech_mode = *TLVP_VAL(tv, RSL_IE_IPAC_SPEECH_MODE); DEBUGPC(DRSL, "speech_mode=0x%02x ", lchan->abis_ip.speech_mode); } if (TLVP_PRESENT(tv, RSL_IE_IPAC_REMOTE_IP)) { ip.s_addr = tlvp_val32_unal(tv, RSL_IE_IPAC_REMOTE_IP); DEBUGPC(DRSL, "REMOTE_IP=%s ", inet_ntoa(ip)); lchan->abis_ip.connect_ip = ntohl(ip.s_addr); } if (TLVP_PRESENT(tv, RSL_IE_IPAC_REMOTE_PORT)) { port = tlvp_val16_unal(tv, RSL_IE_IPAC_REMOTE_PORT); port = ntohs(port); DEBUGPC(DRSL, "REMOTE_PORT=%u ", port); lchan->abis_ip.connect_port = port; } } /*! \brief Issue IPA RSL CRCX to configure RTP on BTS side * \param[in] lchan Logical Channel for which we issue CRCX */ int rsl_ipacc_crcx(struct gsm_lchan *lchan) { struct msgb *msg = rsl_msgb_alloc(); struct abis_rsl_dchan_hdr *dh; dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); init_dchan_hdr(dh, RSL_MT_IPAC_CRCX); dh->c.msg_discr = ABIS_RSL_MDISC_IPACCESS; dh->chan_nr = gsm_lchan2chan_nr(lchan); /* 0x1- == receive-only, 0x-1 == EFR codec */ lchan->abis_ip.speech_mode = 0x10 | ipa_smod_s_for_lchan(lchan); lchan->abis_ip.rtp_payload = ipa_rtp_pt_for_lchan(lchan); msgb_tv_put(msg, RSL_IE_IPAC_SPEECH_MODE, lchan->abis_ip.speech_mode); msgb_tv_put(msg, RSL_IE_IPAC_RTP_PAYLOAD, lchan->abis_ip.rtp_payload); DEBUGP(DRSL, "%s IPAC_BIND speech_mode=0x%02x RTP_PAYLOAD=%d\n", gsm_lchan_name(lchan), lchan->abis_ip.speech_mode, lchan->abis_ip.rtp_payload); msg->dst = lchan->ts->trx->rsl_link; return abis_rsl_sendmsg(msg); } /*! \brief Issue IPA RSL MDCX to configure MGW-side of RTP * \param[in] lchan Logical Channel for which we issue MDCX * \param[in] ip Remote (MGW) IP address for RTP * \param[in] port Remote (MGW) UDP port number for RTP * \param[in] rtp_payload2 Contents of RTP PAYLOAD 2 IE */ int rsl_ipacc_mdcx(struct gsm_lchan *lchan, uint32_t ip, uint16_t port, uint8_t rtp_payload2) { struct msgb *msg = rsl_msgb_alloc(); struct abis_rsl_dchan_hdr *dh; uint32_t *att_ip; struct in_addr ia; dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); init_dchan_hdr(dh, RSL_MT_IPAC_MDCX); dh->c.msg_discr = ABIS_RSL_MDISC_IPACCESS; dh->chan_nr = gsm_lchan2chan_nr(lchan); /* we need to store these now as MDCX_ACK does not return them :( */ lchan->abis_ip.rtp_payload2 = rtp_payload2; lchan->abis_ip.connect_port = port; lchan->abis_ip.connect_ip = ip; /* 0x0- == both directions, 0x-1 == EFR codec */ lchan->abis_ip.speech_mode = 0x00 | ipa_smod_s_for_lchan(lchan); lchan->abis_ip.rtp_payload = ipa_rtp_pt_for_lchan(lchan); ia.s_addr = htonl(ip); DEBUGP(DRSL, "%s IPAC_MDCX IP=%s PORT=%d RTP_PAYLOAD=%d RTP_PAYLOAD2=%d " "CONN_ID=%d speech_mode=0x%02x\n", gsm_lchan_name(lchan), inet_ntoa(ia), port, lchan->abis_ip.rtp_payload, rtp_payload2, lchan->abis_ip.conn_id, lchan->abis_ip.speech_mode); msgb_tv16_put(msg, RSL_IE_IPAC_CONN_ID, lchan->abis_ip.conn_id); msgb_v_put(msg, RSL_IE_IPAC_REMOTE_IP); att_ip = (uint32_t *) msgb_put(msg, sizeof(ip)); *att_ip = ia.s_addr; msgb_tv16_put(msg, RSL_IE_IPAC_REMOTE_PORT, port); msgb_tv_put(msg, RSL_IE_IPAC_SPEECH_MODE, lchan->abis_ip.speech_mode); msgb_tv_put(msg, RSL_IE_IPAC_RTP_PAYLOAD, lchan->abis_ip.rtp_payload); if (rtp_payload2) msgb_tv_put(msg, RSL_IE_IPAC_RTP_PAYLOAD2, rtp_payload2); msg->dst = lchan->ts->trx->rsl_link; return abis_rsl_sendmsg(msg); } /* tell BTS to connect RTP stream to our local RTP socket */ int rsl_ipacc_mdcx_to_rtpsock(struct gsm_lchan *lchan) { struct rtp_socket *rs = lchan->abis_ip.rtp_socket; int rc; rc = rsl_ipacc_mdcx(lchan, ntohl(rs->rtp.sin_local.sin_addr.s_addr), ntohs(rs->rtp.sin_local.sin_port), /* FIXME: use RTP payload of bound socket, not BTS*/ lchan->abis_ip.rtp_payload2); return rc; } int rsl_ipacc_pdch_activate(struct gsm_bts_trx_ts *ts, int act) { struct msgb *msg = rsl_msgb_alloc(); struct abis_rsl_dchan_hdr *dh; uint8_t msg_type; if (act) msg_type = RSL_MT_IPAC_PDCH_ACT; else msg_type = RSL_MT_IPAC_PDCH_DEACT; dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); init_dchan_hdr(dh, msg_type); dh->c.msg_discr = ABIS_RSL_MDISC_DED_CHAN; dh->chan_nr = gsm_ts2chan_nr(ts, 0); DEBUGP(DRSL, "%s IPAC_PDCH_%sACT\n", gsm_ts_name(ts), act ? "" : "DE"); msg->dst = ts->trx->rsl_link; return abis_rsl_sendmsg(msg); } static int abis_rsl_rx_ipacc_crcx_ack(struct msgb *msg) { struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); struct tlv_parsed tv; struct gsm_lchan *lchan = msg->lchan; /* the BTS has acknowledged a local bind, it now tells us the IP * address and port number to which it has bound the given logical * channel */ rsl_tlv_parse(&tv, dh->data, msgb_l2len(msg)-sizeof(*dh)); if (!TLVP_PRESENT(&tv, RSL_IE_IPAC_LOCAL_PORT) || !TLVP_PRESENT(&tv, RSL_IE_IPAC_LOCAL_IP) || !TLVP_PRESENT(&tv, RSL_IE_IPAC_CONN_ID)) { LOGP(DRSL, LOGL_NOTICE, "mandatory IE missing"); return -EINVAL; } ipac_parse_rtp(lchan, &tv); osmo_signal_dispatch(SS_ABISIP, S_ABISIP_CRCX_ACK, msg->lchan); return 0; } static int abis_rsl_rx_ipacc_mdcx_ack(struct msgb *msg) { struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); struct tlv_parsed tv; struct gsm_lchan *lchan = msg->lchan; /* the BTS has acknowledged a remote connect request and * it now tells us the IP address and port number to which it has * connected the given logical channel */ rsl_tlv_parse(&tv, dh->data, msgb_l2len(msg)-sizeof(*dh)); ipac_parse_rtp(lchan, &tv); osmo_signal_dispatch(SS_ABISIP, S_ABISIP_MDCX_ACK, msg->lchan); return 0; } static int abis_rsl_rx_ipacc_dlcx_ind(struct msgb *msg) { struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); struct tlv_parsed tv; rsl_tlv_parse(&tv, dh->data, msgb_l2len(msg)-sizeof(*dh)); if (TLVP_PRESENT(&tv, RSL_IE_CAUSE)) print_rsl_cause(LOGL_DEBUG, TLVP_VAL(&tv, RSL_IE_CAUSE), TLVP_LEN(&tv, RSL_IE_CAUSE)); osmo_signal_dispatch(SS_ABISIP, S_ABISIP_DLCX_IND, msg->lchan); return 0; } static int abis_rsl_rx_ipacc(struct msgb *msg) { struct e1inp_sign_link *sign_link = msg->dst; struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); char *ts_name; int rc = 0; msg->lchan = lchan_lookup(sign_link->trx, rllh->chan_nr); ts_name = gsm_lchan_name(msg->lchan); switch (rllh->c.msg_type) { case RSL_MT_IPAC_CRCX_ACK: DEBUGP(DRSL, "%s IPAC_CRCX_ACK ", ts_name); rc = abis_rsl_rx_ipacc_crcx_ack(msg); break; case RSL_MT_IPAC_CRCX_NACK: /* somehow the BTS was unable to bind the lchan to its local * port?!? */ LOGP(DRSL, LOGL_ERROR, "%s IPAC_CRCX_NACK\n", ts_name); break; case RSL_MT_IPAC_MDCX_ACK: /* the BTS tells us that a connect operation was successful */ DEBUGP(DRSL, "%s IPAC_MDCX_ACK ", ts_name); rc = abis_rsl_rx_ipacc_mdcx_ack(msg); break; case RSL_MT_IPAC_MDCX_NACK: /* somehow the BTS was unable to connect the lchan to a remote * port */ LOGP(DRSL, LOGL_ERROR, "%s IPAC_MDCX_NACK\n", ts_name); break; case RSL_MT_IPAC_DLCX_IND: DEBUGP(DRSL, "%s IPAC_DLCX_IND ", ts_name); rc = abis_rsl_rx_ipacc_dlcx_ind(msg); break; default: LOGP(DRSL, LOGL_NOTICE, "Unknown ip.access msg_type 0x%02x\n", rllh->c.msg_type); break; } DEBUGPC(DRSL, "\n"); return rc; } /* Entry-point where L2 RSL from BTS enters */ int abis_rsl_rcvmsg(struct msgb *msg) { struct abis_rsl_common_hdr *rslh; int rc = 0; if (!msg) { DEBUGP(DRSL, "Empty RSL msg?..\n"); return -1; } if (msgb_l2len(msg) < sizeof(*rslh)) { DEBUGP(DRSL, "Truncated RSL message with l2len: %u\n", msgb_l2len(msg)); msgb_free(msg); return -1; } rslh = msgb_l2(msg); switch (rslh->msg_discr & 0xfe) { case ABIS_RSL_MDISC_RLL: rc = abis_rsl_rx_rll(msg); break; case ABIS_RSL_MDISC_DED_CHAN: rc = abis_rsl_rx_dchan(msg); break; case ABIS_RSL_MDISC_COM_CHAN: rc = abis_rsl_rx_cchan(msg); break; case ABIS_RSL_MDISC_TRX: rc = abis_rsl_rx_trx(msg); break; case ABIS_RSL_MDISC_LOC: LOGP(DRSL, LOGL_NOTICE, "unimplemented RSL msg disc 0x%02x\n", rslh->msg_discr); break; case ABIS_RSL_MDISC_IPACCESS: rc = abis_rsl_rx_ipacc(msg); break; default: LOGP(DRSL, LOGL_NOTICE, "unknown RSL message discriminator " "0x%02x\n", rslh->msg_discr); rc = -EINVAL; } msgb_free(msg); return rc; } int rsl_sms_cb_command(struct gsm_bts *bts, uint8_t chan_number, struct rsl_ie_cb_cmd_type cb_command, const uint8_t *data, int len) { struct abis_rsl_dchan_hdr *dh; struct msgb *cb_cmd; cb_cmd = rsl_msgb_alloc(); if (!cb_cmd) return -1; dh = (struct abis_rsl_dchan_hdr *) msgb_put(cb_cmd, sizeof(*dh)); init_dchan_hdr(dh, RSL_MT_SMS_BC_CMD); dh->c.msg_discr = ABIS_RSL_MDISC_COM_CHAN; dh->chan_nr = chan_number; /* TODO: check the chan config */ msgb_tv_put(cb_cmd, RSL_IE_CB_CMD_TYPE, *(uint8_t*)&cb_command); msgb_tlv_put(cb_cmd, RSL_IE_SMSCB_MSG, len, data); cb_cmd->dst = bts->c0->rsl_link; return abis_rsl_sendmsg(cb_cmd); } int rsl_nokia_si_begin(struct gsm_bts_trx *trx) { struct abis_rsl_common_hdr *ch; struct msgb *msg = rsl_msgb_alloc(); ch = (struct abis_rsl_common_hdr *) msgb_put(msg, sizeof(*ch)); ch->msg_discr = ABIS_RSL_MDISC_TRX; ch->msg_type = 0x40; /* Nokia SI Begin */ msg->dst = trx->rsl_link; return abis_rsl_sendmsg(msg); } int rsl_nokia_si_end(struct gsm_bts_trx *trx) { struct abis_rsl_common_hdr *ch; struct msgb *msg = rsl_msgb_alloc(); ch = (struct abis_rsl_common_hdr *) msgb_put(msg, sizeof(*ch)); ch->msg_discr = ABIS_RSL_MDISC_TRX; ch->msg_type = 0x41; /* Nokia SI End */ msgb_tv_put(msg, 0xFD, 0x00); /* Nokia Pagemode Info, No paging reorganisation required */ msg->dst = trx->rsl_link; return abis_rsl_sendmsg(msg); } int rsl_bs_power_control(struct gsm_bts_trx *trx, uint8_t channel, uint8_t reduction) { struct abis_rsl_common_hdr *ch; struct msgb *msg = rsl_msgb_alloc(); ch = (struct abis_rsl_common_hdr *) msgb_put(msg, sizeof(*ch)); ch->msg_discr = ABIS_RSL_MDISC_DED_CHAN; ch->msg_type = RSL_MT_BS_POWER_CONTROL; msgb_tv_put(msg, RSL_IE_CHAN_NR, channel); msgb_tv_put(msg, RSL_IE_BS_POWER, reduction); /* reduction in 2dB steps */ msg->dst = trx->rsl_link; return abis_rsl_sendmsg(msg); } /** * Release all allocated SAPIs starting from @param start and * release them with the given release mode. Once the release * confirmation arrives it will be attempted to release the * the RF channel. */ int rsl_release_sapis_from(struct gsm_lchan *lchan, int start, enum rsl_rel_mode release_mode) { int no_sapi = 1; int sapi; for (sapi = start; sapi < ARRAY_SIZE(lchan->sapis); ++sapi) { uint8_t link_id; if (lchan->sapis[sapi] == LCHAN_SAPI_UNUSED) continue; link_id = sapi; if (lchan->type == GSM_LCHAN_TCH_F || lchan->type == GSM_LCHAN_TCH_H) link_id |= 0x40; rsl_release_request(lchan, link_id, release_mode); no_sapi = 0; } return no_sapi; } int rsl_start_t3109(struct gsm_lchan *lchan) { struct gsm_bts *bts = lchan->ts->trx->bts; /* Disabled, mostly legacy code */ if (bts->network->T3109 == 0) return -1; lchan->T3109.cb = t3109_expired; lchan->T3109.data = lchan; osmo_timer_schedule(&lchan->T3109, bts->network->T3109, 0); return 0; } /** * \brief directly RF Channel Release the lchan * * When no SAPI was allocated, directly release the logical channel. This * should only be called from chan_alloc.c on channel release handling. In * case no SAPI was established the RF Channel can be directly released, */ int rsl_direct_rf_release(struct gsm_lchan *lchan) { int i; for (i = 0; i < ARRAY_SIZE(lchan->sapis); ++i) { if (lchan->sapis[i] != LCHAN_SAPI_UNUSED) { LOGP(DRSL, LOGL_ERROR, "%s SAPI(%d) still allocated.\n", gsm_lchan_name(lchan), i); return -1; } } /* Now release it */ return rsl_rf_chan_release(lchan, 0, SACCH_NONE); } openbsc-0.15.0/openbsc/src/libbsc/arfcn_range_encode.c000066400000000000000000000200701265565154000226420ustar00rootroot00000000000000/* gsm 04.08 system information (si) encoding and decoding * 3gpp ts 04.08 version 7.21.0 release 1998 / etsi ts 100 940 v7.21.0 */ /* * (C) 2012 Holger Hans Peter Freyther * (C) 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 . */ #include #include #include #include int greatest_power_of_2_lesser_or_equal_to(int index) { int power_of_2 = 1; do { power_of_2 *= 2; } while (power_of_2 <= index); /* now go back one step */ return power_of_2 / 2; } static inline int mod(int data, int range) { int res = data % range; while (res < 0) res += range; return res; } /** * Determine at which index to split the ARFCNs to create an * equally size partition for the given range. Return -1 if * no such partition exists. */ int range_enc_find_index(const int range, const int *freqs, const int size) { int i, j, n; const int RANGE_DELTA = (range - 1) / 2; for (i = 0; i < size; ++i) { n = 0; for (j = 0; j < size; ++j) { if (mod(freqs[j] - freqs[i], range) <= RANGE_DELTA) n += 1; } if (n - 1 == (size - 1) / 2) return i; } return -1; } /** * Range encode the ARFCN list. * \param range The range to use. * \param arfcns The list of ARFCNs * \param size The size of the list of ARFCNs * \param out Place to store the W(i) output. */ int range_enc_arfcns(const int range, const int *arfcns, int size, int *out, const int index) { int split_at; int i; /* * The below is a GNU extension and we can remove it when * we move to a quicksort like in-situ swap with the pivot. */ int arfcns_left[size / 2]; int arfcns_right[size / 2]; int l_size; int r_size; int l_origin; int r_origin; /* Test the two recursion anchors and stop processing */ if (size == 0) return 0; if (size == 1) { out[index] = 1 + arfcns[0]; return 0; } /* Now do the processing */ split_at = range_enc_find_index(range, arfcns, size); /* we now know where to split */ out[index] = 1 + arfcns[split_at]; /* calculate the work that needs to be done for the leafs */ l_origin = mod(arfcns[split_at] + ((range - 1) / 2) + 1, range); r_origin = mod(arfcns[split_at] + 1, range); for (i = 0, l_size = 0, r_size = 0; i < size; ++i) { if (mod(arfcns[i] - l_origin, range) < range / 2) arfcns_left[l_size++] = mod(arfcns[i] - l_origin, range); if (mod(arfcns[i] - r_origin, range) < range / 2) arfcns_right[r_size++] = mod(arfcns[i] - r_origin, range); } /* * Now recurse and we need to make this iterative... but as the * tree is balanced the stack will not be too deep. */ range_enc_arfcns(range / 2, arfcns_left, l_size, out, index + greatest_power_of_2_lesser_or_equal_to(index + 1)); range_enc_arfcns((range -1 ) / 2, arfcns_right, r_size, out, index + (2 * greatest_power_of_2_lesser_or_equal_to(index + 1))); return 0; } /* * The easiest is to use f0 == arfcns[0]. This means that under certain * circumstances we can encode less ARFCNs than possible with an optimal f0. * * TODO: Solve the optimisation problem and pick f0 so that the max distance * is the smallest. Taking into account the modulo operation. I think picking * size/2 will be the optimal arfcn. */ /** * This implements the range determination as described in GSM 04.08 J4. The * result will be a base frequency f0 and the range to use. Note that for range * 1024 encoding f0 always refers to ARFCN 0 even if it is not an element of * the arfcns list. * * \param[in] arfcns The input frequencies, they must be sorted, lowest number first * \param[in] size The length of the array * \param[out] f0 The selected F0 base frequency. It might not be inside the list */ int range_enc_determine_range(const int *arfcns, const int size, int *f0) { int max = 0; /* * Go for the easiest. And pick arfcns[0] == f0. */ max = arfcns[size - 1] - arfcns[0]; *f0 = arfcns[0]; if (max < 128 && size <= 29) return ARFCN_RANGE_128; if (max < 256 && size <= 22) return ARFCN_RANGE_256; if (max < 512 && size <= 18) return ARFCN_RANGE_512; if (max < 1024 && size <= 17) { *f0 = 0; return ARFCN_RANGE_1024; } return ARFCN_RANGE_INVALID; } static void write_orig_arfcn(uint8_t *chan_list, int f0) { chan_list[0] |= (f0 >> 9) & 1; chan_list[1] = (f0 >> 1); chan_list[2] = (f0 & 1) << 7; } static void write_all_wn(uint8_t *chan_list, int bit_offs, int *w, int w_size, int w1_len) { int octet_offs = 0; /* offset into chan_list */ int wk_len = w1_len; /* encoding size in bits of w[k] */ int k; /* 1 based */ int level = 0; /* tree level, top level = 0 */ int lvl_left = 1; /* nodes per tree level */ /* W(2^i) to W(2^(i+1)-1) are on w1_len-i bits when present */ for (k = 1; k <= w_size; k++) { int wk_left = wk_len; DEBUGP(DRR, "k=%d, wk_len=%d, offs=%d:%d, level=%d, " "lvl_left=%d\n", k, wk_len, octet_offs, bit_offs, level, lvl_left); while (wk_left > 0) { int cur_bits = 8 - bit_offs; int cur_mask; int wk_slice; if (cur_bits > wk_left) cur_bits = wk_left; cur_mask = ((1 << cur_bits) - 1); DEBUGP(DRR, " wk_left=%d, cur_bits=%d, offs=%d:%d\n", wk_left, cur_bits, octet_offs, bit_offs); /* advance */ wk_left -= cur_bits; bit_offs += cur_bits; /* right aligned wk data for current out octet */ wk_slice = (w[k-1] >> wk_left) & cur_mask; /* cur_bits now contains the number of bits * that are to be copied from wk to the chan_list. * wk_left is set to the number of bits that must * not yet be copied. * bit_offs points after the bit area that is going to * be overwritten: * * wk_left * | * v * wk: WWWWWWWWWWW * |||||<-- wk_slice, cur_bits=5 * --WWWWW- * ^ * | * bit_offs */ DEBUGP(DRR, " wk=%02x, slice=%02x/%02x, cl=%02x\n", w[k-1], wk_slice, cur_mask, wk_slice << (8 - bit_offs)); chan_list[octet_offs] &= ~(cur_mask << (8 - bit_offs)); chan_list[octet_offs] |= wk_slice << (8 - bit_offs); /* adjust output */ if (bit_offs == 8) { bit_offs = 0; octet_offs += 1; } } /* adjust bit sizes */ lvl_left -= 1; if (!lvl_left) { /* completed tree level, advance to next */ level += 1; lvl_left = 1 << level; wk_len -= 1; } } } int range_enc_range128(uint8_t *chan_list, int f0, int *w) { chan_list[0] = 0x8C; write_orig_arfcn(chan_list, f0); write_all_wn(&chan_list[2], 1, w, 28, 7); return 0; } int range_enc_range256(uint8_t *chan_list, int f0, int *w) { chan_list[0] = 0x8A; write_orig_arfcn(chan_list, f0); write_all_wn(&chan_list[2], 1, w, 21, 8); return 0; } int range_enc_range512(uint8_t *chan_list, int f0, int *w) { chan_list[0] = 0x88; write_orig_arfcn(chan_list, f0); write_all_wn(&chan_list[2], 1, w, 17, 9); return 0; } int range_enc_range1024(uint8_t *chan_list, int f0, int f0_included, int *w) { chan_list[0] = 0x80 | (f0_included << 2); write_all_wn(&chan_list[0], 6, w, 16, 10); return 0; } int range_enc_filter_arfcns(int *arfcns, const int size, const int f0, int *f0_included) { int i, j = 0; *f0_included = 0; for (i = 0; i < size; ++i) { /* * Appendix J.4 says the following: * All frequencies except F(0), minus F(0) + 1. * I assume we need to exclude it here. */ if (arfcns[i] == f0) { *f0_included = 1; continue; } arfcns[j++] = mod(arfcns[i] - (f0 + 1), 1024); } return j; } openbsc-0.15.0/openbsc/src/libbsc/bsc_api.c000066400000000000000000000544001265565154000204640ustar00rootroot00000000000000/* GSM 08.08 like API for OpenBSC. The bridge from MSC to BSC */ /* (C) 2010-2011 by Holger Hans Peter Freyther * (C) 2010-2011 by On-Waves * (C) 2009 by Harald Welte * * 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define GSM0808_T10_VALUE 6, 0 static LLIST_HEAD(sub_connections); static void rll_ind_cb(struct gsm_lchan *, uint8_t, void *, enum bsc_rllr_ind); static void send_sapi_reject(struct gsm_subscriber_connection *conn, int link_id); static void handle_release(struct gsm_subscriber_connection *conn, struct bsc_api *bsc, struct gsm_lchan *lchan); static void handle_chan_ack(struct gsm_subscriber_connection *conn, struct bsc_api *bsc, struct gsm_lchan *lchan); static void handle_chan_nack(struct gsm_subscriber_connection *conn, struct bsc_api *bsc, struct gsm_lchan *lchan); /* GSM 08.08 3.2.2.33 */ static uint8_t lchan_to_chosen_channel(struct gsm_lchan *lchan) { uint8_t channel_mode = 0, channel = 0; switch (lchan->tch_mode) { case GSM48_CMODE_SPEECH_V1: case GSM48_CMODE_SPEECH_EFR: case GSM48_CMODE_SPEECH_AMR: channel_mode = 0x9; break; case GSM48_CMODE_SIGN: channel_mode = 0x8; break; case GSM48_CMODE_DATA_14k5: channel_mode = 0xe; break; case GSM48_CMODE_DATA_12k0: channel_mode = 0xb; break; case GSM48_CMODE_DATA_6k0: channel_mode = 0xc; break; case GSM48_CMODE_DATA_3k6: channel_mode = 0xd; break; } switch (lchan->type) { case GSM_LCHAN_NONE: channel = 0x0; break; case GSM_LCHAN_SDCCH: channel = 0x1; break; case GSM_LCHAN_TCH_F: channel = 0x8; break; case GSM_LCHAN_TCH_H: channel = 0x9; break; case GSM_LCHAN_UNKNOWN: default: LOGP(DMSC, LOGL_ERROR, "Unknown lchan type: %p\n", lchan); break; } return channel_mode << 4 | channel; } static uint8_t chan_mode_to_speech(struct gsm_lchan *lchan) { int mode = 0; switch (lchan->tch_mode) { case GSM48_CMODE_SPEECH_V1: mode = 1; break; case GSM48_CMODE_SPEECH_EFR: mode = 0x11; break; case GSM48_CMODE_SPEECH_AMR: mode = 0x21; break; case GSM48_CMODE_SIGN: case GSM48_CMODE_DATA_14k5: case GSM48_CMODE_DATA_12k0: case GSM48_CMODE_DATA_6k0: case GSM48_CMODE_DATA_3k6: default: LOGP(DMSC, LOGL_ERROR, "Using non speech mode: %d\n", mode); return 0; break; } /* assume to always do AMR HR on any TCH type */ if (lchan->type == GSM_LCHAN_TCH_H || lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) mode |= 0x4; return mode; } static void assignment_t10_timeout(void *_conn) { struct bsc_api *api; struct gsm_subscriber_connection *conn = (struct gsm_subscriber_connection *) _conn; LOGP(DMSC, LOGL_ERROR, "Assigment T10 timeout on %p\n", conn); /* * normal release on the secondary channel but only if the * secondary_channel has not been released by the handle_chan_nack. */ if (conn->secondary_lchan) lchan_release(conn->secondary_lchan, 0, RSL_REL_LOCAL_END); conn->secondary_lchan = NULL; /* inform them about the failure */ api = conn->bts->network->bsc_api; api->assign_fail(conn, GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE, NULL); } /** * Handle the multirate config */ static void handle_mr_config(struct gsm_subscriber_connection *conn, struct gsm_lchan *lchan, int full_rate) { struct bsc_api *api; api = conn->bts->network->bsc_api; struct amr_multirate_conf *mr; struct gsm48_multi_rate_conf *mr_conf; if (api->mr_config) return api->mr_config(conn, lchan, full_rate); if (full_rate) mr = &lchan->ts->trx->bts->mr_full; else mr = &lchan->ts->trx->bts->mr_half; mr_conf = (struct gsm48_multi_rate_conf *) mr->gsm48_ie; mr_conf->ver = 1; /* default, if no AMR codec defined */ if (!mr->gsm48_ie[1]) { mr_conf->icmi = 1; mr_conf->m5_90 = 1; } gsm48_multirate_config(lchan->mr_ms_lv, mr, mr->ms_mode); gsm48_multirate_config(lchan->mr_bts_lv, mr, mr->bts_mode); } /* * Start a new assignment and make sure that it is completed within T10 either * positively, negatively or by the timeout. * * 1.) allocate a new lchan * 2.) copy the encryption key and other data from the * old to the new channel. * 3.) RSL Channel Activate this channel and wait * * -> Signal handler for the LCHAN * 4.) Send GSM 04.08 assignment command to the MS * * -> Assignment Complete/Assignment Failure * 5.) Release the SDCCH, continue signalling on the new link */ static int handle_new_assignment(struct gsm_subscriber_connection *conn, int chan_mode, int full_rate) { struct gsm_lchan *new_lchan; int chan_type; chan_type = full_rate ? GSM_LCHAN_TCH_F : GSM_LCHAN_TCH_H; new_lchan = lchan_alloc(conn->bts, chan_type, 0); if (!new_lchan) { LOGP(DMSC, LOGL_NOTICE, "No free channel.\n"); return -1; } /* copy old data to the new channel */ memcpy(&new_lchan->encr, &conn->lchan->encr, sizeof(new_lchan->encr)); new_lchan->ms_power = conn->lchan->ms_power; new_lchan->bs_power = conn->lchan->bs_power; new_lchan->rqd_ta = conn->lchan->rqd_ta; /* copy new data to it */ new_lchan->tch_mode = chan_mode; new_lchan->rsl_cmode = RSL_CMOD_SPD_SPEECH; /* handle AMR correctly */ if (chan_mode == GSM48_CMODE_SPEECH_AMR) handle_mr_config(conn, new_lchan, full_rate); if (rsl_chan_activate_lchan(new_lchan, 0x1, 0) < 0) { LOGP(DHO, LOGL_ERROR, "could not activate channel\n"); lchan_free(new_lchan); return -1; } /* remember that we have the channel */ conn->secondary_lchan = new_lchan; new_lchan->conn = conn; rsl_lchan_set_state(new_lchan, LCHAN_S_ACT_REQ); return 0; } struct gsm_subscriber_connection *subscr_con_allocate(struct gsm_lchan *lchan) { struct gsm_subscriber_connection *conn; conn = talloc_zero(lchan->ts->trx->bts->network, struct gsm_subscriber_connection); if (!conn) return NULL; /* Configure the time and start it so it will be closed */ conn->lchan = lchan; conn->bts = lchan->ts->trx->bts; lchan->conn = conn; llist_add_tail(&conn->entry, &sub_connections); return conn; } /* TODO: move subscriber put here... */ void subscr_con_free(struct gsm_subscriber_connection *conn) { if (!conn) return; if (conn->subscr) { subscr_put(conn->subscr); conn->subscr = NULL; } if (conn->ho_lchan) { LOGP(DNM, LOGL_ERROR, "The ho_lchan should have been cleared.\n"); conn->ho_lchan->conn = NULL; } if (conn->lchan) { LOGP(DNM, LOGL_ERROR, "The lchan should have been cleared.\n"); conn->lchan->conn = NULL; } if (conn->secondary_lchan) { LOGP(DNM, LOGL_ERROR, "The secondary_lchan should have been cleared.\n"); conn->secondary_lchan->conn = NULL; } llist_del(&conn->entry); talloc_free(conn); } int bsc_api_init(struct gsm_network *network, struct bsc_api *api) { network->bsc_api = api; return 0; } /*! \brief process incoming 08.08 DTAP from MSC (send via BTS to MS) */ int gsm0808_submit_dtap(struct gsm_subscriber_connection *conn, struct msgb *msg, int link_id, int allow_sacch) { uint8_t sapi; if (!conn->lchan) { LOGP(DMSC, LOGL_ERROR, "Called submit dtap without an lchan.\n"); msgb_free(msg); return -1; } sapi = link_id & 0x7; msg->lchan = conn->lchan; msg->dst = msg->lchan->ts->trx->rsl_link; /* If we are on a TCH and need to submit a SMS (on SAPI=3) we need to use the SACH */ if (allow_sacch && sapi != 0) { if (conn->lchan->type == GSM_LCHAN_TCH_F || conn->lchan->type == GSM_LCHAN_TCH_H) link_id |= 0x40; } msg->l3h = msg->data; /* is requested SAPI already up? */ if (conn->lchan->sapis[sapi] == LCHAN_SAPI_UNUSED) { /* Establish L2 for additional SAPI */ OBSC_LINKID_CB(msg) = link_id; if (rll_establish(msg->lchan, sapi, rll_ind_cb, msg) != 0) { msgb_free(msg); send_sapi_reject(conn, link_id); return -1; } return 0; } else { /* Directly forward via RLL/RSL to BTS */ return rsl_data_request(msg, link_id); } } /* * \brief Check if the given channel is compatible with the mode/fullrate */ static int chan_compat_with_mode(struct gsm_lchan *lchan, int chan_mode, int full_rate) { switch (chan_mode) { case GSM48_CMODE_SIGN: /* signalling is always possible */ return 1; case GSM48_CMODE_SPEECH_V1: case GSM48_CMODE_SPEECH_AMR: case GSM48_CMODE_DATA_3k6: case GSM48_CMODE_DATA_6k0: /* these services can all run on TCH/H, but we may have * an explicit override by the 'full_rate' argument */ switch (lchan->type) { case GSM_LCHAN_TCH_F: return 1; case GSM_LCHAN_TCH_H: if (full_rate) return 0; else return 1; break; default: return 0; } break; case GSM48_CMODE_DATA_12k0: case GSM48_CMODE_DATA_14k5: case GSM48_CMODE_SPEECH_EFR: /* these services all explicitly require a TCH/F */ if (lchan->type == GSM_LCHAN_TCH_F) return 1; else return 0; break; } return 0; } /** * Send a GSM08.08 Assignment Request. Right now this does not contain the * audio codec type or the allowed rates for the config. It is assumed that * this is for audio handling only. In case the current channel does not allow * the selected mode a new one will be allocated. * * TODO: Add multirate configuration, make it work for more than audio. */ int gsm0808_assign_req(struct gsm_subscriber_connection *conn, int chan_mode, int full_rate) { struct bsc_api *api; api = conn->bts->network->bsc_api; if (!chan_compat_with_mode(conn->lchan, chan_mode, full_rate)) { if (handle_new_assignment(conn, chan_mode, full_rate) != 0) goto error; } else { LOGP(DMSC, LOGL_NOTICE, "Sending ChanModify for speech %d %d\n", chan_mode, full_rate); if (chan_mode == GSM48_CMODE_SPEECH_AMR) handle_mr_config(conn, conn->lchan, full_rate); gsm48_lchan_modify(conn->lchan, chan_mode); } /* we will now start the timer to complete the assignment */ conn->T10.cb = assignment_t10_timeout; conn->T10.data = conn; osmo_timer_schedule(&conn->T10, GSM0808_T10_VALUE); return 0; error: api->assign_fail(conn, 0, NULL); return -1; } int gsm0808_page(struct gsm_bts *bts, unsigned int page_group, unsigned int mi_len, uint8_t *mi, int chan_type) { return rsl_paging_cmd(bts, page_group, mi_len, mi, chan_type); } static void handle_ass_compl(struct gsm_subscriber_connection *conn, struct msgb *msg) { struct gsm48_hdr *gh; struct bsc_api *api = conn->bts->network->bsc_api; if (conn->secondary_lchan != msg->lchan) { LOGP(DMSC, LOGL_ERROR, "Assignment Compl should occur on second lchan.\n"); return; } gh = msgb_l3(msg); if (msgb_l3len(msg) - sizeof(*gh) != 1) { LOGP(DMSC, LOGL_ERROR, "Assignment Compl invalid: %zu\n", msgb_l3len(msg) - sizeof(*gh)); return; } /* switch TRAU muxer for E1 based BTS from one channel to another */ if (is_e1_bts(conn->bts)) switch_trau_mux(conn->lchan, conn->secondary_lchan); /* swap channels */ osmo_timer_del(&conn->T10); lchan_release(conn->lchan, 0, RSL_REL_LOCAL_END); conn->lchan = conn->secondary_lchan; conn->secondary_lchan = NULL; if (is_ipaccess_bts(conn->bts) && conn->lchan->tch_mode != GSM48_CMODE_SIGN) rsl_ipacc_crcx(conn->lchan); api->assign_compl(conn, gh->data[0], lchan_to_chosen_channel(conn->lchan), conn->lchan->encr.alg_id, chan_mode_to_speech(conn->lchan)); } static void handle_ass_fail(struct gsm_subscriber_connection *conn, struct msgb *msg) { struct bsc_api *api = conn->bts->network->bsc_api; uint8_t *rr_failure; struct gsm48_hdr *gh; if (conn->lchan != msg->lchan) { LOGP(DMSC, LOGL_ERROR, "Assignment failure should occur on primary lchan.\n"); return; } /* stop the timer and release it */ osmo_timer_del(&conn->T10); lchan_release(conn->secondary_lchan, 0, RSL_REL_LOCAL_END); conn->secondary_lchan = NULL; gh = msgb_l3(msg); if (msgb_l3len(msg) - sizeof(*gh) != 1) { LOGP(DMSC, LOGL_ERROR, "assignemnt failure unhandled: %zu\n", msgb_l3len(msg) - sizeof(*gh)); rr_failure = NULL; } else { rr_failure = &gh->data[0]; } api->assign_fail(conn, GSM0808_CAUSE_RADIO_INTERFACE_MESSAGE_FAILURE, rr_failure); } static void handle_classmark_chg(struct gsm_subscriber_connection *conn, struct msgb *msg) { struct bsc_api *api = msg->lchan->ts->trx->bts->network->bsc_api; struct gsm48_hdr *gh = msgb_l3(msg); unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); uint8_t cm2_len, cm3_len = 0; uint8_t *cm2, *cm3 = NULL; DEBUGP(DRR, "CLASSMARK CHANGE "); /* classmark 2 */ cm2_len = gh->data[0]; cm2 = &gh->data[1]; DEBUGPC(DRR, "CM2(len=%u) ", cm2_len); if (payload_len > cm2_len + 1) { /* we must have a classmark3 */ if (gh->data[cm2_len+1] != 0x20) { DEBUGPC(DRR, "ERR CM3 TAG\n"); return; } if (cm2_len > 3) { DEBUGPC(DRR, "CM2 too long!\n"); return; } cm3_len = gh->data[cm2_len+2]; cm3 = &gh->data[cm2_len+3]; if (cm3_len > 14) { DEBUGPC(DRR, "CM3 len %u too long!\n", cm3_len); return; } DEBUGPC(DRR, "CM3(len=%u)\n", cm3_len); } api->classmark_chg(conn, cm2, cm2_len, cm3, cm3_len); } /* Chapter 9.1.16 Handover complete */ static void handle_rr_ho_compl(struct msgb *msg) { struct lchan_signal_data sig; struct gsm48_hdr *gh = msgb_l3(msg); DEBUGP(DRR, "HANDOVER COMPLETE cause = %s\n", rr_cause_name(gh->data[0])); sig.lchan = msg->lchan; sig.mr = NULL; osmo_signal_dispatch(SS_LCHAN, S_LCHAN_HANDOVER_COMPL, &sig); /* FIXME: release old channel */ } /* Chapter 9.1.17 Handover Failure */ static void handle_rr_ho_fail(struct msgb *msg) { struct lchan_signal_data sig; struct gsm48_hdr *gh = msgb_l3(msg); DEBUGP(DRR, "HANDOVER FAILED cause = %s\n", rr_cause_name(gh->data[0])); sig.lchan = msg->lchan; sig.mr = NULL; osmo_signal_dispatch(SS_LCHAN, S_LCHAN_HANDOVER_FAIL, &sig); /* FIXME: release allocated new channel */ } static void dispatch_dtap(struct gsm_subscriber_connection *conn, uint8_t link_id, struct msgb *msg) { struct bsc_api *api = msg->lchan->ts->trx->bts->network->bsc_api; struct gsm48_hdr *gh; uint8_t pdisc; int rc; if (msgb_l3len(msg) < sizeof(*gh)) { LOGP(DMSC, LOGL_ERROR, "Message too short for a GSM48 header.\n"); return; } gh = msgb_l3(msg); pdisc = gh->proto_discr & 0x0f; /* the idea is to handle all RR messages here, and only hand * MM/CC/SMS-CP/LCS up to the MSC. Some messages like PAGING * RESPONSE or CM SERVICE REQUEST will not be covered here, as * they are only possible in the first L3 message of each L2 * channel, i.e. 'conn' will not exist and gsm0408_rcvmsg() * will call api->compl_l3() for it */ switch (pdisc) { case GSM48_PDISC_RR: switch (gh->msg_type) { case GSM48_MT_RR_GPRS_SUSP_REQ: DEBUGP(DRR, "GRPS SUSPEND REQUEST\n"); break; case GSM48_MT_RR_STATUS: LOGP(DRR, LOGL_NOTICE, "RR STATUS (cause: %s)\n", rr_cause_name(gh->data[0])); break; case GSM48_MT_RR_MEAS_REP: /* This shouldn't actually end up here, as RSL treats * L3 Info of 08.58 MEASUREMENT REPORT different by calling * directly into gsm48_parse_meas_rep */ LOGP(DMEAS, LOGL_ERROR, "DIRECT GSM48 MEASUREMENT REPORT ?!? "); break; case GSM48_MT_RR_HANDO_COMPL: handle_rr_ho_compl(msg); break; case GSM48_MT_RR_HANDO_FAIL: handle_rr_ho_fail(msg); break; case GSM48_MT_RR_CIPH_M_COMPL: if (api->cipher_mode_compl) api->cipher_mode_compl(conn, msg, conn->lchan->encr.alg_id); break; case GSM48_MT_RR_ASS_COMPL: handle_ass_compl(conn, msg); break; case GSM48_MT_RR_ASS_FAIL: handle_ass_fail(conn, msg); break; case GSM48_MT_RR_CHAN_MODE_MODIF_ACK: osmo_timer_del(&conn->T10); rc = gsm48_rx_rr_modif_ack(msg); if (rc < 0) { api->assign_fail(conn, GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE, NULL); } else if (rc >= 0) { api->assign_compl(conn, 0, lchan_to_chosen_channel(conn->lchan), conn->lchan->encr.alg_id, chan_mode_to_speech(conn->lchan)); } break; case GSM48_MT_RR_CLSM_CHG: handle_classmark_chg(conn, msg); break; case GSM48_MT_RR_APP_INFO: /* Passing RR APP INFO to MSC, not quite * according to spec */ if (api->dtap) api->dtap(conn, link_id, msg); break; default: /* Normally, a MSC should never receive RR * messages, but we'd rather forward what we * don't know than drop it... */ LOGP(DRR, LOGL_NOTICE, "BSC: Passing unknown 04.08 " "RR message type 0x%02x to MSC\n", gh->msg_type); if (api->dtap) api->dtap(conn, link_id, msg); } break; default: if (api->dtap) api->dtap(conn, link_id, msg); break; } } /*! \brief RSL has received a DATA INDICATION with L3 from MS */ int gsm0408_rcvmsg(struct msgb *msg, uint8_t link_id) { int rc; struct bsc_api *api = msg->lchan->ts->trx->bts->network->bsc_api; struct gsm_lchan *lchan; lchan = msg->lchan; if (lchan->state != LCHAN_S_ACTIVE) { LOGP(DRSL, LOGL_INFO, "Got data in non active state(%s), " "discarding.\n", gsm_lchans_name(lchan->state)); return -1; } if (lchan->conn) { /* if we already have a connection, forward via DTAP to * MSC */ dispatch_dtap(lchan->conn, link_id, msg); } else { /* allocate a new connection */ rc = BSC_API_CONN_POL_REJECT; lchan->conn = subscr_con_allocate(msg->lchan); if (!lchan->conn) { lchan_release(lchan, 1, RSL_REL_NORMAL); return -1; } /* fwd via bsc_api to send COMPLETE L3 INFO to MSC */ rc = api->compl_l3(lchan->conn, msg, 0); if (rc != BSC_API_CONN_POL_ACCEPT) { lchan->conn->lchan = NULL; subscr_con_free(lchan->conn); lchan_release(lchan, 1, RSL_REL_NORMAL); } } return 0; } /*! \brief We received a GSM 08.08 CIPHER MODE from the MSC */ int gsm0808_cipher_mode(struct gsm_subscriber_connection *conn, int cipher, const uint8_t *key, int len, int include_imeisv) { if (cipher > 0 && key == NULL) { LOGP(DRSL, LOGL_ERROR, "Need to have an encrytpion key.\n"); return -1; } if (len > MAX_A5_KEY_LEN) { LOGP(DRSL, LOGL_ERROR, "The key is too long: %d\n", len); return -1; } conn->lchan->encr.alg_id = RSL_ENC_ALG_A5(cipher); if (key) { conn->lchan->encr.key_len = len; memcpy(conn->lchan->encr.key, key, len); } return gsm48_send_rr_ciph_mode(conn->lchan, include_imeisv); } /* * Release all occupied RF Channels but stay around for more. */ int gsm0808_clear(struct gsm_subscriber_connection *conn) { if (conn->ho_lchan) bsc_clear_handover(conn, 1); if (conn->secondary_lchan) lchan_release(conn->secondary_lchan, 0, RSL_REL_LOCAL_END); if (conn->lchan) lchan_release(conn->lchan, 1, RSL_REL_NORMAL); conn->lchan = NULL; conn->secondary_lchan = NULL; conn->ho_lchan = NULL; conn->bts = NULL; osmo_timer_del(&conn->T10); return 0; } static void send_sapi_reject(struct gsm_subscriber_connection *conn, int link_id) { struct bsc_api *api; if (!conn) return; api = conn->bts->network->bsc_api; if (!api || !api->sapi_n_reject) return; api->sapi_n_reject(conn, link_id); } static void rll_ind_cb(struct gsm_lchan *lchan, uint8_t link_id, void *_data, enum bsc_rllr_ind rllr_ind) { struct msgb *msg = _data; /* * There seems to be a small window that the RLL timer can * fire after a lchan_release call and before the S_CHALLOC_FREED * is called. Check if a conn is set before proceeding. */ if (!lchan->conn) return; switch (rllr_ind) { case BSC_RLLR_IND_EST_CONF: rsl_data_request(msg, OBSC_LINKID_CB(msg)); break; case BSC_RLLR_IND_REL_IND: case BSC_RLLR_IND_ERR_IND: case BSC_RLLR_IND_TIMEOUT: send_sapi_reject(lchan->conn, OBSC_LINKID_CB(msg)); msgb_free(msg); break; } } static int bsc_handle_lchan_signal(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct bsc_api *bsc; struct gsm_lchan *lchan; struct lchan_signal_data *lchan_data; if (subsys != SS_LCHAN) return 0; lchan_data = signal_data; if (!lchan_data->lchan || !lchan_data->lchan->conn) return 0; lchan = lchan_data->lchan; bsc = lchan->ts->trx->bts->network->bsc_api; if (!bsc) return 0; switch (signal) { case S_LCHAN_UNEXPECTED_RELEASE: handle_release(lchan->conn, bsc, lchan); break; case S_LCHAN_ACTIVATE_ACK: handle_chan_ack(lchan->conn, bsc, lchan); break; case S_LCHAN_ACTIVATE_NACK: handle_chan_nack(lchan->conn, bsc, lchan); break; } return 0; } static void handle_release(struct gsm_subscriber_connection *conn, struct bsc_api *bsc, struct gsm_lchan *lchan) { int destruct = 1; if (conn->secondary_lchan == lchan) { osmo_timer_del(&conn->T10); conn->secondary_lchan = NULL; bsc->assign_fail(conn, GSM0808_CAUSE_RADIO_INTERFACE_FAILURE, NULL); } /* clear the connection now */ if (bsc->clear_request) destruct = bsc->clear_request(conn, 0); /* now give up all channels */ if (conn->lchan == lchan) conn->lchan = NULL; if (conn->ho_lchan == lchan) { bsc_clear_handover(conn, 0); conn->ho_lchan = NULL; } lchan->conn = NULL; gsm0808_clear(conn); if (destruct) subscr_con_free(conn); } static void handle_chan_ack(struct gsm_subscriber_connection *conn, struct bsc_api *api, struct gsm_lchan *lchan) { if (conn->secondary_lchan != lchan) return; LOGP(DMSC, LOGL_NOTICE, "Sending assignment on chan: %p\n", lchan); gsm48_send_rr_ass_cmd(conn->lchan, lchan, lchan->ms_power); } static void handle_chan_nack(struct gsm_subscriber_connection *conn, struct bsc_api *api, struct gsm_lchan *lchan) { if (conn->secondary_lchan != lchan) return; LOGP(DMSC, LOGL_ERROR, "Channel activation failed. Waiting for timeout now\n"); conn->secondary_lchan->conn = NULL; conn->secondary_lchan = NULL; } static __attribute__((constructor)) void on_dso_load_bsc(void) { osmo_signal_register_handler(SS_LCHAN, bsc_handle_lchan_signal, NULL); } struct llist_head *bsc_api_sub_connections(struct gsm_network *net) { return &sub_connections; } openbsc-0.15.0/openbsc/src/libbsc/bsc_ctrl_commands.c000066400000000000000000000310641265565154000225410ustar00rootroot00000000000000/* * (C) 2013-2015 by Holger Hans Peter Freyther * (C) 2013-2015 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 . * */ #include #include #include #include #include #include #include #include #include #include #define CTRL_CMD_VTY_STRING(cmdname, cmdstr, dtype, element) \ CTRL_HELPER_GET_STRING(cmdname, dtype, element) \ CTRL_HELPER_SET_STRING(cmdname, dtype, element) \ static struct ctrl_cmd_element cmd_##cmdname = { \ .name = cmdstr, \ .get = get_##cmdname, \ .set = set_##cmdname, \ .verify = verify_vty_description_string, \ } /** * Check that there are no newlines or comments or other things * that could make the VTY configuration unparsable. */ static int verify_vty_description_string(struct ctrl_cmd *cmd, const char *value, void *data) { int i; const size_t len = strlen(value); for (i = 0; i < len; ++i) { switch(value[i]) { case '#': case '\n': case '\r': cmd->reply = "String includes illegal character"; return -1; default: break; } } return 0; } CTRL_CMD_DEFINE_RANGE(net_mnc, "mnc", struct gsm_network, network_code, 0, 999); CTRL_CMD_DEFINE_RANGE(net_mcc, "mcc", struct gsm_network, country_code, 1, 999); CTRL_CMD_VTY_STRING(net_short_name, "short-name", struct gsm_network, name_short); CTRL_CMD_VTY_STRING(net_long_name, "long-name", struct gsm_network, name_long); static int verify_net_apply_config(struct ctrl_cmd *cmd, const char *v, void *d) { return 0; } static int get_net_apply_config(struct ctrl_cmd *cmd, void *data) { cmd->reply = "Write only attribute"; return CTRL_CMD_ERROR; } static int set_net_apply_config(struct ctrl_cmd *cmd, void *data) { struct gsm_network *net = cmd->node; struct gsm_bts *bts; llist_for_each_entry(bts, &net->bts_list, list) { if (!is_ipaccess_bts(bts)) continue; ipaccess_drop_oml(bts); } cmd->reply = "Tried to drop the BTS"; return CTRL_CMD_REPLY; } CTRL_CMD_DEFINE(net_apply_config, "apply-configuration"); static int verify_net_mcc_mnc_apply(struct ctrl_cmd *cmd, const char *value, void *d) { char *tmp, *saveptr, *mcc, *mnc; tmp = talloc_strdup(cmd, value); if (!tmp) return 1; mcc = strtok_r(tmp, ",", &saveptr); mnc = strtok_r(NULL, ",", &saveptr); talloc_free(tmp); if (!mcc || !mnc) return 1; return 0; } static int get_net_mcc_mnc_apply(struct ctrl_cmd *cmd, void *data) { cmd->reply = "Write only attribute"; return CTRL_CMD_ERROR; } static int set_net_mcc_mnc_apply(struct ctrl_cmd *cmd, void *data) { struct gsm_network *net = cmd->node; char *tmp, *saveptr, *mcc_str, *mnc_str; int mcc, mnc; tmp = talloc_strdup(cmd, cmd->value); if (!tmp) goto oom; mcc_str = strtok_r(tmp, ",", &saveptr); mnc_str = strtok_r(NULL, ",", &saveptr); mcc = atoi(mcc_str); mnc = atoi(mnc_str); talloc_free(tmp); if (net->network_code == mnc && net->country_code == mcc) { cmd->reply = "Nothing changed"; return CTRL_CMD_REPLY; } net->network_code = mnc; net->country_code = mcc; return set_net_apply_config(cmd, data); oom: cmd->reply = "OOM"; return CTRL_CMD_ERROR; } CTRL_CMD_DEFINE(net_mcc_mnc_apply, "mcc-mnc-apply"); /* BTS related commands below */ CTRL_CMD_DEFINE_RANGE(bts_lac, "location-area-code", struct gsm_bts, location_area_code, 0, 65535); CTRL_CMD_DEFINE_RANGE(bts_ci, "cell-identity", struct gsm_bts, cell_identity, 0, 65535); static int verify_bts_apply_config(struct ctrl_cmd *cmd, const char *v, void *d) { return 0; } static int get_bts_apply_config(struct ctrl_cmd *cmd, void *data) { cmd->reply = "Write only attribute"; return CTRL_CMD_ERROR; } static int set_bts_apply_config(struct ctrl_cmd *cmd, void *data) { struct gsm_bts *bts = cmd->node; if (!is_ipaccess_bts(bts)) { cmd->reply = "BTS is not IP based"; return CTRL_CMD_ERROR; } ipaccess_drop_oml(bts); cmd->reply = "Tried to drop the BTS"; return CTRL_CMD_REPLY; } CTRL_CMD_DEFINE(bts_apply_config, "apply-configuration"); static int verify_bts_si(struct ctrl_cmd *cmd, const char *v, void *d) { return 0; } static int get_bts_si(struct ctrl_cmd *cmd, void *data) { cmd->reply = "Write only attribute"; return CTRL_CMD_ERROR; } static int set_bts_si(struct ctrl_cmd *cmd, void *data) { struct gsm_bts *bts = cmd->node; int rc; rc = gsm_bts_set_system_infos(bts); if (rc != 0) { cmd->reply = "Failed to generate SI"; return CTRL_CMD_ERROR; } cmd->reply = "Generated new System Information"; return CTRL_CMD_REPLY; } CTRL_CMD_DEFINE(bts_si, "send-new-system-informations"); static int verify_bts_chan_load(struct ctrl_cmd *cmd, const char *v, void *d) { return 0; } static int get_bts_chan_load(struct ctrl_cmd *cmd, void *data) { int i; struct pchan_load pl; struct gsm_bts *bts; const char *space = ""; bts = cmd->node; memset(&pl, 0, sizeof(pl)); bts_chan_load(&pl, bts); cmd->reply = talloc_strdup(cmd, ""); for (i = 0; i < ARRAY_SIZE(pl.pchan); ++i) { const struct load_counter *lc = &pl.pchan[i]; /* These can never have user load */ if (i == GSM_PCHAN_NONE) continue; if (i == GSM_PCHAN_CCCH) continue; if (i == GSM_PCHAN_PDCH) continue; if (i == GSM_PCHAN_UNKNOWN) continue; cmd->reply = talloc_asprintf_append(cmd->reply, "%s%s,%u,%u", space, gsm_pchan_name(i), lc->used, lc->total); if (!cmd->reply) goto error; space = " "; } return CTRL_CMD_REPLY; error: cmd->reply = "Memory allocation failure"; return CTRL_CMD_ERROR; } static int set_bts_chan_load(struct ctrl_cmd *cmd, void *data) { cmd->reply = "Read only attribute"; return CTRL_CMD_ERROR; } CTRL_CMD_DEFINE(bts_chan_load, "channel-load"); static int verify_bts_oml_conn(struct ctrl_cmd *cmd, const char *value, void *_data) { struct gsm_bts *bts = cmd->node; if (!is_ipaccess_bts(bts)) { cmd->reply = ""; return -1; } return 0; } static int get_bts_oml_conn(struct ctrl_cmd *cmd, void *data) { struct gsm_bts *bts = cmd->node; cmd->reply = bts->oml_link ? "connected" : "disconnected"; return CTRL_CMD_REPLY; } static int set_bts_oml_conn(struct ctrl_cmd *cmd, void *data) { cmd->reply = "Read only attribute"; return CTRL_CMD_ERROR; } CTRL_CMD_DEFINE(bts_oml_conn, "oml-connection-state"); static int verify_bts_gprs_mode(struct ctrl_cmd *cmd, const char *value, void *_data) { int valid; enum bts_gprs_mode mode; struct gsm_bts *bts = cmd->node; mode = bts_gprs_mode_parse(value, &valid); if (!valid) { cmd->reply = "Mode is not known"; return 1; } if (!bts_gprs_mode_is_compat(bts, mode)) { cmd->reply = "bts does not support this mode"; return 1; } return 0; } static int get_bts_gprs_mode(struct ctrl_cmd *cmd, void *data) { struct gsm_bts *bts = cmd->node; cmd->reply = talloc_strdup(cmd, bts_gprs_mode_name(bts->gprs.mode)); return CTRL_CMD_REPLY; } static int set_bts_gprs_mode(struct ctrl_cmd *cmd, void *data) { struct gsm_bts *bts = cmd->node; bts->gprs.mode = bts_gprs_mode_parse(cmd->value, NULL); return get_bts_gprs_mode(cmd, data); } CTRL_CMD_DEFINE(bts_gprs_mode, "gprs-mode"); static int get_bts_rf_state(struct ctrl_cmd *cmd, void *data) { const char *oper, *admin, *policy; struct gsm_bts *bts = cmd->node; if (!bts) { cmd->reply = "bts not found."; return CTRL_CMD_ERROR; } oper = osmo_bsc_rf_get_opstate_name(osmo_bsc_rf_get_opstate_by_bts(bts)); admin = osmo_bsc_rf_get_adminstate_name(osmo_bsc_rf_get_adminstate_by_bts(bts)); policy = osmo_bsc_rf_get_policy_name(osmo_bsc_rf_get_policy_by_bts(bts)); cmd->reply = talloc_asprintf(cmd, "%s,%s,%s", oper, admin, policy); if (!cmd->reply) { cmd->reply = "OOM."; return CTRL_CMD_ERROR; } return CTRL_CMD_REPLY; } CTRL_CMD_DEFINE_RO(bts_rf_state, "rf_state"); static int get_net_rf_lock(struct ctrl_cmd *cmd, void *data) { struct gsm_network *net = cmd->node; struct gsm_bts *bts; const char *policy_name; policy_name = osmo_bsc_rf_get_policy_name(net->bsc_data->rf_ctrl->policy); llist_for_each_entry(bts, &net->bts_list, list) { struct gsm_bts_trx *trx; /* Exclude the BTS from the global lock */ if (bts->excl_from_rf_lock) continue; llist_for_each_entry(trx, &bts->trx_list, list) { if (trx->mo.nm_state.availability == NM_AVSTATE_OK && trx->mo.nm_state.operational != NM_OPSTATE_DISABLED) { cmd->reply = talloc_asprintf(cmd, "state=on,policy=%s,bts=%u,trx=%u", policy_name, bts->nr, trx->nr); return CTRL_CMD_REPLY; } } } cmd->reply = talloc_asprintf(cmd, "state=off,policy=%s", policy_name); return CTRL_CMD_REPLY; } #define TIME_FORMAT_RFC2822 "%a, %d %b %Y %T %z" static int set_net_rf_lock(struct ctrl_cmd *cmd, void *data) { int locked = atoi(cmd->value); struct gsm_network *net = cmd->node; time_t now = time(NULL); char now_buf[64]; struct osmo_bsc_rf *rf; if (!net) { cmd->reply = "net not found."; return CTRL_CMD_ERROR; } rf = net->bsc_data->rf_ctrl; if (!rf) { cmd->reply = "RF Ctrl is not enabled in the BSC Configuration"; return CTRL_CMD_ERROR; } talloc_free(rf->last_rf_lock_ctrl_command); strftime(now_buf, sizeof(now_buf), TIME_FORMAT_RFC2822, gmtime(&now)); rf->last_rf_lock_ctrl_command = talloc_asprintf(rf, "rf_locked %u (%s)", locked, now_buf); osmo_bsc_rf_schedule_lock(rf, locked == 1 ? '0' : '1'); cmd->reply = talloc_asprintf(cmd, "%u", locked); if (!cmd->reply) { cmd->reply = "OOM."; return CTRL_CMD_ERROR; } return CTRL_CMD_REPLY; } static int verify_net_rf_lock(struct ctrl_cmd *cmd, const char *value, void *data) { int locked = atoi(cmd->value); if ((locked != 0) && (locked != 1)) return 1; return 0; } CTRL_CMD_DEFINE(net_rf_lock, "rf_locked"); static int get_net_bts_num(struct ctrl_cmd *cmd, void *data) { struct gsm_network *net = cmd->node; cmd->reply = talloc_asprintf(cmd, "%u", net->num_bts); return CTRL_CMD_REPLY; } CTRL_CMD_DEFINE_RO(net_bts_num, "number-of-bts"); /* TRX related commands below here */ CTRL_HELPER_GET_INT(trx_max_power, struct gsm_bts_trx, max_power_red); static int verify_trx_max_power(struct ctrl_cmd *cmd, const char *value, void *_data) { int tmp = atoi(value); if (tmp < 0 || tmp > 22) { cmd->reply = "Value must be between 0 and 22"; return -1; } if (tmp & 1) { cmd->reply = "Value must be even"; return -1; } return 0; } CTRL_CMD_DEFINE_RANGE(trx_arfcn, "arfcn", struct gsm_bts_trx, arfcn, 0, 1023); static int set_trx_max_power(struct ctrl_cmd *cmd, void *_data) { struct gsm_bts_trx *trx = cmd->node; int old_power; /* remember the old value, set the new one */ old_power = trx->max_power_red; trx->max_power_red = atoi(cmd->value); /* Maybe update the value */ if (old_power != trx->max_power_red) { LOGP(DCTRL, LOGL_NOTICE, "%s updating max_pwr_red(%d)\n", gsm_trx_name(trx), trx->max_power_red); abis_nm_update_max_power_red(trx); } return get_trx_max_power(cmd, _data); } CTRL_CMD_DEFINE(trx_max_power, "max-power-reduction"); int bsc_base_ctrl_cmds_install(void) { int rc = 0; rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_mnc); rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_mcc); rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_short_name); rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_long_name); rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_apply_config); rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_mcc_mnc_apply); rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_rf_lock); rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_bts_num); rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_lac); rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_ci); rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_apply_config); rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_si); rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_chan_load); rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_oml_conn); rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_gprs_mode); rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_rf_state); rc |= ctrl_cmd_install(CTRL_NODE_TRX, &cmd_trx_max_power); rc |= ctrl_cmd_install(CTRL_NODE_TRX, &cmd_trx_arfcn); return rc; } openbsc-0.15.0/openbsc/src/libbsc/bsc_ctrl_lookup.c000066400000000000000000000060761265565154000222560ustar00rootroot00000000000000/* SNMP-like status interface. Look-up of BTS/TRX * * (C) 2010-2011 by Daniel Willmann * (C) 2010-2011 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 General Public License as published by * the Free Software Foundation; either version 2 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 General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ #include #include #include #include #include extern vector ctrl_node_vec; /*! \brief control interface lookup function for bsc/bts gsm_data * \param[in] data Private data passed to controlif_setup() * \param[in] vline Vector of the line holding the command string * \param[out] node_type type (CTRL_NODE_) that was determined * \param[out] node_data private dta of node that was determined * \param i Current index into vline, up to which it is parsed */ static int bsc_ctrl_node_lookup(void *data, vector vline, int *node_type, void **node_data, int *i) { struct gsm_network *net = data; struct gsm_bts *bts = NULL; struct gsm_bts_trx *trx = NULL; struct gsm_bts_trx_ts *ts = NULL; char *token = vector_slot(vline, *i); long num; /* TODO: We need to make sure that the following chars are digits * and/or use strtol to check if number conversion was successful * Right now something like net.bts_stats will not work */ if (!strcmp(token, "bts")) { if (*node_type != CTRL_NODE_ROOT || !net) goto err_missing; (*i)++; if (!ctrl_parse_get_num(vline, *i, &num)) goto err_index; bts = gsm_bts_num(net, num); if (!bts) goto err_missing; *node_data = bts; *node_type = CTRL_NODE_BTS; } else if (!strcmp(token, "trx")) { if (*node_type != CTRL_NODE_BTS || !*node_data) goto err_missing; bts = *node_data; (*i)++; if (!ctrl_parse_get_num(vline, *i, &num)) goto err_index; trx = gsm_bts_trx_num(bts, num); if (!trx) goto err_missing; *node_data = trx; *node_type = CTRL_NODE_TRX; } else if (!strcmp(token, "ts")) { if (*node_type != CTRL_NODE_TRX || !*node_data) goto err_missing; trx = *node_data; (*i)++; if (!ctrl_parse_get_num(vline, *i, &num)) goto err_index; if ((num >= 0) && (num < TRX_NR_TS)) ts = &trx->ts[num]; if (!ts) goto err_missing; *node_data = ts; *node_type = CTRL_NODE_TS; } else return 0; return 1; err_missing: return -ENODEV; err_index: return -ERANGE; } struct ctrl_handle *bsc_controlif_setup(struct gsm_network *net, uint16_t port) { return ctrl_interface_setup(net, port, bsc_ctrl_node_lookup); } openbsc-0.15.0/openbsc/src/libbsc/bsc_init.c000066400000000000000000000336441265565154000206650ustar00rootroot00000000000000/* A hackish minimal BSC (+MSC +HLR) implementation */ /* (C) 2008-2010 by Harald Welte * (C) 2009 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* global pointer to the gsm network data structure */ extern struct gsm_network *bsc_gsmnet; /* Callback function for NACK on the OML NM */ static int oml_msg_nack(struct nm_nack_signal_data *nack) { if (nack->mt == NM_MT_SET_BTS_ATTR_NACK) { LOGP(DNM, LOGL_ERROR, "Failed to set BTS attributes. That is fatal. " "Was the bts type and frequency properly specified?\n"); goto drop_bts; } else { LOGP(DNM, LOGL_ERROR, "Got a NACK going to drop the OML links.\n"); goto drop_bts; } return 0; drop_bts: if (!nack->bts) { LOGP(DNM, LOGL_ERROR, "Unknown bts. Can not drop it.\n"); return 0; } if (is_ipaccess_bts(nack->bts)) ipaccess_drop_oml(nack->bts); return 0; } /* Callback function to be called every time we receive a signal from NM */ static int nm_sig_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct nm_nack_signal_data *nack; switch (signal) { case S_NM_NACK: nack = signal_data; return oml_msg_nack(nack); default: break; } return 0; } int bsc_shutdown_net(struct gsm_network *net) { struct gsm_bts *bts; llist_for_each_entry(bts, &net->bts_list, list) { LOGP(DNM, LOGL_NOTICE, "shutting down OML for BTS %u\n", bts->nr); osmo_signal_dispatch(SS_L_GLOBAL, S_GLOBAL_BTS_CLOSE_OM, bts); } return 0; } static int rsl_si(struct gsm_bts_trx *trx, enum osmo_sysinfo_type i, int si_len) { struct gsm_bts *bts = trx->bts; int rc; DEBUGP(DRR, "SI%s: %s\n", get_value_string(osmo_sitype_strs, i), osmo_hexdump(GSM_BTS_SI(bts, i), GSM_MACBLOCK_LEN)); switch (i) { case SYSINFO_TYPE_5: case SYSINFO_TYPE_5bis: case SYSINFO_TYPE_5ter: case SYSINFO_TYPE_6: rc = rsl_sacch_filling(trx, osmo_sitype2rsl(i), GSM_BTS_SI(bts, i), si_len); break; default: rc = rsl_bcch_info(trx, osmo_sitype2rsl(i), GSM_BTS_SI(bts, i), si_len); break; } return rc; } /* set all system information types for a TRX */ int gsm_bts_trx_set_system_infos(struct gsm_bts_trx *trx) { int i, rc; struct gsm_bts *bts = trx->bts; uint8_t gen_si[_MAX_SYSINFO_TYPE], n_si = 0, n; int si_len[_MAX_SYSINFO_TYPE]; bts->si_common.cell_sel_par.ms_txpwr_max_ccch = ms_pwr_ctl_lvl(bts->band, bts->ms_max_power); bts->si_common.cell_sel_par.neci = bts->network->neci; /* First, we determine which of the SI messages we actually need */ if (trx == bts->c0) { /* 1...4 are always present on a C0 TRX */ gen_si[n_si++] = SYSINFO_TYPE_1; gen_si[n_si++] = SYSINFO_TYPE_2; gen_si[n_si++] = SYSINFO_TYPE_2bis; gen_si[n_si++] = SYSINFO_TYPE_2ter; gen_si[n_si++] = SYSINFO_TYPE_3; gen_si[n_si++] = SYSINFO_TYPE_4; /* 13 is always present on a C0 TRX of a GPRS BTS */ if (bts->gprs.mode != BTS_GPRS_NONE) gen_si[n_si++] = SYSINFO_TYPE_13; } /* 5 and 6 are always present on every TRX */ gen_si[n_si++] = SYSINFO_TYPE_5; gen_si[n_si++] = SYSINFO_TYPE_5bis; gen_si[n_si++] = SYSINFO_TYPE_5ter; gen_si[n_si++] = SYSINFO_TYPE_6; /* Second, we generate the selected SI via RSL */ for (n = 0; n < n_si; n++) { i = gen_si[n]; /* Only generate SI if this SI is not in "static" (user-defined) mode */ if (!(bts->si_mode_static & (1 << i))) { /* Set SI as being valid. gsm_generate_si() might unset * it, if SI is not required. */ bts->si_valid |= (1 << i); rc = gsm_generate_si(bts, i); if (rc < 0) goto err_out; si_len[i] = rc; } else { if (i == SYSINFO_TYPE_5 || i == SYSINFO_TYPE_5bis || i == SYSINFO_TYPE_5ter) si_len[i] = 18; else if (i == SYSINFO_TYPE_6) si_len[i] = 11; else si_len[i] = 23; } } /* Third, we send the selected SI via RSL */ for (n = 0; n < n_si; n++) { i = gen_si[n]; if (!(bts->si_valid & (1 << i))) continue; rc = rsl_si(trx, i, si_len[i]); if (rc < 0) return rc; } return 0; err_out: LOGP(DRR, LOGL_ERROR, "Cannot generate SI%s for BTS %u, most likely " "a problem with neighbor cell list generation\n", get_value_string(osmo_sitype_strs, i), bts->nr); return rc; } /* set all system information types for a BTS */ int gsm_bts_set_system_infos(struct gsm_bts *bts) { struct gsm_bts_trx *trx; /* Generate a new ID */ bts->bcch_change_mark += 1; bts->bcch_change_mark %= 0x7; llist_for_each_entry(trx, &bts->trx_list, list) { int rc; rc = gsm_bts_trx_set_system_infos(trx); if (rc != 0) return rc; } return 0; } /* Produce a MA as specified in 10.5.2.21 */ static int generate_ma_for_ts(struct gsm_bts_trx_ts *ts) { /* we have three bitvecs: the per-timeslot ARFCNs, the cell chan ARFCNs * and the MA */ struct bitvec *cell_chan = &ts->trx->bts->si_common.cell_alloc; struct bitvec *ts_arfcn = &ts->hopping.arfcns; struct bitvec *ma = &ts->hopping.ma; unsigned int num_cell_arfcns, bitnum, n_chan; int i; /* re-set the MA to all-zero */ ma->cur_bit = 0; ts->hopping.ma_len = 0; memset(ma->data, 0, ma->data_len); if (!ts->hopping.enabled) return 0; /* count the number of ARFCNs in the cell channel allocation */ num_cell_arfcns = 0; for (i = 0; i < 1024; i++) { if (bitvec_get_bit_pos(cell_chan, i)) num_cell_arfcns++; } /* pad it to octet-aligned number of bits */ ts->hopping.ma_len = num_cell_arfcns / 8; if (num_cell_arfcns % 8) ts->hopping.ma_len++; n_chan = 0; for (i = 0; i < 1024; i++) { if (!bitvec_get_bit_pos(cell_chan, i)) continue; /* set the corresponding bit in the MA */ bitnum = (ts->hopping.ma_len * 8) - 1 - n_chan; if (bitvec_get_bit_pos(ts_arfcn, i)) bitvec_set_bit_pos(ma, bitnum, 1); else bitvec_set_bit_pos(ma, bitnum, 0); n_chan++; } /* ARFCN 0 is special: It is coded last in the bitmask */ if (bitvec_get_bit_pos(cell_chan, 0)) { n_chan++; /* set the corresponding bit in the MA */ bitnum = (ts->hopping.ma_len * 8) - 1 - n_chan; if (bitvec_get_bit_pos(ts_arfcn, 0)) bitvec_set_bit_pos(ma, bitnum, 1); else bitvec_set_bit_pos(ma, bitnum, 0); } return 0; } static void bootstrap_rsl(struct gsm_bts_trx *trx) { unsigned int i; LOGP(DRSL, LOGL_NOTICE, "bootstrapping RSL for BTS/TRX (%u/%u) " "on ARFCN %u using MCC=%u MNC=%u LAC=%u CID=%u BSIC=%u TSC=%u\n", trx->bts->nr, trx->nr, trx->arfcn, bsc_gsmnet->country_code, bsc_gsmnet->network_code, trx->bts->location_area_code, trx->bts->cell_identity, trx->bts->bsic, trx->bts->tsc); if (trx->bts->type == GSM_BTS_TYPE_NOKIA_SITE) { rsl_nokia_si_begin(trx); } gsm_bts_trx_set_system_infos(trx); if (trx->bts->type == GSM_BTS_TYPE_NOKIA_SITE) { /* channel unspecific, power reduction in 2 dB steps */ rsl_bs_power_control(trx, 0xFF, trx->max_power_red / 2); rsl_nokia_si_end(trx); } for (i = 0; i < ARRAY_SIZE(trx->ts); i++) generate_ma_for_ts(&trx->ts[i]); } /* Callback function to be called every time we receive a signal from INPUT */ static int inp_sig_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct input_signal_data *isd = signal_data; struct gsm_bts_trx *trx = isd->trx; int ts_no, lchan_no; if (subsys != SS_L_INPUT) return -EINVAL; switch (signal) { case S_L_INP_TEI_UP: if (isd->link_type == E1INP_SIGN_OML) { /* TODO: this is required for the Nokia BTS, hopping is configured during OML, other MA is not set. */ struct gsm_bts_trx *cur_trx; /* was static in system_information.c */ extern int generate_cell_chan_list(uint8_t *chan_list, struct gsm_bts *bts); uint8_t ca[20]; /* has to be called before generate_ma_for_ts to set bts->si_common.cell_alloc */ generate_cell_chan_list(ca, trx->bts); llist_for_each_entry(cur_trx, &trx->bts->trx_list, list) { int i; for (i = 0; i < ARRAY_SIZE(cur_trx->ts); i++) generate_ma_for_ts(&cur_trx->ts[i]); } } if (isd->link_type == E1INP_SIGN_RSL) bootstrap_rsl(trx); break; case S_L_INP_TEI_DN: LOGP(DLMI, LOGL_ERROR, "Lost some E1 TEI link: %d %p\n", isd->link_type, trx); if (isd->link_type == E1INP_SIGN_OML) osmo_counter_inc(trx->bts->network->stats.bts.oml_fail); else if (isd->link_type == E1INP_SIGN_RSL) osmo_counter_inc(trx->bts->network->stats.bts.rsl_fail); /* * free all allocated channels. change the nm_state so the * trx and trx_ts becomes unusable and chan_alloc.c can not * allocate from it. */ for (ts_no = 0; ts_no < ARRAY_SIZE(trx->ts); ++ts_no) { struct gsm_bts_trx_ts *ts = &trx->ts[ts_no]; for (lchan_no = 0; lchan_no < ARRAY_SIZE(ts->lchan); ++lchan_no) { if (ts->lchan[lchan_no].state != LCHAN_S_NONE) lchan_free(&ts->lchan[lchan_no]); lchan_reset(&ts->lchan[lchan_no]); } } gsm_bts_mo_reset(trx->bts); abis_nm_clear_queue(trx->bts); break; default: break; } return 0; } static int bootstrap_bts(struct gsm_bts *bts) { int i, n; if (bts->model->start && !bts->model->started) { int ret = bts->model->start(bts->network); if (ret < 0) return ret; bts->model->started = true; } /* FIXME: What about secondary TRX of a BTS? What about a BTS that has TRX * in different bands? Why is 'band' a parameter of the BTS and not of the TRX? */ switch (bts->band) { case GSM_BAND_1800: if (bts->c0->arfcn < 512 || bts->c0->arfcn > 885) { LOGP(DNM, LOGL_ERROR, "GSM1800 channel must be between 512-885.\n"); return -EINVAL; } break; case GSM_BAND_1900: if (bts->c0->arfcn < 512 || bts->c0->arfcn > 810) { LOGP(DNM, LOGL_ERROR, "GSM1900 channel must be between 512-810.\n"); return -EINVAL; } break; case GSM_BAND_900: if ((bts->c0->arfcn > 124 && bts->c0->arfcn < 955) || bts->c0->arfcn > 1023) { LOGP(DNM, LOGL_ERROR, "GSM900 channel must be between 0-124, 955-1023.\n"); return -EINVAL; } break; case GSM_BAND_850: if (bts->c0->arfcn < 128 || bts->c0->arfcn > 251) { LOGP(DNM, LOGL_ERROR, "GSM850 channel must be between 128-251.\n"); return -EINVAL; } break; default: LOGP(DNM, LOGL_ERROR, "Unsupported frequency band.\n"); return -EINVAL; } if (bts->network->auth_policy == GSM_AUTH_POLICY_ACCEPT_ALL && !bts->si_common.rach_control.cell_bar) LOGP(DNM, LOGL_ERROR, "\nWARNING: You are running an 'accept-all' " "network on a BTS that is not barred. This " "configuration is likely to interfere with production " "GSM networks and should only be used in a RF " "shielded environment such as a faraday cage!\n\n"); /* Control Channel Description is set from vty/config */ /* T3212 is set from vty/config */ /* Set ccch config by looking at ts config */ for (n=0, i=0; i<8; i++) n += bts->c0->ts[i].pchan == GSM_PCHAN_CCCH ? 1 : 0; switch (n) { case 0: bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_1_C; /* Limit reserved block to 2 on combined channel */ if (bts->si_common.chan_desc.bs_ag_blks_res > 2) bts->si_common.chan_desc.bs_ag_blks_res = 2; break; case 1: bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_1_NC; break; case 2: bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_2_NC; break; case 3: bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_3_NC; break; case 4: bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_4_NC; break; default: LOGP(DNM, LOGL_ERROR, "Unsupported CCCH timeslot configuration\n"); return -EINVAL; } /* allow/disallow DTXu */ if (bts->network->dtx_enabled) bts->si_common.cell_options.dtx = 0; else bts->si_common.cell_options.dtx = 2; bts->si_common.cell_options.pwrc = 0; /* PWRC not set */ bts->si_common.cell_sel_par.acs = 0; bts->si_common.ncc_permitted = 0xff; /* Initialize the BTS state */ gsm_bts_mo_reset(bts); return 0; } int bsc_bootstrap_network(int (*mncc_recv)(struct gsm_network *, struct msgb *), const char *config_file) { struct telnet_connection dummy_conn; struct gsm_bts *bts; int rc; /* initialize our data structures */ bsc_gsmnet = gsm_network_init(1, 1, mncc_recv); if (!bsc_gsmnet) return -ENOMEM; bsc_gsmnet->name_long = talloc_strdup(bsc_gsmnet, "OpenBSC"); bsc_gsmnet->name_short = talloc_strdup(bsc_gsmnet, "OpenBSC"); /* our vty command code expects vty->priv to point to a telnet_connection */ dummy_conn.priv = bsc_gsmnet; rc = vty_read_config_file(config_file, &dummy_conn); if (rc < 0) { LOGP(DNM, LOGL_FATAL, "Failed to parse the config file: '%s'\n", config_file); return rc; } rc = telnet_init(tall_bsc_ctx, bsc_gsmnet, OSMO_VTY_PORT_NITB_BSC); if (rc < 0) return rc; osmo_signal_register_handler(SS_NM, nm_sig_cb, NULL); osmo_signal_register_handler(SS_L_INPUT, inp_sig_cb, NULL); llist_for_each_entry(bts, &bsc_gsmnet->bts_list, list) { rc = bootstrap_bts(bts); if (rc < 0) { LOGP(DNM, LOGL_FATAL, "Error bootstrapping BTS\n"); return rc; } rc = e1_reconfig_bts(bts); if (rc < 0) { LOGP(DNM, LOGL_FATAL, "Error enabling E1 input driver\n"); return rc; } } return 0; } openbsc-0.15.0/openbsc/src/libbsc/bsc_msc.c000066400000000000000000000167031265565154000205010ustar00rootroot00000000000000/* Routines to talk to the MSC using the IPA Protocol */ /* * (C) 2010 by Holger Hans Peter Freyther * (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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include static void connection_loss(struct bsc_msc_connection *con) { struct osmo_fd *fd; fd = &con->write_queue.bfd; if (con->pending_msg) { LOGP(DMSC, LOGL_ERROR, "MSC(%s) dropping incomplete message.\n", con->name); msgb_free(con->pending_msg); con->pending_msg = NULL; } close(fd->fd); fd->fd = -1; fd->cb = osmo_wqueue_bfd_cb; fd->when = 0; con->is_connected = 0; con->first_contact = 0; con->connection_loss(con); } static void msc_con_timeout(void *_con) { struct bsc_msc_connection *con = _con; LOGP(DMSC, LOGL_ERROR, "MSC(%s) Connection timeout.\n", con->name); bsc_msc_lost(con); } /* called in the case of a non blocking connect */ static int msc_connection_connect(struct osmo_fd *fd, unsigned int what) { int rc; int val; struct bsc_msc_connection *con; struct osmo_wqueue *queue; socklen_t len = sizeof(val); queue = container_of(fd, struct osmo_wqueue, bfd); con = container_of(queue, struct bsc_msc_connection, write_queue); if ((what & BSC_FD_WRITE) == 0) { LOGP(DMSC, LOGL_ERROR, "MSC(%s) Callback but not writable.\n", con->name); return -1; } /* From here on we will either be connected or reconnect */ osmo_timer_del(&con->timeout_timer); /* check the socket state */ rc = getsockopt(fd->fd, SOL_SOCKET, SO_ERROR, &val, &len); if (rc != 0) { LOGP(DMSC, LOGL_ERROR, "getsockopt for the MSC(%s) socket failed.\n", con->name); goto error; } if (val != 0) { LOGP(DMSC, LOGL_ERROR, "Not connected to the MSC(%s): %d\n", con->name, val); goto error; } /* go to full operation */ fd->cb = osmo_wqueue_bfd_cb; fd->when = BSC_FD_READ | BSC_FD_EXCEPT; con->is_connected = 1; LOGP(DMSC, LOGL_NOTICE, "(Re)Connected to the MSC(%s).\n", con->name); if (con->connected) con->connected(con); return 0; error: osmo_fd_unregister(fd); connection_loss(con); return -1; } static void setnonblocking(struct osmo_fd *fd) { int flags; flags = fcntl(fd->fd, F_GETFL); if (flags < 0) { perror("fcntl get failed"); close(fd->fd); fd->fd = -1; return; } flags |= O_NONBLOCK; flags = fcntl(fd->fd, F_SETFL, flags); if (flags < 0) { perror("fcntl get failed"); close(fd->fd); fd->fd = -1; return; } } int bsc_msc_connect(struct bsc_msc_connection *con) { struct bsc_msc_dest *dest; struct osmo_fd *fd; struct sockaddr_in sin; int on = 1, ret; if (llist_empty(con->dests)) { LOGP(DMSC, LOGL_ERROR, "No MSC(%s) connections configured.\n", con->name); connection_loss(con); return -1; } /* move to the next connection */ dest = (struct bsc_msc_dest *) con->dests->next; llist_del(&dest->list); llist_add_tail(&dest->list, con->dests); LOGP(DMSC, LOGL_NOTICE, "Attempting to connect MSC(%s) at %s:%d\n", con->name, dest->ip, dest->port); con->is_connected = 0; msgb_free(con->pending_msg); con->pending_msg = NULL; fd = &con->write_queue.bfd; fd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); fd->priv_nr = 1; if (fd->fd < 0) { perror("Creating TCP socket failed"); return fd->fd; } /* make it non blocking */ setnonblocking(fd); /* set the socket priority */ ret = setsockopt(fd->fd, IPPROTO_IP, IP_TOS, &dest->dscp, sizeof(dest->dscp)); if (ret != 0) LOGP(DMSC, LOGL_ERROR, "Failed to set DSCP to %d on MSC(%s). %s\n", dest->dscp, con->name, strerror(errno)); memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(dest->port); inet_aton(dest->ip, &sin.sin_addr); setsockopt(fd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); ret = connect(fd->fd, (struct sockaddr *) &sin, sizeof(sin)); if (ret == -1 && errno == EINPROGRESS) { LOGP(DMSC, LOGL_ERROR, "MSC(%s) Connection in progress\n", con->name); fd->when = BSC_FD_WRITE; fd->cb = msc_connection_connect; con->timeout_timer.cb = msc_con_timeout; con->timeout_timer.data = con; osmo_timer_schedule(&con->timeout_timer, 20, 0); } else if (ret < 0) { perror("Connection failed"); connection_loss(con); return ret; } else { fd->when = BSC_FD_READ | BSC_FD_EXCEPT; fd->cb = osmo_wqueue_bfd_cb; con->is_connected = 1; if (con->connected) con->connected(con); } ret = osmo_fd_register(fd); if (ret < 0) { perror("Registering the fd failed"); close(fd->fd); return ret; } return ret; } struct bsc_msc_connection *bsc_msc_create(void *ctx, struct llist_head *dests) { struct bsc_msc_connection *con; con = talloc_zero(NULL, struct bsc_msc_connection); if (!con) { LOGP(DMSC, LOGL_FATAL, "Failed to create the MSC connection.\n"); return NULL; } con->dests = dests; con->write_queue.bfd.fd = -1; con->name = ""; osmo_wqueue_init(&con->write_queue, 100); return con; } void bsc_msc_lost(struct bsc_msc_connection *con) { osmo_wqueue_clear(&con->write_queue); osmo_timer_del(&con->timeout_timer); osmo_timer_del(&con->reconnect_timer); if (con->write_queue.bfd.fd >= 0) osmo_fd_unregister(&con->write_queue.bfd); connection_loss(con); } static void reconnect_msc(void *_msc) { struct bsc_msc_connection *con = _msc; LOGP(DMSC, LOGL_NOTICE, "Attempting to reconnect to the MSC(%s).\n", con->name); bsc_msc_connect(con); } void bsc_msc_schedule_connect(struct bsc_msc_connection *con) { LOGP(DMSC, LOGL_NOTICE, "Attempting to reconnect to the MSC(%s)\n", con->name); con->reconnect_timer.cb = reconnect_msc; con->reconnect_timer.data = con; osmo_timer_schedule(&con->reconnect_timer, 5, 0); } struct msgb *bsc_msc_id_get_resp(int fixed, const char *token, const uint8_t *res, int len) { struct msgb *msg; if (!token) { LOGP(DMSC, LOGL_ERROR, "No token specified.\n"); return NULL; } msg = msgb_alloc_headroom(4096, 128, "id resp"); if (!msg) { LOGP(DMSC, LOGL_ERROR, "Failed to create the message.\n"); return NULL; } /* * The situation is bizarre. The encoding doesn't follow the * TLV structure. It is more like a LV and old versions had * it wrong but we want new versions to old servers so we * introduce the quirk here. */ msg->l2h = msgb_v_put(msg, IPAC_MSGT_ID_RESP); if (fixed) { msgb_put_u8(msg, 0); msgb_put_u8(msg, strlen(token) + 2); msgb_tv_fixed_put(msg, IPAC_IDTAG_UNITNAME, strlen(token) + 1, (uint8_t *) token); if (len > 0) { msgb_put_u8(msg, 0); msgb_put_u8(msg, len + 1); msgb_tv_fixed_put(msg, 0x24, len, res); } } else { msgb_l16tv_put(msg, strlen(token) + 1, IPAC_IDTAG_UNITNAME, (uint8_t *) token); } return msg; } openbsc-0.15.0/openbsc/src/libbsc/bsc_rf_ctrl.c000066400000000000000000000312611265565154000213460ustar00rootroot00000000000000/* RF Ctl handling socket */ /* (C) 2010 by Harald Welte * (C) 2010-2014 by Holger Hans Peter Freyther * (C) 2010-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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define RF_CMD_QUERY '?' #define RF_CMD_OFF '0' #define RF_CMD_ON '1' #define RF_CMD_D_OFF 'd' #define RF_CMD_ON_G 'g' static const struct value_string opstate_names[] = { { OSMO_BSC_RF_OPSTATE_INOPERATIONAL, "inoperational" }, { OSMO_BSC_RF_OPSTATE_OPERATIONAL, "operational" }, { 0, NULL } }; static const struct value_string adminstate_names[] = { { OSMO_BSC_RF_ADMINSTATE_UNLOCKED, "unlocked" }, { OSMO_BSC_RF_ADMINSTATE_LOCKED, "locked" }, { 0, NULL } }; static const struct value_string policy_names[] = { { OSMO_BSC_RF_POLICY_OFF, "off" }, { OSMO_BSC_RF_POLICY_ON, "on" }, { OSMO_BSC_RF_POLICY_GRACE, "grace" }, { OSMO_BSC_RF_POLICY_UNKNOWN, "unknown" }, { 0, NULL } }; const char *osmo_bsc_rf_get_opstate_name(enum osmo_bsc_rf_opstate opstate) { return get_value_string(opstate_names, opstate); } const char *osmo_bsc_rf_get_adminstate_name(enum osmo_bsc_rf_adminstate adminstate) { return get_value_string(adminstate_names, adminstate); } const char *osmo_bsc_rf_get_policy_name(enum osmo_bsc_rf_policy policy) { return get_value_string(policy_names, policy); } enum osmo_bsc_rf_opstate osmo_bsc_rf_get_opstate_by_bts(struct gsm_bts *bts) { struct gsm_bts_trx *trx; llist_for_each_entry(trx, &bts->trx_list, list) { if (trx->mo.nm_state.operational == NM_OPSTATE_ENABLED) return OSMO_BSC_RF_OPSTATE_OPERATIONAL; } /* No trx were active, so this bts is disabled */ return OSMO_BSC_RF_OPSTATE_INOPERATIONAL; } enum osmo_bsc_rf_adminstate osmo_bsc_rf_get_adminstate_by_bts(struct gsm_bts *bts) { struct gsm_bts_trx *trx; llist_for_each_entry(trx, &bts->trx_list, list) { if (trx->mo.nm_state.administrative == NM_STATE_UNLOCKED) return OSMO_BSC_RF_ADMINSTATE_UNLOCKED; } /* All trx administrative states were locked */ return OSMO_BSC_RF_ADMINSTATE_LOCKED; } enum osmo_bsc_rf_policy osmo_bsc_rf_get_policy_by_bts(struct gsm_bts *bts) { struct osmo_bsc_data *bsc_data = bts->network->bsc_data; if (!bsc_data) return OSMO_BSC_RF_POLICY_UNKNOWN; switch (bsc_data->rf_ctrl->policy) { case S_RF_ON: return OSMO_BSC_RF_POLICY_ON; case S_RF_OFF: return OSMO_BSC_RF_POLICY_OFF; case S_RF_GRACE: return OSMO_BSC_RF_POLICY_GRACE; default: return OSMO_BSC_RF_POLICY_UNKNOWN; } } static int lock_each_trx(struct gsm_network *net, int lock) { struct gsm_bts *bts; llist_for_each_entry(bts, &net->bts_list, list) { struct gsm_bts_trx *trx; /* Exclude the BTS from the global lock */ if (bts->excl_from_rf_lock) { LOGP(DLINP, LOGL_DEBUG, "Excluding BTS(%d) from trx lock.\n", bts->nr); continue; } llist_for_each_entry(trx, &bts->trx_list, list) { gsm_trx_lock_rf(trx, lock); } } return 0; } static void send_resp(struct osmo_bsc_rf_conn *conn, char send) { struct msgb *msg; msg = msgb_alloc(10, "RF Query"); if (!msg) { LOGP(DLINP, LOGL_ERROR, "Failed to allocate response msg.\n"); return; } msg->l2h = msgb_put(msg, 1); msg->l2h[0] = send; if (osmo_wqueue_enqueue(&conn->queue, msg) != 0) { LOGP(DLINP, LOGL_ERROR, "Failed to enqueue the answer.\n"); msgb_free(msg); return; } return; } /* * Send a * 'g' when we are in grace mode * '1' when one TRX is online, * '0' otherwise */ static void handle_query(struct osmo_bsc_rf_conn *conn) { struct gsm_bts *bts; char send = RF_CMD_OFF; if (conn->rf->policy == S_RF_GRACE) return send_resp(conn, RF_CMD_ON_G); llist_for_each_entry(bts, &conn->rf->gsm_network->bts_list, list) { struct gsm_bts_trx *trx; /* Exclude the BTS from the global lock */ if (bts->excl_from_rf_lock) { LOGP(DLINP, LOGL_DEBUG, "Excluding BTS(%d) from query.\n", bts->nr); continue; } llist_for_each_entry(trx, &bts->trx_list, list) { if (trx->mo.nm_state.availability == NM_AVSTATE_OK && trx->mo.nm_state.operational != NM_OPSTATE_DISABLED) { send = RF_CMD_ON; break; } } } send_resp(conn, send); } static void rf_check_cb(void *_data) { struct gsm_bts *bts; struct osmo_bsc_rf *rf = _data; llist_for_each_entry(bts, &rf->gsm_network->bts_list, list) { struct gsm_bts_trx *trx; /* don't bother to check a booting or missing BTS */ if (!bts->oml_link || !is_ipaccess_bts(bts)) continue; /* Exclude the BTS from the global lock */ if (bts->excl_from_rf_lock) { LOGP(DLINP, LOGL_DEBUG, "Excluding BTS(%d) from query.\n", bts->nr); continue; } llist_for_each_entry(trx, &bts->trx_list, list) { if (trx->mo.nm_state.availability != NM_AVSTATE_OK || trx->mo.nm_state.operational != NM_OPSTATE_ENABLED || trx->mo.nm_state.administrative != NM_STATE_UNLOCKED) { LOGP(DNM, LOGL_ERROR, "RF activation failed. Starting again.\n"); ipaccess_drop_oml(bts); break; } } } } static void send_signal(struct osmo_bsc_rf *rf, int val) { struct rf_signal_data sig; sig.net = rf->gsm_network; rf->policy = val; osmo_signal_dispatch(SS_RF, val, &sig); } static int switch_rf_off(struct osmo_bsc_rf *rf) { lock_each_trx(rf->gsm_network, 1); send_signal(rf, S_RF_OFF); return 0; } static void grace_timeout(void *_data) { struct osmo_bsc_rf *rf = (struct osmo_bsc_rf *) _data; LOGP(DLINP, LOGL_NOTICE, "Grace timeout. Going to disable all BTS/TRX.\n"); switch_rf_off(rf); } static int enter_grace(struct osmo_bsc_rf *rf) { if (osmo_timer_pending(&rf->grace_timeout)) { LOGP(DLINP, LOGL_NOTICE, "RF Grace timer is pending. Not restarting.\n"); return 0; } rf->grace_timeout.cb = grace_timeout; rf->grace_timeout.data = rf; osmo_timer_schedule(&rf->grace_timeout, rf->gsm_network->bsc_data->mid_call_timeout, 0); LOGP(DLINP, LOGL_NOTICE, "Going to switch RF off in %d seconds.\n", rf->gsm_network->bsc_data->mid_call_timeout); send_signal(rf, S_RF_GRACE); return 0; } static void rf_delay_cmd_cb(void *data) { struct osmo_bsc_rf *rf = data; switch (rf->last_request) { case RF_CMD_D_OFF: rf->last_state_command = "RF Direct Off"; osmo_timer_del(&rf->rf_check); osmo_timer_del(&rf->grace_timeout); switch_rf_off(rf); break; case RF_CMD_ON: rf->last_state_command = "RF Direct On"; osmo_timer_del(&rf->grace_timeout); lock_each_trx(rf->gsm_network, 0); send_signal(rf, S_RF_ON); osmo_timer_schedule(&rf->rf_check, 3, 0); break; case RF_CMD_OFF: rf->last_state_command = "RF Scheduled Off"; osmo_timer_del(&rf->rf_check); enter_grace(rf); break; } } static int rf_read_cmd(struct osmo_fd *fd) { struct osmo_bsc_rf_conn *conn = fd->data; char buf[1]; int rc; rc = read(fd->fd, buf, sizeof(buf)); if (rc != sizeof(buf)) { LOGP(DLINP, LOGL_ERROR, "Short read %d/%s\n", errno, strerror(errno)); osmo_fd_unregister(fd); close(fd->fd); osmo_wqueue_clear(&conn->queue); talloc_free(conn); return -1; } switch (buf[0]) { case RF_CMD_QUERY: handle_query(conn); break; case RF_CMD_D_OFF: case RF_CMD_ON: case RF_CMD_OFF: osmo_bsc_rf_schedule_lock(conn->rf, buf[0]); break; default: conn->rf->last_state_command = "Unknown command"; LOGP(DLINP, LOGL_ERROR, "Unknown command %d\n", buf[0]); break; } return 0; } static int rf_write_cmd(struct osmo_fd *fd, struct msgb *msg) { int rc; rc = write(fd->fd, msg->data, msg->len); if (rc != msg->len) { LOGP(DLINP, LOGL_ERROR, "Short write %d/%s\n", errno, strerror(errno)); return -1; } return 0; } static int rf_ctrl_accept(struct osmo_fd *bfd, unsigned int what) { struct osmo_bsc_rf_conn *conn; struct osmo_bsc_rf *rf = bfd->data; struct sockaddr_un addr; socklen_t len = sizeof(addr); int fd; fd = accept(bfd->fd, (struct sockaddr *) &addr, &len); if (fd < 0) { LOGP(DLINP, LOGL_ERROR, "Failed to accept. errno: %d/%s\n", errno, strerror(errno)); return -1; } conn = talloc_zero(rf, struct osmo_bsc_rf_conn); if (!conn) { LOGP(DLINP, LOGL_ERROR, "Failed to allocate mem.\n"); close(fd); return -1; } osmo_wqueue_init(&conn->queue, 10); conn->queue.bfd.data = conn; conn->queue.bfd.fd = fd; conn->queue.bfd.when = BSC_FD_READ | BSC_FD_WRITE; conn->queue.read_cb = rf_read_cmd; conn->queue.write_cb = rf_write_cmd; conn->rf = rf; if (osmo_fd_register(&conn->queue.bfd) != 0) { close(fd); talloc_free(conn); return -1; } return 0; } static void rf_auto_off_cb(void *_timer) { struct osmo_bsc_rf *rf = _timer; LOGP(DLINP, LOGL_NOTICE, "Going to switch off RF due lack of a MSC connection.\n"); osmo_bsc_rf_schedule_lock(rf, RF_CMD_D_OFF); } static int msc_signal_handler(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct gsm_network *net; struct msc_signal_data *msc; struct osmo_bsc_rf *rf; /* check if we want to handle this signal */ if (subsys != SS_MSC) return 0; net = handler_data; msc = signal_data; /* check if we have the needed information */ if (!net->bsc_data) return 0; if (msc->data->type != MSC_CON_TYPE_NORMAL) return 0; rf = net->bsc_data->rf_ctrl; switch (signal) { case S_MSC_LOST: if (net->bsc_data->auto_off_timeout < 0) return 0; if (osmo_timer_pending(&rf->auto_off_timer)) return 0; osmo_timer_schedule(&rf->auto_off_timer, net->bsc_data->auto_off_timeout, 0); break; case S_MSC_CONNECTED: osmo_timer_del(&rf->auto_off_timer); break; } return 0; } static int rf_create_socket(struct osmo_bsc_rf *rf, const char *path) { unsigned int namelen; struct sockaddr_un local; struct osmo_fd *bfd; int rc; bfd = &rf->listen; bfd->fd = socket(AF_UNIX, SOCK_STREAM, 0); if (bfd->fd < 0) { LOGP(DLINP, LOGL_ERROR, "Can not create socket. %d/%s\n", errno, strerror(errno)); return -1; } local.sun_family = AF_UNIX; strncpy(local.sun_path, path, sizeof(local.sun_path)); local.sun_path[sizeof(local.sun_path) - 1] = '\0'; unlink(local.sun_path); /* we use the same magic that X11 uses in Xtranssock.c for * calculating the proper length of the sockaddr */ #if defined(BSD44SOCKETS) || defined(__UNIXWARE__) local.sun_len = strlen(local.sun_path); #endif #if defined(BSD44SOCKETS) || defined(SUN_LEN) namelen = SUN_LEN(&local); #else namelen = strlen(local.sun_path) + offsetof(struct sockaddr_un, sun_path); #endif rc = bind(bfd->fd, (struct sockaddr *) &local, namelen); if (rc != 0) { LOGP(DLINP, LOGL_ERROR, "Failed to bind '%s' errno: %d/%s\n", local.sun_path, errno, strerror(errno)); close(bfd->fd); return -1; } if (listen(bfd->fd, 0) != 0) { LOGP(DLINP, LOGL_ERROR, "Failed to listen: %d/%s\n", errno, strerror(errno)); close(bfd->fd); return -1; } bfd->when = BSC_FD_READ; bfd->cb = rf_ctrl_accept; bfd->data = rf; if (osmo_fd_register(bfd) != 0) { LOGP(DLINP, LOGL_ERROR, "Failed to register bfd.\n"); close(bfd->fd); return -1; } return 0; } struct osmo_bsc_rf *osmo_bsc_rf_create(const char *path, struct gsm_network *net) { struct osmo_bsc_rf *rf; rf = talloc_zero(NULL, struct osmo_bsc_rf); if (!rf) { LOGP(DLINP, LOGL_ERROR, "Failed to create osmo_bsc_rf.\n"); return NULL; } if (path && rf_create_socket(rf, path) != 0) { talloc_free(rf); return NULL; } rf->gsm_network = net; rf->policy = S_RF_ON; rf->last_state_command = ""; rf->last_rf_lock_ctrl_command = talloc_strdup(rf, ""); /* check the rf state */ rf->rf_check.data = rf; rf->rf_check.cb = rf_check_cb; /* delay cmd handling */ rf->delay_cmd.data = rf; rf->delay_cmd.cb = rf_delay_cmd_cb; rf->auto_off_timer.data = rf; rf->auto_off_timer.cb = rf_auto_off_cb; /* listen to RF signals */ osmo_signal_register_handler(SS_MSC, msc_signal_handler, net); return rf; } void osmo_bsc_rf_schedule_lock(struct osmo_bsc_rf *rf, char cmd) { rf->last_request = cmd; if (!osmo_timer_pending(&rf->delay_cmd)) osmo_timer_schedule(&rf->delay_cmd, 1, 0); } openbsc-0.15.0/openbsc/src/libbsc/bsc_rll.c000066400000000000000000000073541265565154000205120ustar00rootroot00000000000000/* GSM BSC Radio Link Layer API * 3GPP TS 08.58 version 8.6.0 Release 1999 / ETSI TS 100 596 V8.6.0 */ /* (C) 2009 by Harald Welte * * 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 . * */ #include #include #include #include #include #include #include #include #include #include struct bsc_rll_req { struct llist_head list; struct osmo_timer_list timer; struct gsm_lchan *lchan; uint8_t link_id; void (*cb)(struct gsm_lchan *lchan, uint8_t link_id, void *data, enum bsc_rllr_ind); void *data; }; /* we only compare C1, C2 and SAPI */ #define LINKID_MASK 0xC7 static LLIST_HEAD(bsc_rll_reqs); static void complete_rllr(struct bsc_rll_req *rllr, enum bsc_rllr_ind type) { llist_del(&rllr->list); rllr->cb(rllr->lchan, rllr->link_id, rllr->data, type); talloc_free(rllr); } static void timer_cb(void *_rllr) { struct bsc_rll_req *rllr = _rllr; complete_rllr(rllr, BSC_RLLR_IND_TIMEOUT); } /* establish a RLL connection with given SAPI / priority */ int rll_establish(struct gsm_lchan *lchan, uint8_t sapi, void (*cb)(struct gsm_lchan *, uint8_t, void *, enum bsc_rllr_ind), void *data) { struct bsc_rll_req *rllr = talloc_zero(tall_bsc_ctx, struct bsc_rll_req); uint8_t link_id; if (!rllr) return -ENOMEM; link_id = sapi; /* If we are a TCH and not in signalling mode, we need to * indicate that the new RLL connection is to be made on the SACCH */ if ((lchan->type == GSM_LCHAN_TCH_F || lchan->type == GSM_LCHAN_TCH_H) && sapi != 0) link_id |= 0x40; rllr->lchan = lchan; rllr->link_id = link_id; rllr->cb = cb; rllr->data = data; llist_add(&rllr->list, &bsc_rll_reqs); rllr->timer.cb = &timer_cb; rllr->timer.data = rllr; osmo_timer_schedule(&rllr->timer, 7, 0); /* send the RSL RLL ESTablish REQuest */ return rsl_establish_request(rllr->lchan, rllr->link_id); } /* Called from RSL code in case we have received an indication regarding * any RLL link */ void rll_indication(struct gsm_lchan *lchan, uint8_t link_id, uint8_t type) { struct bsc_rll_req *rllr, *rllr2; llist_for_each_entry_safe(rllr, rllr2, &bsc_rll_reqs, list) { if (rllr->lchan == lchan && (rllr->link_id & LINKID_MASK) == (link_id & LINKID_MASK)) { osmo_timer_del(&rllr->timer); complete_rllr(rllr, type); return; } } } static int rll_lchan_signal(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct challoc_signal_data *challoc; struct bsc_rll_req *rllr, *rllr2; if (subsys != SS_CHALLOC || signal != S_CHALLOC_FREED) return 0; challoc = (struct challoc_signal_data *) signal_data; llist_for_each_entry_safe(rllr, rllr2, &bsc_rll_reqs, list) { if (rllr->lchan == challoc->lchan) { osmo_timer_del(&rllr->timer); complete_rllr(rllr, BSC_RLLR_IND_ERR_IND); } } return 0; } static __attribute__((constructor)) void on_dso_load_rll(void) { osmo_signal_register_handler(SS_CHALLOC, rll_lchan_signal, NULL); } openbsc-0.15.0/openbsc/src/libbsc/bsc_vty.c000066400000000000000000003346331265565154000205460ustar00rootroot00000000000000/* OpenBSC interface to quagga VTY */ /* (C) 2009-2010 by Harald Welte * 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../../bscconfig.h" #define NETWORK_STR "Configure the GSM network\n" #define CODE_CMD_STR "Code commands\n" #define NAME_CMD_STR "Name Commands\n" #define NAME_STR "Name to use\n" #define LCHAN_NR_STR "Logical Channel Number\n" /* FIXME: this should go to some common file */ static const struct value_string gprs_ns_timer_strs[] = { { 0, "tns-block" }, { 1, "tns-block-retries" }, { 2, "tns-reset" }, { 3, "tns-reset-retries" }, { 4, "tns-test" }, { 5, "tns-alive" }, { 6, "tns-alive-retries" }, { 0, NULL } }; static const struct value_string gprs_bssgp_cfg_strs[] = { { 0, "blocking-timer" }, { 1, "blocking-retries" }, { 2, "unblocking-retries" }, { 3, "reset-timer" }, { 4, "reset-retries" }, { 5, "suspend-timer" }, { 6, "suspend-retries" }, { 7, "resume-timer" }, { 8, "resume-retries" }, { 9, "capability-update-timer" }, { 10, "capability-update-retries" }, { 0, NULL } }; static const struct value_string bts_neigh_mode_strs[] = { { NL_MODE_AUTOMATIC, "automatic" }, { NL_MODE_MANUAL, "manual" }, { NL_MODE_MANUAL_SI5SEP, "manual-si5" }, { 0, NULL } }; const struct value_string bts_loc_fix_names[] = { { BTS_LOC_FIX_INVALID, "invalid" }, { BTS_LOC_FIX_2D, "fix2d" }, { BTS_LOC_FIX_3D, "fix3d" }, { 0, NULL } }; struct cmd_node net_node = { GSMNET_NODE, "%s(config-net)# ", 1, }; struct cmd_node bts_node = { BTS_NODE, "%s(config-net-bts)# ", 1, }; struct cmd_node trx_node = { TRX_NODE, "%s(config-net-bts-trx)# ", 1, }; struct cmd_node ts_node = { TS_NODE, "%s(config-net-bts-trx-ts)# ", 1, }; extern struct gsm_network *bsc_gsmnet; struct gsm_network *gsmnet_from_vty(struct vty *v) { /* In case we read from the config file, the vty->priv cannot * point to a struct telnet_connection, and thus conn->priv * will not point to the gsm_network structure */ #if 0 struct telnet_connection *conn = v->priv; return (struct gsm_network *) conn->priv; #else return bsc_gsmnet; #endif } static int dummy_config_write(struct vty *v) { return CMD_SUCCESS; } static void net_dump_nmstate(struct vty *vty, struct gsm_nm_state *nms) { vty_out(vty,"Oper '%s', Admin '%s', Avail '%s'%s", abis_nm_opstate_name(nms->operational), get_value_string(abis_nm_adm_state_names, nms->administrative), abis_nm_avail_name(nms->availability), VTY_NEWLINE); } static void dump_pchan_load_vty(struct vty *vty, char *prefix, const struct pchan_load *pl) { int i; for (i = 0; i < ARRAY_SIZE(pl->pchan); i++) { const struct load_counter *lc = &pl->pchan[i]; unsigned int percent; if (lc->total == 0) continue; percent = (lc->used * 100) / lc->total; vty_out(vty, "%s%20s: %3u%% (%u/%u)%s", prefix, gsm_pchan_name(i), percent, lc->used, lc->total, VTY_NEWLINE); } } static void net_dump_vty(struct vty *vty, struct gsm_network *net) { struct pchan_load pl; vty_out(vty, "BSC is on Country Code %u, Network Code %u " "and has %u BTS%s", net->country_code, net->network_code, net->num_bts, VTY_NEWLINE); vty_out(vty, " Long network name: '%s'%s", net->name_long, VTY_NEWLINE); vty_out(vty, " Short network name: '%s'%s", net->name_short, VTY_NEWLINE); vty_out(vty, " Authentication policy: %s%s", gsm_auth_policy_name(net->auth_policy), VTY_NEWLINE); vty_out(vty, " Location updating reject cause: %u%s", net->reject_cause, VTY_NEWLINE); vty_out(vty, " Encryption: A5/%u%s", net->a5_encryption, VTY_NEWLINE); vty_out(vty, " NECI (TCH/H): %u%s", net->neci, VTY_NEWLINE); vty_out(vty, " Use TCH for Paging any: %d%s", net->pag_any_tch, VTY_NEWLINE); vty_out(vty, " RRLP Mode: %s%s", rrlp_mode_name(net->rrlp.mode), VTY_NEWLINE); vty_out(vty, " MM Info: %s%s", net->send_mm_info ? "On" : "Off", VTY_NEWLINE); vty_out(vty, " Handover: %s%s", net->handover.active ? "On" : "Off", VTY_NEWLINE); network_chan_load(&pl, net); vty_out(vty, " Current Channel Load:%s", VTY_NEWLINE); dump_pchan_load_vty(vty, " ", &pl); /* show rf */ if (net->bsc_data) vty_out(vty, " Last RF Command: %s%s", net->bsc_data->rf_ctrl->last_state_command, VTY_NEWLINE); if (net->bsc_data) vty_out(vty, " Last RF Lock Command: %s%s", net->bsc_data->rf_ctrl->last_rf_lock_ctrl_command, VTY_NEWLINE); } DEFUN(show_net, show_net_cmd, "show network", SHOW_STR "Display information about a GSM NETWORK\n") { struct gsm_network *net = gsmnet_from_vty(vty); net_dump_vty(vty, net); return CMD_SUCCESS; } static void e1isl_dump_vty(struct vty *vty, struct e1inp_sign_link *e1l) { struct e1inp_line *line; if (!e1l) { vty_out(vty, " None%s", VTY_NEWLINE); return; } line = e1l->ts->line; vty_out(vty, " E1 Line %u, Type %s: Timeslot %u, Mode %s%s", line->num, line->driver->name, e1l->ts->num, e1inp_signtype_name(e1l->type), VTY_NEWLINE); vty_out(vty, " E1 TEI %u, SAPI %u%s", e1l->tei, e1l->sapi, VTY_NEWLINE); } static void bts_dump_vty(struct vty *vty, struct gsm_bts *bts) { struct pchan_load pl; vty_out(vty, "BTS %u is of %s type in band %s, has CI %u LAC %u, " "BSIC %u, TSC %u and %u TRX%s", bts->nr, btstype2str(bts->type), gsm_band_name(bts->band), bts->cell_identity, bts->location_area_code, bts->bsic, bts->tsc, bts->num_trx, VTY_NEWLINE); vty_out(vty, "Description: %s%s", bts->description ? bts->description : "(null)", VTY_NEWLINE); vty_out(vty, "MS Max power: %u dBm%s", bts->ms_max_power, VTY_NEWLINE); vty_out(vty, "Minimum Rx Level for Access: %i dBm%s", rxlev2dbm(bts->si_common.cell_sel_par.rxlev_acc_min), VTY_NEWLINE); vty_out(vty, "Cell Reselection Hysteresis: %u dBm%s", bts->si_common.cell_sel_par.cell_resel_hyst*2, VTY_NEWLINE); vty_out(vty, "RACH TX-Integer: %u%s", bts->si_common.rach_control.tx_integer, VTY_NEWLINE); vty_out(vty, "RACH Max transmissions: %u%s", rach_max_trans_raw2val(bts->si_common.rach_control.max_trans), VTY_NEWLINE); if (bts->si_common.rach_control.cell_bar) vty_out(vty, " CELL IS BARRED%s", VTY_NEWLINE); vty_out(vty, "Channel Description Attachment: %s%s", (bts->si_common.chan_desc.att) ? "yes" : "no", VTY_NEWLINE); vty_out(vty, "Channel Description BS-PA-MFRMS: %u%s", bts->si_common.chan_desc.bs_pa_mfrms + 2, VTY_NEWLINE); vty_out(vty, "Channel Description BS-AG_BLKS-RES: %u%s", bts->si_common.chan_desc.bs_ag_blks_res, VTY_NEWLINE); vty_out(vty, "System Information present: 0x%08x, static: 0x%08x%s", bts->si_valid, bts->si_mode_static, VTY_NEWLINE); if (is_ipaccess_bts(bts)) vty_out(vty, " Unit ID: %u/%u/0, OML Stream ID 0x%02x%s", bts->ip_access.site_id, bts->ip_access.bts_id, bts->oml_tei, VTY_NEWLINE); else if (bts->type == GSM_BTS_TYPE_NOKIA_SITE) vty_out(vty, " Skip Reset: %d%s", bts->nokia.skip_reset, VTY_NEWLINE); vty_out(vty, " NM State: "); net_dump_nmstate(vty, &bts->mo.nm_state); vty_out(vty, " Site Mgr NM State: "); net_dump_nmstate(vty, &bts->site_mgr.mo.nm_state); vty_out(vty, " GPRS NSE: "); net_dump_nmstate(vty, &bts->gprs.nse.mo.nm_state); vty_out(vty, " GPRS CELL: "); net_dump_nmstate(vty, &bts->gprs.cell.mo.nm_state); vty_out(vty, " GPRS NSVC0: "); net_dump_nmstate(vty, &bts->gprs.nsvc[0].mo.nm_state); vty_out(vty, " GPRS NSVC1: "); net_dump_nmstate(vty, &bts->gprs.nsvc[1].mo.nm_state); vty_out(vty, " Paging: %u pending requests, %u free slots%s", paging_pending_requests_nr(bts), bts->paging.available_slots, VTY_NEWLINE); if (is_ipaccess_bts(bts)) { vty_out(vty, " OML Link state: %s.%s", bts->oml_link ? "connected" : "disconnected", VTY_NEWLINE); } else { vty_out(vty, " E1 Signalling Link:%s", VTY_NEWLINE); e1isl_dump_vty(vty, bts->oml_link); } /* FIXME: chan_desc */ memset(&pl, 0, sizeof(pl)); bts_chan_load(&pl, bts); vty_out(vty, " Current Channel Load:%s", VTY_NEWLINE); dump_pchan_load_vty(vty, " ", &pl); } DEFUN(show_bts, show_bts_cmd, "show bts [<0-255>]", SHOW_STR "Display information about a BTS\n" "BTS number") { struct gsm_network *net = gsmnet_from_vty(vty); int bts_nr; if (argc != 0) { /* use the BTS number that the user has specified */ bts_nr = atoi(argv[0]); if (bts_nr >= net->num_bts) { vty_out(vty, "%% can't find BTS '%s'%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } bts_dump_vty(vty, gsm_bts_num(net, bts_nr)); return CMD_SUCCESS; } /* print all BTS's */ for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) bts_dump_vty(vty, gsm_bts_num(net, bts_nr)); return CMD_SUCCESS; } /* utility functions */ static void parse_e1_link(struct gsm_e1_subslot *e1_link, const char *line, const char *ts, const char *ss) { e1_link->e1_nr = atoi(line); e1_link->e1_ts = atoi(ts); if (!strcmp(ss, "full")) e1_link->e1_ts_ss = 255; else e1_link->e1_ts_ss = atoi(ss); } static void config_write_e1_link(struct vty *vty, struct gsm_e1_subslot *e1_link, const char *prefix) { if (!e1_link->e1_ts) return; if (e1_link->e1_ts_ss == 255) vty_out(vty, "%se1 line %u timeslot %u sub-slot full%s", prefix, e1_link->e1_nr, e1_link->e1_ts, VTY_NEWLINE); else vty_out(vty, "%se1 line %u timeslot %u sub-slot %u%s", prefix, e1_link->e1_nr, e1_link->e1_ts, e1_link->e1_ts_ss, VTY_NEWLINE); } static void config_write_ts_single(struct vty *vty, struct gsm_bts_trx_ts *ts) { vty_out(vty, " timeslot %u%s", ts->nr, VTY_NEWLINE); if (ts->tsc != -1 && ts->tsc != ts->trx->bts->tsc) vty_out(vty, " training_sequence_code %u%s", ts->tsc, VTY_NEWLINE); if (ts->pchan != GSM_PCHAN_NONE) vty_out(vty, " phys_chan_config %s%s", gsm_pchan_name(ts->pchan), VTY_NEWLINE); vty_out(vty, " hopping enabled %u%s", ts->hopping.enabled, VTY_NEWLINE); if (ts->hopping.enabled) { unsigned int i; vty_out(vty, " hopping sequence-number %u%s", ts->hopping.hsn, VTY_NEWLINE); vty_out(vty, " hopping maio %u%s", ts->hopping.maio, VTY_NEWLINE); for (i = 0; i < ts->hopping.arfcns.data_len*8; i++) { if (!bitvec_get_bit_pos(&ts->hopping.arfcns, i)) continue; vty_out(vty, " hopping arfcn add %u%s", i, VTY_NEWLINE); } } config_write_e1_link(vty, &ts->e1_link, " "); if (ts->trx->bts->model->config_write_ts) ts->trx->bts->model->config_write_ts(vty, ts); } static void config_write_trx_single(struct vty *vty, struct gsm_bts_trx *trx) { int i; vty_out(vty, " trx %u%s", trx->nr, VTY_NEWLINE); if (trx->description) vty_out(vty, " description %s%s", trx->description, VTY_NEWLINE); vty_out(vty, " rf_locked %u%s", trx->mo.nm_state.administrative == NM_STATE_LOCKED ? 1 : 0, VTY_NEWLINE); vty_out(vty, " arfcn %u%s", trx->arfcn, VTY_NEWLINE); vty_out(vty, " nominal power %u%s", trx->nominal_power, VTY_NEWLINE); vty_out(vty, " max_power_red %u%s", trx->max_power_red, VTY_NEWLINE); config_write_e1_link(vty, &trx->rsl_e1_link, " rsl "); vty_out(vty, " rsl e1 tei %u%s", trx->rsl_tei, VTY_NEWLINE); if (trx->bts->model->config_write_trx) trx->bts->model->config_write_trx(vty, trx); for (i = 0; i < TRX_NR_TS; i++) config_write_ts_single(vty, &trx->ts[i]); } static void config_write_bts_gprs(struct vty *vty, struct gsm_bts *bts) { unsigned int i; vty_out(vty, " gprs mode %s%s", bts_gprs_mode_name(bts->gprs.mode), VTY_NEWLINE); if (bts->gprs.mode == BTS_GPRS_NONE) return; vty_out(vty, " gprs routing area %u%s", bts->gprs.rac, VTY_NEWLINE); vty_out(vty, " gprs network-control-order nc%u%s", bts->gprs.net_ctrl_ord, VTY_NEWLINE); vty_out(vty, " gprs cell bvci %u%s", bts->gprs.cell.bvci, VTY_NEWLINE); for (i = 0; i < ARRAY_SIZE(bts->gprs.cell.timer); i++) vty_out(vty, " gprs cell timer %s %u%s", get_value_string(gprs_bssgp_cfg_strs, i), bts->gprs.cell.timer[i], VTY_NEWLINE); vty_out(vty, " gprs nsei %u%s", bts->gprs.nse.nsei, VTY_NEWLINE); for (i = 0; i < ARRAY_SIZE(bts->gprs.nse.timer); i++) vty_out(vty, " gprs ns timer %s %u%s", get_value_string(gprs_ns_timer_strs, i), bts->gprs.nse.timer[i], VTY_NEWLINE); for (i = 0; i < ARRAY_SIZE(bts->gprs.nsvc); i++) { struct gsm_bts_gprs_nsvc *nsvc = &bts->gprs.nsvc[i]; struct in_addr ia; ia.s_addr = htonl(nsvc->remote_ip); vty_out(vty, " gprs nsvc %u nsvci %u%s", i, nsvc->nsvci, VTY_NEWLINE); vty_out(vty, " gprs nsvc %u local udp port %u%s", i, nsvc->local_port, VTY_NEWLINE); vty_out(vty, " gprs nsvc %u remote udp port %u%s", i, nsvc->remote_port, VTY_NEWLINE); vty_out(vty, " gprs nsvc %u remote ip %s%s", i, inet_ntoa(ia), VTY_NEWLINE); } } /* Write the model data if there is one */ static void config_write_bts_model(struct vty *vty, struct gsm_bts *bts) { struct gsm_bts_trx *trx; if (!bts->model) return; if (bts->model->config_write_bts) bts->model->config_write_bts(vty, bts); llist_for_each_entry(trx, &bts->trx_list, list) config_write_trx_single(vty, trx); } static void write_amr_modes(struct vty *vty, const char *prefix, const char *name, struct amr_mode *modes, int num) { int i; vty_out(vty, " %s threshold %s", prefix, name); for (i = 0; i < num - 1; i++) vty_out(vty, " %d", modes[i].threshold); vty_out(vty, "%s", VTY_NEWLINE); vty_out(vty, " %s hysteresis %s", prefix, name); for (i = 0; i < num - 1; i++) vty_out(vty, " %d", modes[i].hysteresis); vty_out(vty, "%s", VTY_NEWLINE); } static void config_write_bts_amr(struct vty *vty, struct gsm_bts *bts, struct amr_multirate_conf *mr, int full) { struct gsm48_multi_rate_conf *mr_conf; const char *prefix = (full) ? "amr tch-f" : "amr tch-h"; int i, num; if (!(mr->gsm48_ie[1])) return; mr_conf = (struct gsm48_multi_rate_conf *) mr->gsm48_ie; num = 0; vty_out(vty, " %s modes", prefix); for (i = 0; i < ((full) ? 8 : 6); i++) { if ((mr->gsm48_ie[1] & (1 << i))) { vty_out(vty, " %d", i); num++; } } vty_out(vty, "%s", VTY_NEWLINE); if (num > 4) num = 4; if (num > 1) { write_amr_modes(vty, prefix, "ms", mr->ms_mode, num); write_amr_modes(vty, prefix, "bts", mr->bts_mode, num); } vty_out(vty, " %s start-mode ", prefix); if (mr_conf->icmi) { num = 0; for (i = 0; i < ((full) ? 8 : 6) && num < 4; i++) { if ((mr->gsm48_ie[1] & (1 << i))) num++; if (mr_conf->smod == num - 1) { vty_out(vty, "%d%s", num, VTY_NEWLINE); break; } } } else vty_out(vty, "auto%s", VTY_NEWLINE); } static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts) { int i; vty_out(vty, " bts %u%s", bts->nr, VTY_NEWLINE); vty_out(vty, " type %s%s", btstype2str(bts->type), VTY_NEWLINE); if (bts->description) vty_out(vty, " description %s%s", bts->description, VTY_NEWLINE); vty_out(vty, " band %s%s", gsm_band_name(bts->band), VTY_NEWLINE); vty_out(vty, " cell_identity %u%s", bts->cell_identity, VTY_NEWLINE); vty_out(vty, " location_area_code %u%s", bts->location_area_code, VTY_NEWLINE); vty_out(vty, " base_station_id_code %u%s", bts->bsic, VTY_NEWLINE); if (bts->tsc != (bts->bsic & 7)) vty_out(vty, " training_sequence_code %u%s", bts->tsc, VTY_NEWLINE); if (bts->tz.override != 0) { if (bts->tz.dst) vty_out(vty, " timezone %d %d %d%s", bts->tz.hr, bts->tz.mn, bts->tz.dst, VTY_NEWLINE); else vty_out(vty, " timezone %d %d%s", bts->tz.hr, bts->tz.mn, VTY_NEWLINE); } vty_out(vty, " ms max power %u%s", bts->ms_max_power, VTY_NEWLINE); vty_out(vty, " cell reselection hysteresis %u%s", bts->si_common.cell_sel_par.cell_resel_hyst*2, VTY_NEWLINE); vty_out(vty, " rxlev access min %u%s", bts->si_common.cell_sel_par.rxlev_acc_min, VTY_NEWLINE); if (bts->si_common.cell_ro_sel_par.present) { struct gsm48_si_selection_params *sp; sp = &bts->si_common.cell_ro_sel_par; if (sp->cbq) vty_out(vty, " cell bar qualify %u%s", sp->cbq, VTY_NEWLINE); if (sp->cell_resel_off) vty_out(vty, " cell reselection offset %u%s", sp->cell_resel_off*2, VTY_NEWLINE); if (sp->temp_offs == 7) vty_out(vty, " temporary offset infinite%s", VTY_NEWLINE); else if (sp->temp_offs) vty_out(vty, " temporary offset %u%s", sp->temp_offs*10, VTY_NEWLINE); if (sp->penalty_time == 31) vty_out(vty, " penalty time reserved%s", VTY_NEWLINE); else if (sp->penalty_time) vty_out(vty, " penalty time %u%s", (sp->penalty_time*20)+20, VTY_NEWLINE); } /* Is periodic LU enabled or disabled? */ if (bts->si_common.chan_desc.t3212 == 0) vty_out(vty, " no periodic location update%s", VTY_NEWLINE); else vty_out(vty, " periodic location update %u%s", bts->si_common.chan_desc.t3212 * 6, VTY_NEWLINE); vty_out(vty, " radio-link-timeout %d%s", get_radio_link_timeout(&bts->si_common.cell_options), VTY_NEWLINE); vty_out(vty, " channel allocator %s%s", bts->chan_alloc_reverse ? "descending" : "ascending", VTY_NEWLINE); vty_out(vty, " rach tx integer %u%s", bts->si_common.rach_control.tx_integer, VTY_NEWLINE); vty_out(vty, " rach max transmission %u%s", rach_max_trans_raw2val(bts->si_common.rach_control.max_trans), VTY_NEWLINE); vty_out(vty, " channel-descrption attach %u%s", bts->si_common.chan_desc.att, VTY_NEWLINE); vty_out(vty, " channel-descrption bs-pa-mfrms %u%s", bts->si_common.chan_desc.bs_pa_mfrms + 2, VTY_NEWLINE); vty_out(vty, " channel-descrption bs-ag-blks-res %u%s", bts->si_common.chan_desc.bs_ag_blks_res, VTY_NEWLINE); if (bts->rach_b_thresh != -1) vty_out(vty, " rach nm busy threshold %u%s", bts->rach_b_thresh, VTY_NEWLINE); if (bts->rach_ldavg_slots != -1) vty_out(vty, " rach nm load average %u%s", bts->rach_ldavg_slots, VTY_NEWLINE); if (bts->si_common.rach_control.cell_bar) vty_out(vty, " cell barred 1%s", VTY_NEWLINE); if ((bts->si_common.rach_control.t2 & 0x4) == 0) vty_out(vty, " rach emergency call allowed 1%s", VTY_NEWLINE); if ((bts->si_common.rach_control.t3) != 0) for (i = 0; i < 8; i++) if (bts->si_common.rach_control.t3 & (0x1 << i)) vty_out(vty, " rach access-control-class %d barred%s", i, VTY_NEWLINE); if ((bts->si_common.rach_control.t2 & 0xfb) != 0) for (i = 0; i < 8; i++) if ((i != 2) && (bts->si_common.rach_control.t2 & (0x1 << i))) vty_out(vty, " rach access-control-class %d barred%s", i+8, VTY_NEWLINE); for (i = SYSINFO_TYPE_1; i < _MAX_SYSINFO_TYPE; i++) { if (bts->si_mode_static & (1 << i)) { vty_out(vty, " system-information %s mode static%s", get_value_string(osmo_sitype_strs, i), VTY_NEWLINE); vty_out(vty, " system-information %s static %s%s", get_value_string(osmo_sitype_strs, i), osmo_hexdump_nospc(bts->si_buf[i], sizeof(bts->si_buf[i])), VTY_NEWLINE); } } switch (bts->type) { case GSM_BTS_TYPE_NANOBTS: case GSM_BTS_TYPE_OSMO_SYSMO: vty_out(vty, " ip.access unit_id %u %u%s", bts->ip_access.site_id, bts->ip_access.bts_id, VTY_NEWLINE); if (bts->ip_access.rsl_ip) { struct in_addr ia; ia.s_addr = htonl(bts->ip_access.rsl_ip); vty_out(vty, " ip.access rsl-ip %s%s", inet_ntoa(ia), VTY_NEWLINE); } vty_out(vty, " oml ip.access stream_id %u line %u%s", bts->oml_tei, bts->oml_e1_link.e1_nr, VTY_NEWLINE); break; case GSM_BTS_TYPE_NOKIA_SITE: vty_out(vty, " nokia_site skip-reset %d%s", bts->nokia.skip_reset, VTY_NEWLINE); vty_out(vty, " nokia_site no-local-rel-conf %d%s", bts->nokia.no_loc_rel_cnf, VTY_NEWLINE); vty_out(vty, " nokia_site bts-reset-timer %d%s", bts->nokia.bts_reset_timer_cnf, VTY_NEWLINE); /* fall through: Nokia requires "oml e1" parameters also */ default: config_write_e1_link(vty, &bts->oml_e1_link, " oml "); vty_out(vty, " oml e1 tei %u%s", bts->oml_tei, VTY_NEWLINE); break; } /* if we have a limit, write it */ if (bts->paging.free_chans_need >= 0) vty_out(vty, " paging free %d%s", bts->paging.free_chans_need, VTY_NEWLINE); vty_out(vty, " neighbor-list mode %s%s", get_value_string(bts_neigh_mode_strs, bts->neigh_list_manual_mode), VTY_NEWLINE); if (bts->neigh_list_manual_mode != NL_MODE_AUTOMATIC) { for (i = 0; i < 1024; i++) { if (bitvec_get_bit_pos(&bts->si_common.neigh_list, i)) vty_out(vty, " neighbor-list add arfcn %u%s", i, VTY_NEWLINE); } } if (bts->neigh_list_manual_mode == NL_MODE_MANUAL_SI5SEP) { for (i = 0; i < 1024; i++) { if (bitvec_get_bit_pos(&bts->si_common.si5_neigh_list, i)) vty_out(vty, " si5 neighbor-list add arfcn %u%s", i, VTY_NEWLINE); } } vty_out(vty, " codec-support fr"); if (bts->codec.hr) vty_out(vty, " hr"); if (bts->codec.efr) vty_out(vty, " efr"); if (bts->codec.amr) vty_out(vty, " amr"); vty_out(vty, "%s", VTY_NEWLINE); config_write_bts_amr(vty, bts, &bts->mr_full, 1); config_write_bts_amr(vty, bts, &bts->mr_half, 0); config_write_bts_gprs(vty, bts); if (bts->excl_from_rf_lock) vty_out(vty, " rf-lock-exclude%s", VTY_NEWLINE); vty_out(vty, " %sforce-combined-si%s", bts->force_combined_si ? "" : "no ", VTY_NEWLINE); for (i = 0; i < ARRAY_SIZE(bts->depends_on); ++i) { int j; if (bts->depends_on[i] == 0) continue; for (j = 0; j < sizeof(bts->depends_on[i]) * 8; ++j) { int bts_nr; if ((bts->depends_on[i] & (1<depends_on[i]) * 8) + j; vty_out(vty, " depends-on-bts %d%s", bts_nr, VTY_NEWLINE); } } config_write_bts_model(vty, bts); } static int config_write_bts(struct vty *v) { struct gsm_network *gsmnet = gsmnet_from_vty(v); struct gsm_bts *bts; llist_for_each_entry(bts, &gsmnet->bts_list, list) config_write_bts_single(v, bts); return CMD_SUCCESS; } static int config_write_net(struct vty *vty) { struct gsm_network *gsmnet = gsmnet_from_vty(vty); vty_out(vty, "network%s", VTY_NEWLINE); vty_out(vty, " network country code %u%s", gsmnet->country_code, VTY_NEWLINE); vty_out(vty, " mobile network code %u%s", gsmnet->network_code, VTY_NEWLINE); vty_out(vty, " short name %s%s", gsmnet->name_short, VTY_NEWLINE); vty_out(vty, " long name %s%s", gsmnet->name_long, VTY_NEWLINE); vty_out(vty, " auth policy %s%s", gsm_auth_policy_name(gsmnet->auth_policy), VTY_NEWLINE); vty_out(vty, " location updating reject cause %u%s", gsmnet->reject_cause, VTY_NEWLINE); vty_out(vty, " encryption a5 %u%s", gsmnet->a5_encryption, VTY_NEWLINE); vty_out(vty, " neci %u%s", gsmnet->neci, VTY_NEWLINE); vty_out(vty, " paging any use tch %d%s", gsmnet->pag_any_tch, VTY_NEWLINE); vty_out(vty, " rrlp mode %s%s", rrlp_mode_name(gsmnet->rrlp.mode), VTY_NEWLINE); vty_out(vty, " mm info %u%s", gsmnet->send_mm_info, VTY_NEWLINE); vty_out(vty, " handover %u%s", gsmnet->handover.active, VTY_NEWLINE); vty_out(vty, " handover window rxlev averaging %u%s", gsmnet->handover.win_rxlev_avg, VTY_NEWLINE); vty_out(vty, " handover window rxqual averaging %u%s", gsmnet->handover.win_rxqual_avg, VTY_NEWLINE); vty_out(vty, " handover window rxlev neighbor averaging %u%s", gsmnet->handover.win_rxlev_avg_neigh, VTY_NEWLINE); vty_out(vty, " handover power budget interval %u%s", gsmnet->handover.pwr_interval, VTY_NEWLINE); vty_out(vty, " handover power budget hysteresis %u%s", gsmnet->handover.pwr_hysteresis, VTY_NEWLINE); vty_out(vty, " handover maximum distance %u%s", gsmnet->handover.max_distance, VTY_NEWLINE); vty_out(vty, " timer t3101 %u%s", gsmnet->T3101, VTY_NEWLINE); vty_out(vty, " timer t3103 %u%s", gsmnet->T3103, VTY_NEWLINE); vty_out(vty, " timer t3105 %u%s", gsmnet->T3105, VTY_NEWLINE); vty_out(vty, " timer t3107 %u%s", gsmnet->T3107, VTY_NEWLINE); vty_out(vty, " timer t3109 %u%s", gsmnet->T3109, VTY_NEWLINE); vty_out(vty, " timer t3111 %u%s", gsmnet->T3111, VTY_NEWLINE); vty_out(vty, " timer t3113 %u%s", gsmnet->T3113, VTY_NEWLINE); vty_out(vty, " timer t3115 %u%s", gsmnet->T3115, VTY_NEWLINE); vty_out(vty, " timer t3117 %u%s", gsmnet->T3117, VTY_NEWLINE); vty_out(vty, " timer t3119 %u%s", gsmnet->T3119, VTY_NEWLINE); vty_out(vty, " timer t3122 %u%s", gsmnet->T3122, VTY_NEWLINE); vty_out(vty, " timer t3141 %u%s", gsmnet->T3141, VTY_NEWLINE); vty_out(vty, " dtx-used %u%s", gsmnet->dtx_enabled, VTY_NEWLINE); vty_out(vty, " subscriber-keep-in-ram %d%s", gsmnet->subscr_group->keep_subscr, VTY_NEWLINE); return CMD_SUCCESS; } static void trx_dump_vty(struct vty *vty, struct gsm_bts_trx *trx) { vty_out(vty, "TRX %u of BTS %u is on ARFCN %u%s", trx->nr, trx->bts->nr, trx->arfcn, VTY_NEWLINE); vty_out(vty, "Description: %s%s", trx->description ? trx->description : "(null)", VTY_NEWLINE); vty_out(vty, " RF Nominal Power: %d dBm, reduced by %u dB, " "resulting BS power: %d dBm%s", trx->nominal_power, trx->max_power_red, trx->nominal_power - trx->max_power_red, VTY_NEWLINE); vty_out(vty, " NM State: "); net_dump_nmstate(vty, &trx->mo.nm_state); vty_out(vty, " Baseband Transceiver NM State: "); net_dump_nmstate(vty, &trx->bb_transc.mo.nm_state); if (is_ipaccess_bts(trx->bts)) { vty_out(vty, " ip.access stream ID: 0x%02x%s", trx->rsl_tei, VTY_NEWLINE); } else { vty_out(vty, " E1 Signalling Link:%s", VTY_NEWLINE); e1isl_dump_vty(vty, trx->rsl_link); } } DEFUN(show_trx, show_trx_cmd, "show trx [<0-255>] [<0-255>]", SHOW_STR "Display information about a TRX\n" "BTS Number\n" "TRX Number\n") { struct gsm_network *net = gsmnet_from_vty(vty); struct gsm_bts *bts = NULL; struct gsm_bts_trx *trx; int bts_nr, trx_nr; if (argc >= 1) { /* use the BTS number that the user has specified */ bts_nr = atoi(argv[0]); if (bts_nr >= net->num_bts) { vty_out(vty, "%% can't find BTS '%s'%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } bts = gsm_bts_num(net, bts_nr); } if (argc >= 2) { trx_nr = atoi(argv[1]); if (trx_nr >= bts->num_trx) { vty_out(vty, "%% can't find TRX '%s'%s", argv[1], VTY_NEWLINE); return CMD_WARNING; } trx = gsm_bts_trx_num(bts, trx_nr); trx_dump_vty(vty, trx); return CMD_SUCCESS; } if (bts) { /* print all TRX in this BTS */ for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) { trx = gsm_bts_trx_num(bts, trx_nr); trx_dump_vty(vty, trx); } return CMD_SUCCESS; } for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) { bts = gsm_bts_num(net, bts_nr); for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) { trx = gsm_bts_trx_num(bts, trx_nr); trx_dump_vty(vty, trx); } } return CMD_SUCCESS; } static void ts_dump_vty(struct vty *vty, struct gsm_bts_trx_ts *ts) { vty_out(vty, "BTS %u, TRX %u, Timeslot %u, phys cfg %s, TSC %u", ts->trx->bts->nr, ts->trx->nr, ts->nr, gsm_pchan_name(ts->pchan), gsm_ts_tsc(ts)); if (ts->pchan == GSM_PCHAN_TCH_F_PDCH) vty_out(vty, " (%s mode)", ts->flags & TS_F_PDCH_MODE ? "PDCH" : "TCH/F"); vty_out(vty, "%s", VTY_NEWLINE); vty_out(vty, " NM State: "); net_dump_nmstate(vty, &ts->mo.nm_state); if (!is_ipaccess_bts(ts->trx->bts)) vty_out(vty, " E1 Line %u, Timeslot %u, Subslot %u%s", ts->e1_link.e1_nr, ts->e1_link.e1_ts, ts->e1_link.e1_ts_ss, VTY_NEWLINE); } DEFUN(show_ts, show_ts_cmd, "show timeslot [<0-255>] [<0-255>] [<0-7>]", SHOW_STR "Display information about a TS\n" "BTS Number\n" "TRX Number\n" "Timeslot Number\n") { struct gsm_network *net = gsmnet_from_vty(vty); struct gsm_bts *bts = NULL; struct gsm_bts_trx *trx = NULL; struct gsm_bts_trx_ts *ts = NULL; int bts_nr, trx_nr, ts_nr; if (argc >= 1) { /* use the BTS number that the user has specified */ bts_nr = atoi(argv[0]); if (bts_nr >= net->num_bts) { vty_out(vty, "%% can't find BTS '%s'%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } bts = gsm_bts_num(net, bts_nr); } if (argc >= 2) { trx_nr = atoi(argv[1]); if (trx_nr >= bts->num_trx) { vty_out(vty, "%% can't find TRX '%s'%s", argv[1], VTY_NEWLINE); return CMD_WARNING; } trx = gsm_bts_trx_num(bts, trx_nr); } if (argc >= 3) { ts_nr = atoi(argv[2]); if (ts_nr >= TRX_NR_TS) { vty_out(vty, "%% can't find TS '%s'%s", argv[2], VTY_NEWLINE); return CMD_WARNING; } /* Fully Specified: print and exit */ ts = &trx->ts[ts_nr]; ts_dump_vty(vty, ts); return CMD_SUCCESS; } if (bts && trx) { /* Iterate over all TS in this TRX */ for (ts_nr = 0; ts_nr < TRX_NR_TS; ts_nr++) { ts = &trx->ts[ts_nr]; ts_dump_vty(vty, ts); } } else if (bts) { /* Iterate over all TRX in this BTS, TS in each TRX */ for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) { trx = gsm_bts_trx_num(bts, trx_nr); for (ts_nr = 0; ts_nr < TRX_NR_TS; ts_nr++) { ts = &trx->ts[ts_nr]; ts_dump_vty(vty, ts); } } } else { /* Iterate over all BTS, TRX in each BTS, TS in each TRX */ for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) { bts = gsm_bts_num(net, bts_nr); for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) { trx = gsm_bts_trx_num(bts, trx_nr); for (ts_nr = 0; ts_nr < TRX_NR_TS; ts_nr++) { ts = &trx->ts[ts_nr]; ts_dump_vty(vty, ts); } } } } return CMD_SUCCESS; } static void subscr_dump_vty(struct vty *vty, struct gsm_subscriber *subscr) { vty_out(vty, " ID: %llu, Authorized: %d%s", subscr->id, subscr->authorized, VTY_NEWLINE); if (strlen(subscr->name)) vty_out(vty, " Name: '%s'%s", subscr->name, VTY_NEWLINE); if (strlen(subscr->extension)) vty_out(vty, " Extension: %s%s", subscr->extension, VTY_NEWLINE); vty_out(vty, " IMSI: %s%s", subscr->imsi, VTY_NEWLINE); if (subscr->tmsi != GSM_RESERVED_TMSI) vty_out(vty, " TMSI: %08X%s", subscr->tmsi, VTY_NEWLINE); vty_out(vty, " Use count: %u%s", subscr->use_count, VTY_NEWLINE); } static void meas_rep_dump_uni_vty(struct vty *vty, struct gsm_meas_rep_unidir *mru, const char *prefix, const char *dir) { vty_out(vty, "%s RXL-FULL-%s: %4d dBm, RXL-SUB-%s: %4d dBm ", prefix, dir, rxlev2dbm(mru->full.rx_lev), dir, rxlev2dbm(mru->sub.rx_lev)); vty_out(vty, "RXQ-FULL-%s: %d, RXQ-SUB-%s: %d%s", dir, mru->full.rx_qual, dir, mru->sub.rx_qual, VTY_NEWLINE); } static void meas_rep_dump_vty(struct vty *vty, struct gsm_meas_rep *mr, const char *prefix) { vty_out(vty, "%sMeasurement Report:%s", prefix, VTY_NEWLINE); vty_out(vty, "%s Flags: %s%s%s%s%s", prefix, mr->flags & MEAS_REP_F_UL_DTX ? "DTXu " : "", mr->flags & MEAS_REP_F_DL_DTX ? "DTXd " : "", mr->flags & MEAS_REP_F_FPC ? "FPC " : "", mr->flags & MEAS_REP_F_DL_VALID ? " " : "DLinval ", VTY_NEWLINE); if (mr->flags & MEAS_REP_F_MS_TO) vty_out(vty, "%s MS Timing Offset: %u%s", prefix, mr->ms_timing_offset, VTY_NEWLINE); if (mr->flags & MEAS_REP_F_MS_L1) vty_out(vty, "%s L1 MS Power: %u dBm, Timing Advance: %u%s", prefix, mr->ms_l1.pwr, mr->ms_l1.ta, VTY_NEWLINE); if (mr->flags & MEAS_REP_F_DL_VALID) meas_rep_dump_uni_vty(vty, &mr->dl, prefix, "dl"); meas_rep_dump_uni_vty(vty, &mr->ul, prefix, "ul"); } static void lchan_dump_full_vty(struct vty *vty, struct gsm_lchan *lchan) { int idx; vty_out(vty, "BTS %u, TRX %u, Timeslot %u, Lchan %u: Type %s%s", lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr, lchan->nr, gsm_lchant_name(lchan->type), VTY_NEWLINE); vty_out(vty, " Connection: %u, State: %s%s%s%s", lchan->conn ? 1: 0, gsm_lchans_name(lchan->state), lchan->state == LCHAN_S_BROKEN ? " Error reason: " : "", lchan->state == LCHAN_S_BROKEN ? lchan->broken_reason : "", VTY_NEWLINE); vty_out(vty, " BS Power: %u dBm, MS Power: %u dBm%s", lchan->ts->trx->nominal_power - lchan->ts->trx->max_power_red - lchan->bs_power*2, ms_pwr_dbm(lchan->ts->trx->bts->band, lchan->ms_power), VTY_NEWLINE); if (lchan->conn && lchan->conn->subscr) { vty_out(vty, " Subscriber:%s", VTY_NEWLINE); subscr_dump_vty(vty, lchan->conn->subscr); } else vty_out(vty, " No Subscriber%s", VTY_NEWLINE); if (is_ipaccess_bts(lchan->ts->trx->bts)) { struct in_addr ia; ia.s_addr = htonl(lchan->abis_ip.bound_ip); vty_out(vty, " Bound IP: %s Port %u RTP_TYPE2=%u CONN_ID=%u%s", inet_ntoa(ia), lchan->abis_ip.bound_port, lchan->abis_ip.rtp_payload2, lchan->abis_ip.conn_id, VTY_NEWLINE); } /* we want to report the last measurement report */ idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep), lchan->meas_rep_idx, 1); meas_rep_dump_vty(vty, &lchan->meas_rep[idx], " "); } static void lchan_dump_short_vty(struct vty *vty, struct gsm_lchan *lchan) { struct gsm_meas_rep *mr; int idx; /* we want to report the last measurement report */ idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep), lchan->meas_rep_idx, 1); mr = &lchan->meas_rep[idx]; vty_out(vty, "BTS %u, TRX %u, Timeslot %u, Lchan %u, Type %s - " "L1 MS Power: %u dBm RXL-FULL-dl: %4d dBm RXL-FULL-ul: %4d dBm%s", lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr, lchan->nr, gsm_lchant_name(lchan->type), mr->ms_l1.pwr, rxlev2dbm(mr->dl.full.rx_lev), rxlev2dbm(mr->ul.full.rx_lev), VTY_NEWLINE); } static int dump_lchan_trx_ts(struct gsm_bts_trx_ts *ts, struct vty *vty, void (*dump_cb)(struct vty *, struct gsm_lchan *)) { int lchan_nr; for (lchan_nr = 0; lchan_nr < TS_MAX_LCHAN; lchan_nr++) { struct gsm_lchan *lchan = &ts->lchan[lchan_nr]; if ((lchan->type == GSM_LCHAN_NONE) && (lchan->state == LCHAN_S_NONE)) continue; dump_cb(vty, lchan); } return CMD_SUCCESS; } static int dump_lchan_trx(struct gsm_bts_trx *trx, struct vty *vty, void (*dump_cb)(struct vty *, struct gsm_lchan *)) { int ts_nr; for (ts_nr = 0; ts_nr < TRX_NR_TS; ts_nr++) { struct gsm_bts_trx_ts *ts = &trx->ts[ts_nr]; dump_lchan_trx_ts(ts, vty, dump_cb); } return CMD_SUCCESS; } static int dump_lchan_bts(struct gsm_bts *bts, struct vty *vty, void (*dump_cb)(struct vty *, struct gsm_lchan *)) { int trx_nr; for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) { struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, trx_nr); dump_lchan_trx(trx, vty, dump_cb); } return CMD_SUCCESS; } static int lchan_summary(struct vty *vty, int argc, const char **argv, void (*dump_cb)(struct vty *, struct gsm_lchan *)) { struct gsm_network *net = gsmnet_from_vty(vty); struct gsm_bts *bts; struct gsm_bts_trx *trx; struct gsm_bts_trx_ts *ts; struct gsm_lchan *lchan; int bts_nr, trx_nr, ts_nr, lchan_nr; if (argc >= 1) { /* use the BTS number that the user has specified */ bts_nr = atoi(argv[0]); if (bts_nr >= net->num_bts) { vty_out(vty, "%% can't find BTS %s%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } bts = gsm_bts_num(net, bts_nr); if (argc == 1) return dump_lchan_bts(bts, vty, dump_cb); } if (argc >= 2) { trx_nr = atoi(argv[1]); if (trx_nr >= bts->num_trx) { vty_out(vty, "%% can't find TRX %s%s", argv[1], VTY_NEWLINE); return CMD_WARNING; } trx = gsm_bts_trx_num(bts, trx_nr); if (argc == 2) return dump_lchan_trx(trx, vty, dump_cb); } if (argc >= 3) { ts_nr = atoi(argv[2]); if (ts_nr >= TRX_NR_TS) { vty_out(vty, "%% can't find TS %s%s", argv[2], VTY_NEWLINE); return CMD_WARNING; } ts = &trx->ts[ts_nr]; if (argc == 3) return dump_lchan_trx_ts(ts, vty, dump_cb); } if (argc >= 4) { lchan_nr = atoi(argv[3]); if (lchan_nr >= TS_MAX_LCHAN) { vty_out(vty, "%% can't find LCHAN %s%s", argv[3], VTY_NEWLINE); return CMD_WARNING; } lchan = &ts->lchan[lchan_nr]; dump_cb(vty, lchan); return CMD_SUCCESS; } for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) { bts = gsm_bts_num(net, bts_nr); dump_lchan_bts(bts, vty, dump_cb); } return CMD_SUCCESS; } DEFUN(show_lchan, show_lchan_cmd, "show lchan [<0-255>] [<0-255>] [<0-7>] [lchan_nr]", SHOW_STR "Display information about a logical channel\n" "BTS Number\n" "TRX Number\n" "Timeslot Number\n" LCHAN_NR_STR) { return lchan_summary(vty, argc, argv, lchan_dump_full_vty); } DEFUN(show_lchan_summary, show_lchan_summary_cmd, "show lchan summary [<0-255>] [<0-255>] [<0-7>] [lchan_nr]", SHOW_STR "Display information about a logical channel\n" "Short summary\n" "BTS Number\n" "TRX Number\n" "Timeslot Number\n" LCHAN_NR_STR) { return lchan_summary(vty, argc, argv, lchan_dump_short_vty); } static void paging_dump_vty(struct vty *vty, struct gsm_paging_request *pag) { vty_out(vty, "Paging on BTS %u%s", pag->bts->nr, VTY_NEWLINE); subscr_dump_vty(vty, pag->subscr); } static void bts_paging_dump_vty(struct vty *vty, struct gsm_bts *bts) { struct gsm_paging_request *pag; if (!bts->paging.bts) return; llist_for_each_entry(pag, &bts->paging.pending_requests, entry) paging_dump_vty(vty, pag); } DEFUN(show_paging, show_paging_cmd, "show paging [<0-255>]", SHOW_STR "Display information about paging reuqests of a BTS\n" "BTS Number\n") { struct gsm_network *net = gsmnet_from_vty(vty); struct gsm_bts *bts; int bts_nr; if (argc >= 1) { /* use the BTS number that the user has specified */ bts_nr = atoi(argv[0]); if (bts_nr >= net->num_bts) { vty_out(vty, "%% can't find BTS %s%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } bts = gsm_bts_num(net, bts_nr); bts_paging_dump_vty(vty, bts); return CMD_SUCCESS; } for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) { bts = gsm_bts_num(net, bts_nr); bts_paging_dump_vty(vty, bts); } return CMD_SUCCESS; } DEFUN(show_paging_group, show_paging_group_cmd, "show paging-group <0-255> IMSI", SHOW_STR "Display the paging group\n" "BTS Number\n" "IMSI\n") { struct gsm_network *net = gsmnet_from_vty(vty); struct gsm_bts *bts; unsigned int page_group; int bts_nr = atoi(argv[0]); if (bts_nr >= net->num_bts) { vty_out(vty, "%% can't find BTS %s%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } bts = gsm_bts_num(net, bts_nr); if (!bts) { vty_out(vty, "%% can't find BTS %s%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } page_group = gsm0502_calc_paging_group(&bts->si_common.chan_desc, str_to_imsi(argv[1])); vty_out(vty, "%%Paging group for IMSI %" PRIu64 " on BTS #%d is %u%s", str_to_imsi(argv[1]), bts->nr, page_group, VTY_NEWLINE); return CMD_SUCCESS; } DEFUN(cfg_net, cfg_net_cmd, "network", NETWORK_STR) { vty->index = gsmnet_from_vty(vty); vty->node = GSMNET_NODE; return CMD_SUCCESS; } DEFUN(cfg_net_ncc, cfg_net_ncc_cmd, "network country code <1-999>", "Set the GSM network country code\n" "Country commands\n" CODE_CMD_STR "Network Country Code to use\n") { struct gsm_network *gsmnet = gsmnet_from_vty(vty); gsmnet->country_code = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_net_mnc, cfg_net_mnc_cmd, "mobile network code <0-999>", "Set the GSM mobile network code\n" "Network Commands\n" CODE_CMD_STR "Mobile Network Code to use\n") { struct gsm_network *gsmnet = gsmnet_from_vty(vty); gsmnet->network_code = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_net_name_short, cfg_net_name_short_cmd, "short name NAME", "Set the short GSM network name\n" NAME_CMD_STR NAME_STR) { struct gsm_network *gsmnet = gsmnet_from_vty(vty); bsc_replace_string(gsmnet, &gsmnet->name_short, argv[0]); return CMD_SUCCESS; } DEFUN(cfg_net_name_long, cfg_net_name_long_cmd, "long name NAME", "Set the long GSM network name\n" NAME_CMD_STR NAME_STR) { struct gsm_network *gsmnet = gsmnet_from_vty(vty); bsc_replace_string(gsmnet, &gsmnet->name_long, argv[0]); return CMD_SUCCESS; } DEFUN(cfg_net_auth_policy, cfg_net_auth_policy_cmd, "auth policy (closed|accept-all|token)", "Authentication (not cryptographic)\n" "Set the GSM network authentication policy\n" "Require the MS to be activated in HLR\n" "Accept all MS, whether in HLR or not\n" "Use SMS-token based authentication\n") { enum gsm_auth_policy policy = gsm_auth_policy_parse(argv[0]); struct gsm_network *gsmnet = gsmnet_from_vty(vty); gsmnet->auth_policy = policy; return CMD_SUCCESS; } DEFUN(cfg_net_reject_cause, cfg_net_reject_cause_cmd, "location updating reject cause <2-111>", "Set the reject cause of location updating reject\n" "Set the reject cause of location updating reject\n" "Set the reject cause of location updating reject\n" "Set the reject cause of location updating reject\n" "Cause Value as Per GSM TS 04.08\n") { struct gsm_network *gsmnet = gsmnet_from_vty(vty); gsmnet->reject_cause = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_net_encryption, cfg_net_encryption_cmd, "encryption a5 (0|1|2|3)", "Encryption options\n" "A5 encryption\n" "A5/0: No encryption\n" "A5/1: Encryption\n" "A5/2: Export-grade Encryption\n" "A5/3: 'New' Secure Encryption\n") { struct gsm_network *gsmnet = gsmnet_from_vty(vty); gsmnet->a5_encryption= atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_net_neci, cfg_net_neci_cmd, "neci (0|1)", "New Establish Cause Indication\n" "Don't set the NECI bit\n" "Set the NECI bit\n") { struct gsm_network *gsmnet = gsmnet_from_vty(vty); gsmnet->neci = atoi(argv[0]); gsm_net_update_ctype(gsmnet); return CMD_SUCCESS; } DEFUN(cfg_net_rrlp_mode, cfg_net_rrlp_mode_cmd, "rrlp mode (none|ms-based|ms-preferred|ass-preferred)", "Radio Resource Location Protocol\n" "Set the Radio Resource Location Protocol Mode\n" "Don't send RRLP request\n" "Request MS-based location\n" "Request any location, prefer MS-based\n" "Request any location, prefer MS-assisted\n") { struct gsm_network *gsmnet = gsmnet_from_vty(vty); gsmnet->rrlp.mode = rrlp_mode_parse(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_net_mm_info, cfg_net_mm_info_cmd, "mm info (0|1)", "Mobility Management\n" "Send MM INFO after LOC UPD ACCEPT\n" "Disable\n" "Enable\n") { struct gsm_network *gsmnet = gsmnet_from_vty(vty); gsmnet->send_mm_info = atoi(argv[0]); return CMD_SUCCESS; } #define HANDOVER_STR "Handover Options\n" DEFUN(cfg_net_handover, cfg_net_handover_cmd, "handover (0|1)", HANDOVER_STR "Don't perform in-call handover\n" "Perform in-call handover\n") { int enable = atoi(argv[0]); struct gsm_network *gsmnet = gsmnet_from_vty(vty); if (enable && ipacc_rtp_direct) { vty_out(vty, "%% Cannot enable handover unless RTP Proxy mode " "is enabled by using the -P command line option%s", VTY_NEWLINE); return CMD_WARNING; } gsmnet->handover.active = enable; return CMD_SUCCESS; } #define HO_WIN_STR HANDOVER_STR "Measurement Window\n" #define HO_WIN_RXLEV_STR HO_WIN_STR "Received Level Averaging\n" #define HO_WIN_RXQUAL_STR HO_WIN_STR "Received Quality Averaging\n" #define HO_PBUDGET_STR HANDOVER_STR "Power Budget\n" #define HO_AVG_COUNT_STR "Amount to use for Averaging\n" DEFUN(cfg_net_ho_win_rxlev_avg, cfg_net_ho_win_rxlev_avg_cmd, "handover window rxlev averaging <1-10>", HO_WIN_RXLEV_STR "How many RxLev measurements are used for averaging\n" HO_AVG_COUNT_STR) { struct gsm_network *gsmnet = gsmnet_from_vty(vty); gsmnet->handover.win_rxlev_avg = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_net_ho_win_rxqual_avg, cfg_net_ho_win_rxqual_avg_cmd, "handover window rxqual averaging <1-10>", HO_WIN_RXQUAL_STR "How many RxQual measurements are used for averaging\n" HO_AVG_COUNT_STR) { struct gsm_network *gsmnet = gsmnet_from_vty(vty); gsmnet->handover.win_rxqual_avg = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_net_ho_win_rxlev_neigh_avg, cfg_net_ho_win_rxlev_avg_neigh_cmd, "handover window rxlev neighbor averaging <1-10>", HO_WIN_RXLEV_STR "Neighbor\n" "How many RxQual measurements are used for averaging\n" HO_AVG_COUNT_STR) { struct gsm_network *gsmnet = gsmnet_from_vty(vty); gsmnet->handover.win_rxlev_avg_neigh = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_net_ho_pwr_interval, cfg_net_ho_pwr_interval_cmd, "handover power budget interval <1-99>", HO_PBUDGET_STR "How often to check if we have a better cell (SACCH frames)\n" "Interval\n" "Number\n") { struct gsm_network *gsmnet = gsmnet_from_vty(vty); gsmnet->handover.pwr_interval = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_net_ho_pwr_hysteresis, cfg_net_ho_pwr_hysteresis_cmd, "handover power budget hysteresis <0-999>", HO_PBUDGET_STR "How many dB does a neighbor to be stronger to become a HO candidate\n" "Hysteresis\n" "Number\n") { struct gsm_network *gsmnet = gsmnet_from_vty(vty); gsmnet->handover.pwr_hysteresis = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_net_ho_max_distance, cfg_net_ho_max_distance_cmd, "handover maximum distance <0-9999>", HANDOVER_STR "How big is the maximum timing advance before HO is forced\n" "Distance\n" "Number\n") { struct gsm_network *gsmnet = gsmnet_from_vty(vty); gsmnet->handover.max_distance = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_net_pag_any_tch, cfg_net_pag_any_tch_cmd, "paging any use tch (0|1)", "Assign a TCH when receiving a Paging Any request\n" "Any Channel\n" "Use\n" "TCH\n" "Do not use TCH for Paging Request Any\n" "Do use TCH for Paging Request Any\n") { struct gsm_network *gsmnet = gsmnet_from_vty(vty); gsmnet->pag_any_tch = atoi(argv[0]); gsm_net_update_ctype(gsmnet); return CMD_SUCCESS; } #define DECLARE_TIMER(number, doc) \ DEFUN(cfg_net_T##number, \ cfg_net_T##number##_cmd, \ "timer t" #number " <0-65535>", \ "Configure GSM Timers\n" \ doc "Timer Value in seconds\n") \ { \ struct gsm_network *gsmnet = gsmnet_from_vty(vty); \ int value = atoi(argv[0]); \ \ if (value < 0 || value > 65535) { \ vty_out(vty, "Timer value %s out of range.%s", \ argv[0], VTY_NEWLINE); \ return CMD_WARNING; \ } \ \ gsmnet->T##number = value; \ return CMD_SUCCESS; \ } DECLARE_TIMER(3101, "Set the timeout value for IMMEDIATE ASSIGNMENT.\n") DECLARE_TIMER(3103, "Set the timeout value for HANDOVER.\n") DECLARE_TIMER(3105, "Set the timer for repetition of PHYSICAL INFORMATION.\n") DECLARE_TIMER(3107, "Currently not used.\n") DECLARE_TIMER(3109, "Set the RSL SACCH deactivation timeout.\n") DECLARE_TIMER(3111, "Set the RSL timeout to wait before releasing the RF Channel.\n") DECLARE_TIMER(3113, "Set the time to try paging a subscriber.\n") DECLARE_TIMER(3115, "Currently not used.\n") DECLARE_TIMER(3117, "Currently not used.\n") DECLARE_TIMER(3119, "Currently not used.\n") DECLARE_TIMER(3122, "Waiting time (seconds) after IMM ASS REJECT\n") DECLARE_TIMER(3141, "Currently not used.\n") DEFUN(cfg_net_dtx, cfg_net_dtx_cmd, "dtx-used (0|1)", "Enable the usage of DTX.\n" "DTX is disabled\n" "DTX is enabled\n") { struct gsm_network *gsmnet = gsmnet_from_vty(vty); gsmnet->dtx_enabled = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_net_subscr_keep, cfg_net_subscr_keep_cmd, "subscriber-keep-in-ram (0|1)", "Keep unused subscribers in RAM.\n" "Delete unused subscribers\n" "Keep unused subscribers\n") { struct gsm_network *gsmnet = gsmnet_from_vty(vty); gsmnet->subscr_group->keep_subscr = atoi(argv[0]); return CMD_SUCCESS; } /* per-BTS configuration */ DEFUN(cfg_bts, cfg_bts_cmd, "bts <0-255>", "Select a BTS to configure\n" "BTS Number\n") { struct gsm_network *gsmnet = gsmnet_from_vty(vty); int bts_nr = atoi(argv[0]); struct gsm_bts *bts; if (bts_nr > gsmnet->num_bts) { vty_out(vty, "%% The next unused BTS number is %u%s", gsmnet->num_bts, VTY_NEWLINE); return CMD_WARNING; } else if (bts_nr == gsmnet->num_bts) { /* allocate a new one */ bts = gsm_bts_alloc_register(gsmnet, GSM_BTS_TYPE_UNKNOWN, HARDCODED_TSC, HARDCODED_BSIC); } else bts = gsm_bts_num(gsmnet, bts_nr); if (!bts) { vty_out(vty, "%% Unable to allocate BTS %u%s", gsmnet->num_bts, VTY_NEWLINE); return CMD_WARNING; } vty->index = bts; vty->index_sub = &bts->description; vty->node = BTS_NODE; return CMD_SUCCESS; } DEFUN(cfg_bts_type, cfg_bts_type_cmd, "type TYPE", /* dynamically created */ "Set the BTS type\n" "Type\n") { struct gsm_bts *bts = vty->index; int rc; rc = gsm_set_bts_type(bts, parse_btstype(argv[0])); if (rc < 0) return CMD_WARNING; return CMD_SUCCESS; } DEFUN(cfg_bts_band, cfg_bts_band_cmd, "band BAND", "Set the frequency band of this BTS\n" "Frequency band\n") { struct gsm_bts *bts = vty->index; int band = gsm_band_parse(argv[0]); if (band < 0) { vty_out(vty, "%% BAND %d is not a valid GSM band%s", band, VTY_NEWLINE); return CMD_WARNING; } bts->band = band; return CMD_SUCCESS; } DEFUN(cfg_bts_ci, cfg_bts_ci_cmd, "cell_identity <0-65535>", "Set the Cell identity of this BTS\n" "Cell Identity\n") { struct gsm_bts *bts = vty->index; int ci = atoi(argv[0]); if (ci < 0 || ci > 0xffff) { vty_out(vty, "%% CI %d is not in the valid range (0-65535)%s", ci, VTY_NEWLINE); return CMD_WARNING; } bts->cell_identity = ci; return CMD_SUCCESS; } DEFUN(cfg_bts_lac, cfg_bts_lac_cmd, "location_area_code <0-65535>", "Set the Location Area Code (LAC) of this BTS\n" "LAC\n") { struct gsm_bts *bts = vty->index; int lac = atoi(argv[0]); if (lac < 0 || lac > 0xffff) { vty_out(vty, "%% LAC %d is not in the valid range (0-65535)%s", lac, VTY_NEWLINE); return CMD_WARNING; } if (lac == GSM_LAC_RESERVED_DETACHED || lac == GSM_LAC_RESERVED_ALL_BTS) { vty_out(vty, "%% LAC %d is reserved by GSM 04.08%s", lac, VTY_NEWLINE); return CMD_WARNING; } bts->location_area_code = lac; return CMD_SUCCESS; } DEFUN(cfg_bts_tsc, cfg_bts_tsc_cmd, "training_sequence_code <0-7>", "Set the Training Sequence Code (TSC) of this BTS\n" "TSC\n") { struct gsm_bts *bts = vty->index; int tsc = atoi(argv[0]); if (!gsm_bts_has_feature(bts, BTS_FEAT_MULTI_TSC)) { vty_out(vty, "%% This BTS does not support a TSC != BCC, " "falling back to BCC%s", VTY_NEWLINE); bts->tsc = bts->bsic & 7; return CMD_WARNING; } bts->tsc = tsc; return CMD_SUCCESS; } DEFUN(cfg_bts_bsic, cfg_bts_bsic_cmd, "base_station_id_code <0-63>", "Set the Base Station Identity Code (BSIC) of this BTS\n" "BSIC of this BTS\n") { struct gsm_bts *bts = vty->index; int bsic = atoi(argv[0]); if (bsic < 0 || bsic > 0x3f) { vty_out(vty, "%% BSIC %d is not in the valid range (0-255)%s", bsic, VTY_NEWLINE); return CMD_WARNING; } bts->bsic = bsic; /* automatically re-configuer the TSC if we change the BCC * which is the lower 3 bits of the BSIC */ if (!gsm_bts_has_feature(bts, BTS_FEAT_MULTI_TSC)) bts->tsc = bts->bsic & 7; return CMD_SUCCESS; } DEFUN(cfg_bts_timezone, cfg_bts_timezone_cmd, "timezone <-19-19> (0|15|30|45)", "Set the Timezone Offset of this BTS\n" "Timezone offset (hours)\n" "Timezone offset (00 minutes)\n" "Timezone offset (15 minutes)\n" "Timezone offset (30 minutes)\n" "Timezone offset (45 minutes)\n" ) { struct gsm_bts *bts = vty->index; int tzhr = atoi(argv[0]); int tzmn = atoi(argv[1]); bts->tz.hr = tzhr; bts->tz.mn = tzmn; bts->tz.dst = 0; bts->tz.override = 1; return CMD_SUCCESS; } DEFUN(cfg_bts_timezone_dst, cfg_bts_timezone_dst_cmd, "timezone <-19-19> (0|15|30|45) <0-2>", "Set the Timezone Offset of this BTS\n" "Timezone offset (hours)\n" "Timezone offset (00 minutes)\n" "Timezone offset (15 minutes)\n" "Timezone offset (30 minutes)\n" "Timezone offset (45 minutes)\n" "DST offset (hours)\n" ) { struct gsm_bts *bts = vty->index; int tzhr = atoi(argv[0]); int tzmn = atoi(argv[1]); int tzdst = atoi(argv[2]); bts->tz.hr = tzhr; bts->tz.mn = tzmn; bts->tz.dst = tzdst; bts->tz.override = 1; return CMD_SUCCESS; } DEFUN(cfg_bts_no_timezone, cfg_bts_no_timezone_cmd, "no timezone", NO_STR "Disable BTS specific timezone\n") { struct gsm_bts *bts = vty->index; bts->tz.override = 0; return CMD_SUCCESS; } DEFUN(cfg_bts_unit_id, cfg_bts_unit_id_cmd, "ip.access unit_id <0-65534> <0-255>", "Abis/IP specific options\n" "Set the IPA BTS Unit ID\n" "Unit ID (Site)\n" "Unit ID (BTS)\n") { struct gsm_bts *bts = vty->index; int site_id = atoi(argv[0]); int bts_id = atoi(argv[1]); if (!is_ipaccess_bts(bts)) { vty_out(vty, "%% BTS is not of ip.access type%s", VTY_NEWLINE); return CMD_WARNING; } bts->ip_access.site_id = site_id; bts->ip_access.bts_id = bts_id; return CMD_SUCCESS; } DEFUN(cfg_bts_rsl_ip, cfg_bts_rsl_ip_cmd, "ip.access rsl-ip A.B.C.D", "Abis/IP specific options\n" "Set the IPA RSL IP Address of the BSC\n" "Destination IP address for RSL connection\n") { struct gsm_bts *bts = vty->index; struct in_addr ia; if (!is_ipaccess_bts(bts)) { vty_out(vty, "%% BTS is not of ip.access type%s", VTY_NEWLINE); return CMD_WARNING; } inet_aton(argv[0], &ia); bts->ip_access.rsl_ip = ntohl(ia.s_addr); return CMD_SUCCESS; } #define NOKIA_STR "Nokia *Site related commands\n" DEFUN(cfg_bts_nokia_site_skip_reset, cfg_bts_nokia_site_skip_reset_cmd, "nokia_site skip-reset (0|1)", NOKIA_STR "Skip the reset step during bootstrap process of this BTS\n" "Do NOT skip the reset\n" "Skip the reset\n") { struct gsm_bts *bts = vty->index; if (bts->type != GSM_BTS_TYPE_NOKIA_SITE) { vty_out(vty, "%% BTS is not of Nokia *Site type%s", VTY_NEWLINE); return CMD_WARNING; } bts->nokia.skip_reset = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_bts_nokia_site_no_loc_rel_cnf, cfg_bts_nokia_site_no_loc_rel_cnf_cmd, "nokia_site no-local-rel-conf (0|1)", NOKIA_STR "Do not wait for RELease CONFirm message when releasing channel locally\n" "Wait for RELease CONFirm\n" "Do not wait for RELease CONFirm\n") { struct gsm_bts *bts = vty->index; if (!is_nokia_bts(bts)) { vty_out(vty, "%% BTS is not of Nokia *Site type%s", VTY_NEWLINE); return CMD_WARNING; } bts->nokia.no_loc_rel_cnf = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_bts_nokia_site_bts_reset_timer_cnf, cfg_bts_nokia_site_bts_reset_timer_cnf_cmd, "nokia_site bts-reset-timer <15-100>", NOKIA_STR "The amount of time (in sec.) between BTS_RESET is sent,\n" "and the BTS is being bootstrapped.\n") { struct gsm_bts *bts = vty->index; if (!is_nokia_bts(bts)) { vty_out(vty, "%% BTS is not of Nokia *Site type%s", VTY_NEWLINE); return CMD_WARNING; } bts->nokia.bts_reset_timer_cnf = atoi(argv[0]); return CMD_SUCCESS; } #define OML_STR "Organization & Maintenance Link\n" #define IPA_STR "A-bis/IP Specific Options\n" DEFUN(cfg_bts_stream_id, cfg_bts_stream_id_cmd, "oml ip.access stream_id <0-255> line E1_LINE", OML_STR IPA_STR "Set the ip.access Stream ID of the OML link of this BTS\n" "Stream Identifier\n" "Virtual E1 Line Number\n" "Virtual E1 Line Number\n") { struct gsm_bts *bts = vty->index; int stream_id = atoi(argv[0]), linenr = atoi(argv[1]); if (!is_ipaccess_bts(bts)) { vty_out(vty, "%% BTS is not of ip.access type%s", VTY_NEWLINE); return CMD_WARNING; } bts->oml_tei = stream_id; /* This is used by e1inp_bind_ops callback for each BTS model. */ bts->oml_e1_link.e1_nr = linenr; return CMD_SUCCESS; } #define OML_E1_STR OML_STR "OML E1/T1 Configuration\n" DEFUN(cfg_bts_oml_e1, cfg_bts_oml_e1_cmd, "oml e1 line E1_LINE timeslot <1-31> sub-slot (0|1|2|3|full)", OML_E1_STR "E1/T1 line number to be used for OML\n" "E1/T1 line number to be used for OML\n" "E1/T1 timeslot to be used for OML\n" "E1/T1 timeslot to be used for OML\n" "E1/T1 sub-slot to be used for OML\n" "Use E1/T1 sub-slot 0\n" "Use E1/T1 sub-slot 1\n" "Use E1/T1 sub-slot 2\n" "Use E1/T1 sub-slot 3\n" "Use full E1 slot 3\n" ) { struct gsm_bts *bts = vty->index; parse_e1_link(&bts->oml_e1_link, argv[0], argv[1], argv[2]); return CMD_SUCCESS; } DEFUN(cfg_bts_oml_e1_tei, cfg_bts_oml_e1_tei_cmd, "oml e1 tei <0-63>", OML_E1_STR "Set the TEI to be used for OML\n" "TEI Number\n") { struct gsm_bts *bts = vty->index; bts->oml_tei = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_bts_challoc, cfg_bts_challoc_cmd, "channel allocator (ascending|descending)", "Channnel Allocator\n" "Channel Allocator\n" "Allocate Timeslots and Transceivers in ascending order\n" "Allocate Timeslots and Transceivers in descending order\n") { struct gsm_bts *bts = vty->index; if (!strcmp(argv[0], "ascending")) bts->chan_alloc_reverse = 0; else bts->chan_alloc_reverse = 1; return CMD_SUCCESS; } #define RACH_STR "Random Access Control Channel\n" DEFUN(cfg_bts_rach_tx_integer, cfg_bts_rach_tx_integer_cmd, "rach tx integer <0-15>", RACH_STR "Set the raw tx integer value in RACH Control parameters IE\n" "Set the raw tx integer value in RACH Control parameters IE\n" "Raw tx integer value in RACH Control parameters IE\n") { struct gsm_bts *bts = vty->index; bts->si_common.rach_control.tx_integer = atoi(argv[0]) & 0xf; return CMD_SUCCESS; } DEFUN(cfg_bts_rach_max_trans, cfg_bts_rach_max_trans_cmd, "rach max transmission (1|2|4|7)", RACH_STR "Set the maximum number of RACH burst transmissions\n" "Set the maximum number of RACH burst transmissions\n" "Maximum number of 1 RACH burst transmissions\n" "Maximum number of 2 RACH burst transmissions\n" "Maximum number of 4 RACH burst transmissions\n" "Maximum number of 7 RACH burst transmissions\n") { struct gsm_bts *bts = vty->index; bts->si_common.rach_control.max_trans = rach_max_trans_val2raw(atoi(argv[0])); return CMD_SUCCESS; } #define CD_STR "Channel Description\n" DEFUN(cfg_bts_chan_desc_att, cfg_bts_chan_desc_att_cmd, "channel-descrption attach (0|1)", CD_STR "Set if attachment is required\n" "Attachment is NOT required\n" "Attachment is required (standard)\n") { struct gsm_bts *bts = vty->index; bts->si_common.chan_desc.att = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_bts_chan_desc_bs_pa_mfrms, cfg_bts_chan_desc_bs_pa_mfrms_cmd, "channel-descrption bs-pa-mfrms <2-9>", CD_STR "Set number of multiframe periods for paging groups\n" "Number of multiframe periods for paging groups\n") { struct gsm_bts *bts = vty->index; int bs_pa_mfrms = atoi(argv[0]); bts->si_common.chan_desc.bs_pa_mfrms = bs_pa_mfrms - 2; return CMD_SUCCESS; } DEFUN(cfg_bts_chan_desc_bs_ag_blks_res, cfg_bts_chan_desc_bs_ag_blks_res_cmd, "channel-descrption bs-ag-blks-res <0-7>", CD_STR "Set number of blocks reserved for access grant\n" "Number of blocks reserved for access grant\n") { struct gsm_bts *bts = vty->index; int bs_ag_blks_res = atoi(argv[0]); bts->si_common.chan_desc.bs_ag_blks_res = bs_ag_blks_res; return CMD_SUCCESS; } #define NM_STR "Network Management\n" DEFUN(cfg_bts_rach_nm_b_thresh, cfg_bts_rach_nm_b_thresh_cmd, "rach nm busy threshold <0-255>", RACH_STR NM_STR "Set the NM Busy Threshold\n" "Set the NM Busy Threshold\n" "NM Busy Threshold in dB") { struct gsm_bts *bts = vty->index; bts->rach_b_thresh = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_bts_rach_nm_ldavg, cfg_bts_rach_nm_ldavg_cmd, "rach nm load average <0-65535>", RACH_STR NM_STR "Set the NM Loadaverage Slots value\n" "Set the NM Loadaverage Slots value\n" "NM Loadaverage Slots value\n") { struct gsm_bts *bts = vty->index; bts->rach_ldavg_slots = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_bts_cell_barred, cfg_bts_cell_barred_cmd, "cell barred (0|1)", "Should this cell be barred from access?\n" "Should this cell be barred from access?\n" "Cell should NOT be barred\n" "Cell should be barred\n") { struct gsm_bts *bts = vty->index; bts->si_common.rach_control.cell_bar = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_bts_rach_ec_allowed, cfg_bts_rach_ec_allowed_cmd, "rach emergency call allowed (0|1)", RACH_STR "Should this cell allow emergency calls?\n" "Should this cell allow emergency calls?\n" "Should this cell allow emergency calls?\n" "Do NOT allow emergency calls\n" "Allow emergency calls\n") { struct gsm_bts *bts = vty->index; if (atoi(argv[0]) == 0) bts->si_common.rach_control.t2 |= 0x4; else bts->si_common.rach_control.t2 &= ~0x4; return CMD_SUCCESS; } DEFUN(cfg_bts_rach_ac_class, cfg_bts_rach_ac_class_cmd, "rach access-control-class (0|1|2|3|4|5|6|7|8|9|11|12|13|14|15) (barred|allowed)", RACH_STR "Set access control class\n" "Access control class 0\n" "Access control class 1\n" "Access control class 2\n" "Access control class 3\n" "Access control class 4\n" "Access control class 5\n" "Access control class 6\n" "Access control class 7\n" "Access control class 8\n" "Access control class 9\n" "Access control class 11 for PLMN use\n" "Access control class 12 for security services\n" "Access control class 13 for public utilities (e.g. water/gas suppliers)\n" "Access control class 14 for emergency services\n" "Access control class 15 for PLMN staff\n" "barred to use access control class\n" "allowed to use access control class\n") { struct gsm_bts *bts = vty->index; uint8_t control_class; uint8_t allowed = 0; if (strcmp(argv[1], "allowed") == 0) allowed = 1; control_class = atoi(argv[0]); if (control_class < 8) if (allowed) bts->si_common.rach_control.t3 &= ~(0x1 << control_class); else bts->si_common.rach_control.t3 |= (0x1 << control_class); else if (allowed) bts->si_common.rach_control.t2 &= ~(0x1 << (control_class - 8)); else bts->si_common.rach_control.t2 |= (0x1 << (control_class - 8)); return CMD_SUCCESS; } DEFUN(cfg_bts_ms_max_power, cfg_bts_ms_max_power_cmd, "ms max power <0-40>", "MS Options\n" "Maximum transmit power of the MS\n" "Maximum transmit power of the MS\n" "Maximum transmit power of the MS in dBm") { struct gsm_bts *bts = vty->index; bts->ms_max_power = atoi(argv[0]); return CMD_SUCCESS; } #define CELL_STR "Cell Parameters\n" DEFUN(cfg_bts_cell_resel_hyst, cfg_bts_cell_resel_hyst_cmd, "cell reselection hysteresis <0-14>", CELL_STR "Cell re-selection parameters\n" "Cell Re-Selection Hysteresis in dB\n" "Cell Re-Selection Hysteresis in dB") { struct gsm_bts *bts = vty->index; bts->si_common.cell_sel_par.cell_resel_hyst = atoi(argv[0])/2; return CMD_SUCCESS; } DEFUN(cfg_bts_rxlev_acc_min, cfg_bts_rxlev_acc_min_cmd, "rxlev access min <0-63>", "Minimum RxLev needed for cell access\n" "Minimum RxLev needed for cell access\n" "Minimum RxLev needed for cell access\n" "Minimum RxLev needed for cell access (better than -110dBm)") { struct gsm_bts *bts = vty->index; bts->si_common.cell_sel_par.rxlev_acc_min = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_bts_cell_bar_qualify, cfg_bts_cell_bar_qualify_cmd, "cell bar qualify (0|1)", CELL_STR "Cell Bar Qualify\n" "Cell Bar Qualify\n" "Set CBQ to 0\n" "Set CBQ to 1\n") { struct gsm_bts *bts = vty->index; bts->si_common.cell_ro_sel_par.present = 1; bts->si_common.cell_ro_sel_par.cbq = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_bts_cell_resel_ofs, cfg_bts_cell_resel_ofs_cmd, "cell reselection offset <0-126>", CELL_STR "Cell Re-Selection Parameters\n" "Cell Re-Selection Offset (CRO) in dB\n" "Cell Re-Selection Offset (CRO) in dB\n" ) { struct gsm_bts *bts = vty->index; bts->si_common.cell_ro_sel_par.present = 1; bts->si_common.cell_ro_sel_par.cell_resel_off = atoi(argv[0])/2; return CMD_SUCCESS; } DEFUN(cfg_bts_temp_ofs, cfg_bts_temp_ofs_cmd, "temporary offset <0-60>", "Cell selection temporary negative offset\n" "Cell selection temporary negative offset\n" "Cell selection temporary negative offset in dB") { struct gsm_bts *bts = vty->index; bts->si_common.cell_ro_sel_par.present = 1; bts->si_common.cell_ro_sel_par.temp_offs = atoi(argv[0])/10; return CMD_SUCCESS; } DEFUN(cfg_bts_temp_ofs_inf, cfg_bts_temp_ofs_inf_cmd, "temporary offset infinite", "Cell selection temporary negative offset\n" "Cell selection temporary negative offset\n" "Sets cell selection temporary negative offset to infinity") { struct gsm_bts *bts = vty->index; bts->si_common.cell_ro_sel_par.present = 1; bts->si_common.cell_ro_sel_par.temp_offs = 7; return CMD_SUCCESS; } DEFUN(cfg_bts_penalty_time, cfg_bts_penalty_time_cmd, "penalty time <20-620>", "Cell selection penalty time\n" "Cell selection penalty time\n" "Cell selection penalty time in seconds (by 20s increments)\n") { struct gsm_bts *bts = vty->index; bts->si_common.cell_ro_sel_par.present = 1; bts->si_common.cell_ro_sel_par.penalty_time = (atoi(argv[0])-20)/20; return CMD_SUCCESS; } DEFUN(cfg_bts_penalty_time_rsvd, cfg_bts_penalty_time_rsvd_cmd, "penalty time reserved", "Cell selection penalty time\n" "Cell selection penalty time\n" "Set cell selection penalty time to reserved value 31, " "(indicate that CELL_RESELECT_OFFSET is subtracted from C2 " "and TEMPORARY_OFFSET is ignored)") { struct gsm_bts *bts = vty->index; bts->si_common.cell_ro_sel_par.present = 1; bts->si_common.cell_ro_sel_par.penalty_time = 31; return CMD_SUCCESS; } DEFUN(cfg_bts_per_loc_upd, cfg_bts_per_loc_upd_cmd, "periodic location update <6-1530>", "Periodic Location Updating Interval\n" "Periodic Location Updating Interval\n" "Periodic Location Updating Interval\n" "Periodic Location Updating Interval in Minutes\n") { struct gsm_bts *bts = vty->index; bts->si_common.chan_desc.t3212 = atoi(argv[0]) / 6; return CMD_SUCCESS; } DEFUN(cfg_bts_no_per_loc_upd, cfg_bts_no_per_loc_upd_cmd, "no periodic location update", NO_STR "Periodic Location Updating Interval\n" "Periodic Location Updating Interval\n" "Periodic Location Updating Interval\n") { struct gsm_bts *bts = vty->index; bts->si_common.chan_desc.t3212 = 0; return CMD_SUCCESS; } DEFUN(cfg_bts_radio_link_timeout, cfg_bts_radio_link_timeout_cmd, "radio-link-timeout <4-64>", "Radio link timeout criterion (BTS side)\n" "Radio link timeout value (lost SACCH block)\n") { struct gsm_bts *bts = vty->index; set_radio_link_timeout(&bts->si_common.cell_options, atoi(argv[0])); return CMD_SUCCESS; } #define GPRS_TEXT "GPRS Packet Network\n" DEFUN(cfg_bts_prs_bvci, cfg_bts_gprs_bvci_cmd, "gprs cell bvci <2-65535>", GPRS_TEXT "GPRS Cell Settings\n" "GPRS BSSGP VC Identifier\n" "GPRS BSSGP VC Identifier") { struct gsm_bts *bts = vty->index; if (bts->gprs.mode == BTS_GPRS_NONE) { vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); return CMD_WARNING; } bts->gprs.cell.bvci = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_bts_gprs_nsei, cfg_bts_gprs_nsei_cmd, "gprs nsei <0-65535>", GPRS_TEXT "GPRS NS Entity Identifier\n" "GPRS NS Entity Identifier") { struct gsm_bts *bts = vty->index; if (bts->gprs.mode == BTS_GPRS_NONE) { vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); return CMD_WARNING; } bts->gprs.nse.nsei = atoi(argv[0]); return CMD_SUCCESS; } #define NSVC_TEXT "Network Service Virtual Connection (NS-VC)\n" \ "NSVC Logical Number\n" DEFUN(cfg_bts_gprs_nsvci, cfg_bts_gprs_nsvci_cmd, "gprs nsvc <0-1> nsvci <0-65535>", GPRS_TEXT NSVC_TEXT "NS Virtual Connection Identifier\n" "GPRS NS VC Identifier") { struct gsm_bts *bts = vty->index; int idx = atoi(argv[0]); if (bts->gprs.mode == BTS_GPRS_NONE) { vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); return CMD_WARNING; } bts->gprs.nsvc[idx].nsvci = atoi(argv[1]); return CMD_SUCCESS; } DEFUN(cfg_bts_gprs_nsvc_lport, cfg_bts_gprs_nsvc_lport_cmd, "gprs nsvc <0-1> local udp port <0-65535>", GPRS_TEXT NSVC_TEXT "GPRS NS Local UDP Port\n" "GPRS NS Local UDP Port\n" "GPRS NS Local UDP Port\n" "GPRS NS Local UDP Port Number\n") { struct gsm_bts *bts = vty->index; int idx = atoi(argv[0]); if (bts->gprs.mode == BTS_GPRS_NONE) { vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); return CMD_WARNING; } bts->gprs.nsvc[idx].local_port = atoi(argv[1]); return CMD_SUCCESS; } DEFUN(cfg_bts_gprs_nsvc_rport, cfg_bts_gprs_nsvc_rport_cmd, "gprs nsvc <0-1> remote udp port <0-65535>", GPRS_TEXT NSVC_TEXT "GPRS NS Remote UDP Port\n" "GPRS NS Remote UDP Port\n" "GPRS NS Remote UDP Port\n" "GPRS NS Remote UDP Port Number\n") { struct gsm_bts *bts = vty->index; int idx = atoi(argv[0]); if (bts->gprs.mode == BTS_GPRS_NONE) { vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); return CMD_WARNING; } bts->gprs.nsvc[idx].remote_port = atoi(argv[1]); return CMD_SUCCESS; } DEFUN(cfg_bts_gprs_nsvc_rip, cfg_bts_gprs_nsvc_rip_cmd, "gprs nsvc <0-1> remote ip A.B.C.D", GPRS_TEXT NSVC_TEXT "GPRS NS Remote IP Address\n" "GPRS NS Remote IP Address\n" "GPRS NS Remote IP Address\n") { struct gsm_bts *bts = vty->index; int idx = atoi(argv[0]); struct in_addr ia; if (bts->gprs.mode == BTS_GPRS_NONE) { vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); return CMD_WARNING; } inet_aton(argv[1], &ia); bts->gprs.nsvc[idx].remote_ip = ntohl(ia.s_addr); return CMD_SUCCESS; } DEFUN(cfg_bts_pag_free, cfg_bts_pag_free_cmd, "paging free <-1-1024>", "Paging options\n" "Only page when having a certain amount of free slots\n" "amount of required free paging slots. -1 to disable\n") { struct gsm_bts *bts = vty->index; bts->paging.free_chans_need = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_bts_gprs_ns_timer, cfg_bts_gprs_ns_timer_cmd, "gprs ns timer " NS_TIMERS " <0-255>", GPRS_TEXT "Network Service\n" "Network Service Timer\n" NS_TIMERS_HELP "Timer Value\n") { struct gsm_bts *bts = vty->index; int idx = get_string_value(gprs_ns_timer_strs, argv[0]); int val = atoi(argv[1]); if (bts->gprs.mode == BTS_GPRS_NONE) { vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); return CMD_WARNING; } if (idx < 0 || idx >= ARRAY_SIZE(bts->gprs.nse.timer)) return CMD_WARNING; bts->gprs.nse.timer[idx] = val; return CMD_SUCCESS; } #define BSSGP_TIMERS "(blocking-timer|blocking-retries|unblocking-retries|reset-timer|reset-retries|suspend-timer|suspend-retries|resume-timer|resume-retries|capability-update-timer|capability-update-retries)" #define BSSGP_TIMERS_HELP \ "Tbvc-block timeout\n" \ "Tbvc-block retries\n" \ "Tbvc-unblock retries\n" \ "Tbvcc-reset timeout\n" \ "Tbvc-reset retries\n" \ "Tbvc-suspend timeout\n" \ "Tbvc-suspend retries\n" \ "Tbvc-resume timeout\n" \ "Tbvc-resume retries\n" \ "Tbvc-capa-update timeout\n" \ "Tbvc-capa-update retries\n" DEFUN(cfg_bts_gprs_cell_timer, cfg_bts_gprs_cell_timer_cmd, "gprs cell timer " BSSGP_TIMERS " <0-255>", GPRS_TEXT "Cell / BSSGP\n" "Cell/BSSGP Timer\n" BSSGP_TIMERS_HELP "Timer Value\n") { struct gsm_bts *bts = vty->index; int idx = get_string_value(gprs_bssgp_cfg_strs, argv[0]); int val = atoi(argv[1]); if (bts->gprs.mode == BTS_GPRS_NONE) { vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); return CMD_WARNING; } if (idx < 0 || idx >= ARRAY_SIZE(bts->gprs.cell.timer)) return CMD_WARNING; bts->gprs.cell.timer[idx] = val; return CMD_SUCCESS; } DEFUN(cfg_bts_gprs_rac, cfg_bts_gprs_rac_cmd, "gprs routing area <0-255>", GPRS_TEXT "GPRS Routing Area Code\n" "GPRS Routing Area Code\n" "GPRS Routing Area Code\n") { struct gsm_bts *bts = vty->index; if (bts->gprs.mode == BTS_GPRS_NONE) { vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); return CMD_WARNING; } bts->gprs.rac = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_bts_gprs_net_ctrl_ord, cfg_bts_gprs_net_ctrl_ord_cmd, "gprs network-control-order (nc0|nc1|nc2)", GPRS_TEXT "GPRS Network Control Order\n" "MS controlled cell re-selection, no measurement reporting\n" "MS controlled cell re-selection, MS sends measurement reports\n" "Network controlled cell re-selection, MS sends measurement reports\n") { struct gsm_bts *bts = vty->index; if (bts->gprs.mode == BTS_GPRS_NONE) { vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); return CMD_WARNING; } bts->gprs.net_ctrl_ord = atoi(argv[0] + 2); return CMD_SUCCESS; } DEFUN(cfg_bts_gprs_mode, cfg_bts_gprs_mode_cmd, "gprs mode (none|gprs|egprs)", GPRS_TEXT "GPRS Mode for this BTS\n" "GPRS Disabled on this BTS\n" "GPRS Enabled on this BTS\n" "EGPRS (EDGE) Enabled on this BTS\n") { struct gsm_bts *bts = vty->index; enum bts_gprs_mode mode = bts_gprs_mode_parse(argv[0], NULL); if (!bts_gprs_mode_is_compat(bts, mode)) { vty_out(vty, "This BTS type does not support %s%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } bts->gprs.mode = mode; return CMD_SUCCESS; } #define SI_TEXT "System Information Messages\n" #define SI_TYPE_TEXT "(1|2|3|4|5|6|7|8|9|10|13|16|17|18|19|20|2bis|2ter|2quater|5bis|5ter)" #define SI_TYPE_HELP "System Information Type 1\n" \ "System Information Type 2\n" \ "System Information Type 3\n" \ "System Information Type 4\n" \ "System Information Type 5\n" \ "System Information Type 6\n" \ "System Information Type 7\n" \ "System Information Type 8\n" \ "System Information Type 9\n" \ "System Information Type 10\n" \ "System Information Type 13\n" \ "System Information Type 16\n" \ "System Information Type 17\n" \ "System Information Type 18\n" \ "System Information Type 19\n" \ "System Information Type 20\n" \ "System Information Type 2bis\n" \ "System Information Type 2ter\n" \ "System Information Type 2quater\n" \ "System Information Type 5bis\n" \ "System Information Type 5ter\n" DEFUN(cfg_bts_si_mode, cfg_bts_si_mode_cmd, "system-information " SI_TYPE_TEXT " mode (static|computed)", SI_TEXT SI_TYPE_HELP "System Information Mode\n" "Static user-specified\n" "Dynamic, BSC-computed\n") { struct gsm_bts *bts = vty->index; int type; type = get_string_value(osmo_sitype_strs, argv[0]); if (type < 0) { vty_out(vty, "Error SI Type%s", VTY_NEWLINE); return CMD_WARNING; } if (!strcmp(argv[1], "static")) bts->si_mode_static |= (1 << type); else bts->si_mode_static &= ~(1 << type); return CMD_SUCCESS; } DEFUN(cfg_bts_si_static, cfg_bts_si_static_cmd, "system-information " SI_TYPE_TEXT " static HEXSTRING", SI_TEXT SI_TYPE_HELP "Static System Information filling\n" "Static user-specified SI content in HEX notation\n") { struct gsm_bts *bts = vty->index; int rc, type; type = get_string_value(osmo_sitype_strs, argv[0]); if (type < 0) { vty_out(vty, "Error SI Type%s", VTY_NEWLINE); return CMD_WARNING; } if (!(bts->si_mode_static & (1 << type))) { vty_out(vty, "SI Type %s is not configured in static mode%s", get_value_string(osmo_sitype_strs, type), VTY_NEWLINE); return CMD_WARNING; } /* Fill buffer with padding pattern */ memset(bts->si_buf[type], 0x2b, sizeof(bts->si_buf[type])); /* Parse the user-specified SI in hex format, [partially] overwriting padding */ rc = osmo_hexparse(argv[1], bts->si_buf[type], sizeof(bts->si_buf[0])); if (rc < 0 || rc > sizeof(bts->si_buf[0])) { vty_out(vty, "Error parsing HEXSTRING%s", VTY_NEWLINE); return CMD_WARNING; } /* Mark this SI as present */ bts->si_valid |= (1 << type); return CMD_SUCCESS; } DEFUN(cfg_bts_neigh_mode, cfg_bts_neigh_mode_cmd, "neighbor-list mode (automatic|manual|manual-si5)", "Neighbor List\n" "Mode of Neighbor List generation\n" "Automatically from all BTS in this OpenBSC\n" "Manual\n" "Manual with different lists for SI2 and SI5\n") { struct gsm_bts *bts = vty->index; int mode = get_string_value(bts_neigh_mode_strs, argv[0]); switch (mode) { case NL_MODE_MANUAL_SI5SEP: case NL_MODE_MANUAL: /* make sure we clear the current list when switching to * manual mode */ if (bts->neigh_list_manual_mode == 0) memset(&bts->si_common.data.neigh_list, 0, sizeof(bts->si_common.data.neigh_list)); break; default: break; } bts->neigh_list_manual_mode = mode; return CMD_SUCCESS; } DEFUN(cfg_bts_neigh, cfg_bts_neigh_cmd, "neighbor-list (add|del) arfcn <0-1023>", "Neighbor List\n" "Add to manual neighbor list\n" "Delete from manual neighbor list\n" "ARFCN of neighbor\n" "ARFCN of neighbor\n") { struct gsm_bts *bts = vty->index; struct bitvec *bv = &bts->si_common.neigh_list; uint16_t arfcn = atoi(argv[1]); if (!bts->neigh_list_manual_mode) { vty_out(vty, "%% Cannot configure neighbor list in " "automatic mode%s", VTY_NEWLINE); return CMD_WARNING; } if (!strcmp(argv[0], "add")) bitvec_set_bit_pos(bv, arfcn, 1); else bitvec_set_bit_pos(bv, arfcn, 0); return CMD_SUCCESS; } DEFUN(cfg_bts_si5_neigh, cfg_bts_si5_neigh_cmd, "si5 neighbor-list (add|del) arfcn <0-1023>", "SI5 Neighbor List\n" "SI5 Neighbor List\n" "Add to manual SI5 neighbor list\n" "Delete from SI5 manual neighbor list\n" "ARFCN of neighbor\n" "ARFCN of neighbor\n") { struct gsm_bts *bts = vty->index; struct bitvec *bv = &bts->si_common.si5_neigh_list; uint16_t arfcn = atoi(argv[1]); if (!bts->neigh_list_manual_mode) { vty_out(vty, "%% Cannot configure neighbor list in " "automatic mode%s", VTY_NEWLINE); return CMD_WARNING; } if (!strcmp(argv[0], "add")) bitvec_set_bit_pos(bv, arfcn, 1); else bitvec_set_bit_pos(bv, arfcn, 0); return CMD_SUCCESS; } #define EXCL_RFLOCK_STR "Exclude this BTS from the global RF Lock\n" DEFUN(cfg_bts_excl_rf_lock, cfg_bts_excl_rf_lock_cmd, "rf-lock-exclude", EXCL_RFLOCK_STR) { struct gsm_bts *bts = vty->index; bts->excl_from_rf_lock = 1; return CMD_SUCCESS; } DEFUN(cfg_bts_no_excl_rf_lock, cfg_bts_no_excl_rf_lock_cmd, "no rf-lock-exclude", NO_STR EXCL_RFLOCK_STR) { struct gsm_bts *bts = vty->index; bts->excl_from_rf_lock = 0; return CMD_SUCCESS; } #define FORCE_COMB_SI_STR "Force the generation of a single SI (no ter/bis)\n" DEFUN(cfg_bts_force_comb_si, cfg_bts_force_comb_si_cmd, "force-combined-si", FORCE_COMB_SI_STR) { struct gsm_bts *bts = vty->index; bts->force_combined_si = 1; return CMD_SUCCESS; } DEFUN(cfg_bts_no_force_comb_si, cfg_bts_no_force_comb_si_cmd, "no force-combined-si", NO_STR FORCE_COMB_SI_STR) { struct gsm_bts *bts = vty->index; bts->force_combined_si = 0; return CMD_SUCCESS; } static void _get_codec_from_arg(struct vty *vty, int argc, const char *argv[]) { struct gsm_bts *bts = vty->index; struct bts_codec_conf *codec = &bts->codec; int i; codec->hr = 0; codec->efr = 0; codec->amr = 0; for (i = 0; i < argc; i++) { if (!strcmp(argv[i], "hr")) codec->hr = 1; if (!strcmp(argv[i], "efr")) codec->efr = 1; if (!strcmp(argv[i], "amr")) codec->amr = 1; } } #define CODEC_PAR_STR " (hr|efr|amr)" #define CODEC_HELP_STR "Half Rate\n" \ "Enhanced Full Rate\nAdaptive Multirate\n" DEFUN(cfg_bts_codec0, cfg_bts_codec0_cmd, "codec-support fr", "Codec Support settings\nFullrate\n") { _get_codec_from_arg(vty, 0, argv); return CMD_SUCCESS; } DEFUN(cfg_bts_codec1, cfg_bts_codec1_cmd, "codec-support fr" CODEC_PAR_STR, "Codec Support settings\nFullrate\n" CODEC_HELP_STR) { _get_codec_from_arg(vty, 1, argv); return CMD_SUCCESS; } DEFUN(cfg_bts_codec2, cfg_bts_codec2_cmd, "codec-support fr" CODEC_PAR_STR CODEC_PAR_STR, "Codec Support settings\nFullrate\n" CODEC_HELP_STR CODEC_HELP_STR) { _get_codec_from_arg(vty, 2, argv); return CMD_SUCCESS; } DEFUN(cfg_bts_codec3, cfg_bts_codec3_cmd, "codec-support fr" CODEC_PAR_STR CODEC_PAR_STR CODEC_PAR_STR, "Codec Support settings\nFullrate\n" CODEC_HELP_STR CODEC_HELP_STR CODEC_HELP_STR) { _get_codec_from_arg(vty, 3, argv); return CMD_SUCCESS; } DEFUN(cfg_bts_codec4, cfg_bts_codec4_cmd, "codec-support fr" CODEC_PAR_STR CODEC_PAR_STR CODEC_PAR_STR CODEC_PAR_STR, "Codec Support settings\nFullrate\n" CODEC_HELP_STR CODEC_HELP_STR CODEC_HELP_STR CODEC_HELP_STR) { _get_codec_from_arg(vty, 4, argv); return CMD_SUCCESS; } DEFUN(cfg_bts_depends_on, cfg_bts_depends_on_cmd, "depends-on-bts <0-255>", "This BTS can only be started if another one is up\n" "BTS Number\n") { struct gsm_bts *bts = vty->index; struct gsm_bts *other_bts; int dep = atoi(argv[0]); if (!is_ipaccess_bts(bts)) { vty_out(vty, "This feature is only available for IP systems.%s", VTY_NEWLINE); return CMD_WARNING; } other_bts = gsm_bts_num(bts->network, dep); if (!other_bts || !is_ipaccess_bts(other_bts)) { vty_out(vty, "This feature is only available for IP systems.%s", VTY_NEWLINE); return CMD_WARNING; } if (dep >= bts->nr) { vty_out(vty, "%%Need to depend on an already declared unit.%s", VTY_NEWLINE); return CMD_WARNING; } bts_depend_mark(bts, dep); return CMD_SUCCESS; } DEFUN(cfg_bts_no_depends_on, cfg_bts_no_depends_on_cmd, "depeneds-on-bts <0-255>", NO_STR "This BTS can only be started if another one is up\n" "BTS Number\n") { struct gsm_bts *bts = vty->index; int dep = atoi(argv[0]); bts_depend_clear(bts, dep); return CMD_SUCCESS; } #define AMR_TEXT "Adaptive Multi Rate settings\n" #define AMR_MODE_TEXT "Codec modes to use with AMR codec\n" #define AMR_START_TEXT "Initial codec to use with AMR\n" \ "Automatically\nFirst codec\nSecond codec\nThird codec\nFourth codec\n" #define AMR_TH_TEXT "AMR threshold between codecs\nMS side\nBTS side\n" #define AMR_HY_TEXT "AMR hysteresis between codecs\nMS side\nBTS side\n" static void get_amr_from_arg(struct vty *vty, int argc, const char *argv[], int full) { struct gsm_bts *bts = vty->index; struct amr_multirate_conf *mr = (full) ? &bts->mr_full: &bts->mr_half; struct gsm48_multi_rate_conf *mr_conf = (struct gsm48_multi_rate_conf *) mr->gsm48_ie; int i; mr->gsm48_ie[1] = 0; for (i = 0; i < argc; i++) mr->gsm48_ie[1] |= 1 << atoi(argv[i]); mr_conf->icmi = 0; } static void get_amr_th_from_arg(struct vty *vty, int argc, const char *argv[], int full) { struct gsm_bts *bts = vty->index; struct amr_multirate_conf *mr = (full) ? &bts->mr_full: &bts->mr_half; struct amr_mode *modes; int i; modes = argv[0][0]=='m' ? mr->ms_mode : mr->bts_mode; for (i = 0; i < argc - 1; i++) modes[i].threshold = atoi(argv[i + 1]); } static void get_amr_hy_from_arg(struct vty *vty, int argc, const char *argv[], int full) { struct gsm_bts *bts = vty->index; struct amr_multirate_conf *mr = (full) ? &bts->mr_full: &bts->mr_half; struct amr_mode *modes; int i; modes = argv[0][0]=='m' ? mr->ms_mode : mr->bts_mode; for (i = 0; i < argc - 1; i++) modes[i].hysteresis = atoi(argv[i + 1]); } static void get_amr_start_from_arg(struct vty *vty, const char *argv[], int full) { struct gsm_bts *bts = vty->index; struct amr_multirate_conf *mr = (full) ? &bts->mr_full: &bts->mr_half; struct gsm48_multi_rate_conf *mr_conf = (struct gsm48_multi_rate_conf *) mr->gsm48_ie; int num = 0, i; for (i = 0; i < ((full) ? 8 : 6); i++) { if ((mr->gsm48_ie[1] & (1 << i))) { num++; } } if (argv[0][0] == 'a' || num == 0) mr_conf->icmi = 0; else { mr_conf->icmi = 1; if (num < atoi(argv[0])) mr_conf->smod = num - 1; else mr_conf->smod = atoi(argv[0]) - 1; } } #define AMR_TCHF_PAR_STR " (0|1|2|3|4|5|6|7)" #define AMR_TCHF_HELP_STR "4,75k\n5,15k\n5,90k\n6,70k\n7,40k\n7,95k\n" \ "10,2k\n12,2k\n" #define AMR_TCHH_PAR_STR " (0|1|2|3|4|5)" #define AMR_TCHH_HELP_STR "4,75k\n5,15k\n5,90k\n6,70k\n7,40k\n7,95k\n" #define AMR_TH_HELP_STR "Threshold between codec 1 and 2\n" #define AMR_HY_HELP_STR "Hysteresis between codec 1 and 2\n" DEFUN(cfg_bts_amr_fr_modes1, cfg_bts_amr_fr_modes1_cmd, "amr tch-f modes" AMR_TCHF_PAR_STR, AMR_TEXT "Full Rate\n" AMR_MODE_TEXT AMR_TCHF_HELP_STR) { get_amr_from_arg(vty, 1, argv, 1); return CMD_SUCCESS; } DEFUN(cfg_bts_amr_fr_modes2, cfg_bts_amr_fr_modes2_cmd, "amr tch-f modes" AMR_TCHF_PAR_STR AMR_TCHF_PAR_STR, AMR_TEXT "Full Rate\n" AMR_MODE_TEXT AMR_TCHF_HELP_STR AMR_TCHF_HELP_STR) { get_amr_from_arg(vty, 2, argv, 1); return CMD_SUCCESS; } DEFUN(cfg_bts_amr_fr_modes3, cfg_bts_amr_fr_modes3_cmd, "amr tch-f modes" AMR_TCHF_PAR_STR AMR_TCHF_PAR_STR AMR_TCHF_PAR_STR, AMR_TEXT "Full Rate\n" AMR_MODE_TEXT AMR_TCHF_HELP_STR AMR_TCHF_HELP_STR AMR_TCHF_HELP_STR) { get_amr_from_arg(vty, 3, argv, 1); return CMD_SUCCESS; } DEFUN(cfg_bts_amr_fr_modes4, cfg_bts_amr_fr_modes4_cmd, "amr tch-f modes" AMR_TCHF_PAR_STR AMR_TCHF_PAR_STR AMR_TCHF_PAR_STR AMR_TCHF_PAR_STR, AMR_TEXT "Full Rate\n" AMR_MODE_TEXT AMR_TCHF_HELP_STR AMR_TCHF_HELP_STR AMR_TCHF_HELP_STR AMR_TCHF_HELP_STR) { get_amr_from_arg(vty, 4, argv, 1); return CMD_SUCCESS; } DEFUN(cfg_bts_amr_fr_start_mode, cfg_bts_amr_fr_start_mode_cmd, "amr tch-f start-mode (auto|1|2|3|4)", AMR_TEXT "Full Rate\n" AMR_START_TEXT) { get_amr_start_from_arg(vty, argv, 1); return CMD_SUCCESS; } DEFUN(cfg_bts_amr_fr_thres1, cfg_bts_amr_fr_thres1_cmd, "amr tch-f threshold (ms|bts) <0-63>", AMR_TEXT "Full Rate\n" AMR_TH_TEXT AMR_TH_HELP_STR) { get_amr_th_from_arg(vty, 2, argv, 1); return CMD_SUCCESS; } DEFUN(cfg_bts_amr_fr_thres2, cfg_bts_amr_fr_thres2_cmd, "amr tch-f threshold (ms|bts) <0-63> <0-63>", AMR_TEXT "Full Rate\n" AMR_TH_TEXT AMR_TH_HELP_STR AMR_TH_HELP_STR) { get_amr_th_from_arg(vty, 3, argv, 1); return CMD_SUCCESS; } DEFUN(cfg_bts_amr_fr_thres3, cfg_bts_amr_fr_thres3_cmd, "amr tch-f threshold (ms|bts) <0-63> <0-63> <0-63>", AMR_TEXT "Full Rate\n" AMR_TH_TEXT AMR_TH_HELP_STR AMR_TH_HELP_STR AMR_TH_HELP_STR) { get_amr_th_from_arg(vty, 4, argv, 1); return CMD_SUCCESS; } DEFUN(cfg_bts_amr_fr_hyst1, cfg_bts_amr_fr_hyst1_cmd, "amr tch-f hysteresis (ms|bts) <0-15>", AMR_TEXT "Full Rate\n" AMR_HY_TEXT AMR_HY_HELP_STR) { get_amr_hy_from_arg(vty, 2, argv, 1); return CMD_SUCCESS; } DEFUN(cfg_bts_amr_fr_hyst2, cfg_bts_amr_fr_hyst2_cmd, "amr tch-f hysteresis (ms|bts) <0-15> <0-15>", AMR_TEXT "Full Rate\n" AMR_HY_TEXT AMR_HY_HELP_STR AMR_HY_HELP_STR) { get_amr_hy_from_arg(vty, 3, argv, 1); return CMD_SUCCESS; } DEFUN(cfg_bts_amr_fr_hyst3, cfg_bts_amr_fr_hyst3_cmd, "amr tch-f hysteresis (ms|bts) <0-15> <0-15> <0-15>", AMR_TEXT "Full Rate\n" AMR_HY_TEXT AMR_HY_HELP_STR AMR_HY_HELP_STR AMR_HY_HELP_STR) { get_amr_hy_from_arg(vty, 4, argv, 1); return CMD_SUCCESS; } DEFUN(cfg_bts_amr_hr_modes1, cfg_bts_amr_hr_modes1_cmd, "amr tch-h modes" AMR_TCHH_PAR_STR, AMR_TEXT "Half Rate\n" AMR_MODE_TEXT AMR_TCHH_HELP_STR) { get_amr_from_arg(vty, 1, argv, 0); return CMD_SUCCESS; } DEFUN(cfg_bts_amr_hr_modes2, cfg_bts_amr_hr_modes2_cmd, "amr tch-h modes" AMR_TCHH_PAR_STR AMR_TCHH_PAR_STR, AMR_TEXT "Half Rate\n" AMR_MODE_TEXT AMR_TCHH_HELP_STR AMR_TCHH_HELP_STR) { get_amr_from_arg(vty, 2, argv, 0); return CMD_SUCCESS; } DEFUN(cfg_bts_amr_hr_modes3, cfg_bts_amr_hr_modes3_cmd, "amr tch-h modes" AMR_TCHH_PAR_STR AMR_TCHH_PAR_STR AMR_TCHH_PAR_STR, AMR_TEXT "Half Rate\n" AMR_MODE_TEXT AMR_TCHH_HELP_STR AMR_TCHH_HELP_STR AMR_TCHH_HELP_STR) { get_amr_from_arg(vty, 3, argv, 0); return CMD_SUCCESS; } DEFUN(cfg_bts_amr_hr_modes4, cfg_bts_amr_hr_modes4_cmd, "amr tch-h modes" AMR_TCHH_PAR_STR AMR_TCHH_PAR_STR AMR_TCHH_PAR_STR AMR_TCHH_PAR_STR, AMR_TEXT "Half Rate\n" AMR_MODE_TEXT AMR_TCHH_HELP_STR AMR_TCHH_HELP_STR AMR_TCHH_HELP_STR AMR_TCHH_HELP_STR) { get_amr_from_arg(vty, 4, argv, 0); return CMD_SUCCESS; } DEFUN(cfg_bts_amr_hr_start_mode, cfg_bts_amr_hr_start_mode_cmd, "amr tch-h start-mode (auto|1|2|3|4)", AMR_TEXT "Half Rate\n" AMR_START_TEXT) { get_amr_start_from_arg(vty, argv, 0); return CMD_SUCCESS; } DEFUN(cfg_bts_amr_hr_thres1, cfg_bts_amr_hr_thres1_cmd, "amr tch-h threshold (ms|bts) <0-63>", AMR_TEXT "Half Rate\n" AMR_TH_TEXT AMR_TH_HELP_STR) { get_amr_th_from_arg(vty, 2, argv, 0); return CMD_SUCCESS; } DEFUN(cfg_bts_amr_hr_thres2, cfg_bts_amr_hr_thres2_cmd, "amr tch-h threshold (ms|bts) <0-63> <0-63>", AMR_TEXT "Half Rate\n" AMR_TH_TEXT AMR_TH_HELP_STR AMR_TH_HELP_STR) { get_amr_th_from_arg(vty, 3, argv, 0); return CMD_SUCCESS; } DEFUN(cfg_bts_amr_hr_thres3, cfg_bts_amr_hr_thres3_cmd, "amr tch-h threshold (ms|bts) <0-63> <0-63> <0-63>", AMR_TEXT "Half Rate\n" AMR_TH_TEXT AMR_TH_HELP_STR AMR_TH_HELP_STR AMR_TH_HELP_STR) { get_amr_th_from_arg(vty, 4, argv, 0); return CMD_SUCCESS; } DEFUN(cfg_bts_amr_hr_hyst1, cfg_bts_amr_hr_hyst1_cmd, "amr tch-h hysteresis (ms|bts) <0-15>", AMR_TEXT "Half Rate\n" AMR_HY_TEXT AMR_HY_HELP_STR) { get_amr_hy_from_arg(vty, 2, argv, 0); return CMD_SUCCESS; } DEFUN(cfg_bts_amr_hr_hyst2, cfg_bts_amr_hr_hyst2_cmd, "amr tch-h hysteresis (ms|bts) <0-15> <0-15>", AMR_TEXT "Half Rate\n" AMR_HY_TEXT AMR_HY_HELP_STR AMR_HY_HELP_STR) { get_amr_hy_from_arg(vty, 3, argv, 0); return CMD_SUCCESS; } DEFUN(cfg_bts_amr_hr_hyst3, cfg_bts_amr_hr_hyst3_cmd, "amr tch-h hysteresis (ms|bts) <0-15> <0-15> <0-15>", AMR_TEXT "Half Rate\n" AMR_HY_TEXT AMR_HY_HELP_STR AMR_HY_HELP_STR AMR_HY_HELP_STR) { get_amr_hy_from_arg(vty, 4, argv, 0); return CMD_SUCCESS; } #define TRX_TEXT "Radio Transceiver\n" /* per TRX configuration */ DEFUN(cfg_trx, cfg_trx_cmd, "trx <0-255>", TRX_TEXT "Select a TRX to configure") { int trx_nr = atoi(argv[0]); struct gsm_bts *bts = vty->index; struct gsm_bts_trx *trx; if (trx_nr > bts->num_trx) { vty_out(vty, "%% The next unused TRX number in this BTS is %u%s", bts->num_trx, VTY_NEWLINE); return CMD_WARNING; } else if (trx_nr == bts->num_trx) { /* we need to allocate a new one */ trx = gsm_bts_trx_alloc(bts); } else trx = gsm_bts_trx_num(bts, trx_nr); if (!trx) return CMD_WARNING; vty->index = trx; vty->index_sub = &trx->description; vty->node = TRX_NODE; return CMD_SUCCESS; } DEFUN(cfg_trx_arfcn, cfg_trx_arfcn_cmd, "arfcn <0-1023>", "Set the ARFCN for this TRX\n" "Absolute Radio Frequency Channel Number\n") { int arfcn = atoi(argv[0]); struct gsm_bts_trx *trx = vty->index; /* FIXME: check if this ARFCN is supported by this TRX */ trx->arfcn = arfcn; /* FIXME: patch ARFCN into SYSTEM INFORMATION */ /* FIXME: use OML layer to update the ARFCN */ /* FIXME: use RSL layer to update SYSTEM INFORMATION */ return CMD_SUCCESS; } DEFUN(cfg_trx_nominal_power, cfg_trx_nominal_power_cmd, "nominal power <0-100>", "Nominal TRX RF Power in dBm\n" "Nominal TRX RF Power in dBm\n" "Nominal TRX RF Power in dBm\n") { struct gsm_bts_trx *trx = vty->index; trx->nominal_power = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_trx_max_power_red, cfg_trx_max_power_red_cmd, "max_power_red <0-100>", "Reduction of maximum BS RF Power (relative to nominal power)\n" "Reduction of maximum BS RF Power in dB\n") { int maxpwr_r = atoi(argv[0]); struct gsm_bts_trx *trx = vty->index; int upper_limit = 24; /* default 12.21 max power red. */ /* FIXME: check if our BTS type supports more than 12 */ if (maxpwr_r < 0 || maxpwr_r > upper_limit) { vty_out(vty, "%% Power %d dB is not in the valid range%s", maxpwr_r, VTY_NEWLINE); return CMD_WARNING; } if (maxpwr_r & 1) { vty_out(vty, "%% Power %d dB is not an even value%s", maxpwr_r, VTY_NEWLINE); return CMD_WARNING; } trx->max_power_red = maxpwr_r; /* FIXME: make sure we update this using OML */ return CMD_SUCCESS; } DEFUN(cfg_trx_rsl_e1, cfg_trx_rsl_e1_cmd, "rsl e1 line E1_LINE timeslot <1-31> sub-slot (0|1|2|3|full)", "RSL Parameters\n" "E1/T1 interface to be used for RSL\n" "E1/T1 interface to be used for RSL\n" "E1/T1 Line Number to be used for RSL\n" "E1/T1 Timeslot to be used for RSL\n" "E1/T1 Timeslot to be used for RSL\n" "E1/T1 Sub-slot to be used for RSL\n" "E1/T1 Sub-slot 0 is to be used for RSL\n" "E1/T1 Sub-slot 1 is to be used for RSL\n" "E1/T1 Sub-slot 2 is to be used for RSL\n" "E1/T1 Sub-slot 3 is to be used for RSL\n" "E1/T1 full timeslot is to be used for RSL\n") { struct gsm_bts_trx *trx = vty->index; parse_e1_link(&trx->rsl_e1_link, argv[0], argv[1], argv[2]); return CMD_SUCCESS; } DEFUN(cfg_trx_rsl_e1_tei, cfg_trx_rsl_e1_tei_cmd, "rsl e1 tei <0-63>", "RSL Parameters\n" "Set the TEI to be used for RSL\n" "Set the TEI to be used for RSL\n" "TEI to be used for RSL\n") { struct gsm_bts_trx *trx = vty->index; trx->rsl_tei = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_trx_rf_locked, cfg_trx_rf_locked_cmd, "rf_locked (0|1)", "Set or unset the RF Locking (Turn off RF of the TRX)\n" "TRX is NOT RF locked (active)\n" "TRX is RF locked (turned off)\n") { int locked = atoi(argv[0]); struct gsm_bts_trx *trx = vty->index; gsm_trx_lock_rf(trx, locked); return CMD_SUCCESS; } /* per TS configuration */ DEFUN(cfg_ts, cfg_ts_cmd, "timeslot <0-7>", "Select a Timeslot to configure\n" "Timeslot number\n") { int ts_nr = atoi(argv[0]); struct gsm_bts_trx *trx = vty->index; struct gsm_bts_trx_ts *ts; if (ts_nr >= TRX_NR_TS) { vty_out(vty, "%% A GSM TRX only has %u Timeslots per TRX%s", TRX_NR_TS, VTY_NEWLINE); return CMD_WARNING; } ts = &trx->ts[ts_nr]; vty->index = ts; vty->node = TS_NODE; return CMD_SUCCESS; } DEFUN(cfg_ts_pchan, cfg_ts_pchan_cmd, "phys_chan_config PCHAN", /* dynamically generated! */ "Physical Channel configuration (TCH/SDCCH/...)\n" "Physical Channel\n") { struct gsm_bts_trx_ts *ts = vty->index; int pchanc; pchanc = gsm_pchan_parse(argv[0]); if (pchanc < 0) return CMD_WARNING; ts->pchan = pchanc; return CMD_SUCCESS; } /* used for backwards compatibility with old config files that still * have uppercase pchan type names */ DEFUN_HIDDEN(cfg_ts_pchan_compat, cfg_ts_pchan_compat_cmd, "phys_chan_config PCHAN", "Physical Channel configuration (TCH/SDCCH/...)\n" "Physical Channel\n") { struct gsm_bts_trx_ts *ts = vty->index; int pchanc; pchanc = gsm_pchan_parse(argv[0]); if (pchanc < 0) return CMD_WARNING; ts->pchan = pchanc; return CMD_SUCCESS; } DEFUN(cfg_ts_tsc, cfg_ts_tsc_cmd, "training_sequence_code <0-7>", "Training Sequence Code of the Timeslot\n" "TSC\n") { struct gsm_bts_trx_ts *ts = vty->index; if (!gsm_bts_has_feature(ts->trx->bts, BTS_FEAT_MULTI_TSC)) { vty_out(vty, "%% This BTS does not support a TSC != BCC, " "falling back to BCC%s", VTY_NEWLINE); ts->tsc = -1; return CMD_WARNING; } ts->tsc = atoi(argv[0]); return CMD_SUCCESS; } #define HOPPING_STR "Configure frequency hopping\n" DEFUN(cfg_ts_hopping, cfg_ts_hopping_cmd, "hopping enabled (0|1)", HOPPING_STR "Enable or disable frequency hopping\n" "Disable frequency hopping\n" "Enable frequency hopping\n") { struct gsm_bts_trx_ts *ts = vty->index; int enabled = atoi(argv[0]); if (enabled && !gsm_bts_has_feature(ts->trx->bts, BTS_FEAT_HOPPING)) { vty_out(vty, "BTS model does not support hopping%s", VTY_NEWLINE); return CMD_WARNING; } ts->hopping.enabled = enabled; return CMD_SUCCESS; } DEFUN(cfg_ts_hsn, cfg_ts_hsn_cmd, "hopping sequence-number <0-63>", HOPPING_STR "Which hopping sequence to use for this channel\n" "Hopping Sequence Number (HSN)\n") { struct gsm_bts_trx_ts *ts = vty->index; ts->hopping.hsn = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_ts_maio, cfg_ts_maio_cmd, "hopping maio <0-63>", HOPPING_STR "Which hopping MAIO to use for this channel\n" "Mobile Allocation Index Offset (MAIO)\n") { struct gsm_bts_trx_ts *ts = vty->index; ts->hopping.maio = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_ts_arfcn_add, cfg_ts_arfcn_add_cmd, "hopping arfcn add <0-1023>", HOPPING_STR "Configure hopping ARFCN list\n" "Add an entry to the hopping ARFCN list\n" "ARFCN\n") { struct gsm_bts_trx_ts *ts = vty->index; int arfcn = atoi(argv[0]); bitvec_set_bit_pos(&ts->hopping.arfcns, arfcn, 1); return CMD_SUCCESS; } DEFUN(cfg_ts_arfcn_del, cfg_ts_arfcn_del_cmd, "hopping arfcn del <0-1023>", HOPPING_STR "Configure hopping ARFCN list\n" "Delete an entry to the hopping ARFCN list\n" "ARFCN\n") { struct gsm_bts_trx_ts *ts = vty->index; int arfcn = atoi(argv[0]); bitvec_set_bit_pos(&ts->hopping.arfcns, arfcn, 0); return CMD_SUCCESS; } DEFUN(cfg_ts_e1_subslot, cfg_ts_e1_subslot_cmd, "e1 line E1_LINE timeslot <1-31> sub-slot (0|1|2|3|full)", "E1/T1 channel connected to this on-air timeslot\n" "E1/T1 channel connected to this on-air timeslot\n" "E1/T1 line connected to this on-air timeslot\n" "E1/T1 timeslot connected to this on-air timeslot\n" "E1/T1 timeslot connected to this on-air timeslot\n" "E1/T1 sub-slot connected to this on-air timeslot\n" "E1/T1 sub-slot 0 connected to this on-air timeslot\n" "E1/T1 sub-slot 1 connected to this on-air timeslot\n" "E1/T1 sub-slot 2 connected to this on-air timeslot\n" "E1/T1 sub-slot 3 connected to this on-air timeslot\n" "Full E1/T1 timeslot connected to this on-air timeslot\n") { struct gsm_bts_trx_ts *ts = vty->index; parse_e1_link(&ts->e1_link, argv[0], argv[1], argv[2]); return CMD_SUCCESS; } void openbsc_vty_print_statistics(struct vty *vty, struct gsm_network *net) { vty_out(vty, "Channel Requests : %lu total, %lu no channel%s", osmo_counter_get(net->stats.chreq.total), osmo_counter_get(net->stats.chreq.no_channel), VTY_NEWLINE); vty_out(vty, "Channel Failures : %lu rf_failures, %lu rll failures%s", osmo_counter_get(net->stats.chan.rf_fail), osmo_counter_get(net->stats.chan.rll_err), VTY_NEWLINE); vty_out(vty, "Paging : %lu attempted, %lu complete, %lu expired%s", osmo_counter_get(net->stats.paging.attempted), osmo_counter_get(net->stats.paging.completed), osmo_counter_get(net->stats.paging.expired), VTY_NEWLINE); vty_out(vty, "BTS failures : %lu OML, %lu RSL%s", osmo_counter_get(net->stats.bts.oml_fail), osmo_counter_get(net->stats.bts.rsl_fail), VTY_NEWLINE); } DEFUN(drop_bts, drop_bts_cmd, "drop bts connection <0-65535> (oml|rsl)", "Debug/Simulation command to drop Abis/IP BTS\n" "Debug/Simulation command to drop Abis/IP BTS\n" "Debug/Simulation command to drop Abis/IP BTS\n" "BTS NR\n" "Drop OML Connection\n" "Drop RSL Connection\n") { struct gsm_network *gsmnet; struct gsm_bts_trx *trx; struct gsm_bts *bts; unsigned int bts_nr; gsmnet = gsmnet_from_vty(vty); bts_nr = atoi(argv[0]); if (bts_nr >= gsmnet->num_bts) { vty_out(vty, "BTS number must be between 0 and %d. It was %d.%s", gsmnet->num_bts, bts_nr, VTY_NEWLINE); return CMD_WARNING; } bts = gsm_bts_num(gsmnet, bts_nr); if (!bts) { vty_out(vty, "BTS Nr. %d could not be found.%s", bts_nr, VTY_NEWLINE); return CMD_WARNING; } if (!is_ipaccess_bts(bts)) { vty_out(vty, "This command only works for ipaccess.%s", VTY_NEWLINE); return CMD_WARNING; } /* close all connections */ if (strcmp(argv[1], "oml") == 0) { ipaccess_drop_oml(bts); } else if (strcmp(argv[1], "rsl") == 0) { /* close all rsl connections */ llist_for_each_entry(trx, &bts->trx_list, list) { ipaccess_drop_rsl(trx); } } else { vty_out(vty, "Argument must be 'oml# or 'rsl'.%s", VTY_NEWLINE); return CMD_WARNING; } return CMD_SUCCESS; } DEFUN(smscb_cmd, smscb_cmd_cmd, "bts <0-255> smscb-command <1-4> HEXSTRING", "BTS related commands\n" "BTS Number\n" "SMS Cell Broadcast\n" "Last Valid Block\n" "Hex Encoded SMSCB message (up to 88 octets)\n") { struct gsm_bts *bts; int bts_nr = atoi(argv[0]); int last_block = atoi(argv[1]); struct rsl_ie_cb_cmd_type cb_cmd; uint8_t buf[88]; int rc; bts = gsm_bts_num(bsc_gsmnet, bts_nr); if (!bts) { vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE); return CMD_WARNING; } rc = osmo_hexparse(argv[2], buf, sizeof(buf)); if (rc < 0 || rc > sizeof(buf)) { vty_out(vty, "Error parsing HEXSTRING%s", VTY_NEWLINE); return CMD_WARNING; } cb_cmd.spare = 0; cb_cmd.def_bcast = 0; cb_cmd.command = RSL_CB_CMD_TYPE_NORMAL; switch (last_block) { case 1: cb_cmd.last_block = RSL_CB_CMD_LASTBLOCK_1; break; case 2: cb_cmd.last_block = RSL_CB_CMD_LASTBLOCK_2; break; case 3: cb_cmd.last_block = RSL_CB_CMD_LASTBLOCK_3; break; case 4: cb_cmd.last_block = RSL_CB_CMD_LASTBLOCK_4; break; } rsl_sms_cb_command(bts, RSL_CHAN_SDCCH4_ACCH, cb_cmd, buf, rc); return CMD_SUCCESS; } DEFUN(pdch_act, pdch_act_cmd, "bts <0-255> trx <0-255> timeslot <0-7> pdch (activate|deactivate)", "BTS related commands\n" "BTS Number\n" "Transceiver\n" "Transceiver Number\n" "TRX Timeslot\n" "Timeslot Number\n" "Packet Data Channel\n" "Activate Dynamic PDCH/TCH (-> PDCH mode)\n" "Deactivate Dynamic PDCH/TCH (-> TCH mode)\n") { struct gsm_bts *bts; struct gsm_bts_trx *trx; struct gsm_bts_trx_ts *ts; int bts_nr = atoi(argv[0]); int trx_nr = atoi(argv[1]); int ts_nr = atoi(argv[2]); int activate; bts = gsm_bts_num(bsc_gsmnet, bts_nr); if (!bts) { vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE); return CMD_WARNING; } if (!is_ipaccess_bts(bts)) { vty_out(vty, "%% This command only works for ipaccess BTS%s", VTY_NEWLINE); return CMD_WARNING; } trx = gsm_bts_trx_num(bts, trx_nr); if (!trx) { vty_out(vty, "%% No such TRX (%d)%s", trx_nr, VTY_NEWLINE); return CMD_WARNING; } ts = &trx->ts[ts_nr]; if (ts->pchan != GSM_PCHAN_TCH_F_PDCH) { vty_out(vty, "%% Timeslot %u is not in dynamic TCH_F/PDCH " "mode%s", ts_nr, VTY_NEWLINE); return CMD_WARNING; } if (!strcmp(argv[3], "activate")) activate = 1; else activate = 0; rsl_ipacc_pdch_activate(ts, activate); return CMD_SUCCESS; } extern int bsc_vty_init_extra(void); extern const char *openbsc_copyright; int bsc_vty_init(const struct log_info *cat) { cfg_ts_pchan_cmd.string = vty_cmd_string_from_valstr(tall_bsc_ctx, gsm_pchant_names, "phys_chan_config (", "|", ")", VTY_DO_LOWER); cfg_ts_pchan_cmd.doc = vty_cmd_string_from_valstr(tall_bsc_ctx, gsm_pchant_descs, "Physical Channel Combination\n", "\n", "", 0); cfg_bts_type_cmd.string = vty_cmd_string_from_valstr(tall_bsc_ctx, bts_type_names, "type (", "|", ")", VTY_DO_LOWER); cfg_bts_type_cmd.doc = vty_cmd_string_from_valstr(tall_bsc_ctx, bts_type_descs, "BTS Vendor/Type\n", "\n", "", 0); install_element_ve(&show_net_cmd); install_element_ve(&show_bts_cmd); install_element_ve(&show_trx_cmd); install_element_ve(&show_ts_cmd); install_element_ve(&show_lchan_cmd); install_element_ve(&show_lchan_summary_cmd); install_element_ve(&show_paging_cmd); install_element_ve(&show_paging_group_cmd); logging_vty_add_cmds(cat); install_element(CONFIG_NODE, &cfg_net_cmd); install_node(&net_node, config_write_net); vty_install_default(GSMNET_NODE); install_element(GSMNET_NODE, &cfg_net_ncc_cmd); install_element(GSMNET_NODE, &cfg_net_mnc_cmd); install_element(GSMNET_NODE, &cfg_net_name_short_cmd); install_element(GSMNET_NODE, &cfg_net_name_long_cmd); install_element(GSMNET_NODE, &cfg_net_auth_policy_cmd); install_element(GSMNET_NODE, &cfg_net_reject_cause_cmd); install_element(GSMNET_NODE, &cfg_net_encryption_cmd); install_element(GSMNET_NODE, &cfg_net_neci_cmd); install_element(GSMNET_NODE, &cfg_net_rrlp_mode_cmd); install_element(GSMNET_NODE, &cfg_net_mm_info_cmd); install_element(GSMNET_NODE, &cfg_net_handover_cmd); install_element(GSMNET_NODE, &cfg_net_ho_win_rxlev_avg_cmd); install_element(GSMNET_NODE, &cfg_net_ho_win_rxqual_avg_cmd); install_element(GSMNET_NODE, &cfg_net_ho_win_rxlev_avg_neigh_cmd); install_element(GSMNET_NODE, &cfg_net_ho_pwr_interval_cmd); install_element(GSMNET_NODE, &cfg_net_ho_pwr_hysteresis_cmd); install_element(GSMNET_NODE, &cfg_net_ho_max_distance_cmd); install_element(GSMNET_NODE, &cfg_net_T3101_cmd); install_element(GSMNET_NODE, &cfg_net_T3103_cmd); install_element(GSMNET_NODE, &cfg_net_T3105_cmd); install_element(GSMNET_NODE, &cfg_net_T3107_cmd); install_element(GSMNET_NODE, &cfg_net_T3109_cmd); install_element(GSMNET_NODE, &cfg_net_T3111_cmd); install_element(GSMNET_NODE, &cfg_net_T3113_cmd); install_element(GSMNET_NODE, &cfg_net_T3115_cmd); install_element(GSMNET_NODE, &cfg_net_T3117_cmd); install_element(GSMNET_NODE, &cfg_net_T3119_cmd); install_element(GSMNET_NODE, &cfg_net_T3122_cmd); install_element(GSMNET_NODE, &cfg_net_T3141_cmd); install_element(GSMNET_NODE, &cfg_net_dtx_cmd); install_element(GSMNET_NODE, &cfg_net_subscr_keep_cmd); install_element(GSMNET_NODE, &cfg_net_pag_any_tch_cmd); install_element(GSMNET_NODE, &cfg_bts_cmd); install_node(&bts_node, config_write_bts); vty_install_default(BTS_NODE); install_element(BTS_NODE, &cfg_bts_type_cmd); install_element(BTS_NODE, &cfg_description_cmd); install_element(BTS_NODE, &cfg_no_description_cmd); install_element(BTS_NODE, &cfg_bts_band_cmd); install_element(BTS_NODE, &cfg_bts_ci_cmd); install_element(BTS_NODE, &cfg_bts_lac_cmd); install_element(BTS_NODE, &cfg_bts_tsc_cmd); install_element(BTS_NODE, &cfg_bts_bsic_cmd); install_element(BTS_NODE, &cfg_bts_unit_id_cmd); install_element(BTS_NODE, &cfg_bts_rsl_ip_cmd); install_element(BTS_NODE, &cfg_bts_timezone_cmd); install_element(BTS_NODE, &cfg_bts_timezone_dst_cmd); install_element(BTS_NODE, &cfg_bts_no_timezone_cmd); install_element(BTS_NODE, &cfg_bts_nokia_site_skip_reset_cmd); install_element(BTS_NODE, &cfg_bts_nokia_site_no_loc_rel_cnf_cmd); install_element(BTS_NODE, &cfg_bts_nokia_site_bts_reset_timer_cnf_cmd); install_element(BTS_NODE, &cfg_bts_stream_id_cmd); install_element(BTS_NODE, &cfg_bts_oml_e1_cmd); install_element(BTS_NODE, &cfg_bts_oml_e1_tei_cmd); install_element(BTS_NODE, &cfg_bts_challoc_cmd); install_element(BTS_NODE, &cfg_bts_rach_tx_integer_cmd); install_element(BTS_NODE, &cfg_bts_rach_max_trans_cmd); install_element(BTS_NODE, &cfg_bts_chan_desc_att_cmd); install_element(BTS_NODE, &cfg_bts_chan_desc_bs_pa_mfrms_cmd); install_element(BTS_NODE, &cfg_bts_chan_desc_bs_ag_blks_res_cmd); install_element(BTS_NODE, &cfg_bts_rach_nm_b_thresh_cmd); install_element(BTS_NODE, &cfg_bts_rach_nm_ldavg_cmd); install_element(BTS_NODE, &cfg_bts_cell_barred_cmd); install_element(BTS_NODE, &cfg_bts_rach_ec_allowed_cmd); install_element(BTS_NODE, &cfg_bts_rach_ac_class_cmd); install_element(BTS_NODE, &cfg_bts_ms_max_power_cmd); install_element(BTS_NODE, &cfg_bts_per_loc_upd_cmd); install_element(BTS_NODE, &cfg_bts_no_per_loc_upd_cmd); install_element(BTS_NODE, &cfg_bts_cell_resel_hyst_cmd); install_element(BTS_NODE, &cfg_bts_rxlev_acc_min_cmd); install_element(BTS_NODE, &cfg_bts_cell_bar_qualify_cmd); install_element(BTS_NODE, &cfg_bts_cell_resel_ofs_cmd); install_element(BTS_NODE, &cfg_bts_temp_ofs_cmd); install_element(BTS_NODE, &cfg_bts_temp_ofs_inf_cmd); install_element(BTS_NODE, &cfg_bts_penalty_time_cmd); install_element(BTS_NODE, &cfg_bts_penalty_time_rsvd_cmd); install_element(BTS_NODE, &cfg_bts_radio_link_timeout_cmd); install_element(BTS_NODE, &cfg_bts_gprs_mode_cmd); install_element(BTS_NODE, &cfg_bts_gprs_ns_timer_cmd); install_element(BTS_NODE, &cfg_bts_gprs_rac_cmd); install_element(BTS_NODE, &cfg_bts_gprs_net_ctrl_ord_cmd); install_element(BTS_NODE, &cfg_bts_gprs_bvci_cmd); install_element(BTS_NODE, &cfg_bts_gprs_cell_timer_cmd); install_element(BTS_NODE, &cfg_bts_gprs_nsei_cmd); install_element(BTS_NODE, &cfg_bts_gprs_nsvci_cmd); install_element(BTS_NODE, &cfg_bts_gprs_nsvc_lport_cmd); install_element(BTS_NODE, &cfg_bts_gprs_nsvc_rport_cmd); install_element(BTS_NODE, &cfg_bts_gprs_nsvc_rip_cmd); install_element(BTS_NODE, &cfg_bts_pag_free_cmd); install_element(BTS_NODE, &cfg_bts_si_mode_cmd); install_element(BTS_NODE, &cfg_bts_si_static_cmd); install_element(BTS_NODE, &cfg_bts_neigh_mode_cmd); install_element(BTS_NODE, &cfg_bts_neigh_cmd); install_element(BTS_NODE, &cfg_bts_si5_neigh_cmd); install_element(BTS_NODE, &cfg_bts_excl_rf_lock_cmd); install_element(BTS_NODE, &cfg_bts_no_excl_rf_lock_cmd); install_element(BTS_NODE, &cfg_bts_force_comb_si_cmd); install_element(BTS_NODE, &cfg_bts_no_force_comb_si_cmd); install_element(BTS_NODE, &cfg_bts_codec0_cmd); install_element(BTS_NODE, &cfg_bts_codec1_cmd); install_element(BTS_NODE, &cfg_bts_codec2_cmd); install_element(BTS_NODE, &cfg_bts_codec3_cmd); install_element(BTS_NODE, &cfg_bts_codec4_cmd); install_element(BTS_NODE, &cfg_bts_depends_on_cmd); install_element(BTS_NODE, &cfg_bts_no_depends_on_cmd); install_element(BTS_NODE, &cfg_bts_amr_fr_modes1_cmd); install_element(BTS_NODE, &cfg_bts_amr_fr_modes2_cmd); install_element(BTS_NODE, &cfg_bts_amr_fr_modes3_cmd); install_element(BTS_NODE, &cfg_bts_amr_fr_modes4_cmd); install_element(BTS_NODE, &cfg_bts_amr_fr_thres1_cmd); install_element(BTS_NODE, &cfg_bts_amr_fr_thres2_cmd); install_element(BTS_NODE, &cfg_bts_amr_fr_thres3_cmd); install_element(BTS_NODE, &cfg_bts_amr_fr_hyst1_cmd); install_element(BTS_NODE, &cfg_bts_amr_fr_hyst2_cmd); install_element(BTS_NODE, &cfg_bts_amr_fr_hyst3_cmd); install_element(BTS_NODE, &cfg_bts_amr_fr_start_mode_cmd); install_element(BTS_NODE, &cfg_bts_amr_hr_modes1_cmd); install_element(BTS_NODE, &cfg_bts_amr_hr_modes2_cmd); install_element(BTS_NODE, &cfg_bts_amr_hr_modes3_cmd); install_element(BTS_NODE, &cfg_bts_amr_hr_modes4_cmd); install_element(BTS_NODE, &cfg_bts_amr_hr_thres1_cmd); install_element(BTS_NODE, &cfg_bts_amr_hr_thres2_cmd); install_element(BTS_NODE, &cfg_bts_amr_hr_thres3_cmd); install_element(BTS_NODE, &cfg_bts_amr_hr_hyst1_cmd); install_element(BTS_NODE, &cfg_bts_amr_hr_hyst2_cmd); install_element(BTS_NODE, &cfg_bts_amr_hr_hyst3_cmd); install_element(BTS_NODE, &cfg_bts_amr_hr_start_mode_cmd); install_element(BTS_NODE, &cfg_trx_cmd); install_node(&trx_node, dummy_config_write); vty_install_default(TRX_NODE); install_element(TRX_NODE, &cfg_trx_arfcn_cmd); install_element(TRX_NODE, &cfg_description_cmd); install_element(TRX_NODE, &cfg_no_description_cmd); install_element(TRX_NODE, &cfg_trx_nominal_power_cmd); install_element(TRX_NODE, &cfg_trx_max_power_red_cmd); install_element(TRX_NODE, &cfg_trx_rsl_e1_cmd); install_element(TRX_NODE, &cfg_trx_rsl_e1_tei_cmd); install_element(TRX_NODE, &cfg_trx_rf_locked_cmd); install_element(TRX_NODE, &cfg_ts_cmd); install_node(&ts_node, dummy_config_write); vty_install_default(TS_NODE); install_element(TS_NODE, &cfg_ts_pchan_cmd); install_element(TS_NODE, &cfg_ts_pchan_compat_cmd); install_element(TS_NODE, &cfg_ts_tsc_cmd); install_element(TS_NODE, &cfg_ts_hopping_cmd); install_element(TS_NODE, &cfg_ts_hsn_cmd); install_element(TS_NODE, &cfg_ts_maio_cmd); install_element(TS_NODE, &cfg_ts_arfcn_add_cmd); install_element(TS_NODE, &cfg_ts_arfcn_del_cmd); install_element(TS_NODE, &cfg_ts_e1_subslot_cmd); install_element(ENABLE_NODE, &drop_bts_cmd); install_element(ENABLE_NODE, &pdch_act_cmd); install_element(ENABLE_NODE, &smscb_cmd_cmd); abis_nm_vty_init(); abis_om2k_vty_init(); e1inp_vty_init(); bsc_vty_init_extra(); return 0; } openbsc-0.15.0/openbsc/src/libbsc/bts_ericsson_rbs2000.c000066400000000000000000000163051265565154000227330ustar00rootroot00000000000000/* Ericsson RBS-2xxx specific code */ /* (C) 2011 by Harald Welte * * 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 . * */ #include #include #include #include #include #include #include #include static void bootstrap_om_bts(struct gsm_bts *bts) { LOGP(DNM, LOGL_NOTICE, "bootstrapping OML for BTS %u\n", bts->nr); abis_om2k_tx_start_req(bts, &om2k_mo_cf); /* FIXME */ } static void bootstrap_om_trx(struct gsm_bts_trx *trx) { struct abis_om2k_mo trx_mo = { OM2K_MO_CLS_TRXC, 0, 255, trx->nr }; LOGP(DNM, LOGL_NOTICE, "bootstrapping OML for TRX %u/%u\n", trx->bts->nr, trx->nr); abis_om2k_tx_reset_cmd(trx->bts, &trx_mo); } static int shutdown_om(struct gsm_bts *bts) { /* FIXME */ return 0; } /* Tell LAPD to start start the SAP (send SABM requests) for all signalling * timeslots in this line */ static void start_sabm_in_line(struct e1inp_line *line, int start) { struct e1inp_sign_link *link; int i; for (i = 0; i < ARRAY_SIZE(line->ts); i++) { struct e1inp_ts *ts = &line->ts[i]; if (ts->type != E1INP_TS_TYPE_SIGN) continue; llist_for_each_entry(link, &ts->sign.sign_links, list) { if (start) lapd_sap_start(ts->lapd, link->tei, link->sapi); else lapd_sap_stop(ts->lapd, link->tei, link->sapi); } } } /* Callback function to be called every time we receive a signal from INPUT */ static int gbl_sig_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct gsm_bts *bts; if (subsys != SS_L_GLOBAL) return 0; switch (signal) { case S_GLOBAL_BTS_CLOSE_OM: bts = signal_data; if (bts->type == GSM_BTS_TYPE_RBS2000) shutdown_om(signal_data); break; } return 0; } /* Callback function to be called every time we receive a signal from INPUT */ static int inp_sig_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct input_signal_data *isd = signal_data; if (subsys != SS_L_INPUT) return 0; switch (signal) { case S_L_INP_TEI_UP: switch (isd->link_type) { case E1INP_SIGN_OML: if (isd->trx->bts->type != GSM_BTS_TYPE_RBS2000) break; if (isd->tei == isd->trx->bts->oml_tei) bootstrap_om_bts(isd->trx->bts); else bootstrap_om_trx(isd->trx); break; } break; case S_L_INP_LINE_INIT: case S_L_INP_LINE_NOALARM: if (strcasecmp(isd->line->driver->name, "DAHDI") && strcasecmp(isd->line->driver->name, "MISDN_LAPD")) break; start_sabm_in_line(isd->line, 1); break; case S_L_INP_LINE_ALARM: if (strcasecmp(isd->line->driver->name, "DAHDI") && strcasecmp(isd->line->driver->name, "MISDN_LAPD")) break; start_sabm_in_line(isd->line, 0); break; } return 0; } static void nm_statechg_evt(unsigned int signal, struct nm_statechg_signal_data *nsd) { struct abis_om2k_mo mo; if (nsd->bts->type != GSM_BTS_TYPE_RBS2000) return; switch (nsd->om2k_mo->class) { case OM2K_MO_CLS_CF: if (nsd->new_state->operational != NM_OPSTATE_ENABLED || nsd->new_state->availability != OM2K_MO_S_STARTED) break; /* CF has started, we can trigger IS and TF start */ abis_om2k_tx_connect_cmd(nsd->bts, &om2k_mo_is); abis_om2k_tx_connect_cmd(nsd->bts, &om2k_mo_tf); break; case OM2K_MO_CLS_IS: if (nsd->new_state->availability == OM2K_MO_S_ENABLED) { /* IS is enabled, we can proceed with TRXC/RX/TX/TS */ break; } if (nsd->new_state->operational != NM_OPSTATE_ENABLED) break; /* IS has started, we can configure + enable it */ abis_om2k_tx_is_conf_req(nsd->bts); break; case OM2K_MO_CLS_TF: if (nsd->new_state->operational != NM_OPSTATE_ENABLED || nsd->new_state->availability == OM2K_MO_S_DISABLED) break; if (nsd->new_state->availability == OM2K_MO_S_STARTED) { /* TF has started, configure + enable it */ abis_om2k_tx_tf_conf_req(nsd->bts); } break; case OM2K_MO_CLS_TRXC: if (nsd->new_state->availability != OM2K_MO_S_STARTED) break; /* TRXC is started, connect the TX and RX objects */ memcpy(&mo, nsd->om2k_mo, sizeof(mo)); mo.class = OM2K_MO_CLS_TX; abis_om2k_tx_connect_cmd(nsd->bts, &mo); mo.class = OM2K_MO_CLS_RX; abis_om2k_tx_connect_cmd(nsd->bts, &mo); break; case OM2K_MO_CLS_RX: if (nsd->new_state->operational != NM_OPSTATE_ENABLED || nsd->new_state->availability != OM2K_MO_S_STARTED) break; /* RX is started, configure + enable it */ abis_om2k_tx_rx_conf_req(nsd->obj); break; case OM2K_MO_CLS_TX: if (nsd->new_state->operational != NM_OPSTATE_ENABLED || nsd->new_state->availability != OM2K_MO_S_STARTED) break; /* RX is started, configure + enable it */ abis_om2k_tx_tx_conf_req(nsd->obj); break; } } static void nm_conf_res(struct nm_om2k_signal_data *nsd) { switch (nsd->om2k_mo->class) { case OM2K_MO_CLS_IS: case OM2K_MO_CLS_TF: case OM2K_MO_CLS_RX: case OM2K_MO_CLS_TX: /* If configuration was a success, enable it */ abis_om2k_tx_enable_req(nsd->bts, nsd->om2k_mo); break; } } static int nm_sig_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { if (subsys != SS_NM) return 0; switch (signal) { case S_NM_STATECHG_OPER: case S_NM_STATECHG_ADM: nm_statechg_evt(signal, signal_data); break; case S_NM_OM2K_CONF_RES: nm_conf_res(signal_data); break; default: break; } return 0; } static void config_write_bts(struct vty *vty, struct gsm_bts *bts) { abis_om2k_config_write_bts(vty, bts); } static int bts_model_rbs2k_start(struct gsm_network *net); static void bts_model_rbs2k_e1line_bind_ops(struct e1inp_line *line) { e1inp_line_bind_ops(line, &bts_isdn_e1inp_line_ops); } static struct gsm_bts_model model_rbs2k = { .type = GSM_BTS_TYPE_RBS2000, .name = "rbs2000", .start = bts_model_rbs2k_start, .oml_rcvmsg = &abis_om2k_rcvmsg, .config_write_bts = &config_write_bts, .e1line_bind_ops = &bts_model_rbs2k_e1line_bind_ops, }; static int bts_model_rbs2k_start(struct gsm_network *net) { model_rbs2k.features.data = &model_rbs2k._features_data[0]; model_rbs2k.features.data_len = sizeof(model_rbs2k._features_data); gsm_btsmodel_set_feature(&model_rbs2k, BTS_FEAT_HOPPING); gsm_btsmodel_set_feature(&model_rbs2k, BTS_FEAT_HSCSD); gsm_btsmodel_set_feature(&model_rbs2k, BTS_FEAT_MULTI_TSC); osmo_signal_register_handler(SS_L_INPUT, inp_sig_cb, NULL); osmo_signal_register_handler(SS_L_GLOBAL, gbl_sig_cb, NULL); osmo_signal_register_handler(SS_NM, nm_sig_cb, NULL); return 0; } int bts_model_rbs2k_init(void) { return gsm_bts_model_register(&model_rbs2k); } openbsc-0.15.0/openbsc/src/libbsc/bts_init.c000066400000000000000000000017161265565154000207010ustar00rootroot00000000000000/* (C) 2011 by Harald Welte * * 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 . * */ #include int bts_init(void) { bts_model_bs11_init(); bts_model_rbs2k_init(); bts_model_nanobts_init(); bts_model_nokia_site_init(); bts_model_sysmobts_init(); /* Your new BTS here. */ return 0; } openbsc-0.15.0/openbsc/src/libbsc/bts_ipaccess_nanobts.c000066400000000000000000000526241265565154000232600ustar00rootroot00000000000000/* ip.access nanoBTS specific code */ /* (C) 2009-2010 by Harald Welte * * 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern struct gsm_network *bsc_gsmnet; static int bts_model_nanobts_start(struct gsm_network *net); static void bts_model_nanobts_e1line_bind_ops(struct e1inp_line *line); struct gsm_bts_model bts_model_nanobts = { .type = GSM_BTS_TYPE_NANOBTS, .name = "nanobts", .start = bts_model_nanobts_start, .oml_rcvmsg = &abis_nm_rcvmsg, .e1line_bind_ops = bts_model_nanobts_e1line_bind_ops, .nm_att_tlvdef = { .def = { /* ip.access specifics */ [NM_ATT_IPACC_DST_IP] = { TLV_TYPE_FIXED, 4 }, [NM_ATT_IPACC_DST_IP_PORT] = { TLV_TYPE_FIXED, 2 }, [NM_ATT_IPACC_STREAM_ID] = { TLV_TYPE_TV, }, [NM_ATT_IPACC_SEC_OML_CFG] = { TLV_TYPE_FIXED, 6 }, [NM_ATT_IPACC_IP_IF_CFG] = { TLV_TYPE_FIXED, 8 }, [NM_ATT_IPACC_IP_GW_CFG] = { TLV_TYPE_FIXED, 12 }, [NM_ATT_IPACC_IN_SERV_TIME] = { TLV_TYPE_FIXED, 4 }, [NM_ATT_IPACC_LOCATION] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_PAGING_CFG] = { TLV_TYPE_FIXED, 2 }, [NM_ATT_IPACC_UNIT_ID] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_UNIT_NAME] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_SNMP_CFG] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_PRIM_OML_CFG_LIST] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_NV_FLAGS] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_FREQ_CTRL] = { TLV_TYPE_FIXED, 2 }, [NM_ATT_IPACC_PRIM_OML_FB_TOUT] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_CUR_SW_CFG] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_TIMING_BUS] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_CGI] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_RAC] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_OBJ_VERSION] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_GPRS_PAGING_CFG]= { TLV_TYPE_TL16V }, [NM_ATT_IPACC_NSEI] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_BVCI] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_NSVCI] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_NS_CFG] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_BSSGP_CFG] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_NS_LINK_CFG] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_RLC_CFG] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_ALM_THRESH_LIST]= { TLV_TYPE_TL16V }, [NM_ATT_IPACC_MONIT_VAL_LIST] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_TIB_CONTROL] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_SUPP_FEATURES] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_CODING_SCHEMES] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_RLC_CFG_2] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_HEARTB_TOUT] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_UPTIME] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_RLC_CFG_3] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_SSL_CFG] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_SEC_POSSIBLE] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_IML_SSL_STATE] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_REVOC_DATE] = { TLV_TYPE_TL16V }, }, }, }; static unsigned char nanobts_attr_bts[] = { NM_ATT_INTERF_BOUND, 0x55, 0x5b, 0x61, 0x67, 0x6d, 0x73, /* interference avg. period in numbers of SACCH multifr */ NM_ATT_INTAVE_PARAM, 0x06, /* conn fail based on SACCH error rate */ NM_ATT_CONN_FAIL_CRIT, 0x00, 0x02, 0x01, 0x10, NM_ATT_T200, 0x1e, 0x24, 0x24, 0xa8, 0x34, 0x21, 0xa8, NM_ATT_MAX_TA, 0x3f, NM_ATT_OVERL_PERIOD, 0x00, 0x01, 10, /* seconds */ NM_ATT_CCCH_L_T, 10, /* percent */ NM_ATT_CCCH_L_I_P, 1, /* seconds */ NM_ATT_RACH_B_THRESH, 10, /* busy threshold in - dBm */ NM_ATT_LDAVG_SLOTS, 0x03, 0xe8, /* rach load averaging 1000 slots */ NM_ATT_BTS_AIR_TIMER, 128, /* miliseconds */ NM_ATT_NY1, 10, /* 10 retransmissions of physical config */ NM_ATT_BCCH_ARFCN, HARDCODED_ARFCN >> 8, HARDCODED_ARFCN & 0xff, NM_ATT_BSIC, HARDCODED_BSIC, NM_ATT_IPACC_CGI, 0, 7, 0x00, 0xf1, 0x10, 0x00, 0x01, 0x00, 0x00, }; static unsigned char nanobts_attr_radio[] = { NM_ATT_RF_MAXPOWR_R, 0x0c, /* number of -2dB reduction steps / Pn */ NM_ATT_ARFCN_LIST, 0x00, 0x02, HARDCODED_ARFCN >> 8, HARDCODED_ARFCN & 0xff, }; static unsigned char nanobts_attr_nse[] = { NM_ATT_IPACC_NSEI, 0, 2, 0x03, 0x9d, /* NSEI 925 */ /* all timers in seconds */ NM_ATT_IPACC_NS_CFG, 0, 7, 3, /* (un)blocking timer (Tns-block) */ 3, /* (un)blocking retries */ 3, /* reset timer (Tns-reset) */ 3, /* reset retries */ 30, /* test timer (Tns-test) */ 3, /* alive timer (Tns-alive) */ 10, /* alive retrires */ /* all timers in seconds, unless otherwise stated */ NM_ATT_IPACC_BSSGP_CFG, 0, 11, 3, /* blockimg timer (T1) */ 3, /* blocking retries */ 3, /* unblocking retries */ 3, /* reset timer (T2) */ 3, /* reset retries */ 10, /* suspend timer (T3) in 100ms */ 3, /* suspend retries */ 10, /* resume timer (T4) in 100ms */ 3, /* resume retries */ 10, /* capability update timer (T5) */ 3, /* capability update retries */ }; static unsigned char nanobts_attr_cell[] = { NM_ATT_IPACC_RAC, 0, 1, 1, /* routing area code */ NM_ATT_IPACC_GPRS_PAGING_CFG, 0, 2, 5, /* repeat time (50ms) */ 3, /* repeat count */ NM_ATT_IPACC_BVCI, 0, 2, 0x03, 0x9d, /* BVCI 925 */ /* all timers in seconds, unless otherwise stated */ NM_ATT_IPACC_RLC_CFG, 0, 9, 20, /* T3142 */ 5, /* T3169 */ 5, /* T3191 */ 160, /* T3193 (units of 10ms) */ 5, /* T3195 */ 10, /* N3101 */ 4, /* N3103 */ 8, /* N3105 */ 15, /* RLC CV countdown */ NM_ATT_IPACC_CODING_SCHEMES, 0, 2, 0x0f, 0x00, /* CS1..CS4 */ NM_ATT_IPACC_RLC_CFG_2, 0, 5, 0x00, 250, /* T downlink TBF extension (0..500) */ 0x00, 250, /* T uplink TBF extension (0..500) */ 2, /* CS2 */ #if 0 /* EDGE model only, breaks older models. * Should inquire the BTS capabilities */ NM_ATT_IPACC_RLC_CFG_3, 0, 1, 2, /* MCS2 */ #endif }; static unsigned char nanobts_attr_nsvc0[] = { NM_ATT_IPACC_NSVCI, 0, 2, 0x03, 0x9d, /* 925 */ NM_ATT_IPACC_NS_LINK_CFG, 0, 8, 0x59, 0xd8, /* remote udp port (23000) */ 192, 168, 100, 11, /* remote ip address */ 0x59, 0xd8, /* local udp port (23000) */ }; static void patch_16(uint8_t *data, const uint16_t val) { memcpy(data, &val, sizeof(val)); } static void patch_32(uint8_t *data, const uint32_t val) { memcpy(data, &val, sizeof(val)); } /* * Patch the various SYSTEM INFORMATION tables to update * the LAI */ static void patch_nm_tables(struct gsm_bts *bts) { uint8_t arfcn_low = bts->c0->arfcn & 0xff; uint8_t arfcn_high = (bts->c0->arfcn >> 8) & 0x0f; /* patch ARFCN into BTS Attributes */ nanobts_attr_bts[42] &= 0xf0; nanobts_attr_bts[42] |= arfcn_high; nanobts_attr_bts[43] = arfcn_low; /* patch the RACH attributes */ if (bts->rach_b_thresh != -1) { nanobts_attr_bts[33] = bts->rach_b_thresh & 0xff; } if (bts->rach_ldavg_slots != -1) { uint8_t avg_high = bts->rach_ldavg_slots & 0xff; uint8_t avg_low = (bts->rach_ldavg_slots >> 8) & 0x0f; nanobts_attr_bts[35] = avg_high; nanobts_attr_bts[36] = avg_low; } /* patch BSIC */ nanobts_attr_bts[sizeof(nanobts_attr_bts)-11] = bts->bsic; /* patch CGI */ abis_nm_ipaccess_cgi(nanobts_attr_bts+sizeof(nanobts_attr_bts)-7, bts); /* patch CON_FAIL_CRIT */ nanobts_attr_bts[13] = get_radio_link_timeout(&bts->si_common.cell_options); /* patch the power reduction */ nanobts_attr_radio[1] = bts->c0->max_power_red / 2; /* patch NSEI */ nanobts_attr_nse[3] = bts->gprs.nse.nsei >> 8; nanobts_attr_nse[4] = bts->gprs.nse.nsei & 0xff; memcpy(nanobts_attr_nse+8, bts->gprs.nse.timer, ARRAY_SIZE(bts->gprs.nse.timer)); memcpy(nanobts_attr_nse+18, bts->gprs.cell.timer, ARRAY_SIZE(bts->gprs.cell.timer)); /* patch NSVCI */ nanobts_attr_nsvc0[3] = bts->gprs.nsvc[0].nsvci >> 8; nanobts_attr_nsvc0[4] = bts->gprs.nsvc[0].nsvci & 0xff; /* patch IP address as SGSN IP */ patch_16(nanobts_attr_nsvc0 + 8, htons(bts->gprs.nsvc[0].remote_port)); patch_32(nanobts_attr_nsvc0 + 10, htonl(bts->gprs.nsvc[0].remote_ip)); patch_16(nanobts_attr_nsvc0 + 14, htons(bts->gprs.nsvc[0].local_port)); /* patch BVCI */ nanobts_attr_cell[12] = bts->gprs.cell.bvci >> 8; nanobts_attr_cell[13] = bts->gprs.cell.bvci & 0xff; /* patch RAC */ nanobts_attr_cell[3] = bts->gprs.rac; if (bts->gprs.mode == BTS_GPRS_EGPRS) { /* patch EGPRS coding schemes MCS 1..9 */ nanobts_attr_cell[29] = 0x8f; nanobts_attr_cell[30] = 0xff; } } static uint8_t *nanobts_attr_bts_get(struct gsm_bts *bts, size_t *data_len) { patch_nm_tables(bts); *data_len = sizeof(nanobts_attr_bts); return nanobts_attr_bts; } static uint8_t *nanobts_attr_nse_get(struct gsm_bts *bts, size_t *data_len) { patch_nm_tables(bts); *data_len = sizeof(nanobts_attr_nse); return nanobts_attr_nse; } static uint8_t *nanobts_attr_cell_get(struct gsm_bts *bts, size_t *data_len) { patch_nm_tables(bts); *data_len = sizeof(nanobts_attr_cell); return nanobts_attr_cell; } static uint8_t *nanobts_attr_nscv_get(struct gsm_bts *bts, size_t *data_len) { patch_nm_tables(bts); *data_len = sizeof(nanobts_attr_nsvc0); return nanobts_attr_nsvc0; } static uint8_t *nanobts_attr_radio_get(struct gsm_bts *bts, size_t *data_len) { patch_nm_tables(bts); *data_len = sizeof(nanobts_attr_radio); return nanobts_attr_radio; } /* Callback function to be called whenever we get a GSM 12.21 state change event */ static int nm_statechg_event(int evt, struct nm_statechg_signal_data *nsd) { uint8_t obj_class = nsd->obj_class; void *obj = nsd->obj; struct gsm_nm_state *new_state = nsd->new_state; struct gsm_bts *bts; struct gsm_bts_trx *trx; struct gsm_bts_trx_ts *ts; struct gsm_bts_gprs_nsvc *nsvc; uint8_t *data; size_t data_len; if (!is_ipaccess_bts(nsd->bts)) return 0; /* This event-driven BTS setup is currently only required on nanoBTS */ /* S_NM_STATECHG_ADM is called after we call chg_adm_state() and would create * endless loop */ if (evt != S_NM_STATECHG_OPER) return 0; switch (obj_class) { case NM_OC_SITE_MANAGER: bts = container_of(obj, struct gsm_bts, site_mgr); if ((new_state->operational == NM_OPSTATE_ENABLED && new_state->availability == NM_AVSTATE_OK) || (new_state->operational == NM_OPSTATE_DISABLED && new_state->availability == NM_AVSTATE_OFF_LINE)) abis_nm_opstart(bts, obj_class, 0xff, 0xff, 0xff); break; case NM_OC_BTS: bts = obj; if (new_state->availability == NM_AVSTATE_DEPENDENCY) { data = nanobts_attr_bts_get(bts, &data_len); abis_nm_set_bts_attr(bts, data, data_len); abis_nm_chg_adm_state(bts, obj_class, bts->bts_nr, 0xff, 0xff, NM_STATE_UNLOCKED); abis_nm_opstart(bts, obj_class, bts->bts_nr, 0xff, 0xff); } break; case NM_OC_CHANNEL: ts = obj; trx = ts->trx; if (new_state->operational == NM_OPSTATE_DISABLED && new_state->availability == NM_AVSTATE_DEPENDENCY) { enum abis_nm_chan_comb ccomb = abis_nm_chcomb4pchan(ts->pchan); if (abis_nm_set_channel_attr(ts, ccomb) == -EINVAL) { ipaccess_drop_oml(trx->bts); return -1; } abis_nm_chg_adm_state(trx->bts, obj_class, trx->bts->bts_nr, trx->nr, ts->nr, NM_STATE_UNLOCKED); abis_nm_opstart(trx->bts, obj_class, trx->bts->bts_nr, trx->nr, ts->nr); } break; case NM_OC_RADIO_CARRIER: trx = obj; if (new_state->operational == NM_OPSTATE_DISABLED && new_state->availability == NM_AVSTATE_OK) abis_nm_opstart(trx->bts, obj_class, trx->bts->bts_nr, trx->nr, 0xff); break; case NM_OC_GPRS_NSE: bts = container_of(obj, struct gsm_bts, gprs.nse); if (bts->gprs.mode == BTS_GPRS_NONE) break; if (new_state->availability == NM_AVSTATE_DEPENDENCY) { data = nanobts_attr_nse_get(bts, &data_len); abis_nm_ipaccess_set_attr(bts, obj_class, bts->bts_nr, 0xff, 0xff, data, data_len); abis_nm_opstart(bts, obj_class, bts->bts_nr, 0xff, 0xff); } break; case NM_OC_GPRS_CELL: bts = container_of(obj, struct gsm_bts, gprs.cell); if (bts->gprs.mode == BTS_GPRS_NONE) break; if (new_state->availability == NM_AVSTATE_DEPENDENCY) { data = nanobts_attr_cell_get(bts, &data_len); abis_nm_ipaccess_set_attr(bts, obj_class, bts->bts_nr, 0, 0xff, data, data_len); abis_nm_opstart(bts, obj_class, bts->bts_nr, 0, 0xff); abis_nm_chg_adm_state(bts, obj_class, bts->bts_nr, 0, 0xff, NM_STATE_UNLOCKED); abis_nm_chg_adm_state(bts, NM_OC_GPRS_NSE, bts->bts_nr, 0xff, 0xff, NM_STATE_UNLOCKED); } break; case NM_OC_GPRS_NSVC: nsvc = obj; bts = nsvc->bts; if (bts->gprs.mode == BTS_GPRS_NONE) break; /* We skip NSVC1 since we only use NSVC0 */ if (nsvc->id == 1) break; if ((new_state->availability == NM_AVSTATE_OFF_LINE) || (new_state->availability == NM_AVSTATE_DEPENDENCY)) { data = nanobts_attr_nscv_get(bts, &data_len); abis_nm_ipaccess_set_attr(bts, obj_class, bts->bts_nr, nsvc->id, 0xff, data, data_len); abis_nm_opstart(bts, obj_class, bts->bts_nr, nsvc->id, 0xff); abis_nm_chg_adm_state(bts, obj_class, bts->bts_nr, nsvc->id, 0xff, NM_STATE_UNLOCKED); } default: break; } return 0; } /* Callback function to be called every time we receive a 12.21 SW activated report */ static int sw_activ_rep(struct msgb *mb) { struct abis_om_fom_hdr *foh = msgb_l3(mb); struct e1inp_sign_link *sign_link = mb->dst; struct gsm_bts *bts = sign_link->trx->bts; struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, foh->obj_inst.trx_nr); if (!trx) return -EINVAL; if (!is_ipaccess_bts(trx->bts)) return 0; switch (foh->obj_class) { case NM_OC_BASEB_TRANSC: abis_nm_chg_adm_state(trx->bts, foh->obj_class, trx->bts->bts_nr, trx->nr, 0xff, NM_STATE_UNLOCKED); abis_nm_opstart(trx->bts, foh->obj_class, trx->bts->bts_nr, trx->nr, 0xff); /* TRX software is active, tell it to initiate RSL Link */ abis_nm_ipaccess_rsl_connect(trx, trx->bts->ip_access.rsl_ip, 3003, trx->rsl_tei); break; case NM_OC_RADIO_CARRIER: { /* * Locking the radio carrier will make it go * offline again and we would come here. The * framework should determine that there was * no change and avoid recursion. * * This code is here to make sure that on start * a TRX remains locked. */ int rc_state = trx->mo.nm_state.administrative; /* Patch ARFCN into radio attribute */ size_t data_len; uint8_t *data = nanobts_attr_radio_get(trx->bts, &data_len); data[5] &= 0xf0; data[5] |= trx->arfcn >> 8; data[6] = trx->arfcn & 0xff; abis_nm_set_radio_attr(trx, data, data_len); abis_nm_chg_adm_state(trx->bts, foh->obj_class, trx->bts->bts_nr, trx->nr, 0xff, rc_state); abis_nm_opstart(trx->bts, foh->obj_class, trx->bts->bts_nr, trx->nr, 0xff); break; } } return 0; } /* Callback function to be called every time we receive a signal from NM */ static int bts_ipa_nm_sig_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { if (subsys != SS_NM) return 0; switch (signal) { case S_NM_SW_ACTIV_REP: return sw_activ_rep(signal_data); case S_NM_STATECHG_OPER: case S_NM_STATECHG_ADM: return nm_statechg_event(signal, signal_data); default: break; } return 0; } static int bts_model_nanobts_start(struct gsm_network *net) { osmo_signal_unregister_handler(SS_NM, bts_ipa_nm_sig_cb, NULL); osmo_signal_register_handler(SS_NM, bts_ipa_nm_sig_cb, NULL); return 0; } int bts_model_nanobts_init(void) { bts_model_nanobts.features.data = &bts_model_nanobts._features_data[0]; bts_model_nanobts.features.data_len = sizeof(bts_model_nanobts._features_data); gsm_btsmodel_set_feature(&bts_model_nanobts, BTS_FEAT_GPRS); gsm_btsmodel_set_feature(&bts_model_nanobts, BTS_FEAT_EGPRS); gsm_btsmodel_set_feature(&bts_model_nanobts, BTS_FEAT_MULTI_TSC); return gsm_bts_model_register(&bts_model_nanobts); } #define OML_UP 0x0001 #define RSL_UP 0x0002 static struct gsm_bts * find_bts_by_unitid(struct gsm_network *net, uint16_t site_id, uint16_t bts_id) { struct gsm_bts *bts; llist_for_each_entry(bts, &net->bts_list, list) { if (!is_ipaccess_bts(bts)) continue; if (bts->ip_access.site_id == site_id && bts->ip_access.bts_id == bts_id) return bts; } return NULL; } /* These are exported because they are used by the VTY interface. */ void ipaccess_drop_rsl(struct gsm_bts_trx *trx) { if (!trx->rsl_link) return; e1inp_sign_link_destroy(trx->rsl_link); trx->rsl_link = NULL; } void ipaccess_drop_oml(struct gsm_bts *bts) { struct gsm_bts *rdep_bts; struct gsm_bts_trx *trx; if (!bts->oml_link) return; e1inp_sign_link_destroy(bts->oml_link); bts->oml_link = NULL; /* we have issues reconnecting RSL, drop everything. */ llist_for_each_entry(trx, &bts->trx_list, list) ipaccess_drop_rsl(trx); bts->ip_access.flags = 0; /* * Go through the list and see if we are the depndency of a BTS * and then drop the BTS. This can lead to some recursion but it * should be fine in userspace. * The oml_link is serving as recursion anchor for us and * it is set to NULL some lines above. */ llist_for_each_entry(rdep_bts, &bts->network->bts_list, list) { if (!bts_depend_is_depedency(rdep_bts, bts)) continue; LOGP(DLINP, LOGL_NOTICE, "Dropping BTS(%u) due BTS(%u).\n", rdep_bts->nr, bts->nr); ipaccess_drop_oml(rdep_bts); } } /* This function is called once the OML/RSL link becomes up. */ static struct e1inp_sign_link * ipaccess_sign_link_up(void *unit_data, struct e1inp_line *line, enum e1inp_sign_type type) { struct gsm_bts *bts; struct ipaccess_unit *dev = unit_data; struct e1inp_sign_link *sign_link = NULL; bts = find_bts_by_unitid(bsc_gsmnet, dev->site_id, dev->bts_id); if (!bts) { LOGP(DLINP, LOGL_ERROR, "Unable to find BTS configuration for " " %u/%u/%u, disconnecting\n", dev->site_id, dev->bts_id, dev->trx_id); return NULL; } DEBUGP(DLINP, "Identified BTS %u/%u/%u\n", dev->site_id, dev->bts_id, dev->trx_id); switch(type) { case E1INP_SIGN_OML: /* remove old OML signal link for this BTS. */ ipaccess_drop_oml(bts); if (!bts_depend_check(bts)) { LOGP(DLINP, LOGL_NOTICE, "Dependency not full-filled for %u/%u/%u\n", dev->site_id, dev->bts_id, dev->trx_id); return NULL; } /* create new OML link. */ sign_link = bts->oml_link = e1inp_sign_link_create(&line->ts[E1INP_SIGN_OML - 1], E1INP_SIGN_OML, bts->c0, bts->oml_tei, 0); break; case E1INP_SIGN_RSL: { struct e1inp_ts *ts; struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, dev->trx_id); /* no OML link set yet? give up. */ if (!bts->oml_link || !trx) return NULL; /* remove old RSL link for this TRX. */ ipaccess_drop_rsl(trx); /* set new RSL link for this TRX. */ line = bts->oml_link->ts->line; ts = &line->ts[E1INP_SIGN_RSL + dev->trx_id - 1]; e1inp_ts_config_sign(ts, line); sign_link = trx->rsl_link = e1inp_sign_link_create(ts, E1INP_SIGN_RSL, trx, trx->rsl_tei, 0); trx->rsl_link->ts->sign.delay = 0; break; } default: break; } return sign_link; } static void ipaccess_sign_link_down(struct e1inp_line *line) { /* No matter what link went down, we close both signal links. */ struct e1inp_ts *ts = &line->ts[E1INP_SIGN_OML-1]; struct e1inp_sign_link *link; llist_for_each_entry(link, &ts->sign.sign_links, list) { struct gsm_bts *bts = link->trx->bts; ipaccess_drop_oml(bts); /* Yes, we only use the first element of the list. */ break; } } /* This function is called if we receive one OML/RSL message. */ static int ipaccess_sign_link(struct msgb *msg) { int ret = 0; struct e1inp_sign_link *link = msg->dst; struct e1inp_ts *e1i_ts = link->ts; switch (link->type) { case E1INP_SIGN_RSL: if (!(link->trx->bts->ip_access.flags & (RSL_UP << link->trx->nr))) { e1inp_event(e1i_ts, S_L_INP_TEI_UP, link->tei, link->sapi); link->trx->bts->ip_access.flags |= (RSL_UP << link->trx->nr); } ret = abis_rsl_rcvmsg(msg); break; case E1INP_SIGN_OML: if (!(link->trx->bts->ip_access.flags & OML_UP)) { e1inp_event(e1i_ts, S_L_INP_TEI_UP, link->tei, link->sapi); link->trx->bts->ip_access.flags |= OML_UP; } ret = abis_nm_rcvmsg(msg); break; default: LOGP(DLINP, LOGL_ERROR, "Unknown signal link type %d\n", link->type); msgb_free(msg); break; } return ret; } /* not static, ipaccess-config needs it. */ struct e1inp_line_ops ipaccess_e1inp_line_ops = { .cfg = { .ipa = { .addr = "0.0.0.0", .role = E1INP_LINE_R_BSC, }, }, .sign_link_up = ipaccess_sign_link_up, .sign_link_down = ipaccess_sign_link_down, .sign_link = ipaccess_sign_link, }; static void bts_model_nanobts_e1line_bind_ops(struct e1inp_line *line) { e1inp_line_bind_ops(line, &ipaccess_e1inp_line_ops); } openbsc-0.15.0/openbsc/src/libbsc/bts_nokia_site.c000066400000000000000000001236631265565154000220710ustar00rootroot00000000000000/* Nokia XXXsite family specific code */ /* (C) 2011 by Dieter Spaar * * 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 . * */ /* TODO: Attention: There are some static variables used for states during configuration. Those variables have to be moved to a BTS specific context, otherwise there will most certainly be problems if more than one Nokia BTS is used. */ #include #include #include #include #include #include #include #include #include /* TODO: put in a separate file ? */ extern int abis_nm_sendmsg(struct gsm_bts *bts, struct msgb *msg); /* was static in system_information.c */ extern int generate_cell_chan_list(uint8_t * chan_list, struct gsm_bts *bts); static void nokia_abis_nm_queue_send_next(struct gsm_bts *bts); static void reset_timer_cb(void *_bts); static int abis_nm_reset(struct gsm_bts *bts, uint16_t ref); static int dump_elements(uint8_t * data, int len) __attribute__((unused)); static void bootstrap_om_bts(struct gsm_bts *bts) { LOGP(DNM, LOGL_NOTICE, "bootstrapping OML for BTS %u\n", bts->nr); if (!bts->nokia.skip_reset) { if (!bts->nokia.did_reset) abis_nm_reset(bts, 1); } else bts->nokia.did_reset = 1; } static void bootstrap_om_trx(struct gsm_bts_trx *trx) { LOGP(DNM, LOGL_NOTICE, "bootstrapping OML for TRX %u/%u\n", trx->bts->nr, trx->nr); } static int shutdown_om(struct gsm_bts *bts) { /* TODO !? */ return 0; } #define SAPI_OML 62 #define SAPI_RSL 0 /* Tell LAPD to start start the SAP (send SABM requests) for all signalling timeslots in this line Attention: this has to be adapted for mISDN */ static void start_sabm_in_line(struct e1inp_line *line, int start, int sapi) { struct e1inp_sign_link *link; int i; for (i = 0; i < ARRAY_SIZE(line->ts); i++) { struct e1inp_ts *ts = &line->ts[i]; if (ts->type != E1INP_TS_TYPE_SIGN) continue; llist_for_each_entry(link, &ts->sign.sign_links, list) { if (sapi != -1 && link->sapi != sapi) continue; #if 0 /* debugging */ printf("sap start/stop (%d): %d tei=%d sapi=%d\n", start, i + 1, link->tei, link->sapi); #endif if (start) { ts->lapd->profile.t200_sec = 1; ts->lapd->profile.t200_usec = 0; lapd_sap_start(ts->lapd, link->tei, link->sapi); } else lapd_sap_stop(ts->lapd, link->tei, link->sapi); } } } /* Callback function to be called every time we receive a signal from INPUT */ static int gbl_sig_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct gsm_bts *bts; if (subsys != SS_L_GLOBAL) return 0; switch (signal) { case S_GLOBAL_BTS_CLOSE_OM: bts = signal_data; if (bts->type == GSM_BTS_TYPE_NOKIA_SITE) shutdown_om(signal_data); break; } return 0; } /* Callback function to be called every time we receive a signal from INPUT */ static int inp_sig_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct input_signal_data *isd = signal_data; if (subsys != SS_L_INPUT) return 0; switch (signal) { case S_L_INP_LINE_INIT: start_sabm_in_line(isd->line, 1, SAPI_OML); /* start only OML */ break; case S_L_INP_TEI_DN: break; case S_L_INP_TEI_UP: switch (isd->link_type) { case E1INP_SIGN_OML: if (isd->trx->bts->type != GSM_BTS_TYPE_NOKIA_SITE) break; if (isd->tei == isd->trx->bts->oml_tei) bootstrap_om_bts(isd->trx->bts); else bootstrap_om_trx(isd->trx); break; } break; case S_L_INP_TEI_UNKNOWN: /* We are receiving LAPD frames with one TEI that we do not * seem to know, likely that we (the BSC) stopped working * and lost our local states. However, the BTS is already * configured, we try to take over the RSL links. */ start_sabm_in_line(isd->line, 1, SAPI_RSL); break; } return 0; } static void nm_statechg_evt(unsigned int signal, struct nm_statechg_signal_data *nsd) { if (nsd->bts->type != GSM_BTS_TYPE_NOKIA_SITE) return; } static int nm_sig_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { if (subsys != SS_NM) return 0; switch (signal) { case S_NM_STATECHG_OPER: case S_NM_STATECHG_ADM: nm_statechg_evt(signal, signal_data); break; default: break; } return 0; } /* TODO: put in a separate file ? */ static const struct value_string nokia_msgt_name[] = { { 0x80, "NOKIA_BTS_CONF_DATA" }, { 0x81, "NOKIA_BTS_ACK" }, { 0x82, "NOKIA_BTS_OMU_STARTED" }, { 0x83, "NOKIA_BTS_START_DOWNLOAD_REQ" }, { 0x84, "NOKIA_BTS_MF_REQ" }, { 0x85, "NOKIA_BTS_AF_REQ" }, { 0x86, "NOKIA_BTS_RESET_REQ" }, { 0x87, "NOKIA_reserved" }, { 0x88, "NOKIA_BTS_CONF_REQ" }, { 0x89, "NOKIA_BTS_TEST_REQ" }, { 0x8A, "NOKIA_BTS_TEST_REPORT" }, { 0x8B, "NOKIA_reserved" }, { 0x8C, "NOKIA_reserved" }, { 0x8D, "NOKIA_reserved" }, { 0x8E, "NOKIA_BTS_CONF_COMPL" }, { 0x8F, "NOKIA_reserved" }, { 0x90, "NOKIA_BTS_STM_TEST_REQ" }, { 0x91, "NOKIA_BTS_STM_TEST_REPORT" }, { 0x92, "NOKIA_BTS_TRANSMISSION_COMMAND" }, { 0x93, "NOKIA_BTS_TRANSMISSION_ANSWER" }, { 0x94, "NOKIA_BTS_HW_DB_UPLOAD_REQ" }, { 0x95, "NOKIA_BTS_START_HW_DB_DOWNLOAD_REQ" }, { 0x96, "NOKIA_BTS_HW_DB_SAVE_REQ" }, { 0x97, "NOKIA_BTS_FLASH_ERASURE_REQ" }, { 0x98, "NOKIA_BTS_HW_DB_DOWNLOAD_REQ" }, { 0x99, "NOKIA_BTS_PWR_SUPPLY_CONTROL" }, { 0x9A, "NOKIA_BTS_ATTRIBUTE_REQ" }, { 0x9B, "NOKIA_BTS_ATTRIBUTE_REPORT" }, { 0x9C, "NOKIA_BTS_HW_REQ" }, { 0x9D, "NOKIA_BTS_HW_REPORT" }, { 0x9E, "NOKIA_BTS_RTE_TEST_REQ" }, { 0x9F, "NOKIA_BTS_RTE_TEST_REPORT" }, { 0xA0, "NOKIA_BTS_HW_DB_VERIFICATION_REQ" }, { 0xA1, "NOKIA_BTS_CLOCK_REQ" }, { 0xA2, "NOKIA_AC_CIRCUIT_REQ_NACK" }, { 0xA3, "NOKIA_AC_INTERRUPTED" }, { 0xA4, "NOKIA_BTS_NEW_TRE_INFO" }, { 0xA5, "NOKIA_AC_BSC_CIRCUITS_ALLOCATED" }, { 0xA6, "NOKIA_BTS_TRE_POLL_LIST" }, { 0xA7, "NOKIA_AC_CIRCUIT_REQ" }, { 0xA8, "NOKIA_BTS_BLOCK_CTRL_REQ" }, { 0xA9, "NOKIA_BTS_GSM_TIME_REQ" }, { 0xAA, "NOKIA_BTS_GSM_TIME" }, { 0xAB, "NOKIA_BTS_OUTPUT_CONTROL" }, { 0xAC, "NOKIA_BTS_STATE_CHANGED" }, { 0xAD, "NOKIA_BTS_SW_SAVE_REQ" }, { 0xAE, "NOKIA_BTS_ALARM" }, { 0xAF, "NOKIA_BTS_CHA_ADM_STATE" }, { 0xB0, "NOKIA_AC_POOL_SIZE_REPORT" }, { 0xB1, "NOKIA_AC_POOL_SIZE_INQUIRY" }, { 0xB2, "NOKIA_BTS_COMMISS_TEST_COMPLETED" }, { 0xB3, "NOKIA_BTS_COMMISS_TEST_REQ" }, { 0xB4, "NOKIA_BTS_TRANSP_BTS_TO_BSC" }, { 0xB5, "NOKIA_BTS_TRANSP_BSC_TO_BTS" }, { 0xB6, "NOKIA_BTS_LCS_COMMAND" }, { 0xB7, "NOKIA_BTS_LCS_ANSWER" }, { 0xB8, "NOKIA_BTS_LMU_FN_OFFSET_COMMAND" }, { 0xB9, "NOKIA_BTS_LMU_FN_OFFSET_ANSWER" }, { 0, NULL } }; static const char *get_msg_type_name_string(uint8_t msg_type) { return get_value_string(nokia_msgt_name, msg_type); } static const struct value_string nokia_element_name[] = { { 0x01, "Ny1" }, { 0x02, "T3105_F" }, { 0x03, "Interference band limits" }, { 0x04, "Interference report timer in secs" }, { 0x05, "Channel configuration per TS" }, { 0x06, "BSIC" }, { 0x07, "RACH report timer in secs" }, { 0x08, "Hardware database status" }, { 0x09, "BTS RX level" }, { 0x0A, "ARFN" }, { 0x0B, "STM antenna attenuation" }, { 0x0C, "Cell allocation bitmap" }, { 0x0D, "Radio definition per TS" }, { 0x0E, "Frame number" }, { 0x0F, "Antenna diversity" }, { 0x10, "T3105_D" }, { 0x11, "File format" }, { 0x12, "Last File" }, { 0x13, "BTS type" }, { 0x14, "Erasure mode" }, { 0x15, "Hopping mode" }, { 0x16, "Floating TRX" }, { 0x17, "Power supplies" }, { 0x18, "Reset type" }, { 0x19, "Averaging period" }, { 0x1A, "RBER2" }, { 0x1B, "LAC" }, { 0x1C, "CI" }, { 0x1D, "Failure parameters" }, { 0x1E, "(RF max power reduction)" }, { 0x1F, "Measured RX_SENS" }, { 0x20, "Extended cell radius" }, { 0x21, "reserved" }, { 0x22, "Success-Failure" }, { 0x23, "Ack-Nack" }, { 0x24, "OMU test results" }, { 0x25, "File identity" }, { 0x26, "Generation and version code" }, { 0x27, "SW description" }, { 0x28, "BCCH LEV" }, { 0x29, "Test type" }, { 0x2A, "Subscriber number" }, { 0x2B, "reserved" }, { 0x2C, "HSN" }, { 0x2D, "reserved" }, { 0x2E, "MS RXLEV" }, { 0x2F, "MS TXLEV" }, { 0x30, "RXQUAL" }, { 0x31, "RX SENS" }, { 0x32, "Alarm block" }, { 0x33, "Neighbouring BCCH levels" }, { 0x34, "STM report type" }, { 0x35, "MA" }, { 0x36, "MAIO" }, { 0x37, "H_FLAG" }, { 0x38, "TCH_ARFN" }, { 0x39, "Clock output" }, { 0x3A, "Transmitted power" }, { 0x3B, "Clock sync" }, { 0x3C, "TMS protocol discriminator" }, { 0x3D, "TMS protocol data" }, { 0x3E, "FER" }, { 0x3F, "SWR result" }, { 0x40, "Object identity" }, { 0x41, "STM RX Antenna Test" }, { 0x42, "reserved" }, { 0x43, "reserved" }, { 0x44, "Object current state" }, { 0x45, "reserved" }, { 0x46, "FU channel configuration" }, { 0x47, "reserved" }, { 0x48, "ARFN of a CU" }, { 0x49, "FU radio definition" }, { 0x4A, "reserved" }, { 0x4B, "Severity" }, { 0x4C, "Diversity selection" }, { 0x4D, "RX antenna test" }, { 0x4E, "RX antenna supervision period" }, { 0x4F, "RX antenna state" }, { 0x50, "Sector configuration" }, { 0x51, "Additional info" }, { 0x52, "SWR parameters" }, { 0x53, "HW inquiry mode" }, { 0x54, "reserved" }, { 0x55, "Availability status" }, { 0x56, "reserved" }, { 0x57, "EAC inputs" }, { 0x58, "EAC outputs" }, { 0x59, "reserved" }, { 0x5A, "Position" }, { 0x5B, "HW unit identity" }, { 0x5C, "RF test signal attenuation" }, { 0x5D, "Operational state" }, { 0x5E, "Logical object identity" }, { 0x5F, "reserved" }, { 0x60, "BS_TXPWR_OM" }, { 0x61, "Loop_Duration" }, { 0x62, "LNA_Path_Selection" }, { 0x63, "Serial number" }, { 0x64, "HW version" }, { 0x65, "Obj. identity and obj. state" }, { 0x66, "reserved" }, { 0x67, "EAC input definition" }, { 0x68, "EAC id and text" }, { 0x69, "HW unit status" }, { 0x6A, "SW release version" }, { 0x6B, "FW version" }, { 0x6C, "Bit_Error_Ratio" }, { 0x6D, "RXLEV_with_Attenuation" }, { 0x6E, "RXLEV_without_Attenuation" }, { 0x6F, "reserved" }, { 0x70, "CU_Results" }, { 0x71, "reserved" }, { 0x72, "LNA_Path_Results" }, { 0x73, "RTE Results" }, { 0x74, "Real Time" }, { 0x75, "RX diversity selection" }, { 0x76, "EAC input config" }, { 0x77, "Feature support" }, { 0x78, "File version" }, { 0x79, "Outputs" }, { 0x7A, "FU parameters" }, { 0x7B, "Diagnostic info" }, { 0x7C, "FU BSIC" }, { 0x7D, "TRX Configuration" }, { 0x7E, "Download status" }, { 0x7F, "RX difference limit" }, { 0x80, "TRX HW capability" }, { 0x81, "Common HW config" }, { 0x82, "Autoconfiguration pool size" }, { 0x83, "TRE diagnostic info" }, { 0x84, "TRE object identity" }, { 0x85, "New TRE Info" }, { 0x86, "Acknowledgement period" }, { 0x87, "Synchronization mode" }, { 0x88, "reserved" }, { 0x89, "Block Control Data" }, { 0x8A, "SW load mode" }, { 0x8B, "Recommended recovery action" }, { 0x8C, "BSC BCF id" }, { 0x8D, "Q1 baud rate" }, { 0x8E, "Allocation status" }, { 0x8F, "Functional entity number" }, { 0x90, "Transmission delay" }, { 0x91, "Loop Duration ms" }, { 0x92, "Logical channel" }, { 0x93, "Q1 address" }, { 0x94, "Alarm detail" }, { 0x95, "Cabinet type" }, { 0x96, "HW unit existence" }, { 0x97, "RF power parameters" }, { 0x98, "Message scenario" }, { 0x99, "HW unit max amount" }, { 0x9A, "Master TRX" }, { 0x9B, "Transparent data" }, { 0x9C, "BSC topology info" }, { 0x9D, "Air i/f modulation" }, { 0x9E, "LCS Q1 command data" }, { 0x9F, "Frame number offset" }, { 0xA0, "Abis TSL" }, { 0xA1, "Dynamic pool info" }, { 0xA2, "LCS LLP data" }, { 0xA3, "LCS Q1 answer data" }, { 0xA4, "DFCA FU Radio Definition" }, { 0xA5, "Antenna hopping" }, { 0xA6, "Field record sequence number" }, { 0xA7, "Timeslot offslot" }, { 0xA8, "EPCR capability" }, { 0xA9, "Connectsite optional element" }, { 0xAA, "TSC" }, { 0xAB, "Special TX Power Setting" }, { 0xAC, "Optional sync settings" }, { 0xFA, "Abis If parameters" }, { 0, NULL } }; static const char *get_element_name_string(uint16_t element) { return get_value_string(nokia_element_name, element); } static const struct value_string nokia_bts_types[] = { { 0x0a, "MetroSite GSM 900" }, { 0x0b, "MetroSite GSM 1800" }, { 0x0c, "MetroSite GSM 1900 (PCS)" }, { 0x0d, "MetroSite GSM 900 & 1800" }, { 0x0e, "InSite GSM 900" }, { 0x0f, "InSite GSM 1800" }, { 0x10, "InSite GSM 1900" }, { 0x11, "UltraSite GSM 900" }, { 0x12, "UltraSite GSM 1800" }, { 0x13, "UltraSite GSM/US-TDMA 1900" }, { 0x14, "UltraSite GSM 900 & 1800" }, { 0x16, "UltraSite GSM/US-TDMA 850" }, { 0x18, "MetroSite GSM/US-TDMA 850" }, { 0x19, "UltraSite GSM 800/1900" }, { 0, NULL } }; static const char *get_bts_type_string(uint8_t type) { return get_value_string(nokia_bts_types, type); } static const struct value_string nokia_severity[] = { { 0, "indeterminate" }, { 1, "critical" }, { 2, "major" }, { 3, "minor" }, { 4, "warning" }, { 0, NULL } }; static const char *get_severity_string(uint8_t severity) { return get_value_string(nokia_severity, severity); } /* TODO: put in a separate file ? */ /* some message IDs */ #define NOKIA_MSG_CONF_DATA 128 #define NOKIA_MSG_ACK 129 #define NOKIA_MSG_OMU_STARTED 130 #define NOKIA_MSG_START_DOWNLOAD_REQ 131 #define NOKIA_MSG_MF_REQ 132 #define NOKIA_MSG_RESET_REQ 134 #define NOKIA_MSG_CONF_REQ 136 #define NOKIA_MSG_CONF_COMPLETE 142 #define NOKIA_MSG_BLOCK_CTRL_REQ 168 #define NOKIA_MSG_STATE_CHANGED 172 #define NOKIA_MSG_ALARM 174 /* some element IDs */ #define NOKIA_EI_BTS_TYPE 0x13 #define NOKIA_EI_ACK 0x23 #define NOKIA_EI_ADD_INFO 0x51 #define NOKIA_EI_SEVERITY 0x4B #define NOKIA_EI_ALARM_DETAIL 0x94 #define OM_ALLOC_SIZE 1024 #define OM_HEADROOM_SIZE 128 static uint8_t fu_config_template[] = { 0x7F, 0x7A, 0x39, /* ID = 0x7A (FU parameters) ## constructed ## */ /* length = 57 */ /* [3] */ 0x5F, 0x40, 0x04, /* ID = 0x40 (Object identity) */ /* length = 4 */ /* [6] */ 0x00, 0x07, 0x01, 0xFF, 0x41, 0x02, /* ID = 0x01 (Ny1) */ /* length = 2 */ /* [12] */ 0x00, 0x05, 0x42, 0x02, /* ID = 0x02 (T3105_F) */ /* length = 2 */ /* [16] */ 0x00, 0x28, /* FIXME: use net->T3105 */ 0x50, 0x02, /* ID = 0x10 (T3105_D) */ /* length = 2 */ /* [20] */ 0x00, 0x28, /* FIXME: use net->T3105 */ 0x43, 0x05, /* ID = 0x03 (Interference band limits) */ /* length = 5 */ /* [24] */ 0x0F, 0x1B, 0x27, 0x33, 0x3F, 0x44, 0x02, /* ID = 0x04 (Interference report timer in secs) */ /* length = 2 */ /* [31] */ 0x00, 0x10, 0x47, 0x01, /* ID = 0x07 (RACH report timer in secs) */ /* length = 1 */ /* [35] */ 0x1E, 0x4C, 0x10, /* ID = 0x0C (Cell allocation bitmap) ####### */ /* length = 16 */ /* [38] */ 0x8F, 0xB1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x01, /* ID = 0x19 (Averaging period) */ /* length = 1 */ /* [56] */ 0x01, 0x5E, 0x01, /* ID = 0x1E ((RF max power reduction)) */ /* length = 1 */ /* [59] */ 0x00, 0x7F, 0x46, 0x11, /* ID = 0x46 (FU channel configuration) ## constructed ## */ /* length = 17 */ /* [63] */ 0x5F, 0x40, 0x04, /* ID = 0x40 (Object identity) */ /* length = 4 */ /* [66] */ 0x00, 0x07, 0x01, 0xFF, 0x45, 0x08, /* ID = 0x05 (Channel configuration per TS) */ /* length = 8 */ /* [72] */ 0x01, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x7F, 0x65, 0x0B, /* ID = 0x65 (Obj. identity and obj. state) ## constructed ## */ /* length = 11 */ /* [83] */ 0x5F, 0x40, 0x04, /* ID = 0x40 (Object identity) */ /* length = 4 */ /* [86] */ 0x00, 0x04, 0x01, 0xFF, 0x5F, 0x44, 0x01, /* ID = 0x44 (Object current state) */ /* length = 1 */ /* [93] */ 0x03, 0x7F, 0x7C, 0x0A, /* ID = 0x7C (FU BSIC) ## constructed ## */ /* length = 10 */ /* [97] */ 0x5F, 0x40, 0x04, /* ID = 0x40 (Object identity) */ /* length = 4 */ /* [100] */ 0x00, 0x07, 0x01, 0xFF, 0x46, 0x01, /* ID = 0x06 (BSIC) */ /* length = 1 */ /* [106] */ 0x00, 0x7F, 0x48, 0x0B, /* ID = 0x48 (ARFN of a CU) ## constructed ## */ /* length = 11 */ /* [110] */ 0x5F, 0x40, 0x04, /* ID = 0x40 (Object identity) */ /* length = 4 */ /* [113] */ 0x00, 0x08, 0x01, 0xFF, 0x4A, 0x02, /* ID = 0x0A (ARFN) ####### */ /* length = 2 */ /* [119] */ 0x03, 0x62, 0x7F, 0x49, 0x59, /* ID = 0x49 (FU radio definition) ## constructed ## */ /* length = 89 */ /* [124] */ 0x5F, 0x40, 0x04, /* ID = 0x40 (Object identity) */ /* length = 4 */ /* [127] */ 0x00, 0x07, 0x01, 0xFF, 0x4D, 0x50, /* ID = 0x0D (Radio definition per TS) ####### */ /* length = 80 */ /* [133] */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MA */ 0x03, 0x62, /* HSN, MAIO or ARFCN if no hopping */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x62, }; /* TODO: put in a separate file ? */ /* build the configuration for each TRX */ static int make_fu_config(struct gsm_bts_trx *trx, uint8_t id, uint8_t * fu_config, int *hopping) { int i; *hopping = 0; memcpy(fu_config, fu_config_template, sizeof(fu_config_template)); /* set ID */ fu_config[6 + 2] = id; fu_config[66 + 2] = id; fu_config[86 + 2] = id; fu_config[100 + 2] = id; fu_config[113 + 2] = id; fu_config[127 + 2] = id; /* set ARFCN */ uint16_t arfcn = trx->arfcn; fu_config[119] = arfcn >> 8; fu_config[119 + 1] = arfcn & 0xFF; for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { struct gsm_bts_trx_ts *ts = &trx->ts[i]; if (ts->hopping.enabled) { /* reverse order */ int j; for (j = 0; j < ts->hopping.ma_len; j++) fu_config[133 + (i * 10) + (7 - j)] = ts->hopping.ma_data[j]; fu_config[133 + 8 + (i * 10)] = ts->hopping.hsn; fu_config[133 + 8 + 1 + (i * 10)] = ts->hopping.maio; *hopping = 1; } else { fu_config[133 + 8 + (i * 10)] = arfcn >> 8; fu_config[133 + 8 + 1 + (i * 10)] = arfcn & 0xFF; } } /* set BSIC */ /* Attention: all TRX except the first one seem to get the TSC from the CHANNEL ACTIVATION command (in CHANNEL IDENTIFICATION, GSM 04.08 CHANNEL DESCRIPTION). There was a bug in rsl_chan_activate_lchan() setting this parameter. */ uint8_t bsic = trx->bts->bsic; fu_config[106] = bsic; /* set CA */ if (generate_cell_chan_list(&fu_config[38], trx->bts) != 0) { fprintf(stderr, "generate_cell_chan_list failed\n"); return 0; } /* set channel configuration */ for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { struct gsm_bts_trx_ts *ts = &trx->ts[i]; uint8_t chan_config; /* 0 = FCCH + SCH + BCCH + CCCH 1 = FCCH + SCH + BCCH + CCCH + SDCCH/4 + SACCH/4 2 = BCCH + CCCH (This combination is not used in any BTS) 3 = FCCH + SCH + BCCH + CCCH + SDCCH/4 with SDCCH2 used as CBCH 4 = SDCCH/8 + SACCH/8 5 = SDCCH/8 with SDCCH2 used as CBCH 6 = TCH/F + FACCH/F + SACCH/F 7 = E-RACH (Talk family) 9 = Dual rate (capability for TCH/F and TCH/H) 10 = reserved for BTS internal use 11 = PBCCH + PCCCH + PDTCH + PACCH + PTCCH (can be used in GPRS release 2). 0xFF = spare TS */ if (ts->pchan == GSM_PCHAN_NONE) chan_config = 0xFF; else if (ts->pchan == GSM_PCHAN_CCCH) chan_config = 0; else if (ts->pchan == GSM_PCHAN_CCCH_SDCCH4) chan_config = 1; else if (ts->pchan == GSM_PCHAN_TCH_F) chan_config = 6; /* 9 should work too */ else if (ts->pchan == GSM_PCHAN_TCH_H) chan_config = 9; else if (ts->pchan == GSM_PCHAN_SDCCH8_SACCH8C) chan_config = 4; else if (ts->pchan == GSM_PCHAN_PDCH) chan_config = 11; else { fprintf(stderr, "unsupported channel config %d for timeslot %d\n", ts->pchan, i); return 0; } fu_config[72 + i] = chan_config; } return sizeof(fu_config_template); } /* TODO: put in a separate file ? */ static uint8_t bts_config_1[] = { 0x4E, 0x02, /* ID = 0x0E (Frame number) */ /* length = 2 */ /* [2] */ 0xFF, 0xFF, 0x5F, 0x4E, 0x02, /* ID = 0x4E (RX antenna supervision period) */ /* length = 2 */ /* [7] */ 0xFF, 0xFF, 0x5F, 0x50, 0x02, /* ID = 0x50 (Sector configuration) */ /* length = 2 */ /* [12] */ 0x01, 0x01, }; static uint8_t bts_config_2[] = { 0x55, 0x02, /* ID = 0x15 (Hopping mode) */ /* length = 2 */ /* [2] */ 0x01, 0x00, 0x5F, 0x75, 0x02, /* ID = 0x75 (RX diversity selection) */ /* length = 2 */ /* [7] */ 0x01, 0x01, }; static uint8_t bts_config_3[] = { 0x5F, 0x20, 0x02, /* ID = 0x20 (Extended cell radius) */ /* length = 2 */ /* [3] */ 0x01, 0x00, }; static uint8_t bts_config_4[] = { 0x5F, 0x74, 0x09, /* ID = 0x74 (Real Time) */ /* length = 9 */ /* [3] year-high, year-low, month, day, hour, minute, second, msec-high, msec-low */ 0x07, 0xDB, 0x06, 0x02, 0x0B, 0x20, 0x0C, 0x00, 0x00, 0x5F, 0x76, 0x03, /* ID = 0x76 (EAC input config) */ /* length = 3 */ /* [15] */ 0x01, 0x01, 0x00, 0x5F, 0x76, 0x03, /* ID = 0x76 (EAC input config) */ /* length = 3 */ /* [21] */ 0x02, 0x01, 0x00, 0x5F, 0x76, 0x03, /* ID = 0x76 (EAC input config) */ /* length = 3 */ /* [27] */ 0x03, 0x01, 0x00, 0x5F, 0x76, 0x03, /* ID = 0x76 (EAC input config) */ /* length = 3 */ /* [33] */ 0x04, 0x01, 0x00, 0x5F, 0x76, 0x03, /* ID = 0x76 (EAC input config) */ /* length = 3 */ /* [39] */ 0x05, 0x01, 0x00, 0x5F, 0x76, 0x03, /* ID = 0x76 (EAC input config) */ /* length = 3 */ /* [45] */ 0x06, 0x01, 0x00, 0x5F, 0x76, 0x03, /* ID = 0x76 (EAC input config) */ /* length = 3 */ /* [51] */ 0x07, 0x01, 0x00, 0x5F, 0x76, 0x03, /* ID = 0x76 (EAC input config) */ /* length = 3 */ /* [57] */ 0x08, 0x01, 0x00, 0x5F, 0x76, 0x03, /* ID = 0x76 (EAC input config) */ /* length = 3 */ /* [63] */ 0x09, 0x01, 0x00, 0x5F, 0x76, 0x03, /* ID = 0x76 (EAC input config) */ /* length = 3 */ /* [69] */ 0x0A, 0x01, 0x00, }; static uint8_t bts_config_insite[] = { 0x4E, 0x02, /* ID = 0x0E (Frame number) */ /* length = 2 */ /* [2] */ 0xFF, 0xFF, 0x5F, 0x4E, 0x02, /* ID = 0x4E (RX antenna supervision period) */ /* length = 2 */ /* [7] */ 0xFF, 0xFF, 0x5F, 0x50, 0x02, /* ID = 0x50 (Sector configuration) */ /* length = 2 */ /* [12] */ 0x01, 0x01, 0x55, 0x02, /* ID = 0x15 (Hopping mode) */ /* length = 2 */ /* [16] */ 0x01, 0x00, 0x5F, 0x20, 0x02, /* ID = 0x20 (Extended cell radius) */ /* length = 2 */ /* [21] */ 0x01, 0x00, 0x5F, 0x74, 0x09, /* ID = 0x74 (Real Time) */ /* length = 9 */ /* [26] */ 0x07, 0xDB, 0x07, 0x0A, 0x0F, 0x09, 0x0B, 0x00, 0x00, }; void set_real_time(uint8_t * real_time) { time_t t; struct tm *tm; t = time(NULL); tm = localtime(&t); /* year-high, year-low, month, day, hour, minute, second, msec-high, msec-low */ real_time[0] = (1900 + tm->tm_year) >> 8; real_time[1] = (1900 + tm->tm_year) & 0xFF; real_time[2] = tm->tm_mon + 1; real_time[3] = tm->tm_mday; real_time[4] = tm->tm_hour; real_time[5] = tm->tm_min; real_time[6] = tm->tm_sec; real_time[7] = 0; real_time[8] = 0; } /* TODO: put in a separate file ? */ /* build the configuration data */ static int make_bts_config(uint8_t bts_type, int n_trx, uint8_t * fu_config, int need_hopping) { /* is it an InSite BTS ? */ if (bts_type == 0x0E || bts_type == 0x0F || bts_type == 0x10) { /* TODO */ if (n_trx != 1) { fprintf(stderr, "InSite has only one TRX\n"); return 0; } if (need_hopping != 0) { fprintf(stderr, "InSite does not support hopping\n"); return 0; } memcpy(fu_config, bts_config_insite, sizeof(bts_config_insite)); set_real_time(&fu_config[26]); return sizeof(bts_config_insite); } int len = 0; int i; memcpy(fu_config + len, bts_config_1, sizeof(bts_config_1)); /* set sector configuration */ fu_config[len + 12 - 1] = 1 + n_trx; /* len */ for (i = 0; i < n_trx; i++) fu_config[len + 12 + 1 + i] = ((i + 1) & 0xFF); len += (sizeof(bts_config_1) + (n_trx - 1)); memcpy(fu_config + len, bts_config_2, sizeof(bts_config_2)); /* set hopping mode (Baseband and RF hopping work for the MetroSite) */ if (need_hopping) fu_config[len + 2 + 1] = 1; /* 0: no hopping, 1: Baseband hopping, 2: RF hopping */ len += sizeof(bts_config_2); /* set extended cell radius for each TRX */ for (i = 0; i < n_trx; i++) { memcpy(fu_config + len, bts_config_3, sizeof(bts_config_3)); fu_config[len + 3] = ((i + 1) & 0xFF); len += sizeof(bts_config_3); } memcpy(fu_config + len, bts_config_4, sizeof(bts_config_4)); set_real_time(&fu_config[len + 3]); len += sizeof(bts_config_4); return len; } /* TODO: put in a separate file ? */ static struct msgb *nm_msgb_alloc(void) { return msgb_alloc_headroom(OM_ALLOC_SIZE, OM_HEADROOM_SIZE, "OML"); } /* TODO: put in a separate file ? */ struct abis_om_nokia_hdr { uint8_t msg_type; uint8_t spare; uint16_t reference; uint8_t data[0]; } __attribute__ ((packed)); #define ABIS_OM_NOKIA_HDR_SIZE (sizeof(struct abis_om_hdr) + sizeof(struct abis_om_nokia_hdr)) static int abis_nm_send(struct gsm_bts *bts, uint8_t msg_type, uint16_t ref, uint8_t * data, int len_data) { struct abis_om_hdr *oh; struct abis_om_nokia_hdr *noh; struct msgb *msg = nm_msgb_alloc(); oh = (struct abis_om_hdr *)msgb_put(msg, ABIS_OM_NOKIA_HDR_SIZE + len_data); oh->mdisc = ABIS_OM_MDISC_FOM; oh->placement = ABIS_OM_PLACEMENT_ONLY; oh->sequence = 0; oh->length = sizeof(struct abis_om_nokia_hdr) + len_data; noh = (struct abis_om_nokia_hdr *)oh->data; noh->msg_type = msg_type; noh->spare = 0; noh->reference = htons(ref); memcpy(noh->data, data, len_data); DEBUGPC(DNM, "Sending %s\n", get_msg_type_name_string(msg_type)); return abis_nm_sendmsg(bts, msg); } /* TODO: put in a separate file ? */ static uint8_t download_req[] = { 0x5F, 0x25, 0x0B, /* ID = 0x25 (File identity) */ /* length = 11 */ /* [3] */ 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x5F, 0x78, 0x03, /* ID = 0x78 (File version) */ /* length = 3 */ /* [17] */ 0x2A, 0x2A, 0x2A, 0x5F, 0x81, 0x0A, 0x01, /* ID = 0x8A (SW load mode) */ /* length = 1 */ /* [24] */ 0x01, 0x5F, 0x81, 0x06, 0x01, /* ID = 0x86 (Acknowledgement period) */ /* length = 1 */ /* [29] */ 0x01, }; static int abis_nm_download_req(struct gsm_bts *bts, uint16_t ref) { uint8_t *data = download_req; int len_data = sizeof(download_req); return abis_nm_send(bts, NOKIA_MSG_START_DOWNLOAD_REQ, ref, data, len_data); } /* TODO: put in a separate file ? */ static uint8_t ack[] = { 0x5F, 0x23, 0x01, /* ID = 0x23 (Ack-Nack) */ /* length = 1 */ /* [3] */ 0x01, }; static int abis_nm_ack(struct gsm_bts *bts, uint16_t ref) { uint8_t *data = ack; int len_data = sizeof(ack); return abis_nm_send(bts, NOKIA_MSG_ACK, ref, data, len_data); } /* TODO: put in a separate file ? */ static uint8_t reset[] = { 0x5F, 0x40, 0x04, /* ID = 0x40 (Object identity) */ /* length = 4 */ /* [3] */ 0x00, 0x01, 0xFF, 0xFF, }; static int abis_nm_reset(struct gsm_bts *bts, uint16_t ref) { uint8_t *data = reset; int len_data = sizeof(reset); LOGP(DLINP, LOGL_INFO, "Nokia BTS reset timer: %d\n", bts->nokia.bts_reset_timer_cnf); return abis_nm_send(bts, NOKIA_MSG_RESET_REQ, ref, data, len_data); } /* TODO: put in a separate file ? */ static int abis_nm_send_multi_segments(struct gsm_bts *bts, uint8_t msg_type, uint16_t ref, uint8_t * data, int len) { int len_remain, len_to_send, max_send; int seq = 0; int ret; len_remain = len; while (len_remain) { struct abis_om_hdr *oh; struct abis_om_nokia_hdr *noh; struct msgb *msg = nm_msgb_alloc(); if (seq == 0) max_send = 256 - sizeof(struct abis_om_nokia_hdr); else max_send = 256; if (len_remain > max_send) { len_to_send = max_send; if (seq == 0) { /* first segment */ oh = (struct abis_om_hdr *)msgb_put(msg, ABIS_OM_NOKIA_HDR_SIZE + len_to_send); oh->mdisc = ABIS_OM_MDISC_FOM; oh->placement = ABIS_OM_PLACEMENT_FIRST; /* first segment of multi-segment message */ oh->sequence = seq; oh->length = 0; /* 256 bytes */ noh = (struct abis_om_nokia_hdr *)oh->data; noh->msg_type = msg_type; noh->spare = 0; noh->reference = htons(ref); memcpy(noh->data, data, len_to_send); } else { /* segment in between */ oh = (struct abis_om_hdr *)msgb_put(msg, sizeof (struct abis_om_hdr) + len_to_send); oh->mdisc = ABIS_OM_MDISC_FOM; oh->placement = ABIS_OM_PLACEMENT_MIDDLE; /* segment of multi-segment message */ oh->sequence = seq; oh->length = 0; /* 256 bytes */ memcpy(oh->data, data, len_to_send); } } else { len_to_send = len_remain; /* check if message fits in a single segment */ if (seq == 0) return abis_nm_send(bts, msg_type, ref, data, len_to_send); /* last segment */ oh = (struct abis_om_hdr *)msgb_put(msg, sizeof(struct abis_om_hdr) + len_to_send); oh->mdisc = ABIS_OM_MDISC_FOM; oh->placement = ABIS_OM_PLACEMENT_LAST; /* last segment of multi-segment message */ oh->sequence = seq; oh->length = len_to_send; memcpy(oh->data, data, len_to_send); } DEBUGPC(DNM, "Sending multi-segment %d\n", seq); ret = abis_nm_sendmsg(bts, msg); if (ret < 0) return ret; nokia_abis_nm_queue_send_next(bts); /* next segment */ len_remain -= len_to_send; data += len_to_send; seq++; } return ret; } /* TODO: put in a separate file ? */ static int abis_nm_send_config(struct gsm_bts *bts, uint8_t bts_type) { struct gsm_bts_trx *trx; uint8_t config[2048]; /* TODO: might be too small if lots of TRX are used */ int len = 0; int idx = 0; int ret; int hopping = 0; int need_hopping = 0; memset(config, 0, sizeof(config)); llist_for_each_entry(trx, &bts->trx_list, list) { #if 0 /* debugging */ printf("TRX\n"); printf(" arfcn: %d\n", trx->arfcn); printf(" bsic: %d\n", trx->bts->bsic); uint8_t ca[20]; memset(ca, 0xFF, sizeof(ca)); ret = generate_cell_chan_list(ca, trx->bts); printf(" ca (%d): %s\n", ret, osmo_hexdump(ca, sizeof(ca))); int i; for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { struct gsm_bts_trx_ts *ts = &trx->ts[i]; printf(" pchan %d: %d\n", i, ts->pchan); } #endif ret = make_fu_config(trx, idx + 1, config + len, &hopping); need_hopping |= hopping; len += ret; idx++; } ret = make_bts_config(bts_type, idx, config + len, need_hopping); len += ret; #if 0 /* debugging */ dump_elements(config, len); #endif return abis_nm_send_multi_segments(bts, NOKIA_MSG_CONF_DATA, 1, config, len); } #define GET_NEXT_BYTE if(idx >= len) return 0; \ ub = data[idx++]; static int find_element(uint8_t * data, int len, uint16_t id, uint8_t * value, int max_value) { uint8_t ub; int idx = 0; int found = 0; int constructed __attribute__((unused)); uint16_t id_value; for (;;) { GET_NEXT_BYTE; /* encoding bit, construced means that other elements are contained */ constructed = ((ub & 0x20) ? 1 : 0); if ((ub & 0x1F) == 0x1F) { /* fixed pattern, ID follows */ GET_NEXT_BYTE; /* ID */ id_value = ub & 0x7F; if (ub & 0x80) { /* extension bit */ GET_NEXT_BYTE; /* ID low part */ id_value = (id_value << 7) | (ub & 0x7F); } if (id_value == id) found = 1; } else { id_value = (ub & 0x3F); if (id_value == id) found = 1; } GET_NEXT_BYTE; /* length */ if (found) { /* get data */ uint8_t n = ub; uint8_t i; for (i = 0; i < n; i++) { GET_NEXT_BYTE; if (max_value <= 0) return -1; /* buffer too small */ *value = ub; value++; max_value--; } return n; /* length */ } else { /* skip data */ uint8_t n = ub; uint8_t i; for (i = 0; i < n; i++) { GET_NEXT_BYTE; } } } return 0; /* not found */ } static int dump_elements(uint8_t * data, int len) { uint8_t ub; int idx = 0; int constructed; uint16_t id_value; static char indent[100] = ""; /* TODO: move static to BTS context */ for (;;) { GET_NEXT_BYTE; /* encoding bit, construced means that other elements are contained */ constructed = ((ub & 0x20) ? 1 : 0); if ((ub & 0x1F) == 0x1F) { /* fixed pattern, ID follows */ GET_NEXT_BYTE; /* ID */ id_value = ub & 0x7F; if (ub & 0x80) { /* extension bit */ GET_NEXT_BYTE; /* ID low part */ id_value = (id_value << 7) | (ub & 0x7F); } } else { id_value = (ub & 0x3F); } GET_NEXT_BYTE; /* length */ printf("%s--ID = 0x%02X (%s) %s\n", indent, id_value, get_element_name_string(id_value), constructed ? "** constructed **" : ""); printf("%s length = %d\n", indent, ub); printf("%s %s\n", indent, osmo_hexdump(data + idx, ub)); if (constructed) { int indent_len = strlen(indent); strcat(indent, " "); dump_elements(data + idx, ub); indent[indent_len] = 0; } /* skip data */ uint8_t n = ub; uint8_t i; for (i = 0; i < n; i++) { GET_NEXT_BYTE; } } return 0; } /* TODO: put in a separate file ? */ /* taken from abis_nm.c */ static void nokia_abis_nm_queue_send_next(struct gsm_bts *bts) { int wait = 0; struct msgb *msg; /* the queue is empty */ while (!llist_empty(&bts->abis_queue)) { msg = msgb_dequeue(&bts->abis_queue); wait = OBSC_NM_W_ACK_CB(msg); abis_sendmsg(msg); if (wait) break; } bts->abis_nm_pend = wait; } /* TODO: put in a separate file ? */ /* timer for restarting OML after BTS reset */ static void reset_timer_cb(void *_bts) { struct gsm_bts *bts = _bts; struct gsm_e1_subslot *e1_link = &bts->oml_e1_link; struct e1inp_line *line; bts->nokia.wait_reset = 0; /* OML link */ line = e1inp_line_find(e1_link->e1_nr); if (!line) { LOGP(DLINP, LOGL_ERROR, "BTS %u OML link referring to " "non-existing E1 line %u\n", bts->nr, e1_link->e1_nr); return; } start_sabm_in_line(line, 0, -1); /* stop all first */ start_sabm_in_line(line, 1, SAPI_OML); /* start only OML */ } /* TODO: put in a separate file ? */ /* This is how the configuration is done: - start OML link - reset BTS - receive ACK, wait some time and restart OML link - receive OMU STARTED message, send START DOWNLOAD REQ - receive CNF REQ message, send CONF DATA - receive ACK, start RSL link(s) ACK some other messages received from the BTS. Probably its also possible to configure the BTS without a reset, this has not been tested yet. */ static int abis_nm_rcvmsg_fom(struct msgb *mb) { struct e1inp_sign_link *sign_link = (struct e1inp_sign_link *)mb->dst; struct gsm_bts *bts = sign_link->trx->bts; struct abis_om_hdr *oh = msgb_l2(mb); struct abis_om_nokia_hdr *noh = msgb_l3(mb); uint8_t mt = noh->msg_type; int ret = 0; uint16_t ref = ntohs(noh->reference); uint8_t info[256]; uint8_t ack = 0xFF; uint8_t severity = 0xFF; int str_len; int len_data; if (bts->nokia.wait_reset) { LOGP(DNM, LOGL_INFO, "Ignore message while waiting for reset\n"); return ret; } if (oh->length < sizeof(struct abis_om_nokia_hdr)) { LOGP(DNM, LOGL_ERROR, "Message too short\n"); return -EINVAL; } len_data = oh->length - sizeof(struct abis_om_nokia_hdr); LOGP(DNM, LOGL_INFO, "(0x%02X) %s\n", mt, get_msg_type_name_string(mt)); #if 0 /* debugging */ dump_elements(noh->data, len_data); #endif switch (mt) { case NOKIA_MSG_OMU_STARTED: if (find_element(noh->data, len_data, NOKIA_EI_BTS_TYPE, &bts->nokia.bts_type, sizeof(uint8_t)) == sizeof(uint8_t)) LOGP(DNM, LOGL_INFO, "BTS type = %d (%s)\n", bts->nokia.bts_type, get_bts_type_string(bts->nokia.bts_type)); else LOGP(DNM, LOGL_ERROR, "BTS type not found\n"); /* send START_DOWNLOAD_REQ */ abis_nm_download_req(bts, ref); break; case NOKIA_MSG_MF_REQ: break; case NOKIA_MSG_CONF_REQ: /* send ACK */ abis_nm_ack(bts, ref); nokia_abis_nm_queue_send_next(bts); /* send CONF_DATA */ abis_nm_send_config(bts, bts->nokia.bts_type); bts->nokia.configured = 1; break; case NOKIA_MSG_ACK: if (find_element (noh->data, len_data, NOKIA_EI_ACK, &ack, sizeof(uint8_t)) == sizeof(uint8_t)) { LOGP(DNM, LOGL_INFO, "ACK = %d\n", ack); if (ack != 1) { LOGP(DNM, LOGL_ERROR, "No ACK received (%d)\n", ack); /* TODO: properly handle failures (NACK) */ } } else LOGP(DNM, LOGL_ERROR, "ACK not found\n"); /* TODO: the assumption for the following is that no NACK was received */ /* ACK for reset message ? */ if (!bts->nokia.did_reset) { bts->nokia.did_reset = 1; /* TODO: For the InSite processing the received data is blocked in the driver during reset. Otherwise the LAPD module might assert because the InSite sends garbage on the E1 line during reset. This is done by looking at "wait_reset" in the driver (function handle_ts1_read()) and ignoring the received data. It seems to be necessary for the MetroSite too. */ bts->nokia.wait_reset = 1; bts->nokia.reset_timer.cb = &reset_timer_cb; bts->nokia.reset_timer.data = bts; osmo_timer_schedule(&bts->nokia.reset_timer, bts->nokia.bts_reset_timer_cnf, 0); struct gsm_e1_subslot *e1_link = &bts->oml_e1_link; struct e1inp_line *line; /* OML link */ line = e1inp_line_find(e1_link->e1_nr); if (!line) { LOGP(DLINP, LOGL_ERROR, "BTS %u OML link referring to " "non-existing E1 line %u\n", bts->nr, e1_link->e1_nr); return -ENOMEM; } start_sabm_in_line(line, 0, -1); /* stop all first */ } /* ACK for CONF DATA message ? */ if (bts->nokia.configured != 0) { /* start TRX (RSL link) */ struct gsm_e1_subslot *e1_link = &sign_link->trx->rsl_e1_link; struct e1inp_line *line; bts->nokia.configured = 0; /* RSL Link */ line = e1inp_line_find(e1_link->e1_nr); if (!line) { LOGP(DLINP, LOGL_ERROR, "TRX (%u/%u) RSL link referring " "to non-existing E1 line %u\n", sign_link->trx->bts->nr, sign_link->trx->nr, e1_link->e1_nr); return -ENOMEM; } /* start TRX */ start_sabm_in_line(line, 1, SAPI_RSL); /* start only RSL */ } break; case NOKIA_MSG_STATE_CHANGED: /* send ACK */ abis_nm_ack(bts, ref); break; case NOKIA_MSG_CONF_COMPLETE: /* send ACK */ abis_nm_ack(bts, ref); break; case NOKIA_MSG_BLOCK_CTRL_REQ: /* seems to be send when something goes wrong !? */ /* send ACK (do we have to send an ACK ?) */ abis_nm_ack(bts, ref); break; case NOKIA_MSG_ALARM: find_element(noh->data, len_data, NOKIA_EI_SEVERITY, &severity, sizeof(severity)); /* TODO: there might be alarms with both elements set */ str_len = find_element(noh->data, len_data, NOKIA_EI_ADD_INFO, info, sizeof(info)); if (str_len > 0) { info[str_len] = 0; LOGP(DNM, LOGL_INFO, "ALARM Severity %s (%d) : %s\n", get_severity_string(severity), severity, info); } else { /* nothing found, try details */ str_len = find_element(noh->data, len_data, NOKIA_EI_ALARM_DETAIL, info, sizeof(info)); if (str_len > 0) { uint16_t code; info[str_len] = 0; code = (info[0] << 8) + info[1]; LOGP(DNM, LOGL_INFO, "ALARM Severity %s (%d), code 0x%X : %s\n", get_severity_string(severity), severity, code, info + 2); } } /* send ACK */ abis_nm_ack(bts, ref); break; } nokia_abis_nm_queue_send_next(bts); return ret; } /* TODO: put in a separate file ? */ int abis_nokia_rcvmsg(struct msgb *msg) { struct abis_om_hdr *oh = msgb_l2(msg); int rc = 0; /* Various consistency checks */ if (oh->placement != ABIS_OM_PLACEMENT_ONLY) { LOGP(DNM, LOGL_ERROR, "ABIS OML placement 0x%x not supported\n", oh->placement); if (oh->placement != ABIS_OM_PLACEMENT_FIRST) return -EINVAL; } if (oh->sequence != 0) { LOGP(DNM, LOGL_ERROR, "ABIS OML sequence 0x%x != 0x00\n", oh->sequence); return -EINVAL; } msg->l3h = (unsigned char *)oh + sizeof(*oh); switch (oh->mdisc) { case ABIS_OM_MDISC_FOM: LOGP(DNM, LOGL_INFO, "ABIS_OM_MDISC_FOM\n"); rc = abis_nm_rcvmsg_fom(msg); break; case ABIS_OM_MDISC_MANUF: LOGP(DNM, LOGL_INFO, "ABIS_OM_MDISC_MANUF\n"); break; case ABIS_OM_MDISC_MMI: case ABIS_OM_MDISC_TRAU: LOGP(DNM, LOGL_ERROR, "unimplemented ABIS OML message discriminator 0x%x\n", oh->mdisc); break; default: LOGP(DNM, LOGL_ERROR, "unknown ABIS OML message discriminator 0x%x\n", oh->mdisc); return -EINVAL; } msgb_free(msg); return rc; } static int bts_model_nokia_site_start(struct gsm_network *net); static void bts_model_nokia_site_e1line_bind_ops(struct e1inp_line *line) { e1inp_line_bind_ops(line, &bts_isdn_e1inp_line_ops); } static struct gsm_bts_model model_nokia_site = { .type = GSM_BTS_TYPE_NOKIA_SITE, .name = "nokia_site", .start = bts_model_nokia_site_start, .oml_rcvmsg = &abis_nokia_rcvmsg, .e1line_bind_ops = &bts_model_nokia_site_e1line_bind_ops, }; static struct gsm_network *my_net; static int bts_model_nokia_site_start(struct gsm_network *net) { model_nokia_site.features.data = &model_nokia_site._features_data[0]; model_nokia_site.features.data_len = sizeof(model_nokia_site._features_data); gsm_btsmodel_set_feature(&model_nokia_site, BTS_FEAT_HOPPING); gsm_btsmodel_set_feature(&model_nokia_site, BTS_FEAT_HSCSD); gsm_btsmodel_set_feature(&model_nokia_site, BTS_FEAT_MULTI_TSC); osmo_signal_register_handler(SS_L_INPUT, inp_sig_cb, NULL); osmo_signal_register_handler(SS_L_GLOBAL, gbl_sig_cb, NULL); osmo_signal_register_handler(SS_NM, nm_sig_cb, NULL); my_net = net; return 0; } int bts_model_nokia_site_init(void) { return gsm_bts_model_register(&model_nokia_site); } openbsc-0.15.0/openbsc/src/libbsc/bts_siemens_bs11.c000066400000000000000000000415441265565154000222320ustar00rootroot00000000000000/* Siemens BS-11 specific code */ /* (C) 2009-2010 by Harald Welte * * 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 . * */ #include #include #include #include #include #include static int bts_model_bs11_start(struct gsm_network *net); static void bts_model_bs11_e1line_bind_ops(struct e1inp_line *line) { e1inp_line_bind_ops(line, &bts_isdn_e1inp_line_ops); } static struct gsm_bts_model model_bs11 = { .type = GSM_BTS_TYPE_BS11, .name = "bs11", .start = bts_model_bs11_start, .oml_rcvmsg = &abis_nm_rcvmsg, .e1line_bind_ops = bts_model_bs11_e1line_bind_ops, .nm_att_tlvdef = { .def = { [NM_ATT_AVAIL_STATUS] = { TLV_TYPE_TLV }, /* BS11 specifics */ [NM_ATT_BS11_ESN_FW_CODE_NO] = { TLV_TYPE_TLV }, [NM_ATT_BS11_ESN_HW_CODE_NO] = { TLV_TYPE_TLV }, [NM_ATT_BS11_ESN_PCB_SERIAL] = { TLV_TYPE_TLV }, [NM_ATT_BS11_BOOT_SW_VERS] = { TLV_TYPE_TLV }, [0xd5] = { TLV_TYPE_TLV }, [0xa8] = { TLV_TYPE_TLV }, [NM_ATT_BS11_PASSWORD] = { TLV_TYPE_TLV }, [NM_ATT_BS11_TXPWR] = { TLV_TYPE_TLV }, [NM_ATT_BS11_RSSI_OFFS] = { TLV_TYPE_TLV }, [NM_ATT_BS11_LINE_CFG] = { TLV_TYPE_TV }, [NM_ATT_BS11_L1_PROT_TYPE] = { TLV_TYPE_TV }, [NM_ATT_BS11_BIT_ERR_THESH] = { TLV_TYPE_FIXED, 2 }, [NM_ATT_BS11_DIVERSITY] = { TLV_TYPE_TLV }, [NM_ATT_BS11_LMT_LOGON_SESSION]={ TLV_TYPE_TLV }, [NM_ATT_BS11_LMT_LOGIN_TIME] = { TLV_TYPE_TLV }, [NM_ATT_BS11_LMT_USER_ACC_LEV] ={ TLV_TYPE_TLV }, [NM_ATT_BS11_LMT_USER_NAME] = { TLV_TYPE_TLV }, [NM_ATT_BS11_BTS_STATE] = { TLV_TYPE_TLV }, [NM_ATT_BS11_E1_STATE] = { TLV_TYPE_TLV }, [NM_ATT_BS11_PLL_MODE] = { TLV_TYPE_TLV }, [NM_ATT_BS11_PLL] = { TLV_TYPE_TLV }, [NM_ATT_BS11_CCLK_ACCURACY] = { TLV_TYPE_TV }, [NM_ATT_BS11_CCLK_TYPE] = { TLV_TYPE_TV }, [0x95] = { TLV_TYPE_FIXED, 2 }, }, }, }; /* The following definitions are for OM and NM packets that we cannot yet * generate by code but we just pass on */ // BTS Site Manager, SET ATTRIBUTES /* Object Class: BTS Site Manager Instance 1: FF Instance 2: FF Instance 3: FF SET ATTRIBUTES sAbisExternalTime: 2007/09/08 14:36:11 omLAPDRelTimer: 30sec shortLAPDIntTimer: 5sec emergencyTimer1: 10 minutes emergencyTimer2: 0 minutes */ unsigned char msg_1[] = { NM_MT_BS11_SET_ATTR, NM_OC_SITE_MANAGER, 0xFF, 0xFF, 0xFF, NM_ATT_BS11_ABIS_EXT_TIME, 0x07, 0xD7, 0x09, 0x08, 0x0E, 0x24, 0x0B, 0xCE, 0x02, 0x00, 0x1E, NM_ATT_BS11_SH_LAPD_INT_TIMER, 0x01, 0x05, 0x42, 0x02, 0x00, 0x0A, 0x44, 0x02, 0x00, 0x00 }; // BTS, SET BTS ATTRIBUTES /* Object Class: BTS BTS relat. Number: 0 Instance 2: FF Instance 3: FF SET BTS ATTRIBUTES bsIdentityCode / BSIC: PLMN_colour_code: 7h BS_colour_code: 7h BTS Air Timer T3105: 4 ,unit 10 ms btsIsHopping: FALSE periodCCCHLoadIndication: 1sec thresholdCCCHLoadIndication: 0% cellAllocationNumber: 00h = GSM 900 enableInterferenceClass: 00h = Disabled fACCHQual: 6 (FACCH stealing flags minus 1) intaveParameter: 31 SACCH multiframes interferenceLevelBoundaries: Interference Boundary 1: 0Ah Interference Boundary 2: 0Fh Interference Boundary 3: 14h Interference Boundary 4: 19h Interference Boundary 5: 1Eh mSTxPwrMax: 11 GSM range: 2=39dBm, 15=13dBm, stepsize 2 dBm DCS1800 range: 0=30dBm, 15=0dBm, stepsize 2 dBm PCS1900 range: 0=30dBm, 15=0dBm, stepsize 2 dBm 30=33dBm, 31=32dBm ny1: Maximum number of repetitions for PHYSICAL INFORMATION message (GSM 04.08): 20 powerOutputThresholds: Out Power Fault Threshold: -10 dB Red Out Power Threshold: - 6 dB Excessive Out Power Threshold: 5 dB rACHBusyThreshold: -127 dBm rACHLoadAveragingSlots: 250 ,number of RACH burst periods rfResourceIndicationPeriod: 125 SACCH multiframes T200: SDCCH: 044 in 5 ms FACCH/Full rate: 031 in 5 ms FACCH/Half rate: 041 in 5 ms SACCH with TCH SAPI0: 090 in 10 ms SACCH with SDCCH: 090 in 10 ms SDCCH with SAPI3: 090 in 5 ms SACCH with TCH SAPI3: 135 in 10 ms tSync: 9000 units of 10 msec tTrau: 9000 units of 10 msec enableUmLoopTest: 00h = disabled enableExcessiveDistance: 00h = Disabled excessiveDistance: 64km hoppingMode: 00h = baseband hopping cellType: 00h = Standard Cell BCCH ARFCN / bCCHFrequency: 1 */ static unsigned char bs11_attr_bts[] = { NM_ATT_BSIC, HARDCODED_BSIC, NM_ATT_BTS_AIR_TIMER, 0x04, NM_ATT_BS11_BTSLS_HOPPING, 0x00, NM_ATT_CCCH_L_I_P, 0x01, NM_ATT_CCCH_L_T, 0x00, NM_ATT_BS11_CELL_ALLOC_NR, NM_BS11_CANR_GSM, NM_ATT_BS11_ENA_INTERF_CLASS, 0x01, NM_ATT_BS11_FACCH_QUAL, 0x06, /* interference avg. period in numbers of SACCH multifr */ NM_ATT_INTAVE_PARAM, 0x1F, NM_ATT_INTERF_BOUND, 0x0A, 0x0F, 0x14, 0x19, 0x1E, 0x7B, NM_ATT_CCCH_L_T, 0x23, NM_ATT_GSM_TIME, 0x28, 0x00, NM_ATT_ADM_STATE, 0x03, NM_ATT_RACH_B_THRESH, 0x7F, NM_ATT_LDAVG_SLOTS, 0x00, 0xFA, NM_ATT_BS11_RF_RES_IND_PER, 0x7D, NM_ATT_T200, 0x2C, 0x1F, 0x29, 0x5A, 0x5A, 0x5A, 0x87, NM_ATT_BS11_TSYNC, 0x23, 0x28, NM_ATT_BS11_TTRAU, 0x23, 0x28, NM_ATT_TEST_DUR, 0x01, 0x00, NM_ATT_OUTST_ALARM, 0x01, 0x00, NM_ATT_BS11_EXCESSIVE_DISTANCE, 0x01, 0x40, NM_ATT_BS11_HOPPING_MODE, 0x01, 0x00, NM_ATT_BS11_PLL, 0x01, 0x00, NM_ATT_BCCH_ARFCN, 0x00, HARDCODED_ARFCN/*0x01*/, }; // Handover Recognition, SET ATTRIBUTES /* Illegal Contents GSM Formatted O&M Msg Object Class: Handover Recognition BTS relat. Number: 0 Instance 2: FF Instance 3: FF SET ATTRIBUTES enableDelayPowerBudgetHO: 00h = Disabled enableDistanceHO: 00h = Disabled enableInternalInterCellHandover: 00h = Disabled enableInternalIntraCellHandover: 00h = Disabled enablePowerBudgetHO: 00h = Disabled enableRXLEVHO: 00h = Disabled enableRXQUALHO: 00h = Disabled hoAveragingDistance: 8 SACCH multiframes hoAveragingLev: A_LEV_HO: 8 SACCH multiframes W_LEV_HO: 1 SACCH multiframes hoAveragingPowerBudget: 16 SACCH multiframes hoAveragingQual: A_QUAL_HO: 8 SACCH multiframes W_QUAL_HO: 2 SACCH multiframes hoLowerThresholdLevDL: (10 - 110) dBm hoLowerThresholdLevUL: (5 - 110) dBm hoLowerThresholdQualDL: 06h = 6.4% < BER < 12.8% hoLowerThresholdQualUL: 06h = 6.4% < BER < 12.8% hoThresholdLevDLintra : (20 - 110) dBm hoThresholdLevULintra: (20 - 110) dBm hoThresholdMsRangeMax: 20 km nCell: 06h timerHORequest: 3 ,unit 2 SACCH multiframes */ unsigned char msg_3[] = { NM_MT_BS11_SET_ATTR, NM_OC_BS11_HANDOVER, 0x00, 0xFF, 0xFF, 0xD0, 0x00, /* enableDelayPowerBudgetHO */ 0x64, 0x00, /* enableDistanceHO */ 0x67, 0x00, /* enableInternalInterCellHandover */ 0x68, 0x00, /* enableInternalInterCellHandover */ 0x6A, 0x00, /* enablePowerBudgetHO */ 0x6C, 0x00, /* enableRXLEVHO */ 0x6D, 0x00, /* enableRXQUALHO */ 0x6F, 0x08, /* hoAveragingDistance */ 0x70, 0x08, 0x01, /* hoAveragingLev */ 0x71, 0x10, 0x10, 0x10, 0x72, 0x08, 0x02, /* hoAveragingQual */ 0x73, 0x0A, /* hoLowerThresholdLevDL */ 0x74, 0x05, /* hoLowerThresholdLevUL */ 0x75, 0x06, /* hoLowerThresholdQualDL */ 0x76, 0x06, /* hoLowerThresholdQualUL */ 0x78, 0x14, /* hoThresholdLevDLintra */ 0x79, 0x14, /* hoThresholdLevULintra */ 0x7A, 0x14, /* hoThresholdMsRangeMax */ 0x7D, 0x06, /* nCell */ NM_ATT_BS11_TIMER_HO_REQUEST, 0x03, 0x20, 0x01, 0x00, 0x45, 0x01, 0x00, 0x48, 0x01, 0x00, 0x5A, 0x01, 0x00, 0x5B, 0x01, 0x05, 0x5E, 0x01, 0x1A, 0x5F, 0x01, 0x20, 0x9D, 0x01, 0x00, 0x47, 0x01, 0x00, 0x5C, 0x01, 0x64, 0x5D, 0x01, 0x1E, 0x97, 0x01, 0x20, 0xF7, 0x01, 0x3C, }; // Power Control, SET ATTRIBUTES /* Object Class: Power Control BTS relat. Number: 0 Instance 2: FF Instance 3: FF SET ATTRIBUTES enableMsPowerControl: 00h = Disabled enablePowerControlRLFW: 00h = Disabled pcAveragingLev: A_LEV_PC: 4 SACCH multiframes W_LEV_PC: 1 SACCH multiframes pcAveragingQual: A_QUAL_PC: 4 SACCH multiframes W_QUAL_PC: 2 SACCH multiframes pcLowerThresholdLevDL: 0Fh pcLowerThresholdLevUL: 0Ah pcLowerThresholdQualDL: 05h = 3.2% < BER < 6.4% pcLowerThresholdQualUL: 05h = 3.2% < BER < 6.4% pcRLFThreshold: 0Ch pcUpperThresholdLevDL: 14h pcUpperThresholdLevUL: 0Fh pcUpperThresholdQualDL: 04h = 1.6% < BER < 3.2% pcUpperThresholdQualUL: 04h = 1.6% < BER < 3.2% powerConfirm: 2 ,unit 2 SACCH multiframes powerControlInterval: 2 ,unit 2 SACCH multiframes powerIncrStepSize: 02h = 4 dB powerRedStepSize: 01h = 2 dB radioLinkTimeoutBs: 64 SACCH multiframes enableBSPowerControl: 00h = disabled */ unsigned char msg_4[] = { NM_MT_BS11_SET_ATTR, NM_OC_BS11_PWR_CTRL, 0x00, 0xFF, 0xFF, NM_ATT_BS11_ENA_MS_PWR_CTRL, 0x00, NM_ATT_BS11_ENA_PWR_CTRL_RLFW, 0x00, 0x7E, 0x04, 0x01, /* pcAveragingLev */ 0x7F, 0x04, 0x02, /* pcAveragingQual */ 0x80, 0x0F, /* pcLowerThresholdLevDL */ 0x81, 0x0A, /* pcLowerThresholdLevUL */ 0x82, 0x05, /* pcLowerThresholdQualDL */ 0x83, 0x05, /* pcLowerThresholdQualUL */ 0x84, 0x0C, /* pcRLFThreshold */ 0x85, 0x14, /* pcUpperThresholdLevDL */ 0x86, 0x0F, /* pcUpperThresholdLevUL */ 0x87, 0x04, /* pcUpperThresholdQualDL */ 0x88, 0x04, /* pcUpperThresholdQualUL */ 0x89, 0x02, /* powerConfirm */ 0x8A, 0x02, /* powerConfirmInterval */ 0x8B, 0x02, /* powerIncrStepSize */ 0x8C, 0x01, /* powerRedStepSize */ 0x8D, 0x40, /* radioLinkTimeoutBs */ 0x65, 0x01, 0x00 // set to 0x01 to enable BSPowerControl }; // Transceiver, SET TRX ATTRIBUTES (TRX 0) /* Object Class: Transceiver BTS relat. Number: 0 Tranceiver number: 0 Instance 3: FF SET TRX ATTRIBUTES aRFCNList (HEX): 0001 txPwrMaxReduction: 00h = 30dB radioMeasGran: 254 SACCH multiframes radioMeasRep: 01h = enabled memberOfEmergencyConfig: 01h = TRUE trxArea: 00h = TRX doesn't belong to a concentric cell */ static unsigned char bs11_attr_radio[] = { NM_ATT_ARFCN_LIST, 0x01, 0x00, HARDCODED_ARFCN /*0x01*/, NM_ATT_RF_MAXPOWR_R, 0x00, NM_ATT_BS11_RADIO_MEAS_GRAN, 0x01, 0x05, NM_ATT_BS11_RADIO_MEAS_REP, 0x01, 0x01, NM_ATT_BS11_EMRG_CFG_MEMBER, 0x01, 0x01, NM_ATT_BS11_TRX_AREA, 0x01, 0x00, }; /* * Patch the various SYSTEM INFORMATION tables to update * the LAI */ static void patch_nm_tables(struct gsm_bts *bts) { uint8_t arfcn_low = bts->c0->arfcn & 0xff; uint8_t arfcn_high = (bts->c0->arfcn >> 8) & 0x0f; /* T3105 attribute in units of 10ms */ bs11_attr_bts[2] = bts->network->T3105 / 10; /* patch ARFCN into BTS Attributes */ bs11_attr_bts[69] &= 0xf0; bs11_attr_bts[69] |= arfcn_high; bs11_attr_bts[70] = arfcn_low; /* patch ARFCN into TRX Attributes */ bs11_attr_radio[2] &= 0xf0; bs11_attr_radio[2] |= arfcn_high; bs11_attr_radio[3] = arfcn_low; /* patch the RACH attributes */ if (bts->rach_b_thresh != -1) bs11_attr_bts[33] = bts->rach_b_thresh & 0xff; if (bts->rach_ldavg_slots != -1) { uint8_t avg_high = bts->rach_ldavg_slots & 0xff; uint8_t avg_low = (bts->rach_ldavg_slots >> 8) & 0x0f; bs11_attr_bts[35] = avg_high; bs11_attr_bts[36] = avg_low; } /* patch BSIC */ bs11_attr_bts[1] = bts->bsic; /* patch the power reduction */ bs11_attr_radio[5] = bts->c0->max_power_red / 2; } static void nm_reconfig_ts(struct gsm_bts_trx_ts *ts) { enum abis_nm_chan_comb ccomb = abis_nm_chcomb4pchan(ts->pchan); struct gsm_e1_subslot *e1l = &ts->e1_link; abis_nm_set_channel_attr(ts, ccomb); if (is_ipaccess_bts(ts->trx->bts)) return; switch (ts->pchan) { case GSM_PCHAN_TCH_F: case GSM_PCHAN_TCH_H: abis_nm_conn_terr_traf(ts, e1l->e1_nr, e1l->e1_ts, e1l->e1_ts_ss); break; default: break; } } static void nm_reconfig_trx(struct gsm_bts_trx *trx) { struct gsm_e1_subslot *e1l = &trx->rsl_e1_link; int i; patch_nm_tables(trx->bts); switch (trx->bts->type) { case GSM_BTS_TYPE_BS11: /* FIXME: discover this by fetching an attribute */ #if 0 trx->nominal_power = 15; /* 15dBm == 30mW PA configuration */ #else trx->nominal_power = 24; /* 24dBm == 250mW PA configuration */ #endif abis_nm_conn_terr_sign(trx, e1l->e1_nr, e1l->e1_ts, e1l->e1_ts_ss); abis_nm_establish_tei(trx->bts, trx->nr, e1l->e1_nr, e1l->e1_ts, e1l->e1_ts_ss, trx->rsl_tei); /* Set Radio Attributes */ if (trx == trx->bts->c0) abis_nm_set_radio_attr(trx, bs11_attr_radio, sizeof(bs11_attr_radio)); else { uint8_t trx1_attr_radio[sizeof(bs11_attr_radio)]; uint8_t arfcn_low = trx->arfcn & 0xff; uint8_t arfcn_high = (trx->arfcn >> 8) & 0x0f; memcpy(trx1_attr_radio, bs11_attr_radio, sizeof(trx1_attr_radio)); /* patch ARFCN into TRX Attributes */ trx1_attr_radio[2] &= 0xf0; trx1_attr_radio[2] |= arfcn_high; trx1_attr_radio[3] = arfcn_low; abis_nm_set_radio_attr(trx, trx1_attr_radio, sizeof(trx1_attr_radio)); } break; case GSM_BTS_TYPE_NANOBTS: switch (trx->bts->band) { case GSM_BAND_850: case GSM_BAND_900: trx->nominal_power = 20; break; case GSM_BAND_1800: case GSM_BAND_1900: trx->nominal_power = 23; break; default: LOGP(DNM, LOGL_ERROR, "Unsupported nanoBTS GSM band %s\n", gsm_band_name(trx->bts->band)); break; } break; default: break; } for (i = 0; i < TRX_NR_TS; i++) nm_reconfig_ts(&trx->ts[i]); } static void nm_reconfig_bts(struct gsm_bts *bts) { struct gsm_bts_trx *trx; switch (bts->type) { case GSM_BTS_TYPE_BS11: patch_nm_tables(bts); abis_nm_raw_msg(bts, sizeof(msg_1), msg_1); /* set BTS SiteMgr attr*/ abis_nm_set_bts_attr(bts, bs11_attr_bts, sizeof(bs11_attr_bts)); abis_nm_raw_msg(bts, sizeof(msg_3), msg_3); /* set BTS handover attr */ abis_nm_raw_msg(bts, sizeof(msg_4), msg_4); /* set BTS power control attr */ break; default: break; } llist_for_each_entry(trx, &bts->trx_list, list) nm_reconfig_trx(trx); } static void bootstrap_om_bs11(struct gsm_bts *bts) { LOGP(DNM, LOGL_NOTICE, "bootstrapping OML for BTS %u\n", bts->nr); /* stop sending event reports */ abis_nm_event_reports(bts, 0); /* begin DB transmission */ abis_nm_bs11_db_transmission(bts, 1); /* end DB transmission */ abis_nm_bs11_db_transmission(bts, 0); /* Reset BTS Site manager resource */ abis_nm_bs11_reset_resource(bts); /* begin DB transmission */ abis_nm_bs11_db_transmission(bts, 1); /* reconfigure BTS with all TRX and all TS */ nm_reconfig_bts(bts); /* end DB transmission */ abis_nm_bs11_db_transmission(bts, 0); /* Reset BTS Site manager resource */ abis_nm_bs11_reset_resource(bts); /* restart sending event reports */ abis_nm_event_reports(bts, 1); } static int shutdown_om(struct gsm_bts *bts) { /* stop sending event reports */ abis_nm_event_reports(bts, 0); /* begin DB transmission */ abis_nm_bs11_db_transmission(bts, 1); /* end DB transmission */ abis_nm_bs11_db_transmission(bts, 0); /* Reset BTS Site manager resource */ abis_nm_bs11_reset_resource(bts); return 0; } /* Callback function to be called every time we receive a signal from INPUT */ static int gbl_sig_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct gsm_bts *bts; if (subsys != SS_L_GLOBAL) return 0; switch (signal) { case S_GLOBAL_BTS_CLOSE_OM: bts = signal_data; if (bts->type == GSM_BTS_TYPE_BS11) shutdown_om(signal_data); break; } return 0; } /* Callback function to be called every time we receive a signal from INPUT */ static int inp_sig_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct input_signal_data *isd = signal_data; if (subsys != SS_L_INPUT) return 0; switch (signal) { case S_L_INP_TEI_UP: switch (isd->link_type) { case E1INP_SIGN_OML: if (isd->trx->bts->type == GSM_BTS_TYPE_BS11) bootstrap_om_bs11(isd->trx->bts); break; } } return 0; } static int bts_model_bs11_start(struct gsm_network *net) { model_bs11.features.data = &model_bs11._features_data[0]; model_bs11.features.data_len = sizeof(model_bs11._features_data); gsm_btsmodel_set_feature(&model_bs11, BTS_FEAT_HOPPING); gsm_btsmodel_set_feature(&model_bs11, BTS_FEAT_HSCSD); gsm_btsmodel_set_feature(&model_bs11, BTS_FEAT_MULTI_TSC); osmo_signal_register_handler(SS_L_INPUT, inp_sig_cb, NULL); osmo_signal_register_handler(SS_L_GLOBAL, gbl_sig_cb, NULL); return 0; } int bts_model_bs11_init(void) { return gsm_bts_model_register(&model_bs11); } openbsc-0.15.0/openbsc/src/libbsc/bts_sysmobts.c000066400000000000000000000036321265565154000216200ustar00rootroot00000000000000/* sysmocom sysmoBTS specific code */ /* (C) 2010-2012 by Harald Welte * * 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern struct gsm_bts_model bts_model_nanobts; static struct gsm_bts_model model_sysmobts; int bts_model_sysmobts_init(void) { model_sysmobts = bts_model_nanobts; model_sysmobts.name = "sysmobts"; model_sysmobts.type = GSM_BTS_TYPE_OSMO_SYSMO; model_sysmobts.features.data = &model_sysmobts._features_data[0]; model_sysmobts.features.data_len = sizeof(model_sysmobts._features_data); memset(model_sysmobts.features.data, 0, sizeof(model_sysmobts.features.data_len)); gsm_btsmodel_set_feature(&model_sysmobts, BTS_FEAT_GPRS); gsm_btsmodel_set_feature(&model_sysmobts, BTS_FEAT_EGPRS); return gsm_bts_model_register(&model_sysmobts); } openbsc-0.15.0/openbsc/src/libbsc/bts_unknown.c000066400000000000000000000022471265565154000214350ustar00rootroot00000000000000/* Generic BTS - VTY code tries to allocate this BTS before type is known */ /* (C) 2010 by Daniel Willmann * * 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 . * */ #include #include #include static struct gsm_bts_model model_unknown = { .type = GSM_BTS_TYPE_UNKNOWN, .name = "unknown", .oml_rcvmsg = &abis_nm_rcvmsg, .nm_att_tlvdef = { .def = { }, }, }; int bts_model_unknown_init(void) { return gsm_bts_model_register(&model_unknown); } openbsc-0.15.0/openbsc/src/libbsc/chan_alloc.c000066400000000000000000000241711265565154000211510ustar00rootroot00000000000000/* GSM Channel allocation routines * * (C) 2008 by Harald Welte * (C) 2008, 2009 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include static int ts_is_usable(struct gsm_bts_trx_ts *ts) { /* FIXME: How does this behave for BS-11 ? */ if (is_ipaccess_bts(ts->trx->bts)) { if (!nm_is_running(&ts->mo.nm_state)) return 0; } return 1; } int trx_is_usable(struct gsm_bts_trx *trx) { /* FIXME: How does this behave for BS-11 ? */ if (is_ipaccess_bts(trx->bts)) { if (!nm_is_running(&trx->mo.nm_state) || !nm_is_running(&trx->bb_transc.mo.nm_state)) return 0; } return 1; } static const uint8_t subslots_per_pchan[] = { [GSM_PCHAN_NONE] = 0, [GSM_PCHAN_CCCH] = 0, [GSM_PCHAN_CCCH_SDCCH4] = 4, [GSM_PCHAN_TCH_F] = 1, [GSM_PCHAN_TCH_H] = 2, [GSM_PCHAN_SDCCH8_SACCH8C] = 8, /* FIXME: what about dynamic TCH_F_TCH_H ? */ [GSM_PCHAN_TCH_F_PDCH] = 1, [GSM_PCHAN_CCCH_SDCCH4_CBCH] = 4, [GSM_PCHAN_SDCCH8_SACCH8C_CBCH] = 8, }; static struct gsm_lchan * _lc_find_trx(struct gsm_bts_trx *trx, enum gsm_phys_chan_config pchan) { struct gsm_bts_trx_ts *ts; int j, ss; if (!trx_is_usable(trx)) return NULL; for (j = 0; j < 8; j++) { ts = &trx->ts[j]; if (!ts_is_usable(ts)) continue; /* ip.access dynamic TCH/F + PDCH combination */ if (ts->pchan == GSM_PCHAN_TCH_F_PDCH && pchan == GSM_PCHAN_TCH_F) { /* we can only consider such a dynamic channel * if the PDCH is currently inactive */ if (ts->flags & TS_F_PDCH_MODE) continue; } else if (ts->pchan != pchan) continue; /* check if all sub-slots are allocated yet */ for (ss = 0; ss < subslots_per_pchan[pchan]; ss++) { struct gsm_lchan *lc = &ts->lchan[ss]; if (lc->type == GSM_LCHAN_NONE && lc->state == LCHAN_S_NONE) return lc; } } return NULL; } static struct gsm_lchan * _lc_find_bts(struct gsm_bts *bts, enum gsm_phys_chan_config pchan) { struct gsm_bts_trx *trx; struct gsm_lchan *lc; if (bts->chan_alloc_reverse) { llist_for_each_entry_reverse(trx, &bts->trx_list, list) { lc = _lc_find_trx(trx, pchan); if (lc) return lc; } } else { llist_for_each_entry(trx, &bts->trx_list, list) { lc = _lc_find_trx(trx, pchan); if (lc) return lc; } } return NULL; } /* Allocate a logical channel */ struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type, int allow_bigger) { struct gsm_lchan *lchan = NULL; enum gsm_phys_chan_config first, first_cbch, second, second_cbch; switch (type) { case GSM_LCHAN_SDCCH: if (bts->chan_alloc_reverse) { first = GSM_PCHAN_SDCCH8_SACCH8C; first_cbch = GSM_PCHAN_SDCCH8_SACCH8C_CBCH; second = GSM_PCHAN_CCCH_SDCCH4; second_cbch = GSM_PCHAN_CCCH_SDCCH4_CBCH; } else { first = GSM_PCHAN_CCCH_SDCCH4; first_cbch = GSM_PCHAN_CCCH_SDCCH4_CBCH; second = GSM_PCHAN_SDCCH8_SACCH8C; second_cbch = GSM_PCHAN_SDCCH8_SACCH8C_CBCH; } lchan = _lc_find_bts(bts, first); if (lchan == NULL) lchan = _lc_find_bts(bts, first_cbch); if (lchan == NULL) lchan = _lc_find_bts(bts, second); if (lchan == NULL) lchan = _lc_find_bts(bts, second_cbch); /* allow to assign bigger channels */ if (allow_bigger) { if (lchan == NULL) { lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_H); type = GSM_LCHAN_TCH_H; } if (lchan == NULL) { lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F); type = GSM_LCHAN_TCH_F; } } break; case GSM_LCHAN_TCH_F: lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F); /* If we don't have TCH/F available, fall-back to TCH/H */ if (!lchan) { lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_H); type = GSM_LCHAN_TCH_H; } break; case GSM_LCHAN_TCH_H: lchan =_lc_find_bts(bts, GSM_PCHAN_TCH_H); /* If we don't have TCH/H available, fall-back to TCH/F */ if (!lchan) { lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F); type = GSM_LCHAN_TCH_F; } break; default: LOGP(DRLL, LOGL_ERROR, "Unknown gsm_chan_t %u\n", type); } if (lchan) { lchan->type = type; /* clear sapis */ memset(lchan->sapis, 0, ARRAY_SIZE(lchan->sapis)); /* clear multi rate config */ memset(&lchan->mr_ms_lv, 0, sizeof(lchan->mr_ms_lv)); memset(&lchan->mr_bts_lv, 0, sizeof(lchan->mr_bts_lv)); lchan->broken_reason = ""; } else { struct challoc_signal_data sig; sig.bts = bts; sig.type = type; osmo_signal_dispatch(SS_CHALLOC, S_CHALLOC_ALLOC_FAIL, &sig); } return lchan; } /* Free a logical channel */ void lchan_free(struct gsm_lchan *lchan) { struct challoc_signal_data sig; int i; sig.type = lchan->type; lchan->type = GSM_LCHAN_NONE; if (lchan->conn) { struct lchan_signal_data sig; /* We might kill an active channel... */ sig.lchan = lchan; sig.mr = NULL; osmo_signal_dispatch(SS_LCHAN, S_LCHAN_UNEXPECTED_RELEASE, &sig); } if (lchan->abis_ip.rtp_socket) { LOGP(DRLL, LOGL_ERROR, "%s RTP Proxy Socket remained open.\n", gsm_lchan_name(lchan)); rtp_socket_free(lchan->abis_ip.rtp_socket); lchan->abis_ip.rtp_socket = NULL; } /* stop the timer */ osmo_timer_del(&lchan->T3101); /* clear cached measuement reports */ lchan->meas_rep_idx = 0; for (i = 0; i < ARRAY_SIZE(lchan->meas_rep); i++) { lchan->meas_rep[i].flags = 0; lchan->meas_rep[i].nr = 0; } for (i = 0; i < ARRAY_SIZE(lchan->neigh_meas); i++) lchan->neigh_meas[i].arfcn = 0; if (lchan->rqd_ref) { talloc_free(lchan->rqd_ref); lchan->rqd_ref = NULL; lchan->rqd_ta = 0; } sig.lchan = lchan; sig.bts = lchan->ts->trx->bts; osmo_signal_dispatch(SS_CHALLOC, S_CHALLOC_FREED, &sig); if (lchan->conn) { LOGP(DRLL, LOGL_ERROR, "the subscriber connection should be gone.\n"); lchan->conn = NULL; } /* FIXME: ts_free() the timeslot, if we're the last logical * channel using it */ } /* * There was an error with the TRX and we need to forget * any state so that a lchan can be allocated again after * the trx is fully usable. * * This should be called after lchan_free to force a channel * be available for allocation again. This means that this * method will stop the "delay after error"-timer and set the * state to LCHAN_S_NONE. */ void lchan_reset(struct gsm_lchan *lchan) { osmo_timer_del(&lchan->T3101); osmo_timer_del(&lchan->T3109); osmo_timer_del(&lchan->T3111); osmo_timer_del(&lchan->error_timer); lchan->type = GSM_LCHAN_NONE; lchan->state = LCHAN_S_NONE; if (lchan->abis_ip.rtp_socket) { rtp_socket_free(lchan->abis_ip.rtp_socket); lchan->abis_ip.rtp_socket = NULL; } } /* Drive the release process of the lchan */ static void _lchan_handle_release(struct gsm_lchan *lchan, int sacch_deact, int mode) { /* Release all SAPIs on the local end and continue */ rsl_release_sapis_from(lchan, 1, RSL_REL_LOCAL_END); /* * Shall we send a RR Release, start T3109 and wait for the * release indication from the BTS or just take it down (e.g. * on assignment requests) */ if (sacch_deact) { gsm48_send_rr_release(lchan); /* Deactivate the SACCH on the BTS side */ rsl_deact_sacch(lchan); rsl_start_t3109(lchan); } else if (lchan->sapis[0] == LCHAN_SAPI_UNUSED) { rsl_direct_rf_release(lchan); } else { rsl_release_request(lchan, 0, mode); } } /* Consider releasing the channel now */ int lchan_release(struct gsm_lchan *lchan, int sacch_deact, enum rsl_rel_mode mode) { DEBUGP(DRLL, "%s starting release sequence\n", gsm_lchan_name(lchan)); rsl_lchan_set_state(lchan, LCHAN_S_REL_REQ); lchan->conn = NULL; _lchan_handle_release(lchan, sacch_deact, mode); return 1; } static struct gsm_lchan* lchan_find(struct gsm_bts *bts, struct gsm_subscriber *subscr) { struct gsm_bts_trx *trx; int ts_no, lchan_no; llist_for_each_entry(trx, &bts->trx_list, list) { for (ts_no = 0; ts_no < 8; ++ts_no) { for (lchan_no = 0; lchan_no < TS_MAX_LCHAN; ++lchan_no) { struct gsm_lchan *lchan = &trx->ts[ts_no].lchan[lchan_no]; if (lchan->conn && subscr == lchan->conn->subscr) return lchan; } } } return NULL; } struct gsm_subscriber_connection *connection_for_subscr(struct gsm_subscriber *subscr) { struct gsm_bts *bts; struct gsm_network *net = subscr->group->net; struct gsm_lchan *lchan; llist_for_each_entry(bts, &net->bts_list, list) { lchan = lchan_find(bts, subscr); if (lchan) return lchan->conn; } return NULL; } void bts_chan_load(struct pchan_load *cl, const struct gsm_bts *bts) { struct gsm_bts_trx *trx; llist_for_each_entry(trx, &bts->trx_list, list) { int i; /* skip administratively deactivated tranxsceivers */ if (!nm_is_running(&trx->mo.nm_state) || !nm_is_running(&trx->bb_transc.mo.nm_state)) continue; for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { struct gsm_bts_trx_ts *ts = &trx->ts[i]; struct load_counter *pl = &cl->pchan[ts->pchan]; int j; /* skip administratively deactivated timeslots */ if (!nm_is_running(&ts->mo.nm_state)) continue; for (j = 0; j < subslots_per_pchan[ts->pchan]; j++) { struct gsm_lchan *lchan = &ts->lchan[j]; pl->total++; switch (lchan->state) { case LCHAN_S_NONE: break; default: pl->used++; break; } } } } } void network_chan_load(struct pchan_load *pl, struct gsm_network *net) { struct gsm_bts *bts; memset(pl, 0, sizeof(*pl)); llist_for_each_entry(bts, &net->bts_list, list) bts_chan_load(pl, bts); } openbsc-0.15.0/openbsc/src/libbsc/e1_config.c000066400000000000000000000213041265565154000207130ustar00rootroot00000000000000/* OpenBSC E1 Input code */ /* (C) 2008-2010 by Harald Welte * 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #define SAPI_L2ML 0 #define SAPI_OML 62 #define SAPI_RSL 0 /* 63 ? */ /* The e1_reconfig_*() functions below tale the configuration present in the * bts/trx/ts data structures and ensure the E1 configuration reflects the * timeslot/subslot/TEI configuration */ int e1_reconfig_ts(struct gsm_bts_trx_ts *ts) { struct gsm_e1_subslot *e1_link = &ts->e1_link; struct e1inp_line *line; struct e1inp_ts *e1_ts; DEBUGP(DLMI, "e1_reconfig_ts(%u,%u,%u)\n", ts->trx->bts->nr, ts->trx->nr, ts->nr); if (!e1_link->e1_ts) { LOGP(DLINP, LOGL_ERROR, "TS (%u/%u/%u) without E1 timeslot?\n", ts->nr, ts->trx->nr, ts->trx->bts->nr); return 0; } line = e1inp_line_find(e1_link->e1_nr); if (!line) { LOGP(DLINP, LOGL_ERROR, "TS (%u/%u/%u) referring to " "non-existing E1 line %u\n", ts->nr, ts->trx->nr, ts->trx->bts->nr, e1_link->e1_nr); return -ENOMEM; } switch (ts->pchan) { case GSM_PCHAN_TCH_F: case GSM_PCHAN_TCH_H: e1_ts = &line->ts[e1_link->e1_ts-1]; e1inp_ts_config_trau(e1_ts, line, subch_cb); subch_demux_activate(&e1_ts->trau.demux, e1_link->e1_ts_ss); break; default: break; } return 0; } int e1_reconfig_trx(struct gsm_bts_trx *trx) { struct gsm_e1_subslot *e1_link = &trx->rsl_e1_link; struct e1inp_ts *sign_ts; struct e1inp_line *line; struct e1inp_sign_link *rsl_link; int i; if (!e1_link->e1_ts) { LOGP(DLINP, LOGL_ERROR, "TRX (%u/%u) RSL link without " "timeslot?\n", trx->bts->nr, trx->nr); return -EINVAL; } /* RSL Link */ line = e1inp_line_find(e1_link->e1_nr); if (!line) { LOGP(DLINP, LOGL_ERROR, "TRX (%u/%u) RSL link referring " "to non-existing E1 line %u\n", trx->bts->nr, trx->nr, e1_link->e1_nr); return -ENOMEM; } sign_ts = &line->ts[e1_link->e1_ts-1]; e1inp_ts_config_sign(sign_ts, line); /* Ericsson RBS have a per-TRX OML link in parallel to RSL */ if (trx->bts->type == GSM_BTS_TYPE_RBS2000) { struct e1inp_sign_link *oml_link; oml_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OML, trx, trx->rsl_tei, SAPI_OML); if (!oml_link) { LOGP(DLINP, LOGL_ERROR, "TRX (%u/%u) OML link creation " "failed\n", trx->bts->nr, trx->nr); return -ENOMEM; } if (trx->oml_link) e1inp_sign_link_destroy(trx->oml_link); trx->oml_link = oml_link; } rsl_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_RSL, trx, trx->rsl_tei, SAPI_RSL); if (!rsl_link) { LOGP(DLINP, LOGL_ERROR, "TRX (%u/%u) RSL link creation " "failed\n", trx->bts->nr, trx->nr); return -ENOMEM; } if (trx->rsl_link) e1inp_sign_link_destroy(trx->rsl_link); trx->rsl_link = rsl_link; for (i = 0; i < TRX_NR_TS; i++) e1_reconfig_ts(&trx->ts[i]); return 0; } /* this is the generic callback for all ISDN-based BTS. */ static int bts_isdn_sign_link(struct msgb *msg) { int ret = -EINVAL; struct e1inp_sign_link *link = msg->dst; struct gsm_bts *bts; log_set_context(BSC_CTX_BTS, link->trx->bts); switch (link->type) { case E1INP_SIGN_OML: bts = link->trx->bts; ret = bts->model->oml_rcvmsg(msg); break; case E1INP_SIGN_RSL: ret = abis_rsl_rcvmsg(msg); break; default: LOGP(DLMI, LOGL_ERROR, "unknown link type %u\n", link->type); break; } return ret; } struct e1inp_line_ops bts_isdn_e1inp_line_ops = { .sign_link = bts_isdn_sign_link, }; int e1_reconfig_bts(struct gsm_bts *bts) { struct gsm_e1_subslot *e1_link = &bts->oml_e1_link; struct e1inp_ts *sign_ts; struct e1inp_line *line; struct e1inp_sign_link *oml_link; struct gsm_bts_trx *trx; DEBUGP(DLMI, "e1_reconfig_bts(%u)\n", bts->nr); line = e1inp_line_find(e1_link->e1_nr); if (!line) { LOGP(DLINP, LOGL_ERROR, "BTS %u OML link referring to " "non-existing E1 line %u\n", bts->nr, e1_link->e1_nr); return -ENOMEM; } if (!bts->model->e1line_bind_ops) { LOGP(DLINP, LOGL_ERROR, "no callback to bind E1 line operations\n"); return -EINVAL; } if (!line->ops) bts->model->e1line_bind_ops(line); /* skip signal link initialization, this is done later for these BTS. */ if (bts->type == GSM_BTS_TYPE_NANOBTS || bts->type == GSM_BTS_TYPE_OSMO_SYSMO) return e1inp_line_update(line); /* OML link */ if (!e1_link->e1_ts) { LOGP(DLINP, LOGL_ERROR, "BTS %u OML link without timeslot?\n", bts->nr); return -EINVAL; } sign_ts = &line->ts[e1_link->e1_ts-1]; e1inp_ts_config_sign(sign_ts, line); oml_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OML, bts->c0, bts->oml_tei, SAPI_OML); if (!oml_link) { LOGP(DLINP, LOGL_ERROR, "BTS %u OML link creation failed\n", bts->nr); return -ENOMEM; } if (bts->oml_link) e1inp_sign_link_destroy(bts->oml_link); bts->oml_link = oml_link; llist_for_each_entry(trx, &bts->trx_list, list) e1_reconfig_trx(trx); /* notify E1 input something has changed */ return e1inp_line_update(line); } #if 0 /* do some compiled-in configuration for our BTS/E1 setup */ int e1_config(struct gsm_bts *bts, int cardnr, int release_l2) { struct e1inp_line *line; struct e1inp_ts *sign_ts; struct e1inp_sign_link *oml_link, *rsl_link; struct gsm_bts_trx *trx = bts->c0; int base_ts; switch (bts->nr) { case 0: /* First BTS uses E1 TS 01,02,03,04,05 */ base_ts = HARDCODED_BTS0_TS - 1; break; case 1: /* Second BTS uses E1 TS 06,07,08,09,10 */ base_ts = HARDCODED_BTS1_TS - 1; break; case 2: /* Third BTS uses E1 TS 11,12,13,14,15 */ base_ts = HARDCODED_BTS2_TS - 1; default: return -EINVAL; } line = talloc_zero(tall_bsc_ctx, struct e1inp_line); if (!line) return -ENOMEM; /* create E1 timeslots for signalling and TRAU frames */ e1inp_ts_config(&line->ts[base_ts+1-1], line, E1INP_TS_TYPE_SIGN); e1inp_ts_config(&line->ts[base_ts+2-1], line, E1INP_TS_TYPE_TRAU); e1inp_ts_config(&line->ts[base_ts+3-1], line, E1INP_TS_TYPE_TRAU); /* create signalling links for TS1 */ sign_ts = &line->ts[base_ts+1-1]; oml_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OML, trx, TEI_OML, SAPI_OML); rsl_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_RSL, trx, TEI_RSL, SAPI_RSL); /* create back-links from bts/trx */ bts->oml_link = oml_link; trx->rsl_link = rsl_link; /* enable subchannel demuxer on TS2 */ subch_demux_activate(&line->ts[base_ts+2-1].trau.demux, 1); subch_demux_activate(&line->ts[base_ts+2-1].trau.demux, 2); subch_demux_activate(&line->ts[base_ts+2-1].trau.demux, 3); /* enable subchannel demuxer on TS3 */ subch_demux_activate(&line->ts[base_ts+3-1].trau.demux, 0); subch_demux_activate(&line->ts[base_ts+3-1].trau.demux, 1); subch_demux_activate(&line->ts[base_ts+3-1].trau.demux, 2); subch_demux_activate(&line->ts[base_ts+3-1].trau.demux, 3); trx = gsm_bts_trx_num(bts, 1); if (trx) { /* create E1 timeslots for TRAU frames of TRX1 */ e1inp_ts_config(&line->ts[base_ts+4-1], line, E1INP_TS_TYPE_TRAU); e1inp_ts_config(&line->ts[base_ts+5-1], line, E1INP_TS_TYPE_TRAU); /* create RSL signalling link for TRX1 */ sign_ts = &line->ts[base_ts+1-1]; rsl_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_RSL, trx, TEI_RSL+1, SAPI_RSL); /* create back-links from trx */ trx->rsl_link = rsl_link; /* enable subchannel demuxer on TS2 */ subch_demux_activate(&line->ts[base_ts+4-1].trau.demux, 0); subch_demux_activate(&line->ts[base_ts+4-1].trau.demux, 1); subch_demux_activate(&line->ts[base_ts+4-1].trau.demux, 2); subch_demux_activate(&line->ts[base_ts+4-1].trau.demux, 3); /* enable subchannel demuxer on TS3 */ subch_demux_activate(&line->ts[base_ts+5-1].trau.demux, 0); subch_demux_activate(&line->ts[base_ts+5-1].trau.demux, 1); subch_demux_activate(&line->ts[base_ts+5-1].trau.demux, 2); subch_demux_activate(&line->ts[base_ts+5-1].trau.demux, 3); } return mi_setup(cardnr, line, release_l2); } #endif openbsc-0.15.0/openbsc/src/libbsc/gsm_04_08_utils.c000066400000000000000000000506451265565154000217130ustar00rootroot00000000000000/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 * utility functions */ /* (C) 2008-2009 by Harald Welte * (C) 2008, 2009 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include /* should ip.access BTS use direct RTP streams between each other (1), * or should OpenBSC always act as RTP relay/proxy in between (0) ? */ int ipacc_rtp_direct = 1; static int gsm48_sendmsg(struct msgb *msg) { if (msg->lchan) msg->dst = msg->lchan->ts->trx->rsl_link; msg->l3h = msg->data; return rsl_data_request(msg, 0); } /* Section 9.1.8 / Table 9.9 */ struct chreq { uint8_t val; uint8_t mask; enum chreq_type type; }; /* If SYSTEM INFORMATION TYPE 4 NECI bit == 1 */ static const struct chreq chreq_type_neci1[] = { { 0xa0, 0xe0, CHREQ_T_EMERG_CALL }, { 0xc0, 0xe0, CHREQ_T_CALL_REEST_TCH_F }, { 0x68, 0xfc, CHREQ_T_CALL_REEST_TCH_H }, { 0x6c, 0xfc, CHREQ_T_CALL_REEST_TCH_H_DBL }, { 0xe0, 0xe0, CHREQ_T_TCH_F }, { 0x40, 0xf0, CHREQ_T_VOICE_CALL_TCH_H }, { 0x50, 0xf0, CHREQ_T_DATA_CALL_TCH_H }, { 0x00, 0xf0, CHREQ_T_LOCATION_UPD }, { 0x10, 0xf0, CHREQ_T_SDCCH }, { 0x80, 0xe0, CHREQ_T_PAG_R_ANY_NECI1 }, { 0x20, 0xf0, CHREQ_T_PAG_R_TCH_F }, { 0x30, 0xf0, CHREQ_T_PAG_R_TCH_FH }, { 0x67, 0xff, CHREQ_T_LMU }, { 0x60, 0xf9, CHREQ_T_RESERVED_SDCCH }, { 0x61, 0xfb, CHREQ_T_RESERVED_SDCCH }, { 0x63, 0xff, CHREQ_T_RESERVED_SDCCH }, { 0x7f, 0xff, CHREQ_T_RESERVED_IGNORE }, }; /* If SYSTEM INFORMATION TYPE 4 NECI bit == 0 */ static const struct chreq chreq_type_neci0[] = { { 0xa0, 0xe0, CHREQ_T_EMERG_CALL }, { 0xc0, 0xe0, CHREQ_T_CALL_REEST_TCH_H }, { 0xe0, 0xe0, CHREQ_T_TCH_F }, { 0x50, 0xf0, CHREQ_T_DATA_CALL_TCH_H }, { 0x00, 0xe0, CHREQ_T_LOCATION_UPD }, { 0x80, 0xe0, CHREQ_T_PAG_R_ANY_NECI0 }, { 0x20, 0xf0, CHREQ_T_PAG_R_TCH_F }, { 0x30, 0xf0, CHREQ_T_PAG_R_TCH_FH }, { 0x67, 0xff, CHREQ_T_LMU }, { 0x60, 0xf9, CHREQ_T_RESERVED_SDCCH }, { 0x61, 0xfb, CHREQ_T_RESERVED_SDCCH }, { 0x63, 0xff, CHREQ_T_RESERVED_SDCCH }, { 0x7f, 0xff, CHREQ_T_RESERVED_IGNORE }, }; static const enum gsm_chan_t ctype_by_chreq[] = { [CHREQ_T_EMERG_CALL] = GSM_LCHAN_TCH_F, [CHREQ_T_CALL_REEST_TCH_F] = GSM_LCHAN_TCH_F, [CHREQ_T_CALL_REEST_TCH_H] = GSM_LCHAN_TCH_H, [CHREQ_T_CALL_REEST_TCH_H_DBL] = GSM_LCHAN_TCH_H, [CHREQ_T_SDCCH] = GSM_LCHAN_SDCCH, [CHREQ_T_TCH_F] = GSM_LCHAN_TCH_F, [CHREQ_T_VOICE_CALL_TCH_H] = GSM_LCHAN_TCH_H, [CHREQ_T_DATA_CALL_TCH_H] = GSM_LCHAN_TCH_H, [CHREQ_T_LOCATION_UPD] = GSM_LCHAN_SDCCH, [CHREQ_T_PAG_R_ANY_NECI1] = GSM_LCHAN_SDCCH, [CHREQ_T_PAG_R_ANY_NECI0] = GSM_LCHAN_SDCCH, [CHREQ_T_PAG_R_TCH_F] = GSM_LCHAN_TCH_F, [CHREQ_T_PAG_R_TCH_FH] = GSM_LCHAN_TCH_F, [CHREQ_T_LMU] = GSM_LCHAN_SDCCH, [CHREQ_T_RESERVED_SDCCH] = GSM_LCHAN_SDCCH, [CHREQ_T_RESERVED_IGNORE] = GSM_LCHAN_UNKNOWN, }; static const enum gsm_chreq_reason_t reason_by_chreq[] = { [CHREQ_T_EMERG_CALL] = GSM_CHREQ_REASON_EMERG, [CHREQ_T_CALL_REEST_TCH_F] = GSM_CHREQ_REASON_CALL, [CHREQ_T_CALL_REEST_TCH_H] = GSM_CHREQ_REASON_CALL, [CHREQ_T_CALL_REEST_TCH_H_DBL] = GSM_CHREQ_REASON_CALL, [CHREQ_T_SDCCH] = GSM_CHREQ_REASON_OTHER, [CHREQ_T_TCH_F] = GSM_CHREQ_REASON_OTHER, [CHREQ_T_VOICE_CALL_TCH_H] = GSM_CHREQ_REASON_CALL, [CHREQ_T_DATA_CALL_TCH_H] = GSM_CHREQ_REASON_OTHER, [CHREQ_T_LOCATION_UPD] = GSM_CHREQ_REASON_LOCATION_UPD, [CHREQ_T_PAG_R_ANY_NECI1] = GSM_CHREQ_REASON_PAG, [CHREQ_T_PAG_R_ANY_NECI0] = GSM_CHREQ_REASON_PAG, [CHREQ_T_PAG_R_TCH_F] = GSM_CHREQ_REASON_PAG, [CHREQ_T_PAG_R_TCH_FH] = GSM_CHREQ_REASON_PAG, [CHREQ_T_LMU] = GSM_CHREQ_REASON_OTHER, [CHREQ_T_RESERVED_SDCCH] = GSM_CHREQ_REASON_OTHER, [CHREQ_T_RESERVED_IGNORE] = GSM_CHREQ_REASON_OTHER, }; /* verify that the two tables match */ osmo_static_assert(sizeof(ctype_by_chreq) == sizeof(((struct gsm_network *) NULL)->ctype_by_chreq), assert_size); /* * Update channel types for request based on policy. E.g. in the * case of a TCH/H network/bsc use TCH/H for the emergency calls, * for early assignment assign a SDCCH and some other options. */ void gsm_net_update_ctype(struct gsm_network *network) { /* copy over the data */ memcpy(network->ctype_by_chreq, ctype_by_chreq, sizeof(ctype_by_chreq)); /* * Use TCH/H for emergency calls when this cell allows TCH/H. Maybe it * is better to iterate over the BTS/TRX and check if no TCH/F is available * and then set it to TCH/H. */ if (network->neci) network->ctype_by_chreq[CHREQ_T_EMERG_CALL] = GSM_LCHAN_TCH_H; if (network->pag_any_tch) { if (network->neci) { network->ctype_by_chreq[CHREQ_T_PAG_R_ANY_NECI0] = GSM_LCHAN_TCH_H; network->ctype_by_chreq[CHREQ_T_PAG_R_ANY_NECI1] = GSM_LCHAN_TCH_H; } else { network->ctype_by_chreq[CHREQ_T_PAG_R_ANY_NECI0] = GSM_LCHAN_TCH_F; network->ctype_by_chreq[CHREQ_T_PAG_R_ANY_NECI1] = GSM_LCHAN_TCH_F; } } } enum gsm_chan_t get_ctype_by_chreq(struct gsm_network *network, uint8_t ra) { int i; int length; const struct chreq *chreq; if (network->neci) { chreq = chreq_type_neci1; length = ARRAY_SIZE(chreq_type_neci1); } else { chreq = chreq_type_neci0; length = ARRAY_SIZE(chreq_type_neci0); } for (i = 0; i < length; i++) { const struct chreq *chr = &chreq[i]; if ((ra & chr->mask) == chr->val) return network->ctype_by_chreq[chr->type]; } LOGP(DRR, LOGL_ERROR, "Unknown CHANNEL REQUEST RQD 0x%02x\n", ra); return GSM_LCHAN_SDCCH; } int get_reason_by_chreq(uint8_t ra, int neci) { int i; int length; const struct chreq *chreq; if (neci) { chreq = chreq_type_neci1; length = ARRAY_SIZE(chreq_type_neci1); } else { chreq = chreq_type_neci0; length = ARRAY_SIZE(chreq_type_neci0); } for (i = 0; i < length; i++) { const struct chreq *chr = &chreq[i]; if ((ra & chr->mask) == chr->val) return reason_by_chreq[chr->type]; } LOGP(DRR, LOGL_ERROR, "Unknown CHANNEL REQUEST REASON 0x%02x\n", ra); return GSM_CHREQ_REASON_OTHER; } static void mr_config_for_ms(struct gsm_lchan *lchan, struct msgb *msg) { if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) msgb_tlv_put(msg, GSM48_IE_MUL_RATE_CFG, lchan->mr_ms_lv[0], lchan->mr_ms_lv + 1); } /* 7.1.7 and 9.1.7: RR CHANnel RELease */ int gsm48_send_rr_release(struct gsm_lchan *lchan) { struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); uint8_t *cause; msg->lchan = lchan; gh->proto_discr = GSM48_PDISC_RR; gh->msg_type = GSM48_MT_RR_CHAN_REL; cause = msgb_put(msg, 1); cause[0] = GSM48_RR_CAUSE_NORMAL; DEBUGP(DRR, "Sending Channel Release: Chan: Number: %d Type: %d\n", lchan->nr, lchan->type); /* Send actual release request to MS */ return gsm48_sendmsg(msg); } int send_siemens_mrpci(struct gsm_lchan *lchan, uint8_t *classmark2_lv) { struct rsl_mrpci mrpci; if (classmark2_lv[0] < 2) return -EINVAL; mrpci.power_class = classmark2_lv[1] & 0x7; mrpci.vgcs_capable = classmark2_lv[2] & (1 << 1); mrpci.vbs_capable = classmark2_lv[2] & (1 <<2); mrpci.gsm_phase = (classmark2_lv[1]) >> 5 & 0x3; return rsl_siemens_mrpci(lchan, &mrpci); } int gsm48_extract_mi(uint8_t *classmark2_lv, int length, char *mi_string, uint8_t *mi_type) { /* Check the size for the classmark */ if (length < 1 + *classmark2_lv) return -1; uint8_t *mi_lv = classmark2_lv + *classmark2_lv + 1; if (length < 2 + *classmark2_lv + mi_lv[0]) return -2; *mi_type = mi_lv[1] & GSM_MI_TYPE_MASK; return gsm48_mi_to_string(mi_string, GSM48_MI_SIZE, mi_lv+1, *mi_lv); } int gsm48_paging_extract_mi(struct gsm48_pag_resp *resp, int length, char *mi_string, uint8_t *mi_type) { static const uint32_t classmark_offset = offsetof(struct gsm48_pag_resp, classmark2); uint8_t *classmark2_lv = (uint8_t *) &resp->classmark2; return gsm48_extract_mi(classmark2_lv, length - classmark_offset, mi_string, mi_type); } int gsm48_handle_paging_resp(struct gsm_subscriber_connection *conn, struct msgb *msg, struct gsm_subscriber *subscr) { struct gsm_bts *bts = msg->lchan->ts->trx->bts; struct gsm48_hdr *gh = msgb_l3(msg); uint8_t *classmark2_lv = gh->data + 1; if (is_siemens_bts(bts)) send_siemens_mrpci(msg->lchan, classmark2_lv); if (!conn->subscr) { conn->subscr = subscr; } else if (conn->subscr != subscr) { LOGP(DRR, LOGL_ERROR, "<- Channel already owned by someone else?\n"); subscr_put(subscr); return -EINVAL; } else { DEBUGP(DRR, "<- Channel already owned by us\n"); subscr_put(subscr); subscr = conn->subscr; } osmo_counter_inc(bts->network->stats.paging.completed); /* Stop paging on the bts we received the paging response */ paging_request_stop(conn->bts, subscr, conn, msg); return 0; } /* Chapter 9.1.9: Ciphering Mode Command */ int gsm48_send_rr_ciph_mode(struct gsm_lchan *lchan, int want_imeisv) { struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh; uint8_t ciph_mod_set; msg->lchan = lchan; DEBUGP(DRR, "TX CIPHERING MODE CMD\n"); if (lchan->encr.alg_id <= RSL_ENC_ALG_A5(0)) ciph_mod_set = 0; else ciph_mod_set = (lchan->encr.alg_id-2)<<1 | 1; gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); gh->proto_discr = GSM48_PDISC_RR; gh->msg_type = GSM48_MT_RR_CIPH_M_CMD; gh->data[0] = (want_imeisv & 0x1) << 4 | (ciph_mod_set & 0xf); return rsl_encryption_cmd(msg); } static void gsm48_cell_desc(struct gsm48_cell_desc *cd, const struct gsm_bts *bts) { cd->ncc = (bts->bsic >> 3 & 0x7); cd->bcc = (bts->bsic & 0x7); cd->arfcn_hi = bts->c0->arfcn >> 8; cd->arfcn_lo = bts->c0->arfcn & 0xff; } void gsm48_lchan2chan_desc(struct gsm48_chan_desc *cd, const struct gsm_lchan *lchan) { uint16_t arfcn = lchan->ts->trx->arfcn & 0x3ff; cd->chan_nr = gsm_lchan2chan_nr(lchan); if (!lchan->ts->hopping.enabled) { cd->h0.tsc = gsm_ts_tsc(lchan->ts); cd->h0.h = 0; cd->h0.arfcn_high = arfcn >> 8; cd->h0.arfcn_low = arfcn & 0xff; } else { cd->h1.tsc = gsm_ts_tsc(lchan->ts); cd->h1.h = 1; cd->h1.maio_high = lchan->ts->hopping.maio >> 2; cd->h1.maio_low = lchan->ts->hopping.maio & 0x03; cd->h1.hsn = lchan->ts->hopping.hsn; } } int gsm48_multirate_config(uint8_t *lv, struct amr_multirate_conf *mr, struct amr_mode *modes) { int num = 0, i; for (i = 0; i < 8; i++) { if (((mr->gsm48_ie[1] >> i) & 1)) num++; } if (num > 4) { LOGP(DRR, LOGL_ERROR, "BUG: Using multirate codec with too " "many modes in config.\n"); num = 4; } if (num < 1) { LOGP(DRR, LOGL_ERROR, "BUG: Using multirate codec with no " "mode in config.\n"); num = 1; } lv[0] = (num == 1) ? 2 : (num + 2); memcpy(lv + 1, mr->gsm48_ie, 2); if (num == 1) return 0; lv[3] = modes[0].threshold & 0x3f; lv[4] = modes[0].hysteresis << 4; if (num == 2) return 0; lv[4] |= (modes[1].threshold & 0x3f) >> 2; lv[5] = modes[1].threshold << 6; lv[5] |= (modes[1].hysteresis & 0x0f) << 2; if (num == 3) return 0; lv[5] |= (modes[2].threshold & 0x3f) >> 4; lv[6] = modes[2].threshold << 4; lv[6] |= modes[2].hysteresis & 0x0f; return 0; } #define GSM48_HOCMD_CCHDESC_LEN 16 /* Chapter 9.1.15: Handover Command */ int gsm48_send_ho_cmd(struct gsm_lchan *old_lchan, struct gsm_lchan *new_lchan, uint8_t power_command, uint8_t ho_ref) { struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); struct gsm48_ho_cmd *ho = (struct gsm48_ho_cmd *) msgb_put(msg, sizeof(*ho)); msg->lchan = old_lchan; gh->proto_discr = GSM48_PDISC_RR; gh->msg_type = GSM48_MT_RR_HANDO_CMD; /* mandatory bits */ gsm48_cell_desc(&ho->cell_desc, new_lchan->ts->trx->bts); gsm48_lchan2chan_desc(&ho->chan_desc, new_lchan); ho->ho_ref = ho_ref; ho->power_command = power_command; if (new_lchan->ts->hopping.enabled) { struct gsm_bts *bts = new_lchan->ts->trx->bts; struct gsm48_system_information_type_1 *si1; uint8_t *cur; si1 = GSM_BTS_SI(bts, SYSINFO_TYPE_1); /* Copy the Cell Chan Desc (ARFCNS in this cell) */ msgb_put_u8(msg, GSM48_IE_CELL_CH_DESC); cur = msgb_put(msg, GSM48_HOCMD_CCHDESC_LEN); memcpy(cur, si1->cell_channel_description, GSM48_HOCMD_CCHDESC_LEN); /* Copy the Mobile Allocation */ msgb_tlv_put(msg, GSM48_IE_MA_BEFORE, new_lchan->ts->hopping.ma_len, new_lchan->ts->hopping.ma_data); } /* FIXME: optional bits for type of synchronization? */ return gsm48_sendmsg(msg); } /* Chapter 9.1.2: Assignment Command */ int gsm48_send_rr_ass_cmd(struct gsm_lchan *dest_lchan, struct gsm_lchan *lchan, uint8_t power_command) { struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); struct gsm48_ass_cmd *ass = (struct gsm48_ass_cmd *) msgb_put(msg, sizeof(*ass)); DEBUGP(DRR, "-> ASSIGNMENT COMMAND tch_mode=0x%02x\n", lchan->tch_mode); msg->lchan = dest_lchan; gh->proto_discr = GSM48_PDISC_RR; gh->msg_type = GSM48_MT_RR_ASS_CMD; /* * fill the channel information element, this code * should probably be shared with rsl_rx_chan_rqd(), * gsm48_tx_chan_mode_modify. But beware that 10.5.2.5 * 10.5.2.5.a have slightly different semantic for * the chan_desc. But as long as multi-slot configurations * are not used we seem to be fine. */ gsm48_lchan2chan_desc(&ass->chan_desc, lchan); ass->power_command = power_command; /* optional: cell channel description */ msgb_tv_put(msg, GSM48_IE_CHANMODE_1, lchan->tch_mode); /* mobile allocation in case of hopping */ if (lchan->ts->hopping.enabled) { msgb_tlv_put(msg, GSM48_IE_MA_BEFORE, lchan->ts->hopping.ma_len, lchan->ts->hopping.ma_data); } /* in case of multi rate we need to attach a config */ mr_config_for_ms(lchan, msg); return gsm48_sendmsg(msg); } /* 9.1.5 Channel mode modify: Modify the mode on the MS side */ int gsm48_tx_chan_mode_modify(struct gsm_lchan *lchan, uint8_t mode) { struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); struct gsm48_chan_mode_modify *cmm = (struct gsm48_chan_mode_modify *) msgb_put(msg, sizeof(*cmm)); DEBUGP(DRR, "-> CHANNEL MODE MODIFY mode=0x%02x\n", mode); lchan->tch_mode = mode; msg->lchan = lchan; gh->proto_discr = GSM48_PDISC_RR; gh->msg_type = GSM48_MT_RR_CHAN_MODE_MODIF; /* fill the channel information element, this code * should probably be shared with rsl_rx_chan_rqd() */ gsm48_lchan2chan_desc(&cmm->chan_desc, lchan); cmm->mode = mode; /* in case of multi rate we need to attach a config */ mr_config_for_ms(lchan, msg); return gsm48_sendmsg(msg); } int gsm48_lchan_modify(struct gsm_lchan *lchan, uint8_t lchan_mode) { int rc; rc = gsm48_tx_chan_mode_modify(lchan, lchan_mode); if (rc < 0) return rc; return rc; } int gsm48_rx_rr_modif_ack(struct msgb *msg) { int rc; struct gsm48_hdr *gh = msgb_l3(msg); struct gsm48_chan_mode_modify *mod = (struct gsm48_chan_mode_modify *) gh->data; DEBUGP(DRR, "CHANNEL MODE MODIFY ACK\n"); if (mod->mode != msg->lchan->tch_mode) { LOGP(DRR, LOGL_ERROR, "CHANNEL MODE change failed. Wanted: %d Got: %d\n", msg->lchan->tch_mode, mod->mode); return -1; } /* update the channel type */ switch (mod->mode) { case GSM48_CMODE_SIGN: msg->lchan->rsl_cmode = RSL_CMOD_SPD_SIGN; break; case GSM48_CMODE_SPEECH_V1: case GSM48_CMODE_SPEECH_EFR: case GSM48_CMODE_SPEECH_AMR: msg->lchan->rsl_cmode = RSL_CMOD_SPD_SPEECH; break; case GSM48_CMODE_DATA_14k5: case GSM48_CMODE_DATA_12k0: case GSM48_CMODE_DATA_6k0: case GSM48_CMODE_DATA_3k6: msg->lchan->rsl_cmode = RSL_CMOD_SPD_DATA; break; } /* We've successfully modified the MS side of the channel, * now go on to modify the BTS side of the channel */ rc = rsl_chan_mode_modify_req(msg->lchan); /* FIXME: we not only need to do this after mode modify, but * also after channel activation */ if (is_ipaccess_bts(msg->lchan->ts->trx->bts) && mod->mode != GSM48_CMODE_SIGN) rsl_ipacc_crcx(msg->lchan); return rc; } int gsm48_parse_meas_rep(struct gsm_meas_rep *rep, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); uint8_t *data = gh->data; struct gsm_bts *bts = msg->lchan->ts->trx->bts; struct bitvec *nbv = &bts->si_common.neigh_list; struct gsm_meas_rep_cell *mrc; if (gh->msg_type != GSM48_MT_RR_MEAS_REP) return -EINVAL; if (data[0] & 0x80) rep->flags |= MEAS_REP_F_BA1; if (data[0] & 0x40) rep->flags |= MEAS_REP_F_UL_DTX; if ((data[1] & 0x40) == 0x00) rep->flags |= MEAS_REP_F_DL_VALID; rep->dl.full.rx_lev = data[0] & 0x3f; rep->dl.sub.rx_lev = data[1] & 0x3f; rep->dl.full.rx_qual = (data[2] >> 4) & 0x7; rep->dl.sub.rx_qual = (data[2] >> 1) & 0x7; rep->num_cell = ((data[3] >> 6) & 0x3) | ((data[2] & 0x01) << 2); if (rep->num_cell < 1 || rep->num_cell > 6) return 0; /* an encoding nightmare in perfection */ mrc = &rep->cell[0]; mrc->rxlev = data[3] & 0x3f; mrc->neigh_idx = data[4] >> 3; mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); mrc->bsic = ((data[4] & 0x07) << 3) | (data[5] >> 5); if (rep->num_cell < 2) return 0; mrc = &rep->cell[1]; mrc->rxlev = ((data[5] & 0x1f) << 1) | (data[6] >> 7); mrc->neigh_idx = (data[6] >> 2) & 0x1f; mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); mrc->bsic = ((data[6] & 0x03) << 4) | (data[7] >> 4); if (rep->num_cell < 3) return 0; mrc = &rep->cell[2]; mrc->rxlev = ((data[7] & 0x0f) << 2) | (data[8] >> 6); mrc->neigh_idx = (data[8] >> 1) & 0x1f; mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); mrc->bsic = ((data[8] & 0x01) << 5) | (data[9] >> 3); if (rep->num_cell < 4) return 0; mrc = &rep->cell[3]; mrc->rxlev = ((data[9] & 0x07) << 3) | (data[10] >> 5); mrc->neigh_idx = data[10] & 0x1f; mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); mrc->bsic = data[11] >> 2; if (rep->num_cell < 5) return 0; mrc = &rep->cell[4]; mrc->rxlev = ((data[11] & 0x03) << 4) | (data[12] >> 4); mrc->neigh_idx = ((data[12] & 0xf) << 1) | (data[13] >> 7); mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); mrc->bsic = (data[13] >> 1) & 0x3f; if (rep->num_cell < 6) return 0; mrc = &rep->cell[5]; mrc->rxlev = ((data[13] & 0x01) << 5) | (data[14] >> 3); mrc->neigh_idx = ((data[14] & 0x07) << 2) | (data[15] >> 6); mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); mrc->bsic = data[15] & 0x3f; return 0; } struct msgb *gsm48_create_mm_serv_rej(enum gsm48_reject_value value) { struct msgb *msg; struct gsm48_hdr *gh; msg = gsm48_msgb_alloc(); if (!msg) return NULL; gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); gh->proto_discr = GSM48_PDISC_MM; gh->msg_type = GSM48_MT_MM_CM_SERV_REJ; gh->data[0] = value; return msg; } struct msgb *gsm48_create_loc_upd_rej(uint8_t cause) { struct gsm48_hdr *gh; struct msgb *msg; msg = gsm48_msgb_alloc(); if (!msg) return NULL; gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); gh->proto_discr = GSM48_PDISC_MM; gh->msg_type = GSM48_MT_MM_LOC_UPD_REJECT; gh->data[0] = cause; return msg; } /* 9.2.5 CM service accept */ int gsm48_tx_mm_serv_ack(struct gsm_subscriber_connection *conn) { struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); msg->lchan = conn->lchan; gh->proto_discr = GSM48_PDISC_MM; gh->msg_type = GSM48_MT_MM_CM_SERV_ACC; DEBUGP(DMM, "-> CM SERVICE ACK\n"); return gsm0808_submit_dtap(conn, msg, 0, 0); } /* 9.2.6 CM service reject */ int gsm48_tx_mm_serv_rej(struct gsm_subscriber_connection *conn, enum gsm48_reject_value value) { struct msgb *msg; msg = gsm48_create_mm_serv_rej(value); if (!msg) { LOGP(DMM, LOGL_ERROR, "Failed to allocate CM Service Reject.\n"); return -1; } DEBUGP(DMM, "-> CM SERVICE Reject cause: %d\n", value); return gsm0808_submit_dtap(conn, msg, 0, 0); } openbsc-0.15.0/openbsc/src/libbsc/handover_decision.c000066400000000000000000000166621265565154000225570ustar00rootroot00000000000000/* Handover Decision making for Inter-BTS (Intra-BSC) Handover. This * only implements the handover algorithm/decision, but not execution * of it */ /* (C) 2009 by Harald Welte * * 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 . * */ #include #include #include #include #include #include #include #include #include #include /* issue handover to a cell identified by ARFCN and BSIC */ static int handover_to_arfcn_bsic(struct gsm_lchan *lchan, uint16_t arfcn, uint8_t bsic) { struct gsm_bts *new_bts; /* resolve the gsm_bts structure for the best neighbor */ new_bts = gsm_bts_neighbor(lchan->ts->trx->bts, arfcn, bsic); if (!new_bts) { LOGP(DHO, LOGL_NOTICE, "unable to determine neighbor BTS " "for ARFCN %u BSIC %u ?!?\n", arfcn, bsic); return -EINVAL; } /* and actually try to handover to that cell */ return bsc_handover_start(lchan, new_bts); } /* did we get a RXLEV for a given cell in the given report? */ static int rxlev_for_cell_in_rep(struct gsm_meas_rep *mr, uint16_t arfcn, uint8_t bsic) { int i; for (i = 0; i < mr->num_cell; i++) { struct gsm_meas_rep_cell *mrc = &mr->cell[i]; /* search for matching report */ if (!(mrc->arfcn == arfcn && mrc->bsic == bsic)) continue; mrc->flags |= MRC_F_PROCESSED; return mrc->rxlev; } return -ENODEV; } /* obtain averaged rxlev for given neighbor */ static int neigh_meas_avg(struct neigh_meas_proc *nmp, int window) { unsigned int i, idx; int avg = 0; idx = calc_initial_idx(ARRAY_SIZE(nmp->rxlev), nmp->rxlev_cnt % ARRAY_SIZE(nmp->rxlev), window); for (i = 0; i < window; i++) { int j = (idx+i) % ARRAY_SIZE(nmp->rxlev); avg += nmp->rxlev[j]; } return avg / window; } /* find empty or evict bad neighbor */ static struct neigh_meas_proc *find_evict_neigh(struct gsm_lchan *lchan) { int j, worst = 999999; struct neigh_meas_proc *nmp_worst = NULL; /* first try to find an empty/unused slot */ for (j = 0; j < ARRAY_SIZE(lchan->neigh_meas); j++) { struct neigh_meas_proc *nmp = &lchan->neigh_meas[j]; if (!nmp->arfcn) return nmp; } /* no empty slot found. evict worst neighbor from list */ for (j = 0; j < ARRAY_SIZE(lchan->neigh_meas); j++) { struct neigh_meas_proc *nmp = &lchan->neigh_meas[j]; int avg = neigh_meas_avg(nmp, MAX_WIN_NEIGH_AVG); if (!nmp_worst || avg < worst) { worst = avg; nmp_worst = nmp; } } return nmp_worst; } /* process neighbor cell measurement reports */ static void process_meas_neigh(struct gsm_meas_rep *mr) { int i, j, idx; /* for each reported cell, try to update global state */ for (j = 0; j < ARRAY_SIZE(mr->lchan->neigh_meas); j++) { struct neigh_meas_proc *nmp = &mr->lchan->neigh_meas[j]; unsigned int idx; int rxlev; /* skip unused entries */ if (!nmp->arfcn) continue; rxlev = rxlev_for_cell_in_rep(mr, nmp->arfcn, nmp->bsic); idx = nmp->rxlev_cnt % ARRAY_SIZE(nmp->rxlev); if (rxlev >= 0) { nmp->rxlev[idx] = rxlev; nmp->last_seen_nr = mr->nr; } else nmp->rxlev[idx] = 0; nmp->rxlev_cnt++; } /* iterate over list of reported cells, check if we did not * process all of them */ for (i = 0; i < mr->num_cell; i++) { struct gsm_meas_rep_cell *mrc = &mr->cell[i]; struct neigh_meas_proc *nmp; if (mrc->flags & MRC_F_PROCESSED) continue; nmp = find_evict_neigh(mr->lchan); nmp->arfcn = mrc->arfcn; nmp->bsic = mrc->bsic; idx = nmp->rxlev_cnt % ARRAY_SIZE(nmp->rxlev); nmp->rxlev[idx] = mrc->rxlev; nmp->rxlev_cnt++; nmp->last_seen_nr = mr->nr; mrc->flags |= MRC_F_PROCESSED; } } /* attempt to do a handover */ static int attempt_handover(struct gsm_meas_rep *mr) { struct gsm_network *net = mr->lchan->ts->trx->bts->network; struct neigh_meas_proc *best_cell = NULL; unsigned int best_better_db = 0; int i, rc; /* find the best cell in this report that is at least RXLEV_HYST * better than the current serving cell */ for (i = 0; i < ARRAY_SIZE(mr->lchan->neigh_meas); i++) { struct neigh_meas_proc *nmp = &mr->lchan->neigh_meas[i]; int avg, better; /* skip empty slots */ if (nmp->arfcn == 0) continue; /* caculate average rxlev for this cell over the window */ avg = neigh_meas_avg(nmp, net->handover.win_rxlev_avg_neigh); /* check if hysteresis is fulfilled */ if (avg < mr->dl.full.rx_lev + net->handover.pwr_hysteresis) continue; better = avg - mr->dl.full.rx_lev; if (better > best_better_db) { best_cell = nmp; best_better_db = better; } } if (!best_cell) return 0; LOGP(DHO, LOGL_INFO, "%s: Cell on ARFCN %u is better: ", gsm_ts_name(mr->lchan->ts), best_cell->arfcn); if (!net->handover.active) { LOGPC(DHO, LOGL_INFO, "Skipping, Handover disabled\n"); return 0; } rc = handover_to_arfcn_bsic(mr->lchan, best_cell->arfcn, best_cell->bsic); switch (rc) { case 0: LOGPC(DHO, LOGL_INFO, "Starting handover\n"); break; case -ENOSPC: LOGPC(DHO, LOGL_INFO, "No channel available\n"); break; case -EBUSY: LOGPC(DHO, LOGL_INFO, "Handover already active\n"); break; default: LOGPC(DHO, LOGL_ERROR, "Unknown error\n"); } return rc; } /* process an already parsed measurement report and decide if we want to * attempt a handover */ static int process_meas_rep(struct gsm_meas_rep *mr) { struct gsm_network *net = mr->lchan->ts->trx->bts->network; int av_rxlev; /* we currently only do handover for TCH channels */ switch (mr->lchan->type) { case GSM_LCHAN_TCH_F: case GSM_LCHAN_TCH_H: break; default: return 0; } /* parse actual neighbor cell info */ if (mr->num_cell > 0 && mr->num_cell < 7) process_meas_neigh(mr); av_rxlev = get_meas_rep_avg(mr->lchan, MEAS_REP_DL_RXLEV_FULL, net->handover.win_rxlev_avg); /* Interference HO */ if (rxlev2dbm(av_rxlev) > -85 && meas_rep_n_out_of_m_be(mr->lchan, MEAS_REP_DL_RXQUAL_FULL, 3, 4, 5)) return attempt_handover(mr); /* Bad Quality */ if (meas_rep_n_out_of_m_be(mr->lchan, MEAS_REP_DL_RXQUAL_FULL, 3, 4, 5)) return attempt_handover(mr); /* Low Level */ if (rxlev2dbm(av_rxlev) <= -110) return attempt_handover(mr); /* Distance */ if (mr->ms_l1.ta > net->handover.max_distance) return attempt_handover(mr); /* Power Budget AKA Better Cell */ if ((mr->nr % net->handover.pwr_interval) == 0) return attempt_handover(mr); return 0; } static int ho_dec_sig_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct lchan_signal_data *lchan_data; if (subsys != SS_LCHAN) return 0; lchan_data = signal_data; switch (signal) { case S_LCHAN_MEAS_REP: process_meas_rep(lchan_data->mr); break; } return 0; } void on_dso_load_ho_dec(void) { osmo_signal_register_handler(SS_LCHAN, ho_dec_sig_cb, NULL); } openbsc-0.15.0/openbsc/src/libbsc/handover_logic.c000066400000000000000000000234051265565154000220500ustar00rootroot00000000000000/* Handover Logic for Inter-BTS (Intra-BSC) Handover. This does not * actually implement the handover algorithm/decision, but executes a * handover decision */ /* (C) 2009 by Harald Welte * * 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct bsc_handover { struct llist_head list; struct gsm_lchan *old_lchan; struct gsm_lchan *new_lchan; struct osmo_timer_list T3103; uint8_t ho_ref; }; static LLIST_HEAD(bsc_handovers); static void handover_free(struct bsc_handover *ho) { osmo_timer_del(&ho->T3103); llist_del(&ho->list); talloc_free(ho); } static struct bsc_handover *bsc_ho_by_new_lchan(struct gsm_lchan *new_lchan) { struct bsc_handover *ho; llist_for_each_entry(ho, &bsc_handovers, list) { if (ho->new_lchan == new_lchan) return ho; } return NULL; } static struct bsc_handover *bsc_ho_by_old_lchan(struct gsm_lchan *old_lchan) { struct bsc_handover *ho; llist_for_each_entry(ho, &bsc_handovers, list) { if (ho->old_lchan == old_lchan) return ho; } return NULL; } /* Hand over the specified logical channel to the specified new BTS. * This is the main entry point for the actual handover algorithm, * after it has decided it wants to initiate HO to a specific BTS */ int bsc_handover_start(struct gsm_lchan *old_lchan, struct gsm_bts *bts) { struct gsm_lchan *new_lchan; struct bsc_handover *ho; static uint8_t ho_ref; int rc; /* don't attempt multiple handovers for the same lchan at * the same time */ if (bsc_ho_by_old_lchan(old_lchan)) return -EBUSY; DEBUGP(DHO, "(old_lchan on BTS %u, new BTS %u)\n", old_lchan->ts->trx->bts->nr, bts->nr); osmo_counter_inc(bts->network->stats.handover.attempted); if (!old_lchan->conn) { LOGP(DHO, LOGL_ERROR, "Old lchan lacks connection data.\n"); return -ENOSPC; } new_lchan = lchan_alloc(bts, old_lchan->type, 0); if (!new_lchan) { LOGP(DHO, LOGL_NOTICE, "No free channel\n"); osmo_counter_inc(bts->network->stats.handover.no_channel); return -ENOSPC; } ho = talloc_zero(tall_bsc_ctx, struct bsc_handover); if (!ho) { LOGP(DHO, LOGL_FATAL, "Out of Memory\n"); lchan_free(new_lchan); return -ENOMEM; } ho->old_lchan = old_lchan; ho->new_lchan = new_lchan; ho->ho_ref = ho_ref++; /* copy some parameters from old lchan */ memcpy(&new_lchan->encr, &old_lchan->encr, sizeof(new_lchan->encr)); new_lchan->ms_power = old_lchan->ms_power; new_lchan->bs_power = old_lchan->bs_power; new_lchan->rsl_cmode = old_lchan->rsl_cmode; new_lchan->tch_mode = old_lchan->tch_mode; memcpy(&new_lchan->mr_ms_lv, &old_lchan->mr_ms_lv, ARRAY_SIZE(new_lchan->mr_ms_lv)); memcpy(&new_lchan->mr_bts_lv, &old_lchan->mr_bts_lv, ARRAY_SIZE(new_lchan->mr_bts_lv)); new_lchan->conn = old_lchan->conn; new_lchan->conn->ho_lchan = new_lchan; /* FIXME: do we have a better idea of the timing advance? */ rc = rsl_chan_activate_lchan(new_lchan, RSL_ACT_INTER_ASYNC, ho->ho_ref); if (rc < 0) { LOGP(DHO, LOGL_ERROR, "could not activate channel\n"); new_lchan->conn->ho_lchan = NULL; new_lchan->conn = NULL; talloc_free(ho); lchan_free(new_lchan); return rc; } rsl_lchan_set_state(new_lchan, LCHAN_S_ACT_REQ); llist_add(&ho->list, &bsc_handovers); /* we continue in the SS_LCHAN handler / ho_chan_activ_ack */ return 0; } void bsc_clear_handover(struct gsm_subscriber_connection *conn, int free_lchan) { struct bsc_handover *ho; ho = bsc_ho_by_new_lchan(conn->ho_lchan); if (!ho && conn->ho_lchan) LOGP(DHO, LOGL_ERROR, "BUG: We lost some state.\n"); if (!ho) { LOGP(DHO, LOGL_ERROR, "unable to find HO record\n"); return; } conn->ho_lchan->conn = NULL; conn->ho_lchan = NULL; if (free_lchan) lchan_release(ho->new_lchan, 0, RSL_REL_LOCAL_END); handover_free(ho); } /* T3103 expired: Handover has failed without HO COMPLETE or HO FAIL */ static void ho_T3103_cb(void *_ho) { struct bsc_handover *ho = _ho; struct gsm_network *net = ho->new_lchan->ts->trx->bts->network; DEBUGP(DHO, "HO T3103 expired\n"); osmo_counter_inc(net->stats.handover.timeout); ho->new_lchan->conn->ho_lchan = NULL; ho->new_lchan->conn = NULL; lchan_release(ho->new_lchan, 0, RSL_REL_LOCAL_END); handover_free(ho); } /* RSL has acknowledged activation of the new lchan */ static int ho_chan_activ_ack(struct gsm_lchan *new_lchan) { struct bsc_handover *ho; /* we need to check if this channel activation is related to * a handover at all (and if, which particular handover) */ ho = bsc_ho_by_new_lchan(new_lchan); if (!ho) return -ENODEV; DEBUGP(DHO, "handover activate ack, send HO Command\n"); /* we can now send the 04.08 HANDOVER COMMAND to the MS * using the old lchan */ gsm48_send_ho_cmd(ho->old_lchan, new_lchan, 0, ho->ho_ref); /* start T3103. We can continue either with T3103 expiration, * 04.08 HANDOVER COMPLETE or 04.08 HANDOVER FAIL */ ho->T3103.cb = ho_T3103_cb; ho->T3103.data = ho; osmo_timer_schedule(&ho->T3103, 10, 0); /* create a RTP connection */ if (is_ipaccess_bts(new_lchan->ts->trx->bts)) rsl_ipacc_crcx(new_lchan); return 0; } /* RSL has not acknowledged activation of the new lchan */ static int ho_chan_activ_nack(struct gsm_lchan *new_lchan) { struct bsc_handover *ho; ho = bsc_ho_by_new_lchan(new_lchan); if (!ho) { LOGP(DHO, LOGL_INFO, "ACT NACK: unable to find HO record\n"); return -ENODEV; } new_lchan->conn->ho_lchan = NULL; new_lchan->conn = NULL; handover_free(ho); /* FIXME: maybe we should try to allocate a new LCHAN here? */ return 0; } /* GSM 04.08 HANDOVER COMPLETE has been received on new channel */ static int ho_gsm48_ho_compl(struct gsm_lchan *new_lchan) { struct gsm_network *net; struct bsc_handover *ho; ho = bsc_ho_by_new_lchan(new_lchan); if (!ho) { LOGP(DHO, LOGL_ERROR, "unable to find HO record\n"); return -ENODEV; } net = new_lchan->ts->trx->bts->network; LOGP(DHO, LOGL_INFO, "Subscriber %s HO from BTS %u->%u on ARFCN " "%u->%u\n", subscr_name(ho->old_lchan->conn->subscr), ho->old_lchan->ts->trx->bts->nr, new_lchan->ts->trx->bts->nr, ho->old_lchan->ts->trx->arfcn, new_lchan->ts->trx->arfcn); osmo_counter_inc(net->stats.handover.completed); osmo_timer_del(&ho->T3103); /* switch TRAU muxer for E1 based BTS from one channel to another */ if (is_e1_bts(new_lchan->conn->bts)) switch_trau_mux(ho->old_lchan, new_lchan); /* Replace the ho lchan with the primary one */ if (ho->old_lchan != new_lchan->conn->lchan) LOGP(DHO, LOGL_ERROR, "Primary lchan changed during handover.\n"); if (new_lchan != new_lchan->conn->ho_lchan) LOGP(DHO, LOGL_ERROR, "Handover channel changed during this handover.\n"); new_lchan->conn->ho_lchan = NULL; new_lchan->conn->lchan = new_lchan; ho->old_lchan->conn = NULL; rsl_lchan_set_state(ho->old_lchan, LCHAN_S_INACTIVE); lchan_release(ho->old_lchan, 0, RSL_REL_LOCAL_END); handover_free(ho); return 0; } /* GSM 04.08 HANDOVER FAIL has been received */ static int ho_gsm48_ho_fail(struct gsm_lchan *old_lchan) { struct gsm_network *net = old_lchan->ts->trx->bts->network; struct bsc_handover *ho; struct gsm_lchan *new_lchan; ho = bsc_ho_by_old_lchan(old_lchan); if (!ho) { LOGP(DHO, LOGL_ERROR, "unable to find HO record\n"); return -ENODEV; } osmo_counter_inc(net->stats.handover.failed); new_lchan = ho->new_lchan; /* release the channel and forget about it */ ho->new_lchan->conn->ho_lchan = NULL; ho->new_lchan->conn = NULL; handover_free(ho); lchan_release(new_lchan, 0, RSL_REL_LOCAL_END); return 0; } /* GSM 08.58 HANDOVER DETECT has been received */ static int ho_rsl_detect(struct gsm_lchan *new_lchan) { struct bsc_handover *ho; ho = bsc_ho_by_new_lchan(new_lchan); if (!ho) { LOGP(DHO, LOGL_ERROR, "unable to find HO record\n"); return -ENODEV; } /* FIXME: do we actually want to do something here ? */ return 0; } static int ho_logic_sig_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct lchan_signal_data *lchan_data; struct gsm_lchan *lchan; lchan_data = signal_data; switch (subsys) { case SS_LCHAN: lchan = lchan_data->lchan; switch (signal) { case S_LCHAN_ACTIVATE_ACK: return ho_chan_activ_ack(lchan); case S_LCHAN_ACTIVATE_NACK: return ho_chan_activ_nack(lchan); case S_LCHAN_HANDOVER_DETECT: return ho_rsl_detect(lchan); case S_LCHAN_HANDOVER_COMPL: return ho_gsm48_ho_compl(lchan); case S_LCHAN_HANDOVER_FAIL: return ho_gsm48_ho_fail(lchan); } break; default: break; } return 0; } struct gsm_lchan *bsc_handover_pending(struct gsm_lchan *new_lchan) { struct bsc_handover *ho; ho = bsc_ho_by_new_lchan(new_lchan); if (!ho) return NULL; return ho->old_lchan; } static __attribute__((constructor)) void on_dso_load_ho_logic(void) { osmo_signal_register_handler(SS_LCHAN, ho_logic_sig_cb, NULL); } openbsc-0.15.0/openbsc/src/libbsc/meas_proc.c000066400000000000000000000042641265565154000210370ustar00rootroot00000000000000/* Measurement Processing */ /* (C) 2009 by Harald Welte * * 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 . * */ #include #include #include #include #include #include #include #include /* process an already parsed measurement report */ static int process_meas_rep(struct gsm_meas_rep *mr) { struct gsm_meas_rep_cell *mr_cell = NULL; unsigned int best_better_db; int i; /* FIXME: implement actual averaging over multiple measurement * reports */ /* find the best cell in this report that is at least RXLEV_HYST * better than the current serving cell */ for (i = 0; i < mr->num_cell; i++) { unsigned int better; if (mr->cell[i].rxlev < mr->dl.full.rx_lev + RXLEV_HYST) continue; better = mr->cell[i].rxlev - mr->dl.full.rx_lev; if (better > best_better_db) { mr_cell = &mr->cell[i]; best_better_db = better; } } if (mr_cell) return handover_to_arfcn_bsic(mr->lchan, mr_cell->arfcn, mr_cell->bsic); return 0; } static int meas_proc_sig_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct gsm_lchan *lchan; struct gsm_meas_rep *mr; if (subsys != SS_LCHAN) return 0; switch (signal) { case S_LCHAN_MEAS_REP: mr = signal_data; process_meas_rep(mr); break; } return 0; } static __attribute__((constructor)) void on_dso_load_meas(void) { osmo_signal_register_handler(SS_LCHAN, meas_proc_sig_cb, NULL); } openbsc-0.15.0/openbsc/src/libbsc/meas_rep.c000066400000000000000000000054021265565154000206550ustar00rootroot00000000000000/* Measurement Report Processing */ /* (C) 2009 by Harald Welte * * 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 . * */ #include #include static int get_field(const struct gsm_meas_rep *rep, enum meas_rep_field field) { switch (field) { case MEAS_REP_DL_RXLEV_FULL: return rep->dl.full.rx_lev; case MEAS_REP_DL_RXLEV_SUB: return rep->dl.sub.rx_lev; case MEAS_REP_DL_RXQUAL_FULL: return rep->dl.full.rx_qual; case MEAS_REP_DL_RXQUAL_SUB: return rep->dl.sub.rx_qual; case MEAS_REP_UL_RXLEV_FULL: return rep->ul.full.rx_lev; case MEAS_REP_UL_RXLEV_SUB: return rep->ul.sub.rx_lev; case MEAS_REP_UL_RXQUAL_FULL: return rep->ul.full.rx_qual; case MEAS_REP_UL_RXQUAL_SUB: return rep->ul.sub.rx_qual; } return 0; } unsigned int calc_initial_idx(unsigned int array_size, unsigned int meas_rep_idx, unsigned int num_values) { int offs, idx; /* from which element do we need to start if we're interested * in an average of 'num' elements */ offs = meas_rep_idx - num_values; if (offs < 0) idx = array_size + offs; else idx = offs; return idx; } /* obtain an average over the last 'num' fields in the meas reps */ int get_meas_rep_avg(const struct gsm_lchan *lchan, enum meas_rep_field field, unsigned int num) { unsigned int i, idx; int avg = 0; if (num < 1) return 0; idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep), lchan->meas_rep_idx, num); for (i = 0; i < num; i++) { int j = (idx+i) % ARRAY_SIZE(lchan->meas_rep); avg += get_field(&lchan->meas_rep[j], field); } return avg / num; } /* Check if N out of M last values for FIELD are >= bd */ int meas_rep_n_out_of_m_be(const struct gsm_lchan *lchan, enum meas_rep_field field, unsigned int n, unsigned int m, int be) { unsigned int i, idx; int count = 0; idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep), lchan->meas_rep_idx, m); for (i = 0; i < m; i++) { int j = (idx + i) % ARRAY_SIZE(lchan->meas_rep); int val = get_field(&lchan->meas_rep[j], field); if (val >= be) count++; if (count >= n) return 1; } return 0; } openbsc-0.15.0/openbsc/src/libbsc/net_init.c000066400000000000000000000107111265565154000206720ustar00rootroot00000000000000/* (C) 2008-2010 by Harald Welte * * 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 . * */ #include #include #include struct gsm_network *gsm_network_init(uint16_t country_code, uint16_t network_code, int (*mncc_recv)(struct gsm_network *, struct msgb *)) { struct gsm_network *net; net = talloc_zero(tall_bsc_ctx, struct gsm_network); if (!net) return NULL; net->bsc_data = talloc_zero(net, struct osmo_bsc_data); if (!net->bsc_data) { talloc_free(net); return NULL; } net->subscr_group = talloc_zero(net, struct gsm_subscriber_group); if (!net->subscr_group) { talloc_free(net); return NULL; } /* Init back pointer */ net->bsc_data->auto_off_timeout = -1; net->bsc_data->network = net; INIT_LLIST_HEAD(&net->bsc_data->mscs); net->subscr_group->net = net; net->create_subscriber = 1; net->country_code = country_code; net->network_code = network_code; net->num_bts = 0; net->reject_cause = GSM48_REJECT_ROAMING_NOT_ALLOWED; net->T3101 = GSM_T3101_DEFAULT; net->T3105 = GSM_T3105_DEFAULT; net->T3113 = GSM_T3113_DEFAULT; net->T3122 = GSM_T3122_DEFAULT; /* FIXME: initialize all other timers! */ /* default set of handover parameters */ net->handover.win_rxlev_avg = 10; net->handover.win_rxqual_avg = 1; net->handover.win_rxlev_avg_neigh = 10; net->handover.pwr_interval = 6; net->handover.pwr_hysteresis = 3; net->handover.max_distance = 9999; INIT_LLIST_HEAD(&net->trans_list); INIT_LLIST_HEAD(&net->upqueue); INIT_LLIST_HEAD(&net->bts_list); net->stats.chreq.total = osmo_counter_alloc("net.chreq.total"); net->stats.chreq.no_channel = osmo_counter_alloc("net.chreq.no_channel"); net->stats.handover.attempted = osmo_counter_alloc("net.handover.attempted"); net->stats.handover.no_channel = osmo_counter_alloc("net.handover.no_channel"); net->stats.handover.timeout = osmo_counter_alloc("net.handover.timeout"); net->stats.handover.completed = osmo_counter_alloc("net.handover.completed"); net->stats.handover.failed = osmo_counter_alloc("net.handover.failed"); net->stats.loc_upd_type.attach = osmo_counter_alloc("net.loc_upd_type.attach"); net->stats.loc_upd_type.normal = osmo_counter_alloc("net.loc_upd_type.normal"); net->stats.loc_upd_type.periodic = osmo_counter_alloc("net.loc_upd_type.periodic"); net->stats.loc_upd_type.detach = osmo_counter_alloc("net.imsi_detach.count"); net->stats.loc_upd_resp.reject = osmo_counter_alloc("net.loc_upd_resp.reject"); net->stats.loc_upd_resp.accept = osmo_counter_alloc("net.loc_upd_resp.accept"); net->stats.paging.attempted = osmo_counter_alloc("net.paging.attempted"); net->stats.paging.detached = osmo_counter_alloc("net.paging.detached"); net->stats.paging.completed = osmo_counter_alloc("net.paging.completed"); net->stats.paging.expired = osmo_counter_alloc("net.paging.expired"); net->stats.sms.submitted = osmo_counter_alloc("net.sms.submitted"); net->stats.sms.no_receiver = osmo_counter_alloc("net.sms.no_receiver"); net->stats.sms.delivered = osmo_counter_alloc("net.sms.delivered"); net->stats.sms.rp_err_mem = osmo_counter_alloc("net.sms.rp_err_mem"); net->stats.sms.rp_err_other = osmo_counter_alloc("net.sms.rp_err_other"); net->stats.call.mo_setup = osmo_counter_alloc("net.call.mo_setup"); net->stats.call.mo_connect_ack = osmo_counter_alloc("net.call.mo_connect_ack"); net->stats.call.mt_setup = osmo_counter_alloc("net.call.mt_setup"); net->stats.call.mt_connect = osmo_counter_alloc("net.call.mt_connect"); net->stats.chan.rf_fail = osmo_counter_alloc("net.chan.rf_fail"); net->stats.chan.rll_err = osmo_counter_alloc("net.chan.rll_err"); net->stats.bts.oml_fail = osmo_counter_alloc("net.bts.oml_fail"); net->stats.bts.rsl_fail = osmo_counter_alloc("net.bts.rsl_fail"); net->mncc_recv = mncc_recv; gsm_net_update_ctype(net); return net; } openbsc-0.15.0/openbsc/src/libbsc/paging.c000066400000000000000000000270751265565154000203410ustar00rootroot00000000000000/* Paging helper and manager.... */ /* (C) 2009,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 . * */ /* * Relevant specs: * 12.21: * - 9.4.12 for CCCH Local Threshold * * 05.58: * - 8.5.2 CCCH Load indication * - 9.3.15 Paging Load * * Approach: * - Send paging command to subscriber * - On Channel Request we will remember the reason * - After the ACK we will request the identity * - Then we will send assign the gsm_subscriber and * - and call a callback */ #include #include #include #include #include #include #include #include #include #include #include #include #include void *tall_paging_ctx; #define PAGING_TIMER 0, 500000 /* * Kill one paging request update the internal list... */ static void paging_remove_request(struct gsm_bts_paging_state *paging_bts, struct gsm_paging_request *to_be_deleted) { osmo_timer_del(&to_be_deleted->T3113); llist_del(&to_be_deleted->entry); subscr_put(to_be_deleted->subscr); talloc_free(to_be_deleted); } static void page_ms(struct gsm_paging_request *request) { uint8_t mi[128]; unsigned int mi_len; unsigned int page_group; struct gsm_bts *bts = request->bts; /* the bts is down.. we will just wait for the paging to expire */ if (!bts->oml_link) return; log_set_context(BSC_CTX_SUBSCR, request->subscr); LOGP(DPAG, LOGL_INFO, "Going to send paging commands: imsi: '%s' tmsi: '0x%x'\n", request->subscr->imsi, request->subscr->tmsi); if (request->subscr->tmsi == GSM_RESERVED_TMSI) mi_len = gsm48_generate_mid_from_imsi(mi, request->subscr->imsi); else mi_len = gsm48_generate_mid_from_tmsi(mi, request->subscr->tmsi); page_group = gsm0502_calc_paging_group(&bts->si_common.chan_desc, str_to_imsi(request->subscr->imsi)); gsm0808_page(bts, page_group, mi_len, mi, request->chan_type); log_set_context(BSC_CTX_SUBSCR, NULL); } static void paging_schedule_if_needed(struct gsm_bts_paging_state *paging_bts) { if (llist_empty(&paging_bts->pending_requests)) return; if (!osmo_timer_pending(&paging_bts->work_timer)) osmo_timer_schedule(&paging_bts->work_timer, PAGING_TIMER); } static void paging_handle_pending_requests(struct gsm_bts_paging_state *paging_bts); static void paging_give_credit(void *data) { struct gsm_bts_paging_state *paging_bts = data; LOGP(DPAG, LOGL_NOTICE, "No slots available on bts nr %d\n", paging_bts->bts->nr); paging_bts->available_slots = 20; paging_handle_pending_requests(paging_bts); } static int can_send_pag_req(struct gsm_bts *bts, int rsl_type) { struct pchan_load pl; int count; memset(&pl, 0, sizeof(pl)); bts_chan_load(&pl, bts); switch (rsl_type) { case RSL_CHANNEED_TCH_F: case RSL_CHANNEED_TCH_ForH: goto count_tch; break; case RSL_CHANNEED_SDCCH: goto count_sdcch; break; case RSL_CHANNEED_ANY: default: if (bts->network->pag_any_tch) goto count_tch; else goto count_sdcch; break; } return 0; /* could available SDCCH */ count_sdcch: count = 0; count += pl.pchan[GSM_PCHAN_SDCCH8_SACCH8C].total - pl.pchan[GSM_PCHAN_SDCCH8_SACCH8C].used; count += pl.pchan[GSM_PCHAN_CCCH_SDCCH4].total - pl.pchan[GSM_PCHAN_CCCH_SDCCH4].used; return bts->paging.free_chans_need > count; count_tch: count = 0; count += pl.pchan[GSM_PCHAN_TCH_F].total - pl.pchan[GSM_PCHAN_TCH_F].used; if (bts->network->neci) count += pl.pchan[GSM_PCHAN_TCH_H].total - pl.pchan[GSM_PCHAN_TCH_H].used; return bts->paging.free_chans_need > count; } /* * This is kicked by the periodic PAGING LOAD Indicator * coming from abis_rsl.c * * We attempt to iterate once over the list of items but * only upto available_slots. */ static void paging_handle_pending_requests(struct gsm_bts_paging_state *paging_bts) { struct gsm_paging_request *request = NULL; /* * Determine if the pending_requests list is empty and * return then. */ if (llist_empty(&paging_bts->pending_requests)) { /* since the list is empty, no need to reschedule the timer */ return; } /* * In case the BTS does not provide us with load indication and we * ran out of slots, call an autofill routine. It might be that the * BTS did not like our paging messages and then we have counted down * to zero and we do not get any messages. */ if (paging_bts->available_slots == 0) { paging_bts->credit_timer.cb = paging_give_credit; paging_bts->credit_timer.data = paging_bts; osmo_timer_schedule(&paging_bts->credit_timer, 5, 0); return; } request = llist_entry(paging_bts->pending_requests.next, struct gsm_paging_request, entry); /* we need to determine the number of free channels */ if (paging_bts->free_chans_need != -1) { if (can_send_pag_req(request->bts, request->chan_type) != 0) goto skip_paging; } /* handle the paging request now */ page_ms(request); paging_bts->available_slots--; request->attempts++; /* take the current and add it to the back */ llist_del(&request->entry); llist_add_tail(&request->entry, &paging_bts->pending_requests); skip_paging: osmo_timer_schedule(&paging_bts->work_timer, PAGING_TIMER); } static void paging_worker(void *data) { struct gsm_bts_paging_state *paging_bts = data; paging_handle_pending_requests(paging_bts); } static void paging_init_if_needed(struct gsm_bts *bts) { if (bts->paging.bts) return; bts->paging.bts = bts; INIT_LLIST_HEAD(&bts->paging.pending_requests); bts->paging.work_timer.cb = paging_worker; bts->paging.work_timer.data = &bts->paging; /* Large number, until we get a proper message */ bts->paging.available_slots = 20; } static int paging_pending_request(struct gsm_bts_paging_state *bts, struct gsm_subscriber *subscr) { struct gsm_paging_request *req; llist_for_each_entry(req, &bts->pending_requests, entry) { if (subscr == req->subscr) return 1; } return 0; } static void paging_T3113_expired(void *data) { struct gsm_paging_request *req = (struct gsm_paging_request *)data; void *cbfn_param; gsm_cbfn *cbfn; int msg; log_set_context(BSC_CTX_SUBSCR, req->subscr); LOGP(DPAG, LOGL_INFO, "T3113 expired for request %p (%s)\n", req, req->subscr->imsi); /* must be destroyed before calling cbfn, to prevent double free */ osmo_counter_inc(req->bts->network->stats.paging.expired); cbfn_param = req->cbfn_param; cbfn = req->cbfn; /* did we ever manage to page the subscriber */ msg = req->attempts > 0 ? GSM_PAGING_EXPIRED : GSM_PAGING_BUSY; /* destroy it now. Do not access req afterwards */ paging_remove_request(&req->bts->paging, req); if (cbfn) cbfn(GSM_HOOK_RR_PAGING, msg, NULL, NULL, cbfn_param); } static int _paging_request(struct gsm_bts *bts, struct gsm_subscriber *subscr, int type, gsm_cbfn *cbfn, void *data) { struct gsm_bts_paging_state *bts_entry = &bts->paging; struct gsm_paging_request *req; if (paging_pending_request(bts_entry, subscr)) { LOGP(DPAG, LOGL_INFO, "Paging request already pending for %s\n", subscr->imsi); return -EEXIST; } LOGP(DPAG, LOGL_DEBUG, "Start paging of subscriber %llu on bts %d.\n", subscr->id, bts->nr); req = talloc_zero(tall_paging_ctx, struct gsm_paging_request); req->subscr = subscr_get(subscr); req->bts = bts; req->chan_type = type; req->cbfn = cbfn; req->cbfn_param = data; req->T3113.cb = paging_T3113_expired; req->T3113.data = req; osmo_timer_schedule(&req->T3113, bts->network->T3113, 0); llist_add_tail(&req->entry, &bts_entry->pending_requests); paging_schedule_if_needed(bts_entry); return 0; } int paging_request_bts(struct gsm_bts *bts, struct gsm_subscriber *subscr, int type, gsm_cbfn *cbfn, void *data) { int rc; /* skip all currently inactive TRX */ if (!trx_is_usable(bts->c0)) return 0; /* maybe it is the first time we use it */ paging_init_if_needed(bts); /* Trigger paging, pass any error to the caller */ rc = _paging_request(bts, subscr, type, cbfn, data); if (rc < 0) return rc; return 1; } int paging_request(struct gsm_network *network, struct gsm_subscriber *subscr, int type, gsm_cbfn *cbfn, void *data) { struct gsm_bts *bts = NULL; int num_pages = 0; osmo_counter_inc(network->stats.paging.attempted); /* start paging subscriber on all BTS within Location Area */ do { int rc; bts = gsm_bts_by_lac(network, subscr->lac, bts); if (!bts) break; rc = paging_request_bts(bts, subscr, type, cbfn, data); if (rc < 0) { paging_request_stop(NULL, subscr, NULL, NULL); return rc; } num_pages += rc; } while (1); if (num_pages == 0) osmo_counter_inc(network->stats.paging.detached); return num_pages; } /* we consciously ignore the type of the request here */ static void _paging_request_stop(struct gsm_bts *bts, struct gsm_subscriber *subscr, struct gsm_subscriber_connection *conn, struct msgb *msg) { struct gsm_bts_paging_state *bts_entry = &bts->paging; struct gsm_paging_request *req, *req2; paging_init_if_needed(bts); llist_for_each_entry_safe(req, req2, &bts_entry->pending_requests, entry) { if (req->subscr == subscr) { gsm_cbfn *cbfn = req->cbfn; void *param = req->cbfn_param; /* now give up the data structure */ paging_remove_request(&bts->paging, req); req = NULL; if (conn && cbfn) { LOGP(DPAG, LOGL_DEBUG, "Stop paging on bts %d, calling cbfn.\n", bts->nr); cbfn(GSM_HOOK_RR_PAGING, GSM_PAGING_SUCCEEDED, msg, conn, param); } else LOGP(DPAG, LOGL_DEBUG, "Stop paging on bts %d silently.\n", bts->nr); break; } } } /* Stop paging on all other bts' */ void paging_request_stop(struct gsm_bts *_bts, struct gsm_subscriber *subscr, struct gsm_subscriber_connection *conn, struct msgb *msg) { struct gsm_bts *bts; log_set_context(BSC_CTX_SUBSCR, subscr); /* Stop this first and dispatch the request */ if (_bts) _paging_request_stop(_bts, subscr, conn, msg); /* Make sure to cancel this everywhere else */ llist_for_each_entry(bts, &subscr->group->net->bts_list, list) { /* Sort of an optimization. */ if (bts == _bts) continue; _paging_request_stop(bts, subscr, NULL, NULL); } } void paging_update_buffer_space(struct gsm_bts *bts, uint16_t free_slots) { paging_init_if_needed(bts); osmo_timer_del(&bts->paging.credit_timer); bts->paging.available_slots = free_slots; paging_schedule_if_needed(&bts->paging); } unsigned int paging_pending_requests_nr(struct gsm_bts *bts) { unsigned int requests = 0; struct gsm_paging_request *req; paging_init_if_needed(bts); llist_for_each_entry(req, &bts->paging.pending_requests, entry) ++requests; return requests; } /** * Find any paging data for the given subscriber at the given BTS. */ void *paging_get_data(struct gsm_bts *bts, struct gsm_subscriber *subscr) { struct gsm_paging_request *req; llist_for_each_entry(req, &bts->paging.pending_requests, entry) if (req->subscr == subscr) return req->cbfn_param; return NULL; } openbsc-0.15.0/openbsc/src/libbsc/rest_octets.c000066400000000000000000000256631265565154000214330ustar00rootroot00000000000000/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface, * rest octet handling according to * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */ /* (C) 2009 by Harald Welte * * 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 . * */ #include #include #include #include #include #include /* generate SI1 rest octets */ int rest_octets_si1(uint8_t *data, uint8_t *nch_pos, int is1800_net) { struct bitvec bv; memset(&bv, 0, sizeof(bv)); bv.data = data; bv.data_len = 1; if (nch_pos) { bitvec_set_bit(&bv, H); bitvec_set_uint(&bv, *nch_pos, 5); } else bitvec_set_bit(&bv, L); if (is1800_net) bitvec_set_bit(&bv, L); else bitvec_set_bit(&bv, H); bitvec_spare_padding(&bv, 6); return bv.data_len; } /* Append selection parameters to bitvec */ static void append_selection_params(struct bitvec *bv, const struct gsm48_si_selection_params *sp) { if (sp->present) { bitvec_set_bit(bv, H); bitvec_set_bit(bv, sp->cbq); bitvec_set_uint(bv, sp->cell_resel_off, 6); bitvec_set_uint(bv, sp->temp_offs, 3); bitvec_set_uint(bv, sp->penalty_time, 5); } else bitvec_set_bit(bv, L); } /* Append power offset to bitvec */ static void append_power_offset(struct bitvec *bv, const struct gsm48_si_power_offset *po) { if (po->present) { bitvec_set_bit(bv, H); bitvec_set_uint(bv, po->power_offset, 2); } else bitvec_set_bit(bv, L); } /* Append GPRS indicator to bitvec */ static void append_gprs_ind(struct bitvec *bv, const struct gsm48_si3_gprs_ind *gi) { if (gi->present) { bitvec_set_bit(bv, H); bitvec_set_uint(bv, gi->ra_colour, 3); /* 0 == SI13 in BCCH Norm, 1 == SI13 sent on BCCH Ext */ bitvec_set_bit(bv, gi->si13_position); } else bitvec_set_bit(bv, L); } /* Generate SI3 Rest Octests (Chapter 10.5.2.34 / Table 10.4.72) */ int rest_octets_si3(uint8_t *data, const struct gsm48_si_ro_info *si3) { struct bitvec bv; memset(&bv, 0, sizeof(bv)); bv.data = data; bv.data_len = 4; /* Optional Selection Parameters */ append_selection_params(&bv, &si3->selection_params); /* Optional Power Offset */ append_power_offset(&bv, &si3->power_offset); /* Do we have a SI2ter on the BCCH? */ if (si3->si2ter_indicator) bitvec_set_bit(&bv, H); else bitvec_set_bit(&bv, L); /* Early Classmark Sending Control */ if (si3->early_cm_ctrl) bitvec_set_bit(&bv, H); else bitvec_set_bit(&bv, L); /* Do we have a SI Type 9 on the BCCH? */ if (si3->scheduling.present) { bitvec_set_bit(&bv, H); bitvec_set_uint(&bv, si3->scheduling.where, 3); } else bitvec_set_bit(&bv, L); /* GPRS Indicator */ append_gprs_ind(&bv, &si3->gprs_ind); bitvec_spare_padding(&bv, (bv.data_len*8)-1); return bv.data_len; } static int append_lsa_params(struct bitvec *bv, const struct gsm48_lsa_params *lsa_params) { /* FIXME */ return -1; } /* Generate SI4 Rest Octets (Chapter 10.5.2.35) */ int rest_octets_si4(uint8_t *data, const struct gsm48_si_ro_info *si4, int len) { struct bitvec bv; memset(&bv, 0, sizeof(bv)); bv.data = data; bv.data_len = len; /* SI4 Rest Octets O */ append_selection_params(&bv, &si4->selection_params); append_power_offset(&bv, &si4->power_offset); append_gprs_ind(&bv, &si4->gprs_ind); if (0 /* FIXME */) { /* H and SI4 Rest Octets S */ bitvec_set_bit(&bv, H); /* LSA Parameters */ if (si4->lsa_params.present) { bitvec_set_bit(&bv, H); append_lsa_params(&bv, &si4->lsa_params); } else bitvec_set_bit(&bv, L); /* Cell Identity */ if (1) { bitvec_set_bit(&bv, H); bitvec_set_uint(&bv, si4->cell_id, 16); } else bitvec_set_bit(&bv, L); /* LSA ID Information */ if (0) { bitvec_set_bit(&bv, H); /* FIXME */ } else bitvec_set_bit(&bv, L); } else { /* L and break indicator */ bitvec_set_bit(&bv, L); bitvec_set_bit(&bv, si4->break_ind ? H : L); } return bv.data_len; } /* GPRS Mobile Allocation as per TS 04.60 Chapter 12.10a: < GPRS Mobile Allocation IE > ::= < HSN : bit (6) > { 0 | 1 < RFL number list : < RFL number list struct > > } { 0 < MA_LENGTH : bit (6) > < MA_BITMAP: bit (val(MA_LENGTH) + 1) > | 1 { 0 | 1 > } } ; < RFL number list struct > :: = < RFL_NUMBER : bit (4) > { 0 | 1 < RFL number list struct > } ; < ARFCN index list struct > ::= < ARFCN_INDEX : bit(6) > { 0 | 1 < ARFCN index list struct > } ; */ static int append_gprs_mobile_alloc(struct bitvec *bv) { /* Hopping Sequence Number */ bitvec_set_uint(bv, 0, 6); if (0) { /* We want to use a RFL number list */ bitvec_set_bit(bv, 1); /* FIXME: RFL number list */ } else bitvec_set_bit(bv, 0); if (0) { /* We want to use a MA_BITMAP */ bitvec_set_bit(bv, 0); /* FIXME: MA_LENGTH, MA_BITMAP, ... */ } else { bitvec_set_bit(bv, 1); if (0) { /* We want to provide an ARFCN index list */ bitvec_set_bit(bv, 1); /* FIXME */ } else bitvec_set_bit(bv, 0); } return 0; } static int encode_t3192(unsigned int t3192) { if (t3192 == 0) return 3; else if (t3192 <= 80) return 4; else if (t3192 <= 120) return 5; else if (t3192 <= 160) return 6; else if (t3192 <= 200) return 7; else if (t3192 <= 500) return 0; else if (t3192 <= 1000) return 1; else if (t3192 <= 1500) return 2; else return -EINVAL; } static int encode_drx_timer(unsigned int drx) { if (drx == 0) return 0; else if (drx == 1) return 1; else if (drx == 2) return 2; else if (drx <= 4) return 3; else if (drx <= 8) return 4; else if (drx <= 16) return 5; else if (drx <= 32) return 6; else if (drx <= 64) return 7; else return -EINVAL; } /* GPRS Cell Options as per TS 04.60 Chapter 12.24 < GPRS Cell Options IE > ::= < NMO : bit(2) > < T3168 : bit(3) > < T3192 : bit(3) > < DRX_TIMER_MAX: bit(3) > < ACCESS_BURST_TYPE: bit > < CONTROL_ACK_TYPE : bit > < BS_CV_MAX: bit(4) > { 0 | 1 < PAN_DEC : bit(3) > < PAN_INC : bit(3) > < PAN_MAX : bit(3) > { 0 | 1 < Extension Length : bit(6) > < bit (val(Extension Length) + 1 & { < Extension Information > ! { bit ** = } } ; < Extension Information > ::= { 0 | 1 < EGPRS_PACKET_CHANNEL_REQUEST : bit > < BEP_PERIOD : bit(4) > } < PFC_FEATURE_MODE : bit > < DTM_SUPPORT : bit > ** ; */ static int append_gprs_cell_opt(struct bitvec *bv, const struct gprs_cell_options *gco) { int t3192, drx_timer_max; t3192 = encode_t3192(gco->t3192); if (t3192 < 0) return t3192; drx_timer_max = encode_drx_timer(gco->drx_timer_max); if (drx_timer_max < 0) return drx_timer_max; bitvec_set_uint(bv, gco->nmo, 2); bitvec_set_uint(bv, gco->t3168 / 500, 3); bitvec_set_uint(bv, t3192, 3); bitvec_set_uint(bv, drx_timer_max, 3); /* ACCESS_BURST_TYPE: Hard-code 8bit */ bitvec_set_bit(bv, 0); /* CONTROL_ACK_TYPE: Hard-code to RLC/MAC control block */ bitvec_set_bit(bv, 1); bitvec_set_uint(bv, gco->bs_cv_max, 4); if (0) { /* hard-code no PAN_{DEC,INC,MAX} */ bitvec_set_bit(bv, 0); } else { /* copied from ip.access BSC protocol trace */ bitvec_set_bit(bv, 1); bitvec_set_uint(bv, 1, 3); /* DEC */ bitvec_set_uint(bv, 1, 3); /* INC */ bitvec_set_uint(bv, 15, 3); /* MAX */ } if (!gco->ext_info_present) { /* no extension information */ bitvec_set_bit(bv, 0); } else { /* extension information */ bitvec_set_bit(bv, 1); if (!gco->ext_info.egprs_supported) { /* 6bit length of extension */ bitvec_set_uint(bv, (1 + 3)-1, 6); /* EGPRS supported in the cell */ bitvec_set_bit(bv, 0); } else { /* 6bit length of extension */ bitvec_set_uint(bv, (1 + 5 + 3)-1, 6); /* EGPRS supported in the cell */ bitvec_set_bit(bv, 1); /* 1bit EGPRS PACKET CHANNEL REQUEST */ bitvec_set_bit(bv, gco->ext_info.use_egprs_p_ch_req); /* 4bit BEP PERIOD */ bitvec_set_uint(bv, gco->ext_info.bep_period, 4); } bitvec_set_bit(bv, gco->ext_info.pfc_supported); bitvec_set_bit(bv, gco->ext_info.dtm_supported); bitvec_set_bit(bv, gco->ext_info.bss_paging_coordination); } return 0; } static void append_gprs_pwr_ctrl_pars(struct bitvec *bv, const struct gprs_power_ctrl_pars *pcp) { bitvec_set_uint(bv, pcp->alpha, 4); bitvec_set_uint(bv, pcp->t_avg_w, 5); bitvec_set_uint(bv, pcp->t_avg_t, 5); bitvec_set_uint(bv, pcp->pc_meas_chan, 1); bitvec_set_uint(bv, pcp->n_avg_i, 4); } /* Generate SI13 Rest Octests (04.08 Chapter 10.5.2.37b) */ int rest_octets_si13(uint8_t *data, const struct gsm48_si13_info *si13) { struct bitvec bv; memset(&bv, 0, sizeof(bv)); bv.data = data; bv.data_len = 20; if (0) { /* No rest octets */ bitvec_set_bit(&bv, L); } else { bitvec_set_bit(&bv, H); bitvec_set_uint(&bv, si13->bcch_change_mark, 3); bitvec_set_uint(&bv, si13->si_change_field, 4); if (1) { bitvec_set_bit(&bv, 0); } else { bitvec_set_bit(&bv, 1); bitvec_set_uint(&bv, si13->bcch_change_mark, 2); append_gprs_mobile_alloc(&bv); } if (!si13->pbcch_present) { /* PBCCH not present in cell */ bitvec_set_bit(&bv, 0); bitvec_set_uint(&bv, si13->no_pbcch.rac, 8); bitvec_set_bit(&bv, si13->no_pbcch.spgc_ccch_sup); bitvec_set_uint(&bv, si13->no_pbcch.prio_acc_thr, 3); bitvec_set_uint(&bv, si13->no_pbcch.net_ctrl_ord, 2); append_gprs_cell_opt(&bv, &si13->cell_opts); append_gprs_pwr_ctrl_pars(&bv, &si13->pwr_ctrl_pars); } else { /* PBCCH present in cell */ bitvec_set_bit(&bv, 1); bitvec_set_uint(&bv, si13->pbcch.psi1_rep_per, 4); /* PBCCH Descripiton */ bitvec_set_uint(&bv, si13->pbcch.pb, 4); bitvec_set_uint(&bv, si13->pbcch.tsc, 3); bitvec_set_uint(&bv, si13->pbcch.tn, 3); switch (si13->pbcch.carrier_type) { case PBCCH_BCCH: bitvec_set_bit(&bv, 0); bitvec_set_bit(&bv, 0); break; case PBCCH_ARFCN: bitvec_set_bit(&bv, 0); bitvec_set_bit(&bv, 1); bitvec_set_uint(&bv, si13->pbcch.arfcn, 10); break; case PBCCH_MAIO: bitvec_set_bit(&bv, 1); bitvec_set_uint(&bv, si13->pbcch.maio, 6); break; } } /* 3GPP TS 44.018 Release 6 / 10.5.2.37b */ bitvec_set_bit(&bv, H); /* added Release 99 */ /* claim our SGSN is compatible with Release 99, as EDGE and EGPRS * was only added in this Release */ bitvec_set_bit(&bv, 1); } bitvec_spare_padding(&bv, (bv.data_len*8)-1); return bv.data_len; } openbsc-0.15.0/openbsc/src/libbsc/system_information.c000066400000000000000000000556101265565154000230210ustar00rootroot00000000000000/* GSM 04.08 System Information (SI) encoding and decoding * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */ /* (C) 2008-2010 by Harald Welte * (C) 2012 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include /* * DCS1800 and PCS1900 have overlapping ARFCNs. We would need to set the * ARFCN_PCS flag on the 1900 ARFCNs but this would increase cell_alloc * and other arrays to make sure (ARFCN_PCS + 1024)/8 ARFCNs fit into the * array. DCS1800 and PCS1900 can not be used at the same time so conserve * memory and do the below. */ static int band_compatible(const struct gsm_bts *bts, int arfcn) { enum gsm_band band = gsm_arfcn2band(arfcn); /* normal case */ if (band == bts->band) return 1; /* deal with ARFCN_PCS not set */ if (band == GSM_BAND_1800 && bts->band == GSM_BAND_1900) return 1; return 0; } static int is_dcs_net(const struct gsm_bts *bts) { if (bts->band == GSM_BAND_850) return 0; if (bts->band == GSM_BAND_1900) return 0; return 1; } static int use_arfcn(const struct gsm_bts *bts, const int bis, const int ter, const int pgsm, const int arfcn) { if (bts->force_combined_si) return !bis && !ter; if (!bis && !ter && band_compatible(bts, arfcn)) return 1; /* Correct but somehow broken with either the nanoBTS or the iPhone5 */ if (bis && pgsm && band_compatible(bts, arfcn) && (arfcn < 1 || arfcn > 124)) return 1; if (ter && !band_compatible(bts, arfcn)) return 1; return 0; } /* Frequency Lists as per TS 04.08 10.5.2.13 */ /* 10.5.2.13.2: Bit map 0 format */ static int freq_list_bm0_set_arfcn(uint8_t *chan_list, unsigned int arfcn) { unsigned int byte, bit; if (arfcn > 124 || arfcn < 1) { LOGP(DRR, LOGL_ERROR, "Bitmap 0 only supports ARFCN 1...124\n"); return -EINVAL; } /* the bitmask is from 1..124, not from 0..123 */ arfcn--; byte = arfcn / 8; bit = arfcn % 8; chan_list[GSM48_CELL_CHAN_DESC_SIZE-1-byte] |= (1 << bit); return 0; } /* 10.5.2.13.7: Variable bit map format */ static int freq_list_bmrel_set_arfcn(uint8_t *chan_list, unsigned int arfcn) { unsigned int byte, bit; unsigned int min_arfcn; unsigned int bitno; min_arfcn = (chan_list[0] & 1) << 9; min_arfcn |= chan_list[1] << 1; min_arfcn |= (chan_list[2] >> 7) & 1; /* The lower end of our bitmaks is always implicitly included */ if (arfcn == min_arfcn) return 0; if (((arfcn - min_arfcn) & 1023) > 111) { LOGP(DRR, LOGL_ERROR, "arfcn(%u) > min(%u) + 111\n", arfcn, min_arfcn); return -EINVAL; } bitno = (arfcn - min_arfcn) & 1023; byte = bitno / 8; bit = bitno % 8; chan_list[2 + byte] |= 1 << (7 - bit); return 0; } /* generate a variable bitmap */ static int enc_freq_lst_var_bitmap(uint8_t *chan_list, struct bitvec *bv, const struct gsm_bts *bts, int bis, int ter, int min, int pgsm) { int i; /* set it to 'Variable bitmap format' */ chan_list[0] = 0x8e; chan_list[0] |= (min >> 9) & 1; chan_list[1] = (min >> 1); chan_list[2] = (min & 1) << 7; for (i = 0; i < bv->data_len*8; i++) { /* see notes in bitvec2freq_list */ if (bitvec_get_bit_pos(bv, i) && ((!bis && !ter && band_compatible(bts,i)) || (bis && pgsm && band_compatible(bts,i) && (i < 1 || i > 124)) || (ter && !band_compatible(bts, i)))) { int rc = freq_list_bmrel_set_arfcn(chan_list, i); if (rc < 0) return rc; } } return 0; } /* generate a frequency list with the range 512 format */ static int enc_freq_lst_range(uint8_t *chan_list, struct bitvec *bv, const struct gsm_bts *bts, int bis, int ter, int pgsm) { int arfcns[RANGE_ENC_MAX_ARFCNS]; int w[RANGE_ENC_MAX_ARFCNS]; int f0_included = 0; int arfcns_used = 0; int i, rc, range, f0; /* * Select ARFCNs according to the rules in bitvec2freq_list */ for (i = 0; i < bv->data_len * 8; ++i) { /* More ARFCNs than the maximum */ if (arfcns_used > ARRAY_SIZE(arfcns)) return -1; /* Check if we can select it? */ if (bitvec_get_bit_pos(bv, i) && use_arfcn(bts, bis, ter, pgsm, i)) arfcns[arfcns_used++] = i; } /* * Check if the given list of ARFCNs can be encoded. */ range = range_enc_determine_range(arfcns, arfcns_used, &f0); if (range == ARFCN_RANGE_INVALID) return -2; /* * Manipulate the ARFCN list according to the rules in J4 depending * on the selected range. */ arfcns_used = range_enc_filter_arfcns(arfcns, arfcns_used, f0, &f0_included); memset(w, 0, sizeof(w)); rc = range_enc_arfcns(range, arfcns, arfcns_used, w, 0); if (rc != 0) return -3; /* Select the range and the amount of bits needed */ switch (range) { case ARFCN_RANGE_128: return range_enc_range128(chan_list, f0, w); break; case ARFCN_RANGE_256: return range_enc_range256(chan_list, f0, w); break; case ARFCN_RANGE_512: return range_enc_range512(chan_list, f0, w); break; case ARFCN_RANGE_1024: return range_enc_range1024(chan_list, f0, f0_included, w); break; default: return -4; }; } /* generate a cell channel list as per Section 10.5.2.1b of 04.08 */ static int bitvec2freq_list(uint8_t *chan_list, struct bitvec *bv, const struct gsm_bts *bts, int bis, int ter) { int i, rc, min = -1, max = -1, pgsm = 0, arfcns = 0; memset(chan_list, 0, 16); if (bts->band == GSM_BAND_900 && bts->c0->arfcn >= 1 && bts->c0->arfcn <= 124) pgsm = 1; /* P-GSM-only handsets only support 'bit map 0 format' */ if (!bis && !ter && pgsm) { chan_list[0] = 0; for (i = 0; i < bv->data_len*8; i++) { if (i >= 1 && i <= 124 && bitvec_get_bit_pos(bv, i)) { rc = freq_list_bm0_set_arfcn(chan_list, i); if (rc < 0) return rc; } } return 0; } for (i = 0; i < bv->data_len*8; i++) { /* in case of SI2 or SI5 allow all neighbours in same band * in case of SI*bis, allow neighbours in same band ouside pgsm * in case of SI*ter, allow neighbours in different bands */ if (!bitvec_get_bit_pos(bv, i)) continue; if (!use_arfcn(bts, bis, ter, pgsm, i)) continue; /* count the arfcns we want to carry */ arfcns += 1; /* 955..1023 < 0..885 */ if (min < 0) min = i; if (i >= 955 && min < 955) min = i; if (i >= 955 && min >= 955 && i < min) min = i; if (i < 955 && min < 955 && i < min) min = i; if (max < 0) max = i; if (i < 955 && max >= 955) max = i; if (i >= 955 && max >= 955 && i > max) max = i; if (i < 955 && max < 955 && i > max) max = i; } if (max == -1) { /* Empty set, use 'bit map 0 format' */ chan_list[0] = 0; return 0; } /* Now find the best encoding */ if (((max - min) & 1023) <= 111) return enc_freq_lst_var_bitmap(chan_list, bv, bts, bis, ter, min, pgsm); /* Attempt to do the range encoding */ rc = enc_freq_lst_range(chan_list, bv, bts, bis, ter, pgsm); if (rc == 0) return 0; LOGP(DRR, LOGL_ERROR, "min_arfcn=%u, max_arfcn=%u, arfcns=%d " "can not generate ARFCN list", min, max, arfcns); return -EINVAL; } /* generate a cell channel list as per Section 10.5.2.1b of 04.08 */ /* static*/ int generate_cell_chan_list(uint8_t *chan_list, struct gsm_bts *bts) { struct gsm_bts_trx *trx; struct bitvec *bv = &bts->si_common.cell_alloc; /* Zero-initialize the bit-vector */ memset(bv->data, 0, bv->data_len); /* first we generate a bitvec of all TRX ARFCN's in our BTS */ llist_for_each_entry(trx, &bts->trx_list, list) { unsigned int i, j; /* Always add the TRX's ARFCN */ bitvec_set_bit_pos(bv, trx->arfcn, 1); for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { struct gsm_bts_trx_ts *ts = &trx->ts[i]; /* Add any ARFCNs present in hopping channels */ for (j = 0; j < 1024; j++) { if (bitvec_get_bit_pos(&ts->hopping.arfcns, j)) bitvec_set_bit_pos(bv, j, 1); } } } /* then we generate a GSM 04.08 frequency list from the bitvec */ return bitvec2freq_list(chan_list, bv, bts, 0, 0); } /* generate a cell channel list as per Section 10.5.2.1b of 04.08 */ static int generate_bcch_chan_list(uint8_t *chan_list, struct gsm_bts *bts, int si5, int bis, int ter) { struct gsm_bts *cur_bts; struct bitvec *bv; if (si5 && bts->neigh_list_manual_mode == NL_MODE_MANUAL_SI5SEP) bv = &bts->si_common.si5_neigh_list; else bv = &bts->si_common.neigh_list; /* Generate list of neighbor cells if we are in automatic mode */ if (bts->neigh_list_manual_mode == NL_MODE_AUTOMATIC) { /* Zero-initialize the bit-vector */ memset(bv->data, 0, bv->data_len); /* first we generate a bitvec of the BCCH ARFCN's in our BSC */ llist_for_each_entry(cur_bts, &bts->network->bts_list, list) { if (cur_bts == bts) continue; bitvec_set_bit_pos(bv, cur_bts->c0->arfcn, 1); } } /* then we generate a GSM 04.08 frequency list from the bitvec */ return bitvec2freq_list(chan_list, bv, bts, bis, ter); } static int list_arfcn(uint8_t *chan_list, uint8_t mask, char *text) { int n = 0, i; struct gsm_sysinfo_freq freq[1024]; memset(freq, 0, sizeof(freq)); gsm48_decode_freq_list(freq, chan_list, 16, 0xce, 1); for (i = 0; i < 1024; i++) { if (freq[i].mask) { if (!n) LOGP(DRR, LOGL_INFO, "%s", text); LOGPC(DRR, LOGL_INFO, " %d", i); n++; } } if (n) LOGPC(DRR, LOGL_INFO, "\n"); return n; } static int generate_si1(uint8_t *output, struct gsm_bts *bts) { int rc; struct gsm48_system_information_type_1 *si1 = (struct gsm48_system_information_type_1 *) output; memset(si1, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); si1->header.l2_plen = (21 << 2) | 1; si1->header.rr_protocol_discriminator = GSM48_PDISC_RR; si1->header.skip_indicator = 0; si1->header.system_information = GSM48_MT_RR_SYSINFO_1; rc = generate_cell_chan_list(si1->cell_channel_description, bts); if (rc < 0) return rc; list_arfcn(si1->cell_channel_description, 0xce, "Serving cell:"); si1->rach_control = bts->si_common.rach_control; /* * SI1 Rest Octets (10.5.2.32), contains NCH position and band * indicator but that is not in the 04.08. */ rc = rest_octets_si1(si1->rest_octets, NULL, is_dcs_net(bts)); return sizeof(*si1) + rc; } static int generate_si2(uint8_t *output, struct gsm_bts *bts) { int rc; struct gsm48_system_information_type_2 *si2 = (struct gsm48_system_information_type_2 *) output; memset(si2, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); si2->header.l2_plen = (22 << 2) | 1; si2->header.rr_protocol_discriminator = GSM48_PDISC_RR; si2->header.skip_indicator = 0; si2->header.system_information = GSM48_MT_RR_SYSINFO_2; rc = generate_bcch_chan_list(si2->bcch_frequency_list, bts, 0, 0, 0); if (rc < 0) return rc; list_arfcn(si2->bcch_frequency_list, 0xce, "SI2 Neighbour cells in same band:"); si2->ncc_permitted = bts->si_common.ncc_permitted; si2->rach_control = bts->si_common.rach_control; return sizeof(*si2); } static int generate_si2bis(uint8_t *output, struct gsm_bts *bts) { int rc; struct gsm48_system_information_type_2bis *si2b = (struct gsm48_system_information_type_2bis *) output; int n; memset(si2b, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); si2b->header.l2_plen = (22 << 2) | 1; si2b->header.rr_protocol_discriminator = GSM48_PDISC_RR; si2b->header.skip_indicator = 0; si2b->header.system_information = GSM48_MT_RR_SYSINFO_2bis; rc = generate_bcch_chan_list(si2b->bcch_frequency_list, bts, 0, 1, 0); if (rc < 0) return rc; n = list_arfcn(si2b->bcch_frequency_list, 0xce, "Neighbour cells in same band, but outside P-GSM:"); if (n) { /* indicate in SI2 and SI2bis: there is an extension */ struct gsm48_system_information_type_2 *si2 = (struct gsm48_system_information_type_2 *) bts->si_buf[SYSINFO_TYPE_2]; si2->bcch_frequency_list[0] |= 0x20; si2b->bcch_frequency_list[0] |= 0x20; } else bts->si_valid &= ~(1 << SYSINFO_TYPE_2bis); si2b->rach_control = bts->si_common.rach_control; return sizeof(*si2b); } static int generate_si2ter(uint8_t *output, struct gsm_bts *bts) { int rc; struct gsm48_system_information_type_2ter *si2t = (struct gsm48_system_information_type_2ter *) output; int n; memset(si2t, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); si2t->header.l2_plen = (22 << 2) | 1; si2t->header.rr_protocol_discriminator = GSM48_PDISC_RR; si2t->header.skip_indicator = 0; si2t->header.system_information = GSM48_MT_RR_SYSINFO_2ter; rc = generate_bcch_chan_list(si2t->ext_bcch_frequency_list, bts, 0, 0, 1); if (rc < 0) return rc; n = list_arfcn(si2t->ext_bcch_frequency_list, 0x8e, "Neighbour cells in different band:"); if (!n) bts->si_valid &= ~(1 << SYSINFO_TYPE_2ter); return sizeof(*si2t); } static struct gsm48_si_ro_info si_info = { .selection_params = { .present = 0, }, .power_offset = { .present = 0, }, .si2ter_indicator = 0, .early_cm_ctrl = 1, .scheduling = { .present = 0, }, .gprs_ind = { .si13_position = 0, .ra_colour = 0, .present = 1, }, .lsa_params = { .present = 0, }, .cell_id = 0, /* FIXME: doesn't the bts have this? */ .break_ind = 0, }; static int generate_si3(uint8_t *output, struct gsm_bts *bts) { int rc; struct gsm48_system_information_type_3 *si3 = (struct gsm48_system_information_type_3 *) output; memset(si3, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); si3->header.l2_plen = (18 << 2) | 1; si3->header.rr_protocol_discriminator = GSM48_PDISC_RR; si3->header.skip_indicator = 0; si3->header.system_information = GSM48_MT_RR_SYSINFO_3; si3->cell_identity = htons(bts->cell_identity); gsm48_generate_lai(&si3->lai, bts->network->country_code, bts->network->network_code, bts->location_area_code); si3->control_channel_desc = bts->si_common.chan_desc; si3->cell_options = bts->si_common.cell_options; si3->cell_sel_par = bts->si_common.cell_sel_par; si3->rach_control = bts->si_common.rach_control; if ((bts->si_valid & (1 << SYSINFO_TYPE_2ter))) { LOGP(DRR, LOGL_INFO, "SI 2ter is included.\n"); si_info.si2ter_indicator = 1; } else { si_info.si2ter_indicator = 0; } /* SI3 Rest Octets (10.5.2.34), containing CBQ, CELL_RESELECT_OFFSET, TEMPORARY_OFFSET, PENALTY_TIME Power Offset, 2ter Indicator, Early Classmark Sending, Scheduling if and WHERE, GPRS Indicator, SI13 position */ rc = rest_octets_si3(si3->rest_octets, &si_info); return sizeof(*si3) + rc; } static int generate_si4(uint8_t *output, struct gsm_bts *bts) { int rc; struct gsm48_system_information_type_4 *si4 = (struct gsm48_system_information_type_4 *) output; struct gsm_lchan *cbch_lchan; uint8_t *restoct = si4->data; /* length of all IEs present except SI4 rest octets and l2_plen */ int l2_plen = sizeof(*si4) - 1; memset(si4, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); si4->header.rr_protocol_discriminator = GSM48_PDISC_RR; si4->header.skip_indicator = 0; si4->header.system_information = GSM48_MT_RR_SYSINFO_4; gsm48_generate_lai(&si4->lai, bts->network->country_code, bts->network->network_code, bts->location_area_code); si4->cell_sel_par = bts->si_common.cell_sel_par; si4->rach_control = bts->si_common.rach_control; /* Optional: CBCH Channel Description + CBCH Mobile Allocation */ cbch_lchan = gsm_bts_get_cbch(bts); if (cbch_lchan) { struct gsm48_chan_desc cd; gsm48_lchan2chan_desc(&cd, cbch_lchan); tv_fixed_put(si4->data, GSM48_IE_CBCH_CHAN_DESC, 3, (uint8_t *) &cd); l2_plen += 3 + 1; restoct += 3 + 1; /* we don't use hopping and thus don't need a CBCH MA */ } si4->header.l2_plen = (l2_plen << 2) | 1; /* SI4 Rest Octets (10.5.2.35), containing Optional Power offset, GPRS Indicator, Cell Identity, LSA ID, Selection Parameter */ rc = rest_octets_si4(restoct, &si_info, output + GSM_MACBLOCK_LEN - restoct); return l2_plen + 1 + rc; } static int generate_si5(uint8_t *output, struct gsm_bts *bts) { struct gsm48_system_information_type_5 *si5; int rc, l2_plen = 18; memset(output, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); /* ip.access nanoBTS needs l2_plen!! */ switch (bts->type) { case GSM_BTS_TYPE_NANOBTS: case GSM_BTS_TYPE_OSMO_SYSMO: *output++ = (l2_plen << 2) | 1; l2_plen++; break; default: break; } si5 = (struct gsm48_system_information_type_5 *) output; /* l2 pseudo length, not part of msg: 18 */ si5->rr_protocol_discriminator = GSM48_PDISC_RR; si5->skip_indicator = 0; si5->system_information = GSM48_MT_RR_SYSINFO_5; rc = generate_bcch_chan_list(si5->bcch_frequency_list, bts, 1, 0, 0); if (rc < 0) return rc; list_arfcn(si5->bcch_frequency_list, 0xce, "SI5 Neighbour cells in same band:"); /* 04.08 9.1.37: L2 Pseudo Length of 18 */ return l2_plen; } static int generate_si5bis(uint8_t *output, struct gsm_bts *bts) { struct gsm48_system_information_type_5bis *si5b; int rc, l2_plen = 18; int n; memset(output, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); /* ip.access nanoBTS needs l2_plen!! */ switch (bts->type) { case GSM_BTS_TYPE_NANOBTS: case GSM_BTS_TYPE_OSMO_SYSMO: *output++ = (l2_plen << 2) | 1; l2_plen++; break; default: break; } si5b = (struct gsm48_system_information_type_5bis *) output; /* l2 pseudo length, not part of msg: 18 */ si5b->rr_protocol_discriminator = GSM48_PDISC_RR; si5b->skip_indicator = 0; si5b->system_information = GSM48_MT_RR_SYSINFO_5bis; rc = generate_bcch_chan_list(si5b->bcch_frequency_list, bts, 1, 1, 0); if (rc < 0) return rc; n = list_arfcn(si5b->bcch_frequency_list, 0xce, "Neighbour cells in same band, but outside P-GSM:"); if (n) { /* indicate in SI5 and SI5bis: there is an extension */ struct gsm48_system_information_type_5 *si5 = (struct gsm48_system_information_type_5 *) bts->si_buf[SYSINFO_TYPE_5]; si5->bcch_frequency_list[0] |= 0x20; si5b->bcch_frequency_list[0] |= 0x20; } else bts->si_valid &= ~(1 << SYSINFO_TYPE_5bis); /* 04.08 9.1.37: L2 Pseudo Length of 18 */ return l2_plen; } static int generate_si5ter(uint8_t *output, struct gsm_bts *bts) { struct gsm48_system_information_type_5ter *si5t; int rc, l2_plen = 18; int n; memset(output, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); /* ip.access nanoBTS needs l2_plen!! */ switch (bts->type) { case GSM_BTS_TYPE_NANOBTS: case GSM_BTS_TYPE_OSMO_SYSMO: *output++ = (l2_plen << 2) | 1; l2_plen++; break; default: break; } si5t = (struct gsm48_system_information_type_5ter *) output; /* l2 pseudo length, not part of msg: 18 */ si5t->rr_protocol_discriminator = GSM48_PDISC_RR; si5t->skip_indicator = 0; si5t->system_information = GSM48_MT_RR_SYSINFO_5ter; rc = generate_bcch_chan_list(si5t->bcch_frequency_list, bts, 1, 0, 1); if (rc < 0) return rc; n = list_arfcn(si5t->bcch_frequency_list, 0x8e, "Neighbour cells in different band:"); if (!n) bts->si_valid &= ~(1 << SYSINFO_TYPE_5ter); /* 04.08 9.1.37: L2 Pseudo Length of 18 */ return l2_plen; } static int generate_si6(uint8_t *output, struct gsm_bts *bts) { struct gsm48_system_information_type_6 *si6; int l2_plen = 11; memset(output, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); /* ip.access nanoBTS needs l2_plen!! */ switch (bts->type) { case GSM_BTS_TYPE_NANOBTS: case GSM_BTS_TYPE_OSMO_SYSMO: *output++ = (l2_plen << 2) | 1; l2_plen++; break; default: break; } si6 = (struct gsm48_system_information_type_6 *) output; /* l2 pseudo length, not part of msg: 11 */ si6->rr_protocol_discriminator = GSM48_PDISC_RR; si6->skip_indicator = 0; si6->system_information = GSM48_MT_RR_SYSINFO_6; si6->cell_identity = htons(bts->cell_identity); gsm48_generate_lai(&si6->lai, bts->network->country_code, bts->network->network_code, bts->location_area_code); si6->cell_options = bts->si_common.cell_options; si6->ncc_permitted = bts->si_common.ncc_permitted; /* SI6 Rest Octets: 10.5.2.35a: PCH / NCH info, VBS/VGCS options */ return l2_plen; } static struct gsm48_si13_info si13_default = { .cell_opts = { .nmo = GPRS_NMO_II, .t3168 = 2000, .t3192 = 1500, .drx_timer_max = 3, .bs_cv_max = 15, .ext_info_present = 0, .ext_info = { /* The values below are just guesses ! */ .egprs_supported = 0, .use_egprs_p_ch_req = 1, .bep_period = 5, .pfc_supported = 0, .dtm_supported = 0, .bss_paging_coordination = 0, }, }, .pwr_ctrl_pars = { .alpha = 0, /* a = 0.0 */ .t_avg_w = 16, .t_avg_t = 16, .pc_meas_chan = 0, /* downling measured on CCCH */ .n_avg_i = 8, }, .bcch_change_mark = 1, .si_change_field = 0, .pbcch_present = 0, { .no_pbcch = { .rac = 0, /* needs to be patched */ .spgc_ccch_sup = 0, .net_ctrl_ord = 0, .prio_acc_thr = 6, }, }, }; static int generate_si13(uint8_t *output, struct gsm_bts *bts) { struct gsm48_system_information_type_13 *si13 = (struct gsm48_system_information_type_13 *) output; int ret; memset(si13, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); si13->header.rr_protocol_discriminator = GSM48_PDISC_RR; si13->header.skip_indicator = 0; si13->header.system_information = GSM48_MT_RR_SYSINFO_13; si13_default.no_pbcch.rac = bts->gprs.rac; si13_default.no_pbcch.net_ctrl_ord = bts->gprs.net_ctrl_ord; /* Information about the other SIs */ si13_default.bcch_change_mark = bts->bcch_change_mark; ret = rest_octets_si13(si13->rest_octets, &si13_default); if (ret < 0) return ret; /* length is coded in bit 2 an up */ si13->header.l2_plen = 0x01; return sizeof (*si13) + ret; } typedef int (*gen_si_fn_t)(uint8_t *output, struct gsm_bts *bts); static const gen_si_fn_t gen_si_fn[_MAX_SYSINFO_TYPE] = { [SYSINFO_TYPE_1] = &generate_si1, [SYSINFO_TYPE_2] = &generate_si2, [SYSINFO_TYPE_2bis] = &generate_si2bis, [SYSINFO_TYPE_2ter] = &generate_si2ter, [SYSINFO_TYPE_3] = &generate_si3, [SYSINFO_TYPE_4] = &generate_si4, [SYSINFO_TYPE_5] = &generate_si5, [SYSINFO_TYPE_5bis] = &generate_si5bis, [SYSINFO_TYPE_5ter] = &generate_si5ter, [SYSINFO_TYPE_6] = &generate_si6, [SYSINFO_TYPE_13] = &generate_si13, }; int gsm_generate_si(struct gsm_bts *bts, enum osmo_sysinfo_type si_type) { gen_si_fn_t gen_si; switch (bts->gprs.mode) { case BTS_GPRS_EGPRS: si13_default.cell_opts.ext_info_present = 1; si13_default.cell_opts.ext_info.egprs_supported = 1; /* fallthrough */ case BTS_GPRS_GPRS: si_info.gprs_ind.present = 1; break; case BTS_GPRS_NONE: si_info.gprs_ind.present = 0; break; } memcpy(&si_info.selection_params, &bts->si_common.cell_ro_sel_par, sizeof(struct gsm48_si_selection_params)); gen_si = gen_si_fn[si_type]; if (!gen_si) return -EINVAL; return gen_si(bts->si_buf[si_type], bts); } openbsc-0.15.0/openbsc/src/libcommon/000077500000000000000000000000001265565154000174365ustar00rootroot00000000000000openbsc-0.15.0/openbsc/src/libcommon/Makefile.am000066400000000000000000000005731265565154000214770ustar00rootroot00000000000000AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) \ $(LIBOSMOVTY_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) noinst_LIBRARIES = libcommon.a libcommon_a_SOURCES = bsc_version.c common_vty.c debug.c gsm_data.c \ gsm_data_shared.c socket.c talloc_ctx.c \ gsm_subscriber_base.c openbsc-0.15.0/openbsc/src/libcommon/bsc_version.c000066400000000000000000000023741265565154000221240ustar00rootroot00000000000000/* Hold the copyright and version string */ /* (C) 2010 by Harald Welte * 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 . * */ #include "../../bscconfig.h" const char *openbsc_copyright = "Copyright (C) 2008-2012 Harald Welte, Holger Freyther\r\n" "Contributions by Daniel Willmann, Jan Lübbe, Stefan Schmidt\r\n" "Dieter Spaar, Andreas Eversberg, Sylvain Munaut\r\n\r\n" "License AGPLv3+: GNU AGPL version 3 or later \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"; openbsc-0.15.0/openbsc/src/libcommon/common_vty.c000066400000000000000000000064001265565154000217740ustar00rootroot00000000000000/* OpenBSC VTY common helpers */ /* (C) 2009-2010 by Harald Welte * (C) 2009-2010 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include int bsc_vty_go_parent(struct vty *vty) { switch (vty->node) { case GSMNET_NODE: vty->node = CONFIG_NODE; vty->index = NULL; break; case BTS_NODE: vty->node = GSMNET_NODE; { /* set vty->index correctly ! */ struct gsm_bts *bts = vty->index; vty->index = bts->network; vty->index_sub = NULL; } break; case TRX_NODE: vty->node = BTS_NODE; { /* set vty->index correctly ! */ struct gsm_bts_trx *trx = vty->index; vty->index = trx->bts; vty->index_sub = &trx->bts->description; } break; case TS_NODE: vty->node = TRX_NODE; { /* set vty->index correctly ! */ struct gsm_bts_trx_ts *ts = vty->index; vty->index = ts->trx; vty->index_sub = &ts->trx->description; } break; case OML_NODE: case OM2K_NODE: vty->node = ENABLE_NODE; /* NOTE: this only works because it's not part of the config * tree, where outer commands are searched via vty_go_parent() * and only (!) executed when a matching one is found. */ talloc_free(vty->index); vty->index = NULL; break; case NAT_BSC_NODE: vty->node = NAT_NODE; { struct bsc_config *bsc_config = vty->index; vty->index = bsc_config->nat; } break; case PGROUP_NODE: vty->node = NAT_NODE; vty->index = NULL; break; case TRUNK_NODE: vty->node = MGCP_NODE; vty->index = NULL; break; case SMPP_ESME_NODE: vty->node = SMPP_NODE; vty->index = NULL; break; case SMPP_NODE: case MGCP_NODE: case GBPROXY_NODE: case SGSN_NODE: case NAT_NODE: case BSC_NODE: case MSC_NODE: case MNCC_INT_NODE: case NITB_NODE: default: if (bsc_vty_is_config_node(vty, vty->node)) vty->node = CONFIG_NODE; else vty->node = ENABLE_NODE; vty->index = NULL; } return vty->node; } int bsc_vty_is_config_node(struct vty *vty, int node) { switch (node) { /* add items that are not config */ case OML_NODE: case OM2K_NODE: case SUBSCR_NODE: case CONFIG_NODE: return 0; default: return 1; } } /* a talloc string replace routine */ void bsc_replace_string(void *ctx, char **dst, const char *newstr) { if (*dst) talloc_free(*dst); *dst = talloc_strdup(ctx, newstr); } openbsc-0.15.0/openbsc/src/libcommon/debug.c000066400000000000000000000132711265565154000206740ustar00rootroot00000000000000/* OpenBSC Debugging/Logging support code */ /* (C) 2008-2010 by Harald Welte * (C) 2008 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* default categories */ static const struct log_info_cat default_categories[] = { [DRLL] = { .name = "DRLL", .description = "A-bis Radio Link Layer (RLL)", .color = "\033[1;31m", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DCC] = { .name = "DCC", .description = "Layer3 Call Control (CC)", .color = "\033[1;32m", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DMM] = { .name = "DMM", .description = "Layer3 Mobility Management (MM)", .color = "\033[1;33m", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DRR] = { .name = "DRR", .description = "Layer3 Radio Resource (RR)", .color = "\033[1;34m", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DRSL] = { .name = "DRSL", .description = "A-bis Radio Siganlling Link (RSL)", .color = "\033[1;35m", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DNM] = { .name = "DNM", .description = "A-bis Network Management / O&M (NM/OML)", .color = "\033[1;36m", .enabled = 1, .loglevel = LOGL_INFO, }, [DMNCC] = { .name = "DMNCC", .description = "MNCC API for Call Control application", .color = "\033[1;39m", .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, }, [DSCCP] = { .name = "DSCCP", .description = "SCCP Protocol", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DMSC] = { .name = "DMSC", .description = "Mobile Switching Center", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DMGCP] = { .name = "DMGCP", .description = "Media Gateway Control Protocol", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DHO] = { .name = "DHO", .description = "Hand-Over", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DDB] = { .name = "DDB", .description = "Database Layer", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DREF] = { .name = "DREF", .description = "Reference Counting", .enabled = 0, .loglevel = LOGL_NOTICE, }, [DGPRS] = { .name = "DGPRS", .description = "GPRS Packet Service", .enabled = 1, .loglevel = LOGL_DEBUG, }, [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, }, [DNAT] = { .name = "DNAT", .description = "GSM 08.08 NAT/Multiplexer", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DCTRL] = { .name = "DCTRL", .description = "Control interface", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DSMPP] = { .name = "DSMPP", .description = "SMPP interface for external SMS apps", .enabled = 1, .loglevel = LOGL_DEBUG, }, [DFILTER] = { .name = "DFILTER", .description = "BSC/NAT IMSI based filtering", .enabled = 1, .loglevel = LOGL_DEBUG, }, }; enum log_filter { _FLT_ALL = LOG_FILTER_ALL, /* libosmocore */ FLT_IMSI = 1, FLT_NSVC = 2, FLT_BVC = 3, }; static int filter_fn(const struct log_context *ctx, struct log_target *tar) { struct gsm_subscriber *subscr = ctx->ctx[BSC_CTX_SUBSCR]; const struct gprs_nsvc *nsvc = ctx->ctx[GPRS_CTX_NSVC]; const struct gprs_nsvc *bvc = ctx->ctx[GPRS_CTX_BVC]; if ((tar->filter_map & (1 << FLT_IMSI)) != 0 && subscr && subscr == tar->filter_data[FLT_IMSI]) return 1; /* Filter on the NS Virtual Connection */ if ((tar->filter_map & (1 << FLT_NSVC)) != 0 && nsvc && (nsvc == tar->filter_data[FLT_NSVC])) return 1; /* Filter on the NS Virtual Connection */ if ((tar->filter_map & (1 << FLT_BVC)) != 0 && bvc && (bvc == tar->filter_data[FLT_BVC])) return 1; return 0; } const struct log_info log_info = { .filter_fn = filter_fn, .cat = default_categories, .num_cat = ARRAY_SIZE(default_categories), }; void log_set_imsi_filter(struct log_target *target, struct gsm_subscriber *subscr) { /* free the old data */ if (target->filter_data[FLT_IMSI]) { subscr_put(target->filter_data[FLT_IMSI]); target->filter_data[FLT_IMSI] = NULL; } if (subscr) { target->filter_map |= (1 << FLT_IMSI); target->filter_data[FLT_IMSI] = subscr_get(subscr); } else { target->filter_map &= ~(1 << FLT_IMSI); } } openbsc-0.15.0/openbsc/src/libcommon/gsm_data.c000066400000000000000000000255531265565154000213730ustar00rootroot00000000000000/* (C) 2008-2010 by Harald Welte * * 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include void *tall_bsc_ctx; static LLIST_HEAD(bts_models); void set_ts_e1link(struct gsm_bts_trx_ts *ts, uint8_t e1_nr, uint8_t e1_ts, uint8_t e1_ts_ss) { ts->e1_link.e1_nr = e1_nr; ts->e1_link.e1_ts = e1_ts; ts->e1_link.e1_ts_ss = e1_ts_ss; } static struct gsm_bts_model *bts_model_find(enum gsm_bts_type type) { struct gsm_bts_model *model; llist_for_each_entry(model, &bts_models, list) { if (model->type == type) return model; } return NULL; } int gsm_bts_model_register(struct gsm_bts_model *model) { if (bts_model_find(model->type)) return -EEXIST; tlv_def_patch(&model->nm_att_tlvdef, &abis_nm_att_tlvdef); llist_add_tail(&model->list, &bts_models); return 0; } /* Get reference to a neighbor cell on a given BCCH ARFCN */ struct gsm_bts *gsm_bts_neighbor(const struct gsm_bts *bts, uint16_t arfcn, uint8_t bsic) { struct gsm_bts *neigh; /* FIXME: use some better heuristics here to determine which cell * using this ARFCN really is closest to the target cell. For * now we simply assume that each ARFCN will only be used by one * cell */ llist_for_each_entry(neigh, &bts->network->bts_list, list) { if (neigh->c0->arfcn == arfcn && neigh->bsic == bsic) return neigh; } return NULL; } const struct value_string bts_type_names[_NUM_GSM_BTS_TYPE+1] = { { GSM_BTS_TYPE_UNKNOWN, "unknown" }, { GSM_BTS_TYPE_BS11, "bs11" }, { GSM_BTS_TYPE_NANOBTS, "nanobts" }, { GSM_BTS_TYPE_RBS2000, "rbs2000" }, { GSM_BTS_TYPE_NOKIA_SITE, "nokia_site" }, { GSM_BTS_TYPE_OSMO_SYSMO, "sysmobts" }, { 0, NULL } }; const struct value_string bts_type_descs[_NUM_GSM_BTS_TYPE+1] = { { GSM_BTS_TYPE_UNKNOWN, "Unknown BTS Type" }, { GSM_BTS_TYPE_BS11, "Siemens BTS (BS-11 or compatible)" }, { GSM_BTS_TYPE_NANOBTS, "ip.access nanoBTS or compatible" }, { GSM_BTS_TYPE_RBS2000, "Ericsson RBS2000 Series" }, { GSM_BTS_TYPE_NOKIA_SITE, "Nokia {Metro,Ultra,In}Site" }, { GSM_BTS_TYPE_OSMO_SYSMO, "sysmocom sysmoBTS" }, { 0, NULL } }; enum gsm_bts_type parse_btstype(const char *arg) { return get_string_value(bts_type_names, arg); } const char *btstype2str(enum gsm_bts_type type) { return get_value_string(bts_type_names, type); } struct gsm_bts_trx *gsm_bts_trx_by_nr(struct gsm_bts *bts, int nr) { struct gsm_bts_trx *trx; llist_for_each_entry(trx, &bts->trx_list, list) { if (trx->nr == nr) return trx; } return NULL; } /* Search for a BTS in the given Location Area; optionally start searching * with start_bts (for continuing to search after the first result) */ struct gsm_bts *gsm_bts_by_lac(struct gsm_network *net, unsigned int lac, struct gsm_bts *start_bts) { int i; struct gsm_bts *bts; int skip = 0; if (start_bts) skip = 1; for (i = 0; i < net->num_bts; i++) { bts = gsm_bts_num(net, i); if (skip) { if (start_bts == bts) skip = 0; continue; } if (lac == GSM_LAC_RESERVED_ALL_BTS || bts->location_area_code == lac) return bts; } return NULL; } static const struct value_string auth_policy_names[] = { { GSM_AUTH_POLICY_CLOSED, "closed" }, { GSM_AUTH_POLICY_ACCEPT_ALL, "accept-all" }, { GSM_AUTH_POLICY_TOKEN, "token" }, { 0, NULL } }; enum gsm_auth_policy gsm_auth_policy_parse(const char *arg) { return get_string_value(auth_policy_names, arg); } const char *gsm_auth_policy_name(enum gsm_auth_policy policy) { return get_value_string(auth_policy_names, policy); } static const struct value_string rrlp_mode_names[] = { { RRLP_MODE_NONE, "none" }, { RRLP_MODE_MS_BASED, "ms-based" }, { RRLP_MODE_MS_PREF, "ms-preferred" }, { RRLP_MODE_ASS_PREF, "ass-preferred" }, { 0, NULL } }; enum rrlp_mode rrlp_mode_parse(const char *arg) { return get_string_value(rrlp_mode_names, arg); } const char *rrlp_mode_name(enum rrlp_mode mode) { return get_value_string(rrlp_mode_names, mode); } static const struct value_string bts_gprs_mode_names[] = { { BTS_GPRS_NONE, "none" }, { BTS_GPRS_GPRS, "gprs" }, { BTS_GPRS_EGPRS, "egprs" }, { 0, NULL } }; enum bts_gprs_mode bts_gprs_mode_parse(const char *arg, int *valid) { int rc; rc = get_string_value(bts_gprs_mode_names, arg); if (valid) *valid = rc != -EINVAL; return rc; } const char *bts_gprs_mode_name(enum bts_gprs_mode mode) { return get_value_string(bts_gprs_mode_names, mode); } int bts_gprs_mode_is_compat(struct gsm_bts *bts, enum bts_gprs_mode mode) { if (mode != BTS_GPRS_NONE && !gsm_bts_has_feature(bts, BTS_FEAT_GPRS)) { return 0; } if (mode == BTS_GPRS_EGPRS && !gsm_bts_has_feature(bts, BTS_FEAT_EGPRS)) { return 0; } return 1; } struct gsm_meas_rep *lchan_next_meas_rep(struct gsm_lchan *lchan) { struct gsm_meas_rep *meas_rep; meas_rep = &lchan->meas_rep[lchan->meas_rep_idx]; memset(meas_rep, 0, sizeof(*meas_rep)); meas_rep->lchan = lchan; lchan->meas_rep_idx = (lchan->meas_rep_idx + 1) % ARRAY_SIZE(lchan->meas_rep); return meas_rep; } int gsm_btsmodel_set_feature(struct gsm_bts_model *bts, enum gsm_bts_features feat) { return bitvec_set_bit_pos(&bts->features, feat, 1); } int gsm_bts_has_feature(struct gsm_bts *bts, enum gsm_bts_features feat) { return bitvec_get_bit_pos(&bts->model->features, feat); } int gsm_set_bts_type(struct gsm_bts *bts, enum gsm_bts_type type) { struct gsm_bts_model *model; model = bts_model_find(type); if (!model) return -EINVAL; bts->type = type; bts->model = model; if (model->start && !model->started) { int ret = model->start(bts->network); if (ret < 0) return ret; model->started = true; } switch (bts->type) { case GSM_BTS_TYPE_NANOBTS: case GSM_BTS_TYPE_OSMO_SYSMO: /* Set the default OML Stream ID to 0xff */ bts->oml_tei = 0xff; bts->c0->nominal_power = 23; break; case GSM_BTS_TYPE_RBS2000: INIT_LLIST_HEAD(&bts->rbs2000.is.conn_groups); INIT_LLIST_HEAD(&bts->rbs2000.con.conn_groups); break; case GSM_BTS_TYPE_BS11: case GSM_BTS_TYPE_UNKNOWN: case GSM_BTS_TYPE_NOKIA_SITE: /* Set default BTS reset timer */ bts->nokia.bts_reset_timer_cnf = 15; case _NUM_GSM_BTS_TYPE: break; } return 0; } struct gsm_bts *gsm_bts_alloc_register(struct gsm_network *net, enum gsm_bts_type type, uint8_t tsc, uint8_t bsic) { struct gsm_bts_model *model = bts_model_find(type); struct gsm_bts *bts; if (!model && type != GSM_BTS_TYPE_UNKNOWN) return NULL; bts = gsm_bts_alloc(net); if (!bts) return NULL; bts->network = net; bts->nr = net->num_bts++; bts->type = type; bts->model = model; bts->tsc = tsc; bts->bsic = bsic; bts->neigh_list_manual_mode = 0; bts->si_common.cell_sel_par.cell_resel_hyst = 2; /* 4 dB */ bts->si_common.cell_sel_par.rxlev_acc_min = 0; bts->si_common.neigh_list.data = bts->si_common.data.neigh_list; bts->si_common.neigh_list.data_len = sizeof(bts->si_common.data.neigh_list); bts->si_common.si5_neigh_list.data = bts->si_common.data.si5_neigh_list; bts->si_common.si5_neigh_list.data_len = sizeof(bts->si_common.data.si5_neigh_list); bts->si_common.cell_alloc.data = bts->si_common.data.cell_alloc; bts->si_common.cell_alloc.data_len = sizeof(bts->si_common.data.cell_alloc); bts->si_common.rach_control.re = 1; /* no re-establishment */ bts->si_common.rach_control.tx_integer = 9; /* 12 slots spread - 217/115 slots delay */ bts->si_common.rach_control.max_trans = 3; /* 7 retransmissions */ bts->si_common.rach_control.t2 = 4; /* no emergency calls */ bts->si_common.chan_desc.att = 1; /* attachment required */ bts->si_common.chan_desc.bs_pa_mfrms = RSL_BS_PA_MFRMS_5; /* paging frames */ bts->si_common.chan_desc.bs_ag_blks_res = 1; /* reserved AGCH blocks */ bts->si_common.chan_desc.t3212 = 5; /* Use 30 min periodic update interval as sane default */ set_radio_link_timeout(&bts->si_common.cell_options, 32); /* Use RADIO LINK TIMEOUT of 32 seconds */ llist_add_tail(&bts->list, &net->bts_list); INIT_LLIST_HEAD(&bts->abis_queue); INIT_LLIST_HEAD(&bts->loc_list); return bts; } void gprs_ra_id_by_bts(struct gprs_ra_id *raid, struct gsm_bts *bts) { raid->mcc = bts->network->country_code; raid->mnc = bts->network->network_code; raid->lac = bts->location_area_code; raid->rac = bts->gprs.rac; } int gsm48_ra_id_by_bts(uint8_t *buf, struct gsm_bts *bts) { struct gprs_ra_id raid; gprs_ra_id_by_bts(&raid, bts); return gsm48_construct_ra(buf, &raid); } int gsm_parse_reg(void *ctx, regex_t *reg, char **str, int argc, const char **argv) { int ret; ret = 0; if (*str) { talloc_free(*str); *str = NULL; } regfree(reg); if (argc > 0) { *str = talloc_strdup(ctx, argv[0]); ret = regcomp(reg, argv[0], 0); /* handle compilation failures */ if (ret != 0) { talloc_free(*str); *str = NULL; } } return ret; } /* Assume there are only 256 possible bts */ osmo_static_assert(sizeof(((struct gsm_bts *) 0)->nr) == 1, _bts_nr_is_256); static void depends_calc_index_bit(int bts_nr, int *idx, int *bit) { *idx = bts_nr / (8 * 4); *bit = bts_nr % (8 * 4); } void bts_depend_mark(struct gsm_bts *bts, int dep) { int idx, bit; depends_calc_index_bit(dep, &idx, &bit); bts->depends_on[idx] |= 1 << bit; } void bts_depend_clear(struct gsm_bts *bts, int dep) { int idx, bit; depends_calc_index_bit(dep, &idx, &bit); bts->depends_on[idx] &= ~(1 << bit); } int bts_depend_is_depedency(struct gsm_bts *base, struct gsm_bts *other) { int idx, bit; depends_calc_index_bit(other->nr, &idx, &bit); /* Check if there is a depends bit */ return (base->depends_on[idx] & (1 << bit)) > 0; } static int bts_is_online(struct gsm_bts *bts) { /* TODO: support E1 BTS too */ if (!is_ipaccess_bts(bts)) return 1; if (!bts->oml_link) return 0; return bts->mo.nm_state.operational == NM_OPSTATE_ENABLED; } int bts_depend_check(struct gsm_bts *bts) { struct gsm_bts *other_bts; llist_for_each_entry(other_bts, &bts->network->bts_list, list) { if (!bts_depend_is_depedency(bts, other_bts)) continue; if (bts_is_online(other_bts)) continue; return 0; } return 1; } openbsc-0.15.0/openbsc/src/libcommon/gsm_data_shared.c000066400000000000000000000327311265565154000227150ustar00rootroot00000000000000/* (C) 2008-2010 by Harald Welte * * 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include void gsm_abis_mo_reset(struct gsm_abis_mo *mo) { mo->nm_state.operational = NM_OPSTATE_NULL; mo->nm_state.availability = NM_AVSTATE_POWER_OFF; } static void gsm_mo_init(struct gsm_abis_mo *mo, struct gsm_bts *bts, uint8_t obj_class, uint8_t p1, uint8_t p2, uint8_t p3) { mo->bts = bts; mo->obj_class = obj_class; mo->obj_inst.bts_nr = p1; mo->obj_inst.trx_nr = p2; mo->obj_inst.ts_nr = p3; gsm_abis_mo_reset(mo); } const struct value_string gsm_pchant_names[12] = { { GSM_PCHAN_NONE, "NONE" }, { GSM_PCHAN_CCCH, "CCCH" }, { GSM_PCHAN_CCCH_SDCCH4,"CCCH+SDCCH4" }, { GSM_PCHAN_TCH_F, "TCH/F" }, { GSM_PCHAN_TCH_H, "TCH/H" }, { GSM_PCHAN_SDCCH8_SACCH8C, "SDCCH8" }, { GSM_PCHAN_PDCH, "PDCH" }, { GSM_PCHAN_TCH_F_PDCH, "TCH/F_PDCH" }, { GSM_PCHAN_UNKNOWN, "UNKNOWN" }, { GSM_PCHAN_CCCH_SDCCH4_CBCH, "CCCH+SDCCH4+CBCH" }, { GSM_PCHAN_SDCCH8_SACCH8C_CBCH, "SDCCH8+CBCH" }, { 0, NULL } }; const struct value_string gsm_pchant_descs[12] = { { GSM_PCHAN_NONE, "Physical Channel not configured" }, { GSM_PCHAN_CCCH, "FCCH + SCH + BCCH + CCCH (Comb. IV)" }, { GSM_PCHAN_CCCH_SDCCH4, "FCCH + SCH + BCCH + CCCH + 4 SDCCH + 2 SACCH (Comb. V)" }, { GSM_PCHAN_TCH_F, "TCH/F + FACCH/F + SACCH (Comb. I)" }, { GSM_PCHAN_TCH_H, "2 TCH/H + 2 FACCH/H + 2 SACCH (Comb. II)" }, { GSM_PCHAN_SDCCH8_SACCH8C, "8 SDCCH + 4 SACCH (Comb. VII)" }, { GSM_PCHAN_PDCH, "Packet Data Channel for GPRS/EDGE" }, { GSM_PCHAN_TCH_F_PDCH, "Dynamic TCH/F or GPRS PDCH" }, { GSM_PCHAN_UNKNOWN, "Unknown / Unsupported channel combination" }, { GSM_PCHAN_CCCH_SDCCH4_CBCH, "FCCH + SCH + BCCH + CCCH + CBCH + 3 SDCCH + 2 SACCH (Comb. V)" }, { GSM_PCHAN_SDCCH8_SACCH8C_CBCH, "7 SDCCH + 4 SACCH + CBCH (Comb. VII)" }, { 0, NULL } }; const char *gsm_pchan_name(enum gsm_phys_chan_config c) { return get_value_string(gsm_pchant_names, c); } enum gsm_phys_chan_config gsm_pchan_parse(const char *name) { return get_string_value(gsm_pchant_names, name); } const struct value_string gsm_lchant_names[8] = { { GSM_LCHAN_NONE, "NONE" }, { GSM_LCHAN_SDCCH, "SDCCH" }, { GSM_LCHAN_TCH_F, "TCH/F" }, { GSM_LCHAN_TCH_H, "TCH/H" }, { GSM_LCHAN_UNKNOWN, "UNKNOWN" }, { GSM_LCHAN_CBCH, "CBCH" }, { 0, NULL } }; const char *gsm_lchant_name(enum gsm_chan_t c) { return get_value_string(gsm_lchant_names, c); } static const struct value_string lchan_s_names[] = { { LCHAN_S_NONE, "NONE" }, { LCHAN_S_ACT_REQ, "ACTIVATION REQUESTED" }, { LCHAN_S_ACTIVE, "ACTIVE" }, { LCHAN_S_INACTIVE, "INACTIVE" }, { LCHAN_S_REL_REQ, "RELEASE REQUESTED" }, { LCHAN_S_REL_ERR, "RELEASE DUE ERROR" }, { LCHAN_S_BROKEN, "BROKEN UNUSABLE" }, { 0, NULL } }; const char *gsm_lchans_name(enum gsm_lchan_state s) { return get_value_string(lchan_s_names, s); } static const struct value_string chreq_names[] = { { GSM_CHREQ_REASON_EMERG, "EMERGENCY" }, { GSM_CHREQ_REASON_PAG, "PAGING" }, { GSM_CHREQ_REASON_CALL, "CALL" }, { GSM_CHREQ_REASON_LOCATION_UPD,"LOCATION_UPDATE" }, { GSM_CHREQ_REASON_OTHER, "OTHER" }, { 0, NULL } }; const char *gsm_chreq_name(enum gsm_chreq_reason_t c) { return get_value_string(chreq_names, c); } struct gsm_bts *gsm_bts_num(struct gsm_network *net, int num) { struct gsm_bts *bts; if (num >= net->num_bts) return NULL; llist_for_each_entry(bts, &net->bts_list, list) { if (bts->nr == num) return bts; } return NULL; } struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts) { struct gsm_bts_trx *trx = talloc_zero(bts, struct gsm_bts_trx); int k; if (!trx) return NULL; trx->bts = bts; trx->nr = bts->num_trx++; trx->mo.nm_state.administrative = NM_STATE_UNLOCKED; gsm_mo_init(&trx->mo, bts, NM_OC_RADIO_CARRIER, bts->nr, trx->nr, 0xff); gsm_mo_init(&trx->bb_transc.mo, bts, NM_OC_BASEB_TRANSC, bts->nr, trx->nr, 0xff); for (k = 0; k < TRX_NR_TS; k++) { struct gsm_bts_trx_ts *ts = &trx->ts[k]; int l; ts->trx = trx; ts->nr = k; ts->pchan = GSM_PCHAN_NONE; ts->tsc = -1; gsm_mo_init(&ts->mo, bts, NM_OC_CHANNEL, bts->nr, trx->nr, ts->nr); ts->hopping.arfcns.data_len = sizeof(ts->hopping.arfcns_data); ts->hopping.arfcns.data = ts->hopping.arfcns_data; ts->hopping.ma.data_len = sizeof(ts->hopping.ma_data); ts->hopping.ma.data = ts->hopping.ma_data; for (l = 0; l < TS_MAX_LCHAN; l++) { struct gsm_lchan *lchan; lchan = &ts->lchan[l]; lchan->ts = ts; lchan->nr = l; lchan->type = GSM_LCHAN_NONE; #ifndef ROLE_BSC INIT_LLIST_HEAD(&lchan->sapi_cmds); #endif } } if (trx->nr != 0) trx->nominal_power = bts->c0->nominal_power; llist_add_tail(&trx->list, &bts->trx_list); return trx; } static const uint8_t bts_nse_timer_default[] = { 3, 3, 3, 3, 30, 3, 10 }; static const uint8_t bts_cell_timer_default[] = { 3, 3, 3, 3, 3, 10, 3, 10, 3, 10, 3 }; static const struct gprs_rlc_cfg rlc_cfg_default = { .parameter = { [RLC_T3142] = 20, [RLC_T3169] = 5, [RLC_T3191] = 5, [RLC_T3193] = 160, /* 10ms */ [RLC_T3195] = 5, [RLC_N3101] = 10, [RLC_N3103] = 4, [RLC_N3105] = 8, [CV_COUNTDOWN] = 15, [T_DL_TBF_EXT] = 250 * 10, /* ms */ [T_UL_TBF_EXT] = 250 * 10, /* ms */ }, .paging = { .repeat_time = 5 * 50, /* ms */ .repeat_count = 3, }, .cs_mask = 0x1fff, .initial_cs = 2, .initial_mcs = 6, }; struct gsm_bts *gsm_bts_alloc(void *ctx) { struct gsm_bts *bts = talloc_zero(ctx, struct gsm_bts); int i; if (!bts) return NULL; bts->num_trx = 0; INIT_LLIST_HEAD(&bts->trx_list); bts->ms_max_power = 15; /* dBm */ gsm_mo_init(&bts->mo, bts, NM_OC_BTS, bts->nr, 0xff, 0xff); gsm_mo_init(&bts->site_mgr.mo, bts, NM_OC_SITE_MANAGER, 0xff, 0xff, 0xff); for (i = 0; i < ARRAY_SIZE(bts->gprs.nsvc); i++) { bts->gprs.nsvc[i].bts = bts; bts->gprs.nsvc[i].id = i; gsm_mo_init(&bts->gprs.nsvc[i].mo, bts, NM_OC_GPRS_NSVC, bts->nr, i, 0xff); } memcpy(&bts->gprs.nse.timer, bts_nse_timer_default, sizeof(bts->gprs.nse.timer)); gsm_mo_init(&bts->gprs.nse.mo, bts, NM_OC_GPRS_NSE, bts->nr, 0xff, 0xff); memcpy(&bts->gprs.cell.timer, bts_cell_timer_default, sizeof(bts->gprs.cell.timer)); gsm_mo_init(&bts->gprs.cell.mo, bts, NM_OC_GPRS_CELL, bts->nr, 0xff, 0xff); memcpy(&bts->gprs.cell.rlc_cfg, &rlc_cfg_default, sizeof(bts->gprs.cell.rlc_cfg)); /* create our primary TRX */ bts->c0 = gsm_bts_trx_alloc(bts); if (!bts->c0) { talloc_free(bts); return NULL; } bts->c0->ts[0].pchan = GSM_PCHAN_CCCH_SDCCH4; bts->rach_b_thresh = -1; bts->rach_ldavg_slots = -1; bts->paging.free_chans_need = -1; /* si handling */ bts->bcch_change_mark = 1; return bts; } /* reset the state of all MO in the BTS */ void gsm_bts_mo_reset(struct gsm_bts *bts) { struct gsm_bts_trx *trx; unsigned int i; gsm_abis_mo_reset(&bts->mo); gsm_abis_mo_reset(&bts->site_mgr.mo); for (i = 0; i < ARRAY_SIZE(bts->gprs.nsvc); i++) gsm_abis_mo_reset(&bts->gprs.nsvc[i].mo); gsm_abis_mo_reset(&bts->gprs.nse.mo); gsm_abis_mo_reset(&bts->gprs.cell.mo); llist_for_each_entry(trx, &bts->trx_list, list) { gsm_abis_mo_reset(&trx->mo); gsm_abis_mo_reset(&trx->bb_transc.mo); for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { struct gsm_bts_trx_ts *ts = &trx->ts[i]; gsm_abis_mo_reset(&ts->mo); } } } struct gsm_bts_trx *gsm_bts_trx_num(const struct gsm_bts *bts, int num) { struct gsm_bts_trx *trx; if (num >= bts->num_trx) return NULL; llist_for_each_entry(trx, &bts->trx_list, list) { if (trx->nr == num) return trx; } return NULL; } static char ts2str[255]; char *gsm_trx_name(const struct gsm_bts_trx *trx) { snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d)", trx->bts->nr, trx->nr); return ts2str; } char *gsm_ts_name(const struct gsm_bts_trx_ts *ts) { snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d,ts=%d)", ts->trx->bts->nr, ts->trx->nr, ts->nr); return ts2str; } char *gsm_lchan_name(const struct gsm_lchan *lchan) { struct gsm_bts_trx_ts *ts = lchan->ts; snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d,ts=%d,ss=%d)", ts->trx->bts->nr, ts->trx->nr, ts->nr, lchan->nr); return ts2str; } /* obtain the MO structure for a given object instance */ struct gsm_abis_mo * gsm_objclass2mo(struct gsm_bts *bts, uint8_t obj_class, const struct abis_om_obj_inst *obj_inst) { struct gsm_bts_trx *trx; struct gsm_abis_mo *mo = NULL; switch (obj_class) { case NM_OC_BTS: mo = &bts->mo; break; case NM_OC_RADIO_CARRIER: if (obj_inst->trx_nr >= bts->num_trx) { return NULL; } trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); mo = &trx->mo; break; case NM_OC_BASEB_TRANSC: if (obj_inst->trx_nr >= bts->num_trx) { return NULL; } trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); mo = &trx->bb_transc.mo; break; case NM_OC_CHANNEL: if (obj_inst->trx_nr >= bts->num_trx) { return NULL; } trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); if (obj_inst->ts_nr >= TRX_NR_TS) return NULL; mo = &trx->ts[obj_inst->ts_nr].mo; break; case NM_OC_SITE_MANAGER: mo = &bts->site_mgr.mo; break; case NM_OC_BS11: switch (obj_inst->bts_nr) { case BS11_OBJ_CCLK: mo = &bts->bs11.cclk.mo; break; case BS11_OBJ_BBSIG: if (obj_inst->ts_nr > bts->num_trx) return NULL; trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); mo = &trx->bs11.bbsig.mo; break; case BS11_OBJ_PA: if (obj_inst->ts_nr > bts->num_trx) return NULL; trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); mo = &trx->bs11.pa.mo; break; default: return NULL; } break; case NM_OC_BS11_RACK: mo = &bts->bs11.rack.mo; break; case NM_OC_BS11_ENVABTSE: if (obj_inst->trx_nr >= ARRAY_SIZE(bts->bs11.envabtse)) return NULL; mo = &bts->bs11.envabtse[obj_inst->trx_nr].mo; break; case NM_OC_GPRS_NSE: mo = &bts->gprs.nse.mo; break; case NM_OC_GPRS_CELL: mo = &bts->gprs.cell.mo; break; case NM_OC_GPRS_NSVC: if (obj_inst->trx_nr >= ARRAY_SIZE(bts->gprs.nsvc)) return NULL; mo = &bts->gprs.nsvc[obj_inst->trx_nr].mo; break; } return mo; } /* obtain the gsm_nm_state data structure for a given object instance */ struct gsm_nm_state * gsm_objclass2nmstate(struct gsm_bts *bts, uint8_t obj_class, const struct abis_om_obj_inst *obj_inst) { struct gsm_abis_mo *mo; mo = gsm_objclass2mo(bts, obj_class, obj_inst); if (!mo) return NULL; return &mo->nm_state; } /* obtain the in-memory data structure of a given object instance */ void * gsm_objclass2obj(struct gsm_bts *bts, uint8_t obj_class, const struct abis_om_obj_inst *obj_inst) { struct gsm_bts_trx *trx; void *obj = NULL; switch (obj_class) { case NM_OC_BTS: obj = bts; break; case NM_OC_RADIO_CARRIER: if (obj_inst->trx_nr >= bts->num_trx) { return NULL; } trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); obj = trx; break; case NM_OC_BASEB_TRANSC: if (obj_inst->trx_nr >= bts->num_trx) { return NULL; } trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); obj = &trx->bb_transc; break; case NM_OC_CHANNEL: if (obj_inst->trx_nr >= bts->num_trx) { return NULL; } trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); if (obj_inst->ts_nr >= TRX_NR_TS) return NULL; obj = &trx->ts[obj_inst->ts_nr]; break; case NM_OC_SITE_MANAGER: obj = &bts->site_mgr; break; case NM_OC_GPRS_NSE: obj = &bts->gprs.nse; break; case NM_OC_GPRS_CELL: obj = &bts->gprs.cell; break; case NM_OC_GPRS_NSVC: if (obj_inst->trx_nr >= ARRAY_SIZE(bts->gprs.nsvc)) return NULL; obj = &bts->gprs.nsvc[obj_inst->trx_nr]; break; } return obj; } /* See Table 10.5.25 of GSM04.08 */ uint8_t gsm_ts2chan_nr(const struct gsm_bts_trx_ts *ts, uint8_t lchan_nr) { uint8_t cbits, chan_nr; switch (ts->pchan) { case GSM_PCHAN_TCH_F: case GSM_PCHAN_PDCH: case GSM_PCHAN_TCH_F_PDCH: cbits = 0x01; break; case GSM_PCHAN_TCH_H: cbits = 0x02; cbits += lchan_nr; break; case GSM_PCHAN_CCCH_SDCCH4: case GSM_PCHAN_CCCH_SDCCH4_CBCH: cbits = 0x04; cbits += lchan_nr; break; case GSM_PCHAN_SDCCH8_SACCH8C: case GSM_PCHAN_SDCCH8_SACCH8C_CBCH: cbits = 0x08; cbits += lchan_nr; break; default: case GSM_PCHAN_CCCH: cbits = 0x10; break; } chan_nr = (cbits << 3) | (ts->nr & 0x7); return chan_nr; } uint8_t gsm_lchan2chan_nr(const struct gsm_lchan *lchan) { return gsm_ts2chan_nr(lchan->ts, lchan->nr); } /* return the gsm_lchan for the CBCH (if it exists at all) */ struct gsm_lchan *gsm_bts_get_cbch(struct gsm_bts *bts) { struct gsm_lchan *lchan = NULL; struct gsm_bts_trx *trx = bts->c0; if (trx->ts[0].pchan == GSM_PCHAN_CCCH_SDCCH4_CBCH) lchan = &trx->ts[0].lchan[2]; else { int i; for (i = 0; i < 8; i++) { if (trx->ts[i].pchan == GSM_PCHAN_SDCCH8_SACCH8C_CBCH) { lchan = &trx->ts[i].lchan[2]; break; } } } return lchan; } openbsc-0.15.0/openbsc/src/libcommon/gsm_subscriber_base.c000066400000000000000000000075111265565154000236110ustar00rootroot00000000000000/* The concept of a subscriber as seen by the BSC */ /* (C) 2008 by Harald Welte * (C) 2009-2010 by Holger Hans Peter Freyther * (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 . * */ #include #include #include #include #include #include #include #include LLIST_HEAD(active_subscribers); void *tall_subscr_ctx; /* for the gsm_subscriber.c */ struct llist_head *subscr_bsc_active_subscribers(void) { return &active_subscribers; } char *subscr_name(struct gsm_subscriber *subscr) { if (strlen(subscr->name)) return subscr->name; return subscr->imsi; } struct gsm_subscriber *subscr_alloc(void) { struct gsm_subscriber *s; s = talloc_zero(tall_subscr_ctx, struct gsm_subscriber); if (!s) return NULL; llist_add_tail(&s->entry, &active_subscribers); s->use_count = 1; s->tmsi = GSM_RESERVED_TMSI; INIT_LLIST_HEAD(&s->requests); return s; } static void subscr_free(struct gsm_subscriber *subscr) { llist_del(&subscr->entry); talloc_free(subscr); } void subscr_direct_free(struct gsm_subscriber *subscr) { OSMO_ASSERT(subscr->use_count == 1); subscr_free(subscr); } struct gsm_subscriber *subscr_get(struct gsm_subscriber *subscr) { subscr->use_count++; DEBUGP(DREF, "subscr %s usage increases usage to: %d\n", subscr->extension, subscr->use_count); return subscr; } struct gsm_subscriber *subscr_put(struct gsm_subscriber *subscr) { subscr->use_count--; DEBUGP(DREF, "subscr %s usage decreased usage to: %d\n", subscr->extension, subscr->use_count); if (subscr->use_count <= 0 && !((subscr->group && subscr->group->keep_subscr) || subscr->keep_in_ram)) subscr_free(subscr); return NULL; } struct gsm_subscriber *subscr_get_or_create(struct gsm_subscriber_group *sgrp, const char *imsi) { struct gsm_subscriber *subscr; llist_for_each_entry(subscr, subscr_bsc_active_subscribers(), entry) { if (strcmp(subscr->imsi, imsi) == 0 && subscr->group == sgrp) return subscr_get(subscr); } subscr = subscr_alloc(); if (!subscr) return NULL; strncpy(subscr->imsi, imsi, GSM_IMSI_LENGTH-1); subscr->group = sgrp; return subscr; } struct gsm_subscriber *subscr_active_by_tmsi(struct gsm_subscriber_group *sgrp, uint32_t tmsi) { struct gsm_subscriber *subscr; llist_for_each_entry(subscr, subscr_bsc_active_subscribers(), entry) { if (subscr->tmsi == tmsi && subscr->group == sgrp) return subscr_get(subscr); } return NULL; } struct gsm_subscriber *subscr_active_by_imsi(struct gsm_subscriber_group *sgrp, const char *imsi) { struct gsm_subscriber *subscr; llist_for_each_entry(subscr, subscr_bsc_active_subscribers(), entry) { if (strcmp(subscr->imsi, imsi) == 0 && subscr->group == sgrp) return subscr_get(subscr); } return NULL; } int subscr_purge_inactive(struct gsm_subscriber_group *sgrp) { struct gsm_subscriber *subscr, *tmp; int purged = 0; llist_for_each_entry_safe(subscr, tmp, subscr_bsc_active_subscribers(), entry) { if (subscr->group == sgrp && subscr->use_count <= 0) { subscr_free(subscr); purged += 1; } } return purged; } openbsc-0.15.0/openbsc/src/libcommon/socket.c000066400000000000000000000051721265565154000210770ustar00rootroot00000000000000/* OpenBSC sokcet code, taken from Abis input driver for ip.access */ /* (C) 2009 by Harald Welte * (C) 2010 by Holger Hans Peter Freyther * (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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int make_sock(struct osmo_fd *bfd, int proto, uint32_t ip, uint16_t port, int priv_nr, int (*cb)(struct osmo_fd *fd, unsigned int what), void *data) { struct sockaddr_in addr; int ret, on = 1; int type = SOCK_STREAM; switch (proto) { case IPPROTO_TCP: type = SOCK_STREAM; break; case IPPROTO_UDP: type = SOCK_DGRAM; break; #ifdef IPPROTO_GRE case IPPROTO_GRE: type = SOCK_RAW; break; #endif default: return -EINVAL; } bfd->fd = socket(AF_INET, type, proto); bfd->cb = cb; bfd->when = BSC_FD_READ; bfd->data = data; bfd->priv_nr = priv_nr; if (bfd->fd < 0) { LOGP(DLINP, LOGL_ERROR, "could not create socket.\n"); return -EIO; } memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(port); if (ip != INADDR_ANY) addr.sin_addr.s_addr = htonl(ip); else addr.sin_addr.s_addr = INADDR_ANY; setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr)); if (ret < 0) { LOGP(DLINP, LOGL_ERROR, "could not bind socket %s\n", strerror(errno)); close(bfd->fd); return -EIO; } if (proto == IPPROTO_TCP) { ret = listen(bfd->fd, 1); if (ret < 0) { perror("listen"); close(bfd->fd); return ret; } } ret = osmo_fd_register(bfd); if (ret < 0) { perror("register_listen_fd"); close(bfd->fd); return ret; } return 0; } openbsc-0.15.0/openbsc/src/libcommon/talloc_ctx.c000066400000000000000000000030701265565154000217360ustar00rootroot00000000000000#include #include extern void *tall_msgb_ctx; extern void *tall_fle_ctx; extern void *tall_locop_ctx; extern void *tall_authciphop_ctx; extern void *tall_gsms_ctx; extern void *tall_subscr_ctx; extern void *tall_sub_req_ctx; extern void *tall_call_ctx; extern void *tall_paging_ctx; extern void *tall_sigh_ctx; extern void *tall_tqe_ctx; extern void *tall_trans_ctx; extern void *tall_map_ctx; extern void *tall_upq_ctx; extern void *tall_ctr_ctx; void talloc_ctx_init(void) { tall_msgb_ctx = talloc_named_const(tall_bsc_ctx, 0, "msgb"); tall_fle_ctx = talloc_named_const(tall_bsc_ctx, 0, "bs11_file_list_entry"); tall_locop_ctx = talloc_named_const(tall_bsc_ctx, 0, "loc_updating_oper"); tall_authciphop_ctx = talloc_named_const(tall_bsc_ctx, 0, "auth_ciph_oper"); tall_gsms_ctx = talloc_named_const(tall_bsc_ctx, 0, "sms"); tall_subscr_ctx = talloc_named_const(tall_bsc_ctx, 0, "subscriber"); tall_sub_req_ctx = talloc_named_const(tall_bsc_ctx, 0, "subscr_request"); tall_call_ctx = talloc_named_const(tall_bsc_ctx, 0, "gsm_call"); tall_paging_ctx = talloc_named_const(tall_bsc_ctx, 0, "paging_request"); tall_sigh_ctx = talloc_named_const(tall_bsc_ctx, 0, "signal_handler"); tall_tqe_ctx = talloc_named_const(tall_bsc_ctx, 0, "subch_txq_entry"); tall_trans_ctx = talloc_named_const(tall_bsc_ctx, 0, "transaction"); tall_map_ctx = talloc_named_const(tall_bsc_ctx, 0, "trau_map_entry"); tall_upq_ctx = talloc_named_const(tall_bsc_ctx, 0, "trau_upq_entry"); tall_ctr_ctx = talloc_named_const(tall_bsc_ctx, 0, "counter"); } openbsc-0.15.0/openbsc/src/libfilter/000077500000000000000000000000001265565154000174335ustar00rootroot00000000000000openbsc-0.15.0/openbsc/src/libfilter/Makefile.am000066400000000000000000000005101265565154000214630ustar00rootroot00000000000000AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) \ $(LIBOSMOVTY_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(LIBOSMOSCCP_CFLAGS) $(COVERAGE_CFLAGS) noinst_LIBRARIES = libfilter.a libfilter_a_SOURCES = \ bsc_msg_filter.c \ bsc_msg_acc.c \ bsc_msg_vty.c openbsc-0.15.0/openbsc/src/libfilter/bsc_msg_acc.c000066400000000000000000000061131265565154000220230ustar00rootroot00000000000000/* * (C) 2010-2015 by Holger Hans Peter Freyther * (C) 2010-2011 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 . * */ #include #include #include #include static const struct rate_ctr_desc acc_list_ctr_description[] = { [ACC_LIST_LOCAL_FILTER] = { "access-list.local-filter", "Rejected by rule for local"}, [ACC_LIST_GLOBAL_FILTER]= { "access-list.global-filter", "Rejected by rule for global"}, }; static const struct rate_ctr_group_desc bsc_cfg_acc_list_desc = { .group_name_prefix = "nat.filter", .group_description = "NAT Access-List Statistics", .num_ctr = ARRAY_SIZE(acc_list_ctr_description), .ctr_desc = acc_list_ctr_description, }; int bsc_msg_acc_lst_check_allow(struct bsc_msg_acc_lst *lst, const char *mi_string) { struct bsc_msg_acc_lst_entry *entry; llist_for_each_entry(entry, &lst->fltr_list, list) { if (!entry->imsi_allow) continue; if (regexec(&entry->imsi_allow_re, mi_string, 0, NULL, 0) == 0) return 0; } return 1; } struct bsc_msg_acc_lst *bsc_msg_acc_lst_find(struct llist_head *head, const char *name) { struct bsc_msg_acc_lst *lst; if (!name) return NULL; llist_for_each_entry(lst, head, list) if (strcmp(lst->name, name) == 0) return lst; return NULL; } struct bsc_msg_acc_lst *bsc_msg_acc_lst_get(void *ctx, struct llist_head *head, const char *name) { struct bsc_msg_acc_lst *lst; lst = bsc_msg_acc_lst_find(head, name); if (lst) return lst; lst = talloc_zero(ctx, struct bsc_msg_acc_lst); if (!lst) { LOGP(DNAT, LOGL_ERROR, "Failed to allocate access list"); return NULL; } /* TODO: get the index right */ lst->stats = rate_ctr_group_alloc(lst, &bsc_cfg_acc_list_desc, 0); if (!lst->stats) { talloc_free(lst); return NULL; } INIT_LLIST_HEAD(&lst->fltr_list); lst->name = talloc_strdup(lst, name); llist_add_tail(&lst->list, head); return lst; } void bsc_msg_acc_lst_delete(struct bsc_msg_acc_lst *lst) { llist_del(&lst->list); rate_ctr_group_free(lst->stats); talloc_free(lst); } struct bsc_msg_acc_lst_entry *bsc_msg_acc_lst_entry_create(struct bsc_msg_acc_lst *lst) { struct bsc_msg_acc_lst_entry *entry; entry = talloc_zero(lst, struct bsc_msg_acc_lst_entry); if (!entry) return NULL; entry->cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED; entry->lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED; llist_add_tail(&entry->list, &lst->fltr_list); return entry; } openbsc-0.15.0/openbsc/src/libfilter/bsc_msg_filter.c000066400000000000000000000243171265565154000225700ustar00rootroot00000000000000/* * Access filtering */ /* * (C) 2010-2015 by Holger Hans Peter Freyther * (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 . * */ #include #include #include #include #include #include #include #include #include #include int bsc_filter_barr_find(struct rb_root *root, const char *imsi, int *cm, int *lu) { struct bsc_filter_barr_entry *n; n = rb_entry(root->rb_node, struct bsc_filter_barr_entry, node); while (n) { int rc = strcmp(imsi, n->imsi); if (rc == 0) { *cm = n->cm_reject_cause; *lu = n->lu_reject_cause; return 1; } n = rb_entry( (rc < 0) ? n->node.rb_left : n->node.rb_right, struct bsc_filter_barr_entry, node); }; return 0; } static int insert_barr_node(struct bsc_filter_barr_entry *entry, struct rb_root *root) { struct rb_node **new = &root->rb_node, *parent = NULL; while (*new) { int rc; struct bsc_filter_barr_entry *this; this = rb_entry(*new, struct bsc_filter_barr_entry, node); parent = *new; rc = strcmp(entry->imsi, this->imsi); if (rc < 0) new = &((*new)->rb_left); else if (rc > 0) new = &((*new)->rb_right); else { LOGP(DFILTER, LOGL_ERROR, "Duplicate entry for IMSI(%s)\n", entry->imsi); talloc_free(entry); return -1; } } rb_link_node(&entry->node, parent, new); rb_insert_color(&entry->node, root); return 0; } int bsc_filter_barr_adapt(void *ctx, struct rb_root *root, const struct osmo_config_list *list) { struct osmo_config_entry *cfg_entry; int err = 0; /* free the old data */ while (!RB_EMPTY_ROOT(root)) { struct rb_node *node = rb_first(root); rb_erase(node, root); talloc_free(node); } if (!list) return 0; /* now adapt the new list */ llist_for_each_entry(cfg_entry, &list->entry, list) { struct bsc_filter_barr_entry *entry; entry = talloc_zero(ctx, struct bsc_filter_barr_entry); if (!entry) { LOGP(DFILTER, LOGL_ERROR, "Allocation of the barr entry failed.\n"); continue; } entry->imsi = talloc_strdup(entry, cfg_entry->mcc); entry->cm_reject_cause = atoi(cfg_entry->mnc); entry->lu_reject_cause = atoi(cfg_entry->option); err |= insert_barr_node(entry, root); } return err; } static int lst_check_deny(struct bsc_msg_acc_lst *lst, const char *mi_string, int *cm_cause, int *lu_cause) { struct bsc_msg_acc_lst_entry *entry; llist_for_each_entry(entry, &lst->fltr_list, list) { if (!entry->imsi_deny) continue; if (regexec(&entry->imsi_deny_re, mi_string, 0, NULL, 0) == 0) { *cm_cause = entry->cm_reject_cause; *lu_cause = entry->lu_reject_cause; return 0; } } return 1; } /* apply white/black list */ static int auth_imsi(struct bsc_filter_request *req, const char *imsi, struct bsc_filter_reject_cause *cause) { /* * Now apply blacklist/whitelist of the BSC and the NAT. * 1.) Check the global IMSI barr list * 2.) Allow directly if the IMSI is allowed at the BSC * 3.) Reject if the IMSI is not allowed at the BSC * 4.) Reject if the IMSI not allowed at the global level. * 5.) Allow directly if the IMSI is allowed at the global level */ int cm, lu; struct bsc_msg_acc_lst *nat_lst = NULL; struct bsc_msg_acc_lst *bsc_lst = NULL; /* 1. global check for barred imsis */ if (req->black_list && bsc_filter_barr_find(req->black_list, imsi, &cm, &lu)) { cause->cm_reject_cause = cm; cause->lu_reject_cause = lu; LOGP(DFILTER, LOGL_DEBUG, "Blocking subscriber IMSI %s with CM: %d LU: %d\n", imsi, cm, lu); return -4; } bsc_lst = bsc_msg_acc_lst_find(req->access_lists, req->local_lst_name); nat_lst = bsc_msg_acc_lst_find(req->access_lists, req->global_lst_name); if (bsc_lst) { /* 2. BSC allow */ if (bsc_msg_acc_lst_check_allow(bsc_lst, imsi) == 0) return 1; /* 3. BSC deny */ if (lst_check_deny(bsc_lst, imsi, &cm, &lu) == 0) { LOGP(DFILTER, LOGL_ERROR, "Filtering %s by imsi_deny on config nr: %d.\n", imsi, req->bsc_nr); rate_ctr_inc(&bsc_lst->stats->ctr[ACC_LIST_LOCAL_FILTER]); cause->cm_reject_cause = cm; cause->lu_reject_cause = lu; return -2; } } /* 4. NAT deny */ if (nat_lst) { if (lst_check_deny(nat_lst, imsi, &cm, &lu) == 0) { LOGP(DFILTER, LOGL_ERROR, "Filtering %s global imsi_deny on bsc nr: %d.\n", imsi, req->bsc_nr); rate_ctr_inc(&nat_lst->stats->ctr[ACC_LIST_GLOBAL_FILTER]); cause->cm_reject_cause = cm; cause->lu_reject_cause = lu; return -3; } } return 1; } static int _cr_check_loc_upd(void *ctx, uint8_t *data, unsigned int length, char **imsi) { uint8_t mi_type; struct gsm48_loc_upd_req *lu; char mi_string[GSM48_MI_SIZE]; if (length < sizeof(*lu)) { LOGP(DFILTER, LOGL_ERROR, "LU does not fit. Length is %d \n", length); return -1; } lu = (struct gsm48_loc_upd_req *) data; mi_type = lu->mi[0] & GSM_MI_TYPE_MASK; /* * We can only deal with the IMSI. This will fail for a phone that * will send the TMSI of a previous network to us. */ if (mi_type != GSM_MI_TYPE_IMSI) return 0; gsm48_mi_to_string(mi_string, sizeof(mi_string), lu->mi, lu->mi_len); *imsi = talloc_strdup(ctx, mi_string); return 1; } static int _cr_check_cm_serv_req(void *ctx, uint8_t *data, unsigned int length, int *con_type, char **imsi) { static const uint32_t classmark_offset = offsetof(struct gsm48_service_request, classmark); char mi_string[GSM48_MI_SIZE]; uint8_t mi_type; int rc; struct gsm48_service_request *req; /* unfortunately in Phase1 the classmark2 length is variable */ if (length < sizeof(*req)) { LOGP(DFILTER, LOGL_ERROR, "CM Serv Req does not fit. Length is %d\n", length); return -1; } req = (struct gsm48_service_request *) data; if (req->cm_service_type == 0x8) *con_type = FLT_CON_TYPE_SSA; rc = gsm48_extract_mi((uint8_t *) &req->classmark, length - classmark_offset, mi_string, &mi_type); if (rc < 0) { LOGP(DFILTER, LOGL_ERROR, "Failed to parse the classmark2/mi. error: %d\n", rc); return -1; } /* we have to let the TMSI or such pass */ if (mi_type != GSM_MI_TYPE_IMSI) return 0; *imsi = talloc_strdup(ctx, mi_string); return 1; } static int _cr_check_pag_resp(void *ctx, uint8_t *data, unsigned int length, char **imsi) { struct gsm48_pag_resp *resp; char mi_string[GSM48_MI_SIZE]; uint8_t mi_type; if (length < sizeof(*resp)) { LOGP(DFILTER, LOGL_ERROR, "PAG RESP does not fit. Length was %d.\n", length); return -1; } resp = (struct gsm48_pag_resp *) data; if (gsm48_paging_extract_mi(resp, length, mi_string, &mi_type) < 0) { LOGP(DFILTER, LOGL_ERROR, "Failed to extract the MI.\n"); return -1; } /* we need to let it pass for now */ if (mi_type != GSM_MI_TYPE_IMSI) return 0; *imsi = talloc_strdup(ctx, mi_string); return 1; } static int _dt_check_id_resp(struct bsc_filter_request *req, uint8_t *data, unsigned int length, struct bsc_filter_state *state, struct bsc_filter_reject_cause *cause) { char mi_string[GSM48_MI_SIZE]; uint8_t mi_type; if (length < 2) { LOGP(DFILTER, LOGL_ERROR, "mi does not fit.\n"); return -1; } if (data[0] < length - 1) { LOGP(DFILTER, LOGL_ERROR, "mi length too big.\n"); return -2; } mi_type = data[1] & GSM_MI_TYPE_MASK; gsm48_mi_to_string(mi_string, sizeof(mi_string), &data[1], data[0]); if (mi_type != GSM_MI_TYPE_IMSI) return 0; state->imsi_checked = 1; state->imsi = talloc_strdup(req->ctx, mi_string); return auth_imsi(req, mi_string, cause); } /* Filter out CR data... */ int bsc_msg_filter_initial(struct gsm48_hdr *hdr48, size_t hdr48_len, struct bsc_filter_request *req, int *con_type, char **imsi, struct bsc_filter_reject_cause *cause) { int ret = 0; uint8_t msg_type, proto; *con_type = FLT_CON_TYPE_NONE; cause->cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED; cause->lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED; *imsi = NULL; proto = hdr48->proto_discr & 0x0f; msg_type = hdr48->msg_type & 0xbf; if (proto == GSM48_PDISC_MM && msg_type == GSM48_MT_MM_LOC_UPD_REQUEST) { *con_type = FLT_CON_TYPE_LU; ret = _cr_check_loc_upd(req->ctx, &hdr48->data[0], hdr48_len - sizeof(*hdr48), imsi); } else if (proto == GSM48_PDISC_MM && msg_type == GSM48_MT_MM_CM_SERV_REQ) { *con_type = FLT_CON_TYPE_CM_SERV_REQ; ret = _cr_check_cm_serv_req(req->ctx, &hdr48->data[0], hdr48_len - sizeof(*hdr48), con_type, imsi); } else if (proto == GSM48_PDISC_RR && msg_type == GSM48_MT_RR_PAG_RESP) { *con_type = FLT_CON_TYPE_PAG_RESP; ret = _cr_check_pag_resp(req->ctx, &hdr48->data[0], hdr48_len - sizeof(*hdr48), imsi); } else { /* We only want to filter the above, let other things pass */ *con_type = FLT_CON_TYPE_OTHER; return 0; } /* check if we are done */ if (ret != 1) return ret; /* the memory allocation failed */ if (!*imsi) return -1; /* now check the imsi */ return auth_imsi(req, *imsi, cause); } int bsc_msg_filter_data(struct gsm48_hdr *hdr48, size_t len, struct bsc_filter_request *req, struct bsc_filter_state *state, struct bsc_filter_reject_cause *cause) { uint8_t msg_type, proto; cause->cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED; cause->lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED; if (state->imsi_checked) return 0; proto = hdr48->proto_discr & 0x0f; msg_type = hdr48->msg_type & 0xbf; if (proto != GSM48_PDISC_MM || msg_type != GSM48_MT_MM_ID_RESP) return 0; return _dt_check_id_resp(req, &hdr48->data[0], len - sizeof(*hdr48), state, cause); } openbsc-0.15.0/openbsc/src/libfilter/bsc_msg_vty.c000066400000000000000000000074241265565154000221250ustar00rootroot00000000000000/* (C) 2010-2015 by Holger Hans Peter Freyther * (C) 2010-2013 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 . * */ #include #include #include #include static struct llist_head *_acc_lst; static void *_ctx; DEFUN(cfg_lst_no, cfg_lst_no_cmd, "no access-list NAME", NO_STR "Remove an access-list by name\n" "The access-list to remove\n") { struct bsc_msg_acc_lst *acc; acc = bsc_msg_acc_lst_find(_acc_lst, argv[0]); if (!acc) return CMD_WARNING; bsc_msg_acc_lst_delete(acc); return CMD_SUCCESS; } DEFUN(show_acc_lst, show_acc_lst_cmd, "show access-list NAME", SHOW_STR "IMSI access list\n" "Name of the access list\n") { struct bsc_msg_acc_lst *acc; acc = bsc_msg_acc_lst_find(_acc_lst, argv[0]); if (!acc) return CMD_WARNING; vty_out(vty, "access-list %s%s", acc->name, VTY_NEWLINE); vty_out_rate_ctr_group(vty, " ", acc->stats); return CMD_SUCCESS; } DEFUN(cfg_lst_imsi_allow, cfg_lst_imsi_allow_cmd, "access-list NAME imsi-allow [REGEXP]", "Access list commands\n" "Name of the access list\n" "Add allowed IMSI to the list\n" "Regexp for IMSIs\n") { struct bsc_msg_acc_lst *acc; struct bsc_msg_acc_lst_entry *entry; acc = bsc_msg_acc_lst_get(_ctx, _acc_lst, argv[0]); if (!acc) return CMD_WARNING; entry = bsc_msg_acc_lst_entry_create(acc); if (!entry) return CMD_WARNING; if (gsm_parse_reg(acc, &entry->imsi_allow_re, &entry->imsi_allow, argc - 1, &argv[1]) != 0) return CMD_WARNING; return CMD_SUCCESS; } DEFUN(cfg_lst_imsi_deny, cfg_lst_imsi_deny_cmd, "access-list NAME imsi-deny [REGEXP] (<0-256>) (<0-256>)", "Access list commands\n" "Name of the access list\n" "Add denied IMSI to the list\n" "Regexp for IMSIs\n" "CM Service Reject reason\n" "LU Reject reason\n") { struct bsc_msg_acc_lst *acc; struct bsc_msg_acc_lst_entry *entry; acc = bsc_msg_acc_lst_get(_ctx, _acc_lst, argv[0]); if (!acc) return CMD_WARNING; entry = bsc_msg_acc_lst_entry_create(acc); if (!entry) return CMD_WARNING; if (gsm_parse_reg(acc, &entry->imsi_deny_re, &entry->imsi_deny, argc - 1, &argv[1]) != 0) return CMD_WARNING; if (argc >= 3) entry->cm_reject_cause = atoi(argv[2]); if (argc >= 4) entry->lu_reject_cause = atoi(argv[3]); return CMD_SUCCESS; } void bsc_msg_acc_lst_write(struct vty *vty, struct bsc_msg_acc_lst *lst) { struct bsc_msg_acc_lst_entry *entry; llist_for_each_entry(entry, &lst->fltr_list, list) { if (entry->imsi_allow) vty_out(vty, " access-list %s imsi-allow %s%s", lst->name, entry->imsi_allow, VTY_NEWLINE); if (entry->imsi_deny) vty_out(vty, " access-list %s imsi-deny %s %d %d%s", lst->name, entry->imsi_deny, entry->cm_reject_cause, entry->lu_reject_cause, VTY_NEWLINE); } } void bsc_msg_lst_vty_init(void *ctx, struct llist_head *lst, int node) { _ctx = ctx; _acc_lst = lst; install_element_ve(&show_acc_lst_cmd); /* access-list */ install_element(node, &cfg_lst_imsi_allow_cmd); install_element(node, &cfg_lst_imsi_deny_cmd); install_element(node, &cfg_lst_no_cmd); } openbsc-0.15.0/openbsc/src/libgb/000077500000000000000000000000001265565154000165365ustar00rootroot00000000000000openbsc-0.15.0/openbsc/src/libgb/Makefile.am000066400000000000000000000005651265565154000206000ustar00rootroot00000000000000AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) AM_CFLAGS=-Wall -fno-strict-aliasing $(LIBOSMOCORE_CFLAGS) \ $(LIBOSMOVTY_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) noinst_LIBRARIES = libgb.a libgb_a_SOURCES = gprs_ns.c gprs_ns_frgre.c gprs_ns_vty.c \ gprs_bssgp.c gprs_bssgp_util.c gprs_bssgp_vty.c \ gprs_bssgp_bss.c common_vty.c openbsc-0.15.0/openbsc/src/libmgcp/000077500000000000000000000000001265565154000170745ustar00rootroot00000000000000openbsc-0.15.0/openbsc/src/libmgcp/Makefile.am000066400000000000000000000010431265565154000211260ustar00rootroot00000000000000AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) \ $(LIBOSMONETIF_CFLAGS) $(COVERAGE_CFLAGS) $(LIBBCG729_CFLAGS) AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) \ $(LIBOSMONETIF_LIBS) $(COVERAGE_LDFLAGS) $(LIBBCG729_LIBS) noinst_LIBRARIES = libmgcp.a noinst_HEADERS = g711common.h libmgcp_a_SOURCES = mgcp_protocol.c mgcp_network.c mgcp_vty.c mgcp_osmux.c \ mgcp_sdp.c if BUILD_MGCP_TRANSCODING libmgcp_a_SOURCES += mgcp_transcode.c endif openbsc-0.15.0/openbsc/src/libmgcp/g711common.h000066400000000000000000000116421265565154000211410ustar00rootroot00000000000000/* * PCM - A-Law conversion * Copyright (c) 2000 by Abramo Bagnara * * Wrapper for linphone Codec class by Simon Morlat * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ static inline int val_seg(int val) { int r = 0; val >>= 7; /*7 = 4 + 3*/ if (val & 0xf0) { val >>= 4; r += 4; } if (val & 0x0c) { val >>= 2; r += 2; } if (val & 0x02) r += 1; return r; } /* * s16_to_alaw() - Convert a 16-bit linear PCM value to 8-bit A-law * * s16_to_alaw() accepts an 16-bit integer and encodes it as A-law data. * * Linear Input Code Compressed Code * ------------------------ --------------- * 0000000wxyza 000wxyz * 0000001wxyza 001wxyz * 000001wxyzab 010wxyz * 00001wxyzabc 011wxyz * 0001wxyzabcd 100wxyz * 001wxyzabcde 101wxyz * 01wxyzabcdef 110wxyz * 1wxyzabcdefg 111wxyz * * For further information see John C. Bellamy's Digital Telephony, 1982, * John Wiley & Sons, pps 98-111 and 472-476. * G711 is designed for 13 bits input signal, this function add extra shifting to take this into account. */ static inline unsigned char s16_to_alaw(int pcm_val) { int mask; int seg; unsigned char aval; if (pcm_val >= 0) { mask = 0xD5; } else { mask = 0x55; pcm_val = -pcm_val; if (pcm_val > 0x7fff) pcm_val = 0x7fff; } if (pcm_val < 256) /*256 = 32 << 3*/ aval = pcm_val >> 4; /*4 = 1 + 3*/ else { /* Convert the scaled magnitude to segment number. */ seg = val_seg(pcm_val); aval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0x0f); } return aval ^ mask; } /* * alaw_to_s16() - Convert an A-law value to 16-bit linear PCM * */ static inline int alaw_to_s16(unsigned char a_val) { int t; int seg; a_val ^= 0x55; t = a_val & 0x7f; if (t < 16) t = (t << 4) + 8; else { seg = (t >> 4) & 0x07; t = ((t & 0x0f) << 4) + 0x108; t <<= seg -1; } return ((a_val & 0x80) ? t : -t); } /* * s16_to_ulaw() - Convert a linear PCM value to u-law * * In order to simplify the encoding process, the original linear magnitude * is biased by adding 33 which shifts the encoding range from (0 - 8158) to * (33 - 8191). The result can be seen in the following encoding table: * * Biased Linear Input Code Compressed Code * ------------------------ --------------- * 00000001wxyza 000wxyz * 0000001wxyzab 001wxyz * 000001wxyzabc 010wxyz * 00001wxyzabcd 011wxyz * 0001wxyzabcde 100wxyz * 001wxyzabcdef 101wxyz * 01wxyzabcdefg 110wxyz * 1wxyzabcdefgh 111wxyz * * Each biased linear code has a leading 1 which identifies the segment * number. The value of the segment number is equal to 7 minus the number * of leading 0's. The quantization interval is directly available as the * four bits wxyz. * The trailing bits (a - h) are ignored. * * Ordinarily the complement of the resulting code word is used for * transmission, and so the code word is complemented before it is returned. * * For further information see John C. Bellamy's Digital Telephony, 1982, * John Wiley & Sons, pps 98-111 and 472-476. */ static inline unsigned char s16_to_ulaw(int pcm_val) /* 2's complement (16-bit range) */ { int mask; int seg; unsigned char uval; if (pcm_val < 0) { pcm_val = 0x84 - pcm_val; mask = 0x7f; } else { pcm_val += 0x84; mask = 0xff; } if (pcm_val > 0x7fff) pcm_val = 0x7fff; /* Convert the scaled magnitude to segment number. */ seg = val_seg(pcm_val); /* * Combine the sign, segment, quantization bits; * and complement the code word. */ uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0x0f); return uval ^ mask; } /* * ulaw_to_s16() - Convert a u-law value to 16-bit linear PCM * * First, a biased linear code is derived from the code word. An unbiased * output can then be obtained by subtracting 33 from the biased code. * * Note that this function expects to be passed the complement of the * original code word. This is in keeping with ISDN conventions. */ static inline int ulaw_to_s16(unsigned char u_val) { int t; /* Complement to obtain normal u-law value. */ u_val = ~u_val; /* * Extract and bias the quantization bits. Then * shift up by the segment number and subtract out the bias. */ t = ((u_val & 0x0f) << 3) + 0x84; t <<= (u_val & 0x70) >> 4; return ((u_val & 0x80) ? (0x84 - t) : (t - 0x84)); } openbsc-0.15.0/openbsc/src/libmgcp/mgcp_network.c000066400000000000000000000716001265565154000217430ustar00rootroot00000000000000/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */ /* The protocol implementation */ /* * (C) 2009-2012 by Holger Hans Peter Freyther * (C) 2009-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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #warning "Make use of the rtp proxy code" #define RTP_SEQ_MOD (1 << 16) #define RTP_MAX_DROPOUT 3000 #define RTP_MAX_MISORDER 100 #define RTP_BUF_SIZE 4096 enum { MGCP_PROTO_RTP, MGCP_PROTO_RTCP, }; /** * This does not need to be a precision timestamp and * is allowed to wrap quite fast. The returned value is * 1/unit seconds. */ static uint32_t get_current_ts(unsigned unit) { struct timespec tp; uint64_t ret; if (!unit) return 0; memset(&tp, 0, sizeof(tp)); if (clock_gettime(CLOCK_MONOTONIC, &tp) != 0) LOGP(DMGCP, LOGL_NOTICE, "Getting the clock failed.\n"); /* convert it to 1/unit seconds */ ret = tp.tv_sec; ret *= unit; ret += (int64_t)tp.tv_nsec * unit / 1000 / 1000 / 1000; return ret; } int mgcp_udp_send(int fd, struct in_addr *addr, int port, char *buf, int len) { struct sockaddr_in out; out.sin_family = AF_INET; out.sin_port = port; memcpy(&out.sin_addr, addr, sizeof(*addr)); return sendto(fd, buf, len, 0, (struct sockaddr *)&out, sizeof(out)); } int mgcp_send_dummy(struct mgcp_endpoint *endp) { static char buf[] = { MGCP_DUMMY_LOAD }; int rc; int was_rtcp = 0; rc = mgcp_udp_send(endp->net_end.rtp.fd, &endp->net_end.addr, endp->net_end.rtp_port, buf, 1); if (rc == -1) goto failed; if (endp->tcfg->omit_rtcp) return rc; was_rtcp = 1; rc = mgcp_udp_send(endp->net_end.rtcp.fd, &endp->net_end.addr, endp->net_end.rtcp_port, buf, 1); if (rc >= 0) return rc; failed: LOGP(DMGCP, LOGL_ERROR, "Failed to send dummy %s packet: %s on: 0x%x to %s:%d\n", was_rtcp ? "RTCP" : "RTP", strerror(errno), ENDPOINT_NUMBER(endp), inet_ntoa(endp->net_end.addr), was_rtcp ? endp->net_end.rtcp_port : endp->net_end.rtp_port); return -1; } static int32_t compute_timestamp_aligment_error(struct mgcp_rtp_stream_state *sstate, int ptime, uint32_t timestamp) { int32_t timestamp_delta; if (ptime == 0) return 0; /* Align according to: T - Tlast = k * Tptime */ timestamp_delta = timestamp - sstate->last_timestamp; return timestamp_delta % ptime; } static int check_rtp_timestamp(struct mgcp_endpoint *endp, struct mgcp_rtp_state *state, struct mgcp_rtp_stream_state *sstate, struct mgcp_rtp_end *rtp_end, struct sockaddr_in *addr, uint16_t seq, uint32_t timestamp, const char *text, int32_t *tsdelta_out) { int32_t tsdelta; int32_t timestamp_error; /* Not fully intialized, skip */ if (sstate->last_tsdelta == 0 && timestamp == sstate->last_timestamp) return 0; if (seq == sstate->last_seq) { if (timestamp != sstate->last_timestamp) { sstate->err_ts_counter += 1; LOGP(DMGCP, LOGL_ERROR, "The %s timestamp delta is != 0 but the sequence " "number %d is the same, " "TS offset: %d, SeqNo offset: %d " "on 0x%x SSRC: %u timestamp: %u " "from %s:%d in %d\n", text, seq, state->timestamp_offset, state->seq_offset, ENDPOINT_NUMBER(endp), sstate->ssrc, timestamp, inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), endp->conn_mode); } return 0; } tsdelta = (int32_t)(timestamp - sstate->last_timestamp) / (int16_t)(seq - sstate->last_seq); if (tsdelta == 0) { /* Don't update *tsdelta_out */ LOGP(DMGCP, LOGL_NOTICE, "The %s timestamp delta is %d " "on 0x%x SSRC: %u timestamp: %u " "from %s:%d in %d\n", text, tsdelta, ENDPOINT_NUMBER(endp), sstate->ssrc, timestamp, inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), endp->conn_mode); return 0; } if (sstate->last_tsdelta != tsdelta) { if (sstate->last_tsdelta) { LOGP(DMGCP, LOGL_INFO, "The %s timestamp delta changes from %d to %d " "on 0x%x SSRC: %u timestamp: %u from %s:%d in %d\n", text, sstate->last_tsdelta, tsdelta, ENDPOINT_NUMBER(endp), sstate->ssrc, timestamp, inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), endp->conn_mode); } } if (tsdelta_out) *tsdelta_out = tsdelta; timestamp_error = compute_timestamp_aligment_error(sstate, state->packet_duration, timestamp); if (timestamp_error) { sstate->err_ts_counter += 1; LOGP(DMGCP, LOGL_NOTICE, "The %s timestamp has an alignment error of %d " "on 0x%x SSRC: %u " "SeqNo delta: %d, TS delta: %d, dTS/dSeq: %d " "from %s:%d in mode %d. ptime: %d\n", text, timestamp_error, ENDPOINT_NUMBER(endp), sstate->ssrc, (int16_t)(seq - sstate->last_seq), (int32_t)(timestamp - sstate->last_timestamp), tsdelta, inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), endp->conn_mode, state->packet_duration); } return 1; } /* Set the timestamp offset according to the packet duration. */ static int adjust_rtp_timestamp_offset(struct mgcp_endpoint *endp, struct mgcp_rtp_state *state, struct mgcp_rtp_end *rtp_end, struct sockaddr_in *addr, int16_t delta_seq, uint32_t in_timestamp) { int32_t tsdelta = state->packet_duration; int timestamp_offset; uint32_t out_timestamp; if (tsdelta == 0) { tsdelta = state->out_stream.last_tsdelta; if (tsdelta != 0) { LOGP(DMGCP, LOGL_NOTICE, "A fixed packet duration is not available on 0x%x, " "using last output timestamp delta instead: %d " "from %s:%d in %d\n", ENDPOINT_NUMBER(endp), tsdelta, inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), endp->conn_mode); } else { tsdelta = rtp_end->codec.rate * 20 / 1000; LOGP(DMGCP, LOGL_NOTICE, "Fixed packet duration and last timestamp delta " "are not available on 0x%x, " "using fixed 20ms instead: %d " "from %s:%d in %d\n", ENDPOINT_NUMBER(endp), tsdelta, inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), endp->conn_mode); } } out_timestamp = state->out_stream.last_timestamp + delta_seq * tsdelta; timestamp_offset = out_timestamp - in_timestamp; if (state->timestamp_offset != timestamp_offset) { state->timestamp_offset = timestamp_offset; LOGP(DMGCP, LOGL_NOTICE, "Timestamp offset change on 0x%x SSRC: %u " "SeqNo delta: %d, TS offset: %d, " "from %s:%d in %d\n", ENDPOINT_NUMBER(endp), state->in_stream.ssrc, delta_seq, state->timestamp_offset, inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), endp->conn_mode); } return timestamp_offset; } /* Set the timestamp offset according to the packet duration. */ static int align_rtp_timestamp_offset(struct mgcp_endpoint *endp, struct mgcp_rtp_state *state, struct mgcp_rtp_end *rtp_end, struct sockaddr_in *addr, uint32_t timestamp) { int timestamp_error = 0; int ptime = state->packet_duration; /* Align according to: T + Toffs - Tlast = k * Tptime */ timestamp_error = compute_timestamp_aligment_error( &state->out_stream, ptime, timestamp + state->timestamp_offset); if (timestamp_error) { state->timestamp_offset += ptime - timestamp_error; LOGP(DMGCP, LOGL_NOTICE, "Corrected timestamp alignment error of %d on 0x%x SSRC: %u " "new TS offset: %d, " "from %s:%d in %d\n", timestamp_error, ENDPOINT_NUMBER(endp), state->in_stream.ssrc, state->timestamp_offset, inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), endp->conn_mode); } OSMO_ASSERT(compute_timestamp_aligment_error(&state->out_stream, ptime, timestamp + state->timestamp_offset) == 0); return timestamp_error; } int mgcp_rtp_processing_default(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end, char *data, int *len, int buf_size) { return 0; } int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end, struct mgcp_rtp_end *src_end) { return 0; } void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp, int *payload_type, const char**audio_name, const char**fmtp_extra) { /* Use the BTS side parameters when passing the SDP data (for * downlink) to the net peer. */ *payload_type = endp->bts_end.codec.payload_type; *audio_name = endp->bts_end.codec.audio_name; *fmtp_extra = endp->bts_end.fmtp_extra; } void mgcp_rtp_annex_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *state, const uint16_t seq, const int32_t transit, const uint32_t ssrc) { int32_t d; /* initialize or re-initialize */ if (!state->stats_initialized || state->stats_ssrc != ssrc) { state->stats_initialized = 1; state->stats_base_seq = seq; state->stats_max_seq = seq - 1; state->stats_ssrc = ssrc; state->stats_jitter = 0; state->stats_transit = transit; state->stats_cycles = 0; } else { uint16_t udelta; /* * The below takes the shape of the validation of * Appendix A. Check if there is something weird with * the sequence number, otherwise check for a wrap * around in the sequence number. * It can't wrap during the initialization so let's * skip it here. The Appendix A probably doesn't have * this issue because of the probation. */ udelta = seq - state->stats_max_seq; if (udelta < RTP_MAX_DROPOUT) { if (seq < state->stats_max_seq) state->stats_cycles += RTP_SEQ_MOD; } else if (udelta <= RTP_SEQ_MOD - RTP_MAX_MISORDER) { LOGP(DMGCP, LOGL_NOTICE, "RTP seqno made a very large jump on 0x%x delta: %u\n", ENDPOINT_NUMBER(endp), udelta); } } /* * Calculate the jitter between the two packages. The TS should be * taken closer to the read function. This was taken from the * Appendix A of RFC 3550. Timestamp and arrival_time have a 1/rate * resolution. */ d = transit - state->stats_transit; state->stats_transit = transit; if (d < 0) d = -d; state->stats_jitter += d - ((state->stats_jitter + 8) >> 4); state->stats_max_seq = seq; } /** * The RFC 3550 Appendix A assumes there are multiple sources but * some of the supported endpoints (e.g. the nanoBTS) can only handle * one source and this code will patch RTP header to appear as if there * is only one source. * There is also no probation period for new sources. Every RTP header * we receive will be seen as a switch in streams. */ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *state, struct mgcp_rtp_end *rtp_end, struct sockaddr_in *addr, char *data, int len) { uint32_t arrival_time; int32_t transit; uint16_t seq; uint32_t timestamp, ssrc; struct rtp_hdr *rtp_hdr; int payload = rtp_end->codec.payload_type; if (len < sizeof(*rtp_hdr)) return; rtp_hdr = (struct rtp_hdr *) data; seq = ntohs(rtp_hdr->sequence); timestamp = ntohl(rtp_hdr->timestamp); arrival_time = get_current_ts(rtp_end->codec.rate); ssrc = ntohl(rtp_hdr->ssrc); transit = arrival_time - timestamp; mgcp_rtp_annex_count(endp, state, seq, transit, ssrc); if (!state->initialized) { state->initialized = 1; state->in_stream.last_seq = seq - 1; state->in_stream.ssrc = state->orig_ssrc = ssrc; state->in_stream.last_tsdelta = 0; state->packet_duration = mgcp_rtp_packet_duration(endp, rtp_end); state->out_stream = state->in_stream; state->out_stream.last_timestamp = timestamp; state->out_stream.ssrc = ssrc - 1; /* force output SSRC change */ LOGP(DMGCP, LOGL_INFO, "Initializing stream on 0x%x SSRC: %u timestamp: %u " "pkt-duration: %d, from %s:%d in %d\n", ENDPOINT_NUMBER(endp), state->in_stream.ssrc, state->seq_offset, state->packet_duration, inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), endp->conn_mode); if (state->packet_duration == 0) { state->packet_duration = rtp_end->codec.rate * 20 / 1000; LOGP(DMGCP, LOGL_NOTICE, "Fixed packet duration is not available on 0x%x, " "using fixed 20ms instead: %d from %s:%d in %d\n", ENDPOINT_NUMBER(endp), state->packet_duration, inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), endp->conn_mode); } } else if (state->in_stream.ssrc != ssrc) { LOGP(DMGCP, LOGL_NOTICE, "The SSRC changed on 0x%x: %u -> %u " "from %s:%d in %d\n", ENDPOINT_NUMBER(endp), state->in_stream.ssrc, rtp_hdr->ssrc, inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), endp->conn_mode); state->in_stream.ssrc = ssrc; if (rtp_end->force_constant_ssrc) { int16_t delta_seq; /* Always increment seqno by 1 */ state->seq_offset = (state->out_stream.last_seq + 1) - seq; /* Estimate number of packets that would have been sent */ delta_seq = (arrival_time - state->in_stream.last_arrival_time + state->packet_duration/2) / state->packet_duration; adjust_rtp_timestamp_offset(endp, state, rtp_end, addr, delta_seq, timestamp); state->patch_ssrc = 1; ssrc = state->orig_ssrc; if (rtp_end->force_constant_ssrc != -1) rtp_end->force_constant_ssrc -= 1; LOGP(DMGCP, LOGL_NOTICE, "SSRC patching enabled on 0x%x SSRC: %u " "SeqNo offset: %d, TS offset: %d " "from %s:%d in %d\n", ENDPOINT_NUMBER(endp), state->in_stream.ssrc, state->seq_offset, state->timestamp_offset, inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), endp->conn_mode); } state->in_stream.last_tsdelta = 0; } else { /* Compute current per-packet timestamp delta */ check_rtp_timestamp(endp, state, &state->in_stream, rtp_end, addr, seq, timestamp, "input", &state->in_stream.last_tsdelta); if (state->patch_ssrc) ssrc = state->orig_ssrc; } /* Save before patching */ state->in_stream.last_timestamp = timestamp; state->in_stream.last_seq = seq; state->in_stream.last_arrival_time = arrival_time; if (rtp_end->force_aligned_timing && state->out_stream.ssrc == ssrc && state->packet_duration) /* Align the timestamp offset */ align_rtp_timestamp_offset(endp, state, rtp_end, addr, timestamp); /* Store the updated SSRC back to the packet */ if (state->patch_ssrc) rtp_hdr->ssrc = htonl(ssrc); /* Apply the offset and store it back to the packet. * This won't change anything if the offset is 0, so the conditional is * omitted. */ seq += state->seq_offset; rtp_hdr->sequence = htons(seq); timestamp += state->timestamp_offset; rtp_hdr->timestamp = htonl(timestamp); /* Check again, whether the timestamps are still valid */ if (state->out_stream.ssrc == ssrc) check_rtp_timestamp(endp, state, &state->out_stream, rtp_end, addr, seq, timestamp, "output", &state->out_stream.last_tsdelta); /* Save output values */ state->out_stream.last_seq = seq; state->out_stream.last_timestamp = timestamp; state->out_stream.ssrc = ssrc; if (payload < 0) return; rtp_hdr->payload_type = payload; } /* * The below code is for dispatching. We have a dedicated port for * the data coming from the net and one to discover the BTS. */ static int forward_data(int fd, struct mgcp_rtp_tap *tap, const char *buf, int len) { if (!tap->enabled) return 0; return sendto(fd, buf, len, 0, (struct sockaddr *)&tap->forward, sizeof(tap->forward)); } static int mgcp_send_transcoder(struct mgcp_rtp_end *end, struct mgcp_config *cfg, int is_rtp, const char *buf, int len) { int rc; int port; struct sockaddr_in addr; port = is_rtp ? end->rtp_port : end->rtcp_port; addr.sin_family = AF_INET; addr.sin_addr = cfg->transcoder_in; addr.sin_port = port; rc = sendto(is_rtp ? end->rtp.fd : end->rtcp.fd, buf, len, 0, (struct sockaddr *) &addr, sizeof(addr)); if (rc != len) LOGP(DMGCP, LOGL_ERROR, "Failed to send data to the transcoder: %s\n", strerror(errno)); return rc; } int mgcp_send(struct mgcp_endpoint *endp, int dest, int is_rtp, struct sockaddr_in *addr, char *buf, int rc) { struct mgcp_trunk_config *tcfg = endp->tcfg; struct mgcp_rtp_end *rtp_end; struct mgcp_rtp_state *rtp_state; int tap_idx; /* For loop toggle the destination and then dispatch. */ if (tcfg->audio_loop) dest = !dest; /* Loop based on the conn_mode, maybe undoing the above */ if (endp->conn_mode == MGCP_CONN_LOOPBACK) dest = !dest; if (dest == MGCP_DEST_NET) { rtp_end = &endp->net_end; rtp_state = &endp->bts_state; tap_idx = MGCP_TAP_NET_OUT; } else { rtp_end = &endp->bts_end; rtp_state = &endp->net_state; tap_idx = MGCP_TAP_BTS_OUT; } if (!rtp_end->output_enabled) rtp_end->dropped_packets += 1; else if (is_rtp) { int cont; int nbytes = 0; int len = rc; do { cont = endp->cfg->rtp_processing_cb(endp, rtp_end, buf, &len, RTP_BUF_SIZE); if (cont < 0) break; mgcp_patch_and_count(endp, rtp_state, rtp_end, addr, buf, len); forward_data(rtp_end->rtp.fd, &endp->taps[tap_idx], buf, len); rc = mgcp_udp_send(rtp_end->rtp.fd, &rtp_end->addr, rtp_end->rtp_port, buf, len); if (rc <= 0) return rc; nbytes += rc; len = cont; } while (len > 0); return nbytes; } else if (!tcfg->omit_rtcp) { return mgcp_udp_send(rtp_end->rtcp.fd, &rtp_end->addr, rtp_end->rtcp_port, buf, rc); } return 0; } static int receive_from(struct mgcp_endpoint *endp, int fd, struct sockaddr_in *addr, char *buf, int bufsize) { int rc; socklen_t slen = sizeof(*addr); rc = recvfrom(fd, buf, bufsize, 0, (struct sockaddr *) addr, &slen); if (rc < 0) { LOGP(DMGCP, LOGL_ERROR, "Failed to receive message on: 0x%x errno: %d/%s\n", ENDPOINT_NUMBER(endp), errno, strerror(errno)); return -1; } /* do not forward aynthing... maybe there is a packet from the bts */ if (!endp->allocated) return -1; #warning "Slight spec violation. With connection mode recvonly we should attempt to forward." return rc; } static int rtp_data_net(struct osmo_fd *fd, unsigned int what) { char buf[RTP_BUF_SIZE]; struct sockaddr_in addr; struct mgcp_endpoint *endp; int rc, proto; endp = (struct mgcp_endpoint *) fd->data; rc = receive_from(endp, fd->fd, &addr, buf, sizeof(buf)); if (rc <= 0) return -1; if (memcmp(&addr.sin_addr, &endp->net_end.addr, sizeof(addr.sin_addr)) != 0) { LOGP(DMGCP, LOGL_ERROR, "Endpoint 0x%x data from wrong address %s vs. ", ENDPOINT_NUMBER(endp), inet_ntoa(addr.sin_addr)); LOGPC(DMGCP, LOGL_ERROR, "%s\n", inet_ntoa(endp->net_end.addr)); return -1; } switch(endp->type) { case MGCP_RTP_DEFAULT: case MGCP_RTP_TRANSCODED: if (endp->net_end.rtp_port != addr.sin_port && endp->net_end.rtcp_port != addr.sin_port) { LOGP(DMGCP, LOGL_ERROR, "Data from wrong source port %d on 0x%x\n", ntohs(addr.sin_port), ENDPOINT_NUMBER(endp)); return -1; } break; case MGCP_OSMUX_BSC: case MGCP_OSMUX_BSC_NAT: break; } /* throw away the dummy message */ if (rc == 1 && buf[0] == MGCP_DUMMY_LOAD) { LOGP(DMGCP, LOGL_NOTICE, "Filtered dummy from network on 0x%x\n", ENDPOINT_NUMBER(endp)); return 0; } proto = fd == &endp->net_end.rtp ? MGCP_PROTO_RTP : MGCP_PROTO_RTCP; endp->net_end.packets += 1; endp->net_end.octets += rc; forward_data(fd->fd, &endp->taps[MGCP_TAP_NET_IN], buf, rc); switch (endp->type) { case MGCP_RTP_DEFAULT: return mgcp_send(endp, MGCP_DEST_BTS, proto == MGCP_PROTO_RTP, &addr, buf, rc); case MGCP_RTP_TRANSCODED: return mgcp_send_transcoder(&endp->trans_net, endp->cfg, proto == MGCP_PROTO_RTP, buf, rc); case MGCP_OSMUX_BSC_NAT: return osmux_xfrm_to_osmux(MGCP_DEST_BTS, buf, rc, endp); case MGCP_OSMUX_BSC: /* Should not happen */ break; } LOGP(DMGCP, LOGL_ERROR, "Bad MGCP type %u on endpoint %u\n", endp->type, ENDPOINT_NUMBER(endp)); return 0; } static void discover_bts(struct mgcp_endpoint *endp, int proto, struct sockaddr_in *addr) { struct mgcp_config *cfg = endp->cfg; if (proto == MGCP_PROTO_RTP && endp->bts_end.rtp_port == 0) { if (!cfg->bts_ip || memcmp(&addr->sin_addr, &cfg->bts_in, sizeof(cfg->bts_in)) == 0 || memcmp(&addr->sin_addr, &endp->bts_end.addr, sizeof(endp->bts_end.addr)) == 0) { endp->bts_end.rtp_port = addr->sin_port; endp->bts_end.addr = addr->sin_addr; LOGP(DMGCP, LOGL_NOTICE, "Found BTS for endpoint: 0x%x on port: %d/%d of %s\n", ENDPOINT_NUMBER(endp), ntohs(endp->bts_end.rtp_port), ntohs(endp->bts_end.rtcp_port), inet_ntoa(addr->sin_addr)); } } else if (proto == MGCP_PROTO_RTCP && endp->bts_end.rtcp_port == 0) { if (memcmp(&endp->bts_end.addr, &addr->sin_addr, sizeof(endp->bts_end.addr)) == 0) { endp->bts_end.rtcp_port = addr->sin_port; } } } static int rtp_data_bts(struct osmo_fd *fd, unsigned int what) { char buf[RTP_BUF_SIZE]; struct sockaddr_in addr; struct mgcp_endpoint *endp; int rc, proto; endp = (struct mgcp_endpoint *) fd->data; rc = receive_from(endp, fd->fd, &addr, buf, sizeof(buf)); if (rc <= 0) return -1; proto = fd == &endp->bts_end.rtp ? MGCP_PROTO_RTP : MGCP_PROTO_RTCP; /* We have no idea who called us, maybe it is the BTS. */ /* it was the BTS... */ discover_bts(endp, proto, &addr); if (memcmp(&endp->bts_end.addr, &addr.sin_addr, sizeof(addr.sin_addr)) != 0) { LOGP(DMGCP, LOGL_ERROR, "Data from wrong bts %s on 0x%x\n", inet_ntoa(addr.sin_addr), ENDPOINT_NUMBER(endp)); return -1; } if (endp->bts_end.rtp_port != addr.sin_port && endp->bts_end.rtcp_port != addr.sin_port) { LOGP(DMGCP, LOGL_ERROR, "Data from wrong bts source port %d on 0x%x\n", ntohs(addr.sin_port), ENDPOINT_NUMBER(endp)); return -1; } /* throw away the dummy message */ if (rc == 1 && buf[0] == MGCP_DUMMY_LOAD) { LOGP(DMGCP, LOGL_NOTICE, "Filtered dummy from bts on 0x%x\n", ENDPOINT_NUMBER(endp)); return 0; } /* do this before the loop handling */ endp->bts_end.packets += 1; endp->bts_end.octets += rc; forward_data(fd->fd, &endp->taps[MGCP_TAP_BTS_IN], buf, rc); switch (endp->type) { case MGCP_RTP_DEFAULT: return mgcp_send(endp, MGCP_DEST_NET, proto == MGCP_PROTO_RTP, &addr, buf, rc); case MGCP_RTP_TRANSCODED: return mgcp_send_transcoder(&endp->trans_bts, endp->cfg, proto == MGCP_PROTO_RTP, buf, rc); case MGCP_OSMUX_BSC: /* OSMUX translation: BTS -> BSC */ return osmux_xfrm_to_osmux(MGCP_DEST_NET, buf, rc, endp); case MGCP_OSMUX_BSC_NAT: break; /* Should not happen */ } LOGP(DMGCP, LOGL_ERROR, "Bad MGCP type %u on endpoint %u\n", endp->type, ENDPOINT_NUMBER(endp)); return 0; } static int rtp_data_transcoder(struct mgcp_rtp_end *end, struct mgcp_endpoint *_endp, int dest, struct osmo_fd *fd) { char buf[RTP_BUF_SIZE]; struct sockaddr_in addr; struct mgcp_config *cfg; int rc, proto; cfg = _endp->cfg; rc = receive_from(_endp, fd->fd, &addr, buf, sizeof(buf)); if (rc <= 0) return -1; proto = fd == &end->rtp ? MGCP_PROTO_RTP : MGCP_PROTO_RTCP; if (memcmp(&addr.sin_addr, &cfg->transcoder_in, sizeof(addr.sin_addr)) != 0) { LOGP(DMGCP, LOGL_ERROR, "Data not coming from transcoder dest: %d %s on 0x%x\n", dest, inet_ntoa(addr.sin_addr), ENDPOINT_NUMBER(_endp)); return -1; } if (end->rtp_port != addr.sin_port && end->rtcp_port != addr.sin_port) { LOGP(DMGCP, LOGL_ERROR, "Data from wrong transcoder dest %d source port %d on 0x%x\n", dest, ntohs(addr.sin_port), ENDPOINT_NUMBER(_endp)); return -1; } /* throw away the dummy message */ if (rc == 1 && buf[0] == MGCP_DUMMY_LOAD) { LOGP(DMGCP, LOGL_NOTICE, "Filtered dummy from transcoder dest %d on 0x%x\n", dest, ENDPOINT_NUMBER(_endp)); return 0; } end->packets += 1; return mgcp_send(_endp, dest, proto == MGCP_PROTO_RTP, &addr, buf, rc); } static int rtp_data_trans_net(struct osmo_fd *fd, unsigned int what) { struct mgcp_endpoint *endp; endp = (struct mgcp_endpoint *) fd->data; return rtp_data_transcoder(&endp->trans_net, endp, MGCP_DEST_NET, fd); } static int rtp_data_trans_bts(struct osmo_fd *fd, unsigned int what) { struct mgcp_endpoint *endp; endp = (struct mgcp_endpoint *) fd->data; return rtp_data_transcoder(&endp->trans_bts, endp, MGCP_DEST_BTS, fd); } int mgcp_create_bind(const char *source_addr, struct osmo_fd *fd, int port) { struct sockaddr_in addr; int on = 1; fd->fd = socket(AF_INET, SOCK_DGRAM, 0); if (fd->fd < 0) { LOGP(DMGCP, LOGL_ERROR, "Failed to create UDP port.\n"); return -1; } setsockopt(fd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(port); inet_aton(source_addr, &addr.sin_addr); if (bind(fd->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { close(fd->fd); fd->fd = -1; return -1; } return 0; } int mgcp_set_ip_tos(int fd, int tos) { int ret; ret = setsockopt(fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)); return ret != 0; } static int bind_rtp(struct mgcp_config *cfg, const char *source_addr, struct mgcp_rtp_end *rtp_end, int endpno) { if (mgcp_create_bind(source_addr, &rtp_end->rtp, rtp_end->local_port) != 0) { LOGP(DMGCP, LOGL_ERROR, "Failed to create RTP port: %s:%d on 0x%x\n", source_addr, rtp_end->local_port, endpno); goto cleanup0; } if (mgcp_create_bind(source_addr, &rtp_end->rtcp, rtp_end->local_port + 1) != 0) { LOGP(DMGCP, LOGL_ERROR, "Failed to create RTCP port: %s:%d on 0x%x\n", source_addr, rtp_end->local_port + 1, endpno); goto cleanup1; } mgcp_set_ip_tos(rtp_end->rtp.fd, cfg->endp_dscp); mgcp_set_ip_tos(rtp_end->rtcp.fd, cfg->endp_dscp); rtp_end->rtp.when = BSC_FD_READ; if (osmo_fd_register(&rtp_end->rtp) != 0) { LOGP(DMGCP, LOGL_ERROR, "Failed to register RTP port %d on 0x%x\n", rtp_end->local_port, endpno); goto cleanup2; } rtp_end->rtcp.when = BSC_FD_READ; if (osmo_fd_register(&rtp_end->rtcp) != 0) { LOGP(DMGCP, LOGL_ERROR, "Failed to register RTCP port %d on 0x%x\n", rtp_end->local_port + 1, endpno); goto cleanup3; } return 0; cleanup3: osmo_fd_unregister(&rtp_end->rtp); cleanup2: close(rtp_end->rtcp.fd); rtp_end->rtcp.fd = -1; cleanup1: close(rtp_end->rtp.fd); rtp_end->rtp.fd = -1; cleanup0: return -1; } static int int_bind(const char *port, struct mgcp_rtp_end *end, int (*cb)(struct osmo_fd *, unsigned), struct mgcp_endpoint *_endp, const char *source_addr, int rtp_port) { if (end->rtp.fd != -1 || end->rtcp.fd != -1) { LOGP(DMGCP, LOGL_ERROR, "Previous %s was still bound on %d\n", port, ENDPOINT_NUMBER(_endp)); mgcp_free_rtp_port(end); } end->local_port = rtp_port; end->rtp.cb = cb; end->rtp.data = _endp; end->rtcp.data = _endp; end->rtcp.cb = cb; return bind_rtp(_endp->cfg, source_addr, end, ENDPOINT_NUMBER(_endp)); } int mgcp_bind_bts_rtp_port(struct mgcp_endpoint *endp, int rtp_port) { return int_bind("bts-port", &endp->bts_end, rtp_data_bts, endp, mgcp_bts_src_addr(endp), rtp_port); } int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port) { return int_bind("net-port", &endp->net_end, rtp_data_net, endp, mgcp_net_src_addr(endp), rtp_port); } int mgcp_bind_trans_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port) { return int_bind("trans-net", &endp->trans_net, rtp_data_trans_net, endp, endp->cfg->source_addr, rtp_port); } int mgcp_bind_trans_bts_rtp_port(struct mgcp_endpoint *endp, int rtp_port) { return int_bind("trans-bts", &endp->trans_bts, rtp_data_trans_bts, endp, endp->cfg->source_addr, rtp_port); } int mgcp_free_rtp_port(struct mgcp_rtp_end *end) { if (end->rtp.fd != -1) { close(end->rtp.fd); end->rtp.fd = -1; osmo_fd_unregister(&end->rtp); } if (end->rtcp.fd != -1) { close(end->rtcp.fd); end->rtcp.fd = -1; osmo_fd_unregister(&end->rtcp); } return 0; } void mgcp_state_calc_loss(struct mgcp_rtp_state *state, struct mgcp_rtp_end *end, uint32_t *expected, int *loss) { *expected = state->stats_cycles + state->stats_max_seq; *expected = *expected - state->stats_base_seq + 1; if (!state->stats_initialized) { *expected = 0; *loss = 0; return; } /* * Make sure the sign is correct and use the biggest * positive/negative number that fits. */ *loss = *expected - end->packets; if (*expected < end->packets) { if (*loss > 0) *loss = INT_MIN; } else { if (*loss < 0) *loss = INT_MAX; } } uint32_t mgcp_state_calc_jitter(struct mgcp_rtp_state *state) { if (!state->stats_initialized) return 0; return state->stats_jitter >> 4; } openbsc-0.15.0/openbsc/src/libmgcp/mgcp_osmux.c000066400000000000000000000351751265565154000214340ustar00rootroot00000000000000/* * (C) 2012-2013 by Pablo Neira Ayuso * (C) 2012-2013 by On Waves ehf * All rights not specifically granted under this license are 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. */ #include /* for printf */ #include /* for memcpy */ #include /* for abs */ #include /* for PRIu64 */ #include #include #include #include #include #include #include #include static struct osmo_fd osmux_fd; static LLIST_HEAD(osmux_handle_list); struct osmux_handle { struct llist_head head; struct osmux_in_handle *in; struct in_addr rem_addr; int rem_port; int refcnt; }; static void *osmux; static void osmux_deliver(struct msgb *batch_msg, void *data) { struct osmux_handle *handle = data; struct sockaddr_in out = { .sin_family = AF_INET, .sin_port = handle->rem_port, }; memcpy(&out.sin_addr, &handle->rem_addr, sizeof(handle->rem_addr)); sendto(osmux_fd.fd, batch_msg->data, batch_msg->len, 0, (struct sockaddr *)&out, sizeof(out)); msgb_free(batch_msg); } static struct osmux_handle * osmux_handle_find_get(struct in_addr *addr, int rem_port) { struct osmux_handle *h; /* Lookup for existing OSMUX handle for this destination address. */ llist_for_each_entry(h, &osmux_handle_list, head) { if (memcmp(&h->rem_addr, addr, sizeof(struct in_addr)) == 0 && h->rem_port == rem_port) { LOGP(DMGCP, LOGL_DEBUG, "using existing OSMUX handle " "for addr=%s:%d\n", inet_ntoa(*addr), ntohs(rem_port)); h->refcnt++; return h; } } return NULL; } static void osmux_handle_put(struct osmux_in_handle *in) { struct osmux_handle *h; /* Lookup for existing OSMUX handle for this destination address. */ llist_for_each_entry(h, &osmux_handle_list, head) { if (h->in == in) { if (--h->refcnt == 0) { LOGP(DMGCP, LOGL_INFO, "Releasing unused osmux handle for %s:%d\n", inet_ntoa(h->rem_addr), ntohs(h->rem_port)); LOGP(DMGCP, LOGL_INFO, "Stats: " "input RTP msgs: %u bytes: %"PRIu64" " "output osmux msgs: %u bytes: %"PRIu64"\n", in->stats.input_rtp_msgs, in->stats.input_rtp_bytes, in->stats.output_osmux_msgs, in->stats.output_osmux_bytes); llist_del(&h->head); osmux_xfrm_input_fini(h->in); talloc_free(h); } return; } } LOGP(DMGCP, LOGL_ERROR, "cannot find Osmux input handle %p\n", in); } static struct osmux_handle * osmux_handle_alloc(struct mgcp_config *cfg, struct in_addr *addr, int rem_port) { struct osmux_handle *h; h = talloc_zero(osmux, struct osmux_handle); if (!h) return NULL; h->rem_addr = *addr; h->rem_port = rem_port; h->refcnt++; h->in = talloc_zero(h, struct osmux_in_handle); if (!h->in) { talloc_free(h); return NULL; } h->in->osmux_seq = 0; /* sequence number to start OSmux message from */ h->in->batch_factor = cfg->osmux_batch; /* If batch size is zero, the library defaults to 1470 bytes. */ h->in->batch_size = cfg->osmux_batch_size; h->in->deliver = osmux_deliver; osmux_xfrm_input_init(h->in); h->in->data = h; llist_add(&h->head, &osmux_handle_list); LOGP(DMGCP, LOGL_DEBUG, "created new OSMUX handle for addr=%s:%d\n", inet_ntoa(*addr), ntohs(rem_port)); return h; } static struct osmux_in_handle * osmux_handle_lookup(struct mgcp_config *cfg, struct in_addr *addr, int rem_port) { struct osmux_handle *h; h = osmux_handle_find_get(addr, rem_port); if (h != NULL) return h->in; h = osmux_handle_alloc(cfg, addr, rem_port); if (h == NULL) return NULL; return h->in; } int osmux_xfrm_to_osmux(int type, char *buf, int rc, struct mgcp_endpoint *endp) { int ret; struct msgb *msg; msg = msgb_alloc(4096, "RTP"); if (!msg) return 0; memcpy(msg->data, buf, rc); msgb_put(msg, rc); while ((ret = osmux_xfrm_input(endp->osmux.in, msg, endp->osmux.cid)) > 0) { /* batch full, build and deliver it */ osmux_xfrm_input_deliver(endp->osmux.in); } return 0; } static struct mgcp_endpoint * endpoint_lookup(struct mgcp_config *cfg, int cid, struct in_addr *from_addr, int type) { struct mgcp_endpoint *tmp = NULL; int i; /* Lookup for the endpoint that corresponds to this port */ for (i=0; itrunk.number_endpoints; i++) { struct in_addr *this; tmp = &cfg->trunk.endpoints[i]; if (!tmp->allocated) continue; switch(type) { case MGCP_DEST_NET: this = &tmp->net_end.addr; break; case MGCP_DEST_BTS: this = &tmp->bts_end.addr; break; default: /* Should not ever happen */ LOGP(DMGCP, LOGL_ERROR, "Bad type %d. Fix your code.\n", type); return NULL; } if (tmp->osmux.cid == cid && this->s_addr == from_addr->s_addr) return tmp; } LOGP(DMGCP, LOGL_ERROR, "Cannot find endpoint with cid=%d\n", cid); return NULL; } static void scheduled_tx_net_cb(struct msgb *msg, void *data) { struct mgcp_endpoint *endp = data; struct sockaddr_in addr = { .sin_addr = endp->net_end.addr, .sin_port = endp->net_end.rtp_port, }; endp->bts_end.octets += msg->len; endp->bts_end.packets++; mgcp_send(endp, MGCP_DEST_NET, 1, &addr, (char *)msg->data, msg->len); msgb_free(msg); } static void scheduled_tx_bts_cb(struct msgb *msg, void *data) { struct mgcp_endpoint *endp = data; struct sockaddr_in addr = { .sin_addr = endp->bts_end.addr, .sin_port = endp->bts_end.rtp_port, }; endp->net_end.octets += msg->len; endp->net_end.packets++; mgcp_send(endp, MGCP_DEST_BTS, 1, &addr, (char *)msg->data, msg->len); msgb_free(msg); } static struct msgb *osmux_recv(struct osmo_fd *ofd, struct sockaddr_in *addr) { struct msgb *msg; socklen_t slen = sizeof(*addr); int ret; msg = msgb_alloc(4096, "OSMUX"); if (!msg) { LOGP(DMGCP, LOGL_ERROR, "cannot allocate message\n"); return NULL; } ret = recvfrom(ofd->fd, msg->data, msg->data_len, 0, (struct sockaddr *)addr, &slen); if (ret <= 0) { msgb_free(msg); LOGP(DMGCP, LOGL_ERROR, "cannot receive message\n"); return NULL; } msgb_put(msg, ret); return msg; } #define osmux_chunk_length(msg, rem) (rem - msg->len); int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what) { struct msgb *msg; struct osmux_hdr *osmuxh; struct llist_head list; struct sockaddr_in addr; struct mgcp_config *cfg = ofd->data; uint32_t rem; msg = osmux_recv(ofd, &addr); if (!msg) return -1; /* not any further processing dummy messages */ if (msg->data[0] == MGCP_DUMMY_LOAD) goto out; rem = msg->len; while((osmuxh = osmux_xfrm_output_pull(msg)) != NULL) { struct mgcp_endpoint *endp; /* Yes, we use MGCP_DEST_NET to locate the origin */ endp = endpoint_lookup(cfg, osmuxh->circuit_id, &addr.sin_addr, MGCP_DEST_NET); if (!endp) { LOGP(DMGCP, LOGL_ERROR, "Cannot find an endpoint for circuit_id=%d\n", osmuxh->circuit_id); goto out; } endp->osmux.stats.octets += osmux_chunk_length(msg, rem); endp->osmux.stats.chunks++; rem = msg->len; osmux_xfrm_output(osmuxh, &endp->osmux.out, &list); osmux_tx_sched(&list, scheduled_tx_bts_cb, endp); } out: msgb_free(msg); return 0; } /* This is called from the bsc-nat */ static int osmux_handle_dummy(struct mgcp_config *cfg, struct sockaddr_in *addr, struct msgb *msg) { struct mgcp_endpoint *endp; uint8_t osmux_cid; if (msg->len < 1 + sizeof(osmux_cid)) { LOGP(DMGCP, LOGL_ERROR, "Discarding truncated Osmux dummy load\n"); goto out; } LOGP(DMGCP, LOGL_DEBUG, "Received Osmux dummy load from %s\n", inet_ntoa(addr->sin_addr)); if (!cfg->osmux) { LOGP(DMGCP, LOGL_ERROR, "bsc wants to use Osmux but bsc-nat did not request it\n"); goto out; } /* extract the osmux CID from the dummy message */ memcpy(&osmux_cid, &msg->data[1], sizeof(osmux_cid)); endp = endpoint_lookup(cfg, osmux_cid, &addr->sin_addr, MGCP_DEST_BTS); if (!endp) { LOGP(DMGCP, LOGL_ERROR, "Cannot find endpoint for Osmux CID %d\n", osmux_cid); goto out; } if (endp->osmux.state == OSMUX_STATE_ENABLED) goto out; if (osmux_enable_endpoint(endp, OSMUX_ROLE_BSC_NAT, &addr->sin_addr, addr->sin_port) < 0 ){ LOGP(DMGCP, LOGL_ERROR, "Could not enable osmux in endpoint %d\n", ENDPOINT_NUMBER(endp)); goto out; } LOGP(DMGCP, LOGL_INFO, "Enabling osmux in endpoint %d for %s:%u\n", ENDPOINT_NUMBER(endp), inet_ntoa(addr->sin_addr), ntohs(addr->sin_port)); out: msgb_free(msg); return 0; } int osmux_read_from_bsc_cb(struct osmo_fd *ofd, unsigned int what) { struct msgb *msg; struct osmux_hdr *osmuxh; struct llist_head list; struct sockaddr_in addr; struct mgcp_config *cfg = ofd->data; uint32_t rem; msg = osmux_recv(ofd, &addr); if (!msg) return -1; /* not any further processing dummy messages */ if (msg->data[0] == MGCP_DUMMY_LOAD) return osmux_handle_dummy(cfg, &addr, msg); rem = msg->len; while((osmuxh = osmux_xfrm_output_pull(msg)) != NULL) { struct mgcp_endpoint *endp; /* Yes, we use MGCP_DEST_BTS to locate the origin */ endp = endpoint_lookup(cfg, osmuxh->circuit_id, &addr.sin_addr, MGCP_DEST_BTS); if (!endp) { LOGP(DMGCP, LOGL_ERROR, "Cannot find an endpoint for circuit_id=%d\n", osmuxh->circuit_id); goto out; } endp->osmux.stats.octets += osmux_chunk_length(msg, rem); endp->osmux.stats.chunks++; rem = msg->len; osmux_xfrm_output(osmuxh, &endp->osmux.out, &list); osmux_tx_sched(&list, scheduled_tx_net_cb, endp); } out: msgb_free(msg); return 0; } int osmux_init(int role, struct mgcp_config *cfg) { int ret; switch(role) { case OSMUX_ROLE_BSC: osmux_fd.cb = osmux_read_from_bsc_nat_cb; break; case OSMUX_ROLE_BSC_NAT: osmux_fd.cb = osmux_read_from_bsc_cb; break; default: LOGP(DMGCP, LOGL_ERROR, "wrong role for OSMUX\n"); return -1; } osmux_fd.data = cfg; ret = mgcp_create_bind("0.0.0.0", &osmux_fd, cfg->osmux_port); if (ret < 0) { LOGP(DMGCP, LOGL_ERROR, "cannot bind OSMUX socket\n"); return ret; } mgcp_set_ip_tos(osmux_fd.fd, cfg->endp_dscp); osmux_fd.when |= BSC_FD_READ; ret = osmo_fd_register(&osmux_fd); if (ret < 0) { LOGP(DMGCP, LOGL_ERROR, "cannot register OSMUX socket\n"); return ret; } cfg->osmux_init = 1; return 0; } int osmux_enable_endpoint(struct mgcp_endpoint *endp, int role, struct in_addr *addr, uint16_t port) { /* If osmux is enabled, initialize the output handler. This handler is * used to reconstruct the RTP flow from osmux. The RTP SSRC is * allocated based on the circuit ID (endp->osmux.cid), which is unique * in the local scope to the BSC/BSC-NAT. We use it to divide the RTP * SSRC space (2^32) by the 256 possible circuit IDs, then randomly * select one value from that window. Thus, we have no chance to have * overlapping RTP SSRC traveling to the BTSes behind the BSC, * similarly, for flows traveling to the MSC. */ static const uint32_t rtp_ssrc_winlen = UINT32_MAX / 256; if (endp->osmux.state == OSMUX_STATE_DISABLED) { LOGP(DMGCP, LOGL_ERROR, "Endpoint %u didn't request Osmux\n", ENDPOINT_NUMBER(endp)); return -1; } osmux_xfrm_output_init(&endp->osmux.out, (endp->osmux.cid * rtp_ssrc_winlen) + (random() % rtp_ssrc_winlen)); endp->osmux.in = osmux_handle_lookup(endp->cfg, addr, port); if (!endp->osmux.in) { LOGP(DMGCP, LOGL_ERROR, "Cannot allocate input osmux handle\n"); return -1; } if (!osmux_xfrm_input_open_circuit(endp->osmux.in, endp->osmux.cid, endp->cfg->osmux_dummy)) { LOGP(DMGCP, LOGL_ERROR, "Cannot open osmux circuit %u\n", endp->osmux.cid); return -1; } switch (endp->cfg->role) { case MGCP_BSC_NAT: endp->type = MGCP_OSMUX_BSC_NAT; break; case MGCP_BSC: endp->type = MGCP_OSMUX_BSC; break; } endp->osmux.state = OSMUX_STATE_ENABLED; return 0; } void osmux_disable_endpoint(struct mgcp_endpoint *endp) { LOGP(DMGCP, LOGL_INFO, "Releasing endpoint %u using Osmux CID %u\n", ENDPOINT_NUMBER(endp), endp->osmux.cid); osmux_xfrm_input_close_circuit(endp->osmux.in, endp->osmux.cid); endp->osmux.state = OSMUX_STATE_DISABLED; endp->osmux.cid = -1; osmux_handle_put(endp->osmux.in); } void osmux_release_cid(struct mgcp_endpoint *endp) { if (endp->osmux.allocated_cid >= 0) osmux_put_cid(endp->osmux.allocated_cid); endp->osmux.allocated_cid = -1; } void osmux_allocate_cid(struct mgcp_endpoint *endp) { osmux_release_cid(endp); endp->osmux.allocated_cid = osmux_get_cid(); } /* We don't need to send the dummy load for osmux so often as another endpoint * may have already punched the hole in the firewall. This approach is simple * though. */ int osmux_send_dummy(struct mgcp_endpoint *endp) { char buf[1 + sizeof(uint8_t)]; struct in_addr addr_unset = {}; buf[0] = MGCP_DUMMY_LOAD; memcpy(&buf[1], &endp->osmux.cid, sizeof(endp->osmux.cid)); /* Wait until we have the connection information from MDCX */ if (memcmp(&endp->net_end.addr, &addr_unset, sizeof(addr_unset)) == 0) return 0; if (endp->osmux.state == OSMUX_STATE_ACTIVATING) { if (osmux_enable_endpoint(endp, OSMUX_ROLE_BSC, &endp->net_end.addr, htons(endp->cfg->osmux_port)) < 0) { LOGP(DMGCP, LOGL_ERROR, "Could not activate osmux in endpoint %d\n", ENDPOINT_NUMBER(endp)); } LOGP(DMGCP, LOGL_ERROR, "Osmux CID %u for %s:%u is now enabled\n", endp->osmux.cid, inet_ntoa(endp->net_end.addr), endp->cfg->osmux_port); } LOGP(DMGCP, LOGL_DEBUG, "sending OSMUX dummy load to %s CID %u\n", inet_ntoa(endp->net_end.addr), endp->osmux.cid); return mgcp_udp_send(osmux_fd.fd, &endp->net_end.addr, htons(endp->cfg->osmux_port), buf, sizeof(buf)); } /* bsc-nat allocates/releases the Osmux circuit ID */ static uint8_t osmux_cid_bitmap[16]; int osmux_used_cid(void) { int i, j, used = 0; for (i = 0; i < sizeof(osmux_cid_bitmap); i++) { for (j = 0; j < 8; j++) { if (osmux_cid_bitmap[i] & (1 << j)) used += 1; } } return used; } int osmux_get_cid(void) { int i, j; for (i = 0; i < sizeof(osmux_cid_bitmap); i++) { for (j = 0; j < 8; j++) { if (osmux_cid_bitmap[i] & (1 << j)) continue; osmux_cid_bitmap[i] |= (1 << j); LOGP(DMGCP, LOGL_DEBUG, "Allocating Osmux CID %u from pool\n", (i * 8) + j); return (i * 8) + j; } } LOGP(DMGCP, LOGL_ERROR, "All Osmux circuits are in use!\n"); return -1; } void osmux_put_cid(uint8_t osmux_cid) { LOGP(DMGCP, LOGL_DEBUG, "Osmux CID %u is back to the pool\n", osmux_cid); osmux_cid_bitmap[osmux_cid / 8] &= ~(1 << (osmux_cid % 8)); } openbsc-0.15.0/openbsc/src/libmgcp/mgcp_protocol.c000066400000000000000000001204741265565154000221170ustar00rootroot00000000000000/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */ /* The protocol implementation */ /* * (C) 2009-2012 by Holger Hans Peter Freyther * (C) 2009-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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #define for_each_non_empty_line(line, save) \ for (line = strtok_r(NULL, "\r\n", &save); line;\ line = strtok_r(NULL, "\r\n", &save)) static void mgcp_rtp_end_reset(struct mgcp_rtp_end *end); struct mgcp_request { char *name; struct msgb *(*handle_request) (struct mgcp_parse_data *data); char *debug_name; }; #define MGCP_REQUEST(NAME, REQ, DEBUG_NAME) \ { .name = NAME, .handle_request = REQ, .debug_name = DEBUG_NAME }, static struct msgb *handle_audit_endpoint(struct mgcp_parse_data *data); static struct msgb *handle_create_con(struct mgcp_parse_data *data); static struct msgb *handle_delete_con(struct mgcp_parse_data *data); static struct msgb *handle_modify_con(struct mgcp_parse_data *data); static struct msgb *handle_rsip(struct mgcp_parse_data *data); static struct msgb *handle_noti_req(struct mgcp_parse_data *data); static void create_transcoder(struct mgcp_endpoint *endp); static void delete_transcoder(struct mgcp_endpoint *endp); static int setup_rtp_processing(struct mgcp_endpoint *endp); static int mgcp_analyze_header(struct mgcp_parse_data *parse, char *data); static int mgcp_check_param(const struct mgcp_endpoint *endp, const char *line) { const size_t line_len = strlen(line); if (line[0] != '\0' && line_len < 2) { LOGP(DMGCP, LOGL_ERROR, "Wrong MGCP option format: '%s' on 0x%x\n", line, ENDPOINT_NUMBER(endp)); return 0; } return 1; } static uint32_t generate_call_id(struct mgcp_config *cfg) { int i; /* use the call id */ ++cfg->last_call_id; /* handle wrap around */ if (cfg->last_call_id == CI_UNUSED) ++cfg->last_call_id; /* callstack can only be of size number_of_endpoints */ /* verify that the call id is free, e.g. in case of overrun */ for (i = 1; i < cfg->trunk.number_endpoints; ++i) if (cfg->trunk.endpoints[i].ci == cfg->last_call_id) return generate_call_id(cfg); return cfg->last_call_id; } /* * array of function pointers for handling various * messages. In the future this might be binary sorted * for performance reasons. */ static const struct mgcp_request mgcp_requests [] = { MGCP_REQUEST("AUEP", handle_audit_endpoint, "AuditEndpoint") MGCP_REQUEST("CRCX", handle_create_con, "CreateConnection") MGCP_REQUEST("DLCX", handle_delete_con, "DeleteConnection") MGCP_REQUEST("MDCX", handle_modify_con, "ModifiyConnection") MGCP_REQUEST("RQNT", handle_noti_req, "NotificationRequest") /* SPEC extension */ MGCP_REQUEST("RSIP", handle_rsip, "ReSetInProgress") }; static struct msgb *mgcp_msgb_alloc(void) { struct msgb *msg; msg = msgb_alloc_headroom(4096, 128, "MGCP msg"); if (!msg) LOGP(DMGCP, LOGL_ERROR, "Failed to msgb for MGCP data.\n"); return msg; } static struct msgb *do_retransmission(const struct mgcp_endpoint *endp) { struct msgb *msg = mgcp_msgb_alloc(); if (!msg) return NULL; msg->l2h = msgb_put(msg, strlen(endp->last_response)); memcpy(msg->l2h, endp->last_response, msgb_l2len(msg)); return msg; } static struct msgb *create_resp(struct mgcp_endpoint *endp, int code, const char *txt, const char *msg, const char *trans, const char *param, const char *sdp) { int len; struct msgb *res; res = mgcp_msgb_alloc(); if (!res) return NULL; len = snprintf((char *) res->data, 2048, "%d %s%s%s\r\n%s", code, trans, txt, param ? param : "", sdp ? sdp : ""); if (len < 0) { LOGP(DMGCP, LOGL_ERROR, "Failed to sprintf MGCP response.\n"); msgb_free(res); return NULL; } res->l2h = msgb_put(res, len); LOGP(DMGCP, LOGL_DEBUG, "Generated response: code: %d for '%s'\n", code, res->l2h); /* * Remember the last transmission per endpoint. */ if (endp) { struct mgcp_trunk_config *tcfg = endp->tcfg; talloc_free(endp->last_response); talloc_free(endp->last_trans); endp->last_trans = talloc_strdup(tcfg->endpoints, trans); endp->last_response = talloc_strndup(tcfg->endpoints, (const char *) res->l2h, msgb_l2len(res)); } return res; } static struct msgb *create_ok_resp_with_param(struct mgcp_endpoint *endp, int code, const char *msg, const char *trans, const char *param) { return create_resp(endp, code, " OK", msg, trans, param, NULL); } static struct msgb *create_ok_response(struct mgcp_endpoint *endp, int code, const char *msg, const char *trans) { return create_ok_resp_with_param(endp, code, msg, trans, NULL); } static struct msgb *create_err_response(struct mgcp_endpoint *endp, int code, const char *msg, const char *trans) { return create_resp(endp, code, " FAIL", msg, trans, NULL, NULL); } static int write_response_sdp(struct mgcp_endpoint *endp, char *sdp_record, size_t size, const char *addr) { const char *fmtp_extra; const char *audio_name; int payload_type; int len; int nchars; endp->cfg->get_net_downlink_format_cb(endp, &payload_type, &audio_name, &fmtp_extra); len = snprintf(sdp_record, size, "v=0\r\n" "o=- %u 23 IN IP4 %s\r\n" "s=-\r\n" "c=IN IP4 %s\r\n" "t=0 0\r\n", endp->ci, addr, addr); if (len < 0 || len >= size) goto buffer_too_small; if (payload_type >= 0) { nchars = snprintf(sdp_record + len, size - len, "m=audio %d RTP/AVP %d\r\n", endp->net_end.local_port, payload_type); if (nchars < 0 || nchars >= size - len) goto buffer_too_small; len += nchars; if (audio_name && endp->tcfg->audio_send_name) { nchars = snprintf(sdp_record + len, size - len, "a=rtpmap:%d %s\r\n", payload_type, audio_name); if (nchars < 0 || nchars >= size - len) goto buffer_too_small; len += nchars; } if (fmtp_extra) { nchars = snprintf(sdp_record + len, size - len, "%s\r\n", fmtp_extra); if (nchars < 0 || nchars >= size - len) goto buffer_too_small; len += nchars; } } if (endp->bts_end.packet_duration_ms > 0 && endp->tcfg->audio_send_ptime) { nchars = snprintf(sdp_record + len, size - len, "a=ptime:%d\r\n", endp->bts_end.packet_duration_ms); if (nchars < 0 || nchars >= size - len) goto buffer_too_small; len += nchars; } return len; buffer_too_small: LOGP(DMGCP, LOGL_ERROR, "SDP buffer too small: %zu (needed %d)\n", size, len); return -1; } static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp, const char *msg, const char *trans_id) { const char *addr = endp->cfg->local_ip; char sdp_record[4096]; int len; int nchars; char osmux_extension[strlen("\nX-Osmux: 255") + 1]; if (!addr) addr = mgcp_net_src_addr(endp); if (endp->osmux.state == OSMUX_STATE_ACTIVATING) sprintf(osmux_extension, "\nX-Osmux: %u", endp->osmux.cid); else osmux_extension[0] = '\0'; len = snprintf(sdp_record, sizeof(sdp_record), "I: %u%s\n\n", endp->ci, osmux_extension); if (len < 0) return NULL; nchars = write_response_sdp(endp, sdp_record + len, sizeof(sdp_record) - len - 1, addr); if (nchars < 0) return NULL; len += nchars; sdp_record[sizeof(sdp_record) - 1] = '\0'; return create_resp(endp, 200, " OK", msg, trans_id, NULL, sdp_record); } static void send_dummy(struct mgcp_endpoint *endp) { if (endp->osmux.state != OSMUX_STATE_DISABLED) osmux_send_dummy(endp); else mgcp_send_dummy(endp); } /* * handle incoming messages: * - this can be a command (four letters, space, transaction id) * - or a response (three numbers, space, transaction id) */ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg) { struct mgcp_parse_data pdata; int i, code, handled = 0; struct msgb *resp = NULL; char *data; unsigned char *tail = msg->l2h + msgb_l2len(msg); /* char after l2 data */ if (msgb_l2len(msg) < 4) { LOGP(DMGCP, LOGL_ERROR, "msg too short: %d\n", msg->len); return NULL; } /* Ensure that the msg->l2h is NUL terminated. */ if (tail[-1] == '\0') /* nothing to do */; else if (msgb_tailroom(msg) > 0) tail[0] = '\0'; else if (tail[-1] == '\r' || tail[-1] == '\n') tail[-1] = '\0'; else { LOGP(DMGCP, LOGL_ERROR, "Cannot NUL terminate MGCP message: " "Length: %d, Buffer size: %d\n", msgb_l2len(msg), msg->data_len); return NULL; } /* attempt to treat it as a response */ if (sscanf((const char *)&msg->l2h[0], "%3d %*s", &code) == 1) { LOGP(DMGCP, LOGL_DEBUG, "Response: Code: %d\n", code); return NULL; } msg->l3h = &msg->l2h[4]; /* * Check for a duplicate message and respond. */ memset(&pdata, 0, sizeof(pdata)); pdata.cfg = cfg; data = strline_r((char *) msg->l3h, &pdata.save); pdata.found = mgcp_analyze_header(&pdata, data); if (pdata.endp && pdata.trans && pdata.endp->last_trans && strcmp(pdata.endp->last_trans, pdata.trans) == 0) { return do_retransmission(pdata.endp); } for (i = 0; i < ARRAY_SIZE(mgcp_requests); ++i) { if (strncmp(mgcp_requests[i].name, (const char *) &msg->l2h[0], 4) == 0) { handled = 1; resp = mgcp_requests[i].handle_request(&pdata); break; } } if (!handled) LOGP(DMGCP, LOGL_NOTICE, "MSG with type: '%.4s' not handled\n", &msg->l2h[0]); return resp; } /** * We have a null terminated string with the endpoint name here. We only * support two kinds. Simple ones as seen on the BSC level and the ones * seen on the trunk side. */ static struct mgcp_endpoint *find_e1_endpoint(struct mgcp_config *cfg, const char *mgcp) { char *rest = NULL; struct mgcp_trunk_config *tcfg; int trunk, endp; trunk = strtoul(mgcp + 6, &rest, 10); if (rest == NULL || rest[0] != '/' || trunk < 1) { LOGP(DMGCP, LOGL_ERROR, "Wrong trunk name '%s'\n", mgcp); return NULL; } endp = strtoul(rest + 1, &rest, 10); if (rest == NULL || rest[0] != '@') { LOGP(DMGCP, LOGL_ERROR, "Wrong endpoint name '%s'\n", mgcp); return NULL; } /* signalling is on timeslot 1 */ if (endp == 1) return NULL; tcfg = mgcp_trunk_num(cfg, trunk); if (!tcfg) { LOGP(DMGCP, LOGL_ERROR, "The trunk %d is not declared.\n", trunk); return NULL; } if (!tcfg->endpoints) { LOGP(DMGCP, LOGL_ERROR, "Endpoints of trunk %d not allocated.\n", trunk); return NULL; } if (endp < 1 || endp >= tcfg->number_endpoints) { LOGP(DMGCP, LOGL_ERROR, "Failed to find endpoint '%s'\n", mgcp); return NULL; } return &tcfg->endpoints[endp]; } static struct mgcp_endpoint *find_endpoint(struct mgcp_config *cfg, const char *mgcp) { char *endptr = NULL; unsigned int gw = INT_MAX; if (strncmp(mgcp, "ds/e1", 5) == 0) return find_e1_endpoint(cfg, mgcp); gw = strtoul(mgcp, &endptr, 16); if (gw > 0 && gw < cfg->trunk.number_endpoints && endptr[0] == '@') return &cfg->trunk.endpoints[gw]; LOGP(DMGCP, LOGL_ERROR, "Not able to find the endpoint: '%s'\n", mgcp); return NULL; } /** * @returns 0 when the status line was complete and transaction_id and * endp out parameters are set. */ static int mgcp_analyze_header(struct mgcp_parse_data *pdata, char *data) { int i = 0; char *elem, *save = NULL; pdata->trans = "000000"; for (elem = strtok_r(data, " ", &save); elem; elem = strtok_r(NULL, " ", &save)) { switch (i) { case 0: pdata->trans = elem; break; case 1: pdata->endp = find_endpoint(pdata->cfg, elem); if (!pdata->endp) { LOGP(DMGCP, LOGL_ERROR, "Unable to find Endpoint `%s'\n", elem); return -1; } break; case 2: if (strcmp("MGCP", elem)) { LOGP(DMGCP, LOGL_ERROR, "MGCP header parsing error\n"); return -1; } break; case 3: if (strcmp("1.0", elem)) { LOGP(DMGCP, LOGL_ERROR, "MGCP version `%s' " "not supported\n", elem); return -1; } break; } i++; } if (i != 4) { LOGP(DMGCP, LOGL_ERROR, "MGCP status line too short.\n"); pdata->trans = "000000"; pdata->endp = NULL; return -1; } return 0; } static int verify_call_id(const struct mgcp_endpoint *endp, const char *callid) { if (strcmp(endp->callid, callid) != 0) { LOGP(DMGCP, LOGL_ERROR, "CallIDs does not match on 0x%x. '%s' != '%s'\n", ENDPOINT_NUMBER(endp), endp->callid, callid); return -1; } return 0; } static int verify_ci(const struct mgcp_endpoint *endp, const char *_ci) { uint32_t ci = strtoul(_ci, NULL, 10); if (ci != endp->ci) { LOGP(DMGCP, LOGL_ERROR, "ConnectionIdentifiers do not match on 0x%x. %u != %s\n", ENDPOINT_NUMBER(endp), endp->ci, _ci); return -1; } return 0; } static struct msgb *handle_audit_endpoint(struct mgcp_parse_data *p) { if (p->found != 0) return create_err_response(NULL, 500, "AUEP", p->trans); else return create_ok_response(p->endp, 200, "AUEP", p->trans); } static int parse_conn_mode(const char *msg, struct mgcp_endpoint *endp) { int ret = 0; if (strcmp(msg, "recvonly") == 0) endp->conn_mode = MGCP_CONN_RECV_ONLY; else if (strcmp(msg, "sendrecv") == 0) endp->conn_mode = MGCP_CONN_RECV_SEND; else if (strcmp(msg, "sendonly") == 0) endp->conn_mode = MGCP_CONN_SEND_ONLY; else if (strcmp(msg, "loopback") == 0) endp->conn_mode = MGCP_CONN_LOOPBACK; else { LOGP(DMGCP, LOGL_ERROR, "Unknown connection mode: '%s'\n", msg); ret = -1; } endp->net_end.output_enabled = endp->conn_mode & MGCP_CONN_SEND_ONLY ? 1 : 0; endp->bts_end.output_enabled = endp->conn_mode & MGCP_CONN_RECV_ONLY ? 1 : 0; return ret; } static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_rtp_end *end, struct mgcp_port_range *range, int (*alloc)(struct mgcp_endpoint *endp, int port)) { int i; if (range->mode == PORT_ALLOC_STATIC) { end->local_alloc = PORT_ALLOC_STATIC; return 0; } /* attempt to find a port */ for (i = 0; i < 200; ++i) { int rc; if (range->last_port >= range->range_end) range->last_port = range->range_start; rc = alloc(endp, range->last_port); range->last_port += 2; if (rc == 0) { end->local_alloc = PORT_ALLOC_DYNAMIC; return 0; } } LOGP(DMGCP, LOGL_ERROR, "Allocating a RTP/RTCP port failed 200 times 0x%x.\n", ENDPOINT_NUMBER(endp)); return -1; } static int allocate_ports(struct mgcp_endpoint *endp) { if (allocate_port(endp, &endp->net_end, &endp->cfg->net_ports, mgcp_bind_net_rtp_port) != 0) return -1; if (allocate_port(endp, &endp->bts_end, &endp->cfg->bts_ports, mgcp_bind_bts_rtp_port) != 0) { mgcp_rtp_end_reset(&endp->net_end); return -1; } if (endp->cfg->transcoder_ip && endp->tcfg->trunk_type == MGCP_TRUNK_VIRTUAL) { if (allocate_port(endp, &endp->trans_net, &endp->cfg->transcoder_ports, mgcp_bind_trans_net_rtp_port) != 0) { mgcp_rtp_end_reset(&endp->net_end); mgcp_rtp_end_reset(&endp->bts_end); return -1; } if (allocate_port(endp, &endp->trans_bts, &endp->cfg->transcoder_ports, mgcp_bind_trans_bts_rtp_port) != 0) { mgcp_rtp_end_reset(&endp->net_end); mgcp_rtp_end_reset(&endp->bts_end); mgcp_rtp_end_reset(&endp->trans_net); return -1; } /* remember that we have set up transcoding */ endp->type = MGCP_RTP_TRANSCODED; } return 0; } /* Set the LCO from a string (see RFC 3435). * The string is stored in the 'string' field. A NULL string is handled excatly * like an empty string, the 'string' field is never NULL after this function * has been called. */ static void set_local_cx_options(void *ctx, struct mgcp_lco *lco, const char *options) { char *p_opt, *a_opt; char codec[9]; talloc_free(lco->string); talloc_free(lco->codec); lco->codec = NULL; lco->pkt_period_min = lco->pkt_period_max = 0; lco->string = talloc_strdup(ctx, options ? options : ""); p_opt = strstr(lco->string, "p:"); if (p_opt && sscanf(p_opt, "p:%d-%d", &lco->pkt_period_min, &lco->pkt_period_max) == 1) lco->pkt_period_max = lco->pkt_period_min; a_opt = strstr(lco->string, "a:"); if (a_opt && sscanf(a_opt, "a:%8[^,]", codec) == 1) lco->codec = talloc_strdup(ctx, codec); } void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change, struct mgcp_rtp_end *rtp) { struct mgcp_trunk_config *tcfg = endp->tcfg; int patch_ssrc = expect_ssrc_change && tcfg->force_constant_ssrc; rtp->force_aligned_timing = tcfg->force_aligned_timing; rtp->force_constant_ssrc = patch_ssrc ? 1 : 0; LOGP(DMGCP, LOGL_DEBUG, "Configuring RTP endpoint: local port %d%s%s\n", ntohs(rtp->rtp_port), rtp->force_aligned_timing ? ", force constant timing" : "", rtp->force_constant_ssrc ? ", force constant ssrc" : ""); } uint32_t mgcp_rtp_packet_duration(struct mgcp_endpoint *endp, struct mgcp_rtp_end *rtp) { int f = 0; /* Get the number of frames per channel and packet */ if (rtp->frames_per_packet) f = rtp->frames_per_packet; else if (rtp->packet_duration_ms && rtp->codec.frame_duration_num) { int den = 1000 * rtp->codec.frame_duration_num; f = (rtp->packet_duration_ms * rtp->codec.frame_duration_den + den/2) / den; } return rtp->codec.rate * f * rtp->codec.frame_duration_num / rtp->codec.frame_duration_den; } static int mgcp_parse_osmux_cid(const char *line) { int osmux_cid; if (sscanf(line + 2, "Osmux: %u", &osmux_cid) != 1) return -1; if (osmux_cid > OSMUX_CID_MAX) { LOGP(DMGCP, LOGL_ERROR, "Osmux ID too large: %u > %u\n", osmux_cid, OSMUX_CID_MAX); return -1; } LOGP(DMGCP, LOGL_DEBUG, "bsc-nat offered Osmux CID %u\n", osmux_cid); return osmux_cid; } static int mgcp_osmux_setup(struct mgcp_endpoint *endp, const char *line) { if (!endp->cfg->osmux_init) { if (osmux_init(OSMUX_ROLE_BSC, endp->cfg) < 0) { LOGP(DMGCP, LOGL_ERROR, "Cannot init OSMUX\n"); return -1; } LOGP(DMGCP, LOGL_NOTICE, "OSMUX socket has been set up\n"); } return mgcp_parse_osmux_cid(line); } static struct msgb *handle_create_con(struct mgcp_parse_data *p) { struct mgcp_trunk_config *tcfg; struct mgcp_endpoint *endp = p->endp; int error_code = 400; const char *local_options = NULL; const char *callid = NULL; const char *mode = NULL; char *line; int have_sdp = 0, osmux_cid = -1; if (p->found != 0) return create_err_response(NULL, 510, "CRCX", p->trans); /* parse CallID C: and LocalParameters L: */ for_each_line(line, p->save) { if (!mgcp_check_param(endp, line)) continue; switch (line[0]) { case 'L': local_options = (const char *) line + 3; break; case 'C': callid = (const char *) line + 3; break; case 'M': mode = (const char *) line + 3; break; case 'X': /* Osmux is not enabled in this bsc, ignore it so the * bsc-nat knows that we don't want to use Osmux. */ if (!p->endp->cfg->osmux) break; if (strncmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) osmux_cid = mgcp_osmux_setup(endp, line); break; case '\0': have_sdp = 1; goto mgcp_header_done; default: LOGP(DMGCP, LOGL_NOTICE, "Unhandled option: '%c'/%d on 0x%x\n", *line, *line, ENDPOINT_NUMBER(endp)); break; } } mgcp_header_done: tcfg = p->endp->tcfg; /* Check required data */ if (!callid || !mode) { LOGP(DMGCP, LOGL_ERROR, "Missing callid and mode in CRCX on 0x%x\n", ENDPOINT_NUMBER(endp)); return create_err_response(endp, 400, "CRCX", p->trans); } if (endp->allocated) { if (tcfg->force_realloc) { LOGP(DMGCP, LOGL_NOTICE, "Endpoint 0x%x already allocated. Forcing realloc.\n", ENDPOINT_NUMBER(endp)); mgcp_release_endp(endp); if (p->cfg->realloc_cb) p->cfg->realloc_cb(tcfg, ENDPOINT_NUMBER(endp)); } else { LOGP(DMGCP, LOGL_ERROR, "Endpoint is already used. 0x%x\n", ENDPOINT_NUMBER(endp)); return create_err_response(endp, 400, "CRCX", p->trans); } } /* copy some parameters */ endp->callid = talloc_strdup(tcfg->endpoints, callid); set_local_cx_options(endp->tcfg->endpoints, &endp->local_options, local_options); if (parse_conn_mode(mode, endp) != 0) { error_code = 517; goto error2; } /* initialize */ endp->net_end.rtp_port = endp->net_end.rtcp_port = endp->bts_end.rtp_port = endp->bts_end.rtcp_port = 0; mgcp_rtp_end_config(endp, 0, &endp->net_end); mgcp_rtp_end_config(endp, 0, &endp->bts_end); /* set to zero until we get the info */ memset(&endp->net_end.addr, 0, sizeof(endp->net_end.addr)); /* bind to the port now */ if (allocate_ports(endp) != 0) goto error2; /* assign a local call identifier or fail */ endp->ci = generate_call_id(p->cfg); if (endp->ci == CI_UNUSED) goto error2; /* Annotate Osmux circuit ID and set it to activating state until this * is fully set up from the dummy load. */ endp->osmux.state = OSMUX_STATE_DISABLED; if (osmux_cid >= 0) { endp->osmux.cid = osmux_cid; endp->osmux.state = OSMUX_STATE_ACTIVATING; } else if(endp->cfg->osmux == OSMUX_USAGE_ONLY) { LOGP(DMGCP, LOGL_ERROR, "Osmux only and no osmux offered on 0x%x\n", ENDPOINT_NUMBER(endp)); goto error2; } endp->allocated = 1; /* set up RTP media parameters */ mgcp_set_audio_info(p->cfg, &endp->bts_end.codec, tcfg->audio_payload, tcfg->audio_name); endp->bts_end.fmtp_extra = talloc_strdup(tcfg->endpoints, tcfg->audio_fmtp_extra); if (have_sdp) mgcp_parse_sdp_data(endp, &endp->net_end, p); else if (endp->local_options.codec) mgcp_set_audio_info(p->cfg, &endp->net_end.codec, PTYPE_UNDEFINED, endp->local_options.codec); if (p->cfg->bts_force_ptime) { endp->bts_end.packet_duration_ms = p->cfg->bts_force_ptime; endp->bts_end.force_output_ptime = 1; } if (setup_rtp_processing(endp) != 0) goto error2; /* policy CB */ if (p->cfg->policy_cb) { int rc; rc = p->cfg->policy_cb(tcfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_CRCX, p->trans); switch (rc) { case MGCP_POLICY_REJECT: LOGP(DMGCP, LOGL_NOTICE, "CRCX rejected by policy on 0x%x\n", ENDPOINT_NUMBER(endp)); mgcp_release_endp(endp); return create_err_response(endp, 400, "CRCX", p->trans); break; case MGCP_POLICY_DEFER: /* stop processing */ create_transcoder(endp); return NULL; break; case MGCP_POLICY_CONT: /* just continue */ break; } } LOGP(DMGCP, LOGL_DEBUG, "Creating endpoint on: 0x%x CI: %u port: %u/%u\n", ENDPOINT_NUMBER(endp), endp->ci, endp->net_end.local_port, endp->bts_end.local_port); if (p->cfg->change_cb) p->cfg->change_cb(tcfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_CRCX); if (endp->conn_mode & MGCP_CONN_RECV_ONLY && tcfg->keepalive_interval != 0) { send_dummy(endp); } create_transcoder(endp); return create_response_with_sdp(endp, "CRCX", p->trans); error2: mgcp_release_endp(endp); LOGP(DMGCP, LOGL_NOTICE, "Resource error on 0x%x\n", ENDPOINT_NUMBER(endp)); return create_err_response(endp, error_code, "CRCX", p->trans); } static struct msgb *handle_modify_con(struct mgcp_parse_data *p) { struct mgcp_endpoint *endp = p->endp; int error_code = 500; int silent = 0; int have_sdp = 0; char *line; const char *local_options = NULL; if (p->found != 0) return create_err_response(NULL, 510, "MDCX", p->trans); if (endp->ci == CI_UNUSED) { LOGP(DMGCP, LOGL_ERROR, "Endpoint is not " "holding a connection. 0x%x\n", ENDPOINT_NUMBER(endp)); return create_err_response(endp, 400, "MDCX", p->trans); } for_each_line(line, p->save) { if (!mgcp_check_param(endp, line)) continue; switch (line[0]) { case 'C': { if (verify_call_id(endp, line + 3) != 0) goto error3; break; } case 'I': { if (verify_ci(endp, line + 3) != 0) goto error3; break; } case 'L': local_options = (const char *) line + 3; break; case 'M': if (parse_conn_mode(line + 3, endp) != 0) { error_code = 517; goto error3; } endp->orig_mode = endp->conn_mode; break; case 'Z': silent = strcmp("noanswer", line + 3) == 0; break; case '\0': /* SDP file begins */ have_sdp = 1; mgcp_parse_sdp_data(endp, &endp->net_end, p); /* This will exhaust p->save, so the loop will * terminate next time. */ break; default: LOGP(DMGCP, LOGL_NOTICE, "Unhandled MGCP option: '%c'/%d on 0x%x\n", line[0], line[0], ENDPOINT_NUMBER(endp)); break; } } set_local_cx_options(endp->tcfg->endpoints, &endp->local_options, local_options); if (!have_sdp && endp->local_options.codec) mgcp_set_audio_info(p->cfg, &endp->net_end.codec, PTYPE_UNDEFINED, endp->local_options.codec); if (setup_rtp_processing(endp) != 0) goto error3; /* policy CB */ if (p->cfg->policy_cb) { int rc; rc = p->cfg->policy_cb(endp->tcfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_MDCX, p->trans); switch (rc) { case MGCP_POLICY_REJECT: LOGP(DMGCP, LOGL_NOTICE, "MDCX rejected by policy on 0x%x\n", ENDPOINT_NUMBER(endp)); if (silent) goto out_silent; return create_err_response(endp, 400, "MDCX", p->trans); break; case MGCP_POLICY_DEFER: /* stop processing */ return NULL; break; case MGCP_POLICY_CONT: /* just continue */ break; } } mgcp_rtp_end_config(endp, 1, &endp->net_end); mgcp_rtp_end_config(endp, 1, &endp->bts_end); /* modify */ LOGP(DMGCP, LOGL_DEBUG, "Modified endpoint on: 0x%x Server: %s:%u\n", ENDPOINT_NUMBER(endp), inet_ntoa(endp->net_end.addr), ntohs(endp->net_end.rtp_port)); if (p->cfg->change_cb) p->cfg->change_cb(endp->tcfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_MDCX); if (endp->conn_mode & MGCP_CONN_RECV_ONLY && endp->tcfg->keepalive_interval != 0) send_dummy(endp); if (silent) goto out_silent; return create_response_with_sdp(endp, "MDCX", p->trans); error3: return create_err_response(endp, error_code, "MDCX", p->trans); out_silent: return NULL; } static struct msgb *handle_delete_con(struct mgcp_parse_data *p) { struct mgcp_endpoint *endp = p->endp; int error_code = 400; int silent = 0; char *line; char stats[1048]; if (p->found != 0) return create_err_response(NULL, error_code, "DLCX", p->trans); if (!p->endp->allocated) { LOGP(DMGCP, LOGL_ERROR, "Endpoint is not used. 0x%x\n", ENDPOINT_NUMBER(endp)); return create_err_response(endp, 400, "DLCX", p->trans); } for_each_line(line, p->save) { if (!mgcp_check_param(endp, line)) continue; switch (line[0]) { case 'C': if (verify_call_id(endp, line + 3) != 0) goto error3; break; case 'I': if (verify_ci(endp, line + 3) != 0) goto error3; break; case 'Z': silent = strcmp("noanswer", line + 3) == 0; break; default: LOGP(DMGCP, LOGL_NOTICE, "Unhandled option: '%c'/%d on 0x%x\n", line[0], line[0], ENDPOINT_NUMBER(endp)); break; } } /* policy CB */ if (p->cfg->policy_cb) { int rc; rc = p->cfg->policy_cb(endp->tcfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_DLCX, p->trans); switch (rc) { case MGCP_POLICY_REJECT: LOGP(DMGCP, LOGL_NOTICE, "DLCX rejected by policy on 0x%x\n", ENDPOINT_NUMBER(endp)); if (silent) goto out_silent; return create_err_response(endp, 400, "DLCX", p->trans); break; case MGCP_POLICY_DEFER: /* stop processing */ delete_transcoder(endp); return NULL; break; case MGCP_POLICY_CONT: /* just continue */ break; } } /* free the connection */ LOGP(DMGCP, LOGL_DEBUG, "Deleted endpoint on: 0x%x Server: %s:%u\n", ENDPOINT_NUMBER(endp), inet_ntoa(endp->net_end.addr), ntohs(endp->net_end.rtp_port)); /* save the statistics of the current call */ mgcp_format_stats(endp, stats, sizeof(stats)); delete_transcoder(endp); mgcp_release_endp(endp); if (p->cfg->change_cb) p->cfg->change_cb(endp->tcfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_DLCX); if (silent) goto out_silent; return create_ok_resp_with_param(endp, 250, "DLCX", p->trans, stats); error3: return create_err_response(endp, error_code, "DLCX", p->trans); out_silent: return NULL; } static struct msgb *handle_rsip(struct mgcp_parse_data *p) { if (p->found != 0) { LOGP(DMGCP, LOGL_ERROR, "Failed to find the endpoint.\n"); return NULL; } if (p->cfg->reset_cb) p->cfg->reset_cb(p->endp->tcfg); return NULL; } static char extract_tone(const char *line) { const char *str = strstr(line, "D/"); if (!str) return CHAR_MAX; return str[2]; } /* * This can request like DTMF detection and forward, fax detection... it * can also request when the notification should be send and such. We don't * do this right now. */ static struct msgb *handle_noti_req(struct mgcp_parse_data *p) { int res = 0; char *line; char tone = CHAR_MAX; if (p->found != 0) return create_err_response(NULL, 400, "RQNT", p->trans); for_each_line(line, p->save) { switch (line[0]) { case 'S': tone = extract_tone(line); break; } } /* we didn't see a signal request with a tone */ if (tone == CHAR_MAX) return create_ok_response(p->endp, 200, "RQNT", p->trans); if (p->cfg->rqnt_cb) res = p->cfg->rqnt_cb(p->endp, tone); return res == 0 ? create_ok_response(p->endp, 200, "RQNT", p->trans) : create_err_response(p->endp, res, "RQNT", p->trans); } static void mgcp_keepalive_timer_cb(void *_tcfg) { struct mgcp_trunk_config *tcfg = _tcfg; int i; LOGP(DMGCP, LOGL_DEBUG, "Triggered trunk %d keepalive timer.\n", tcfg->trunk_nr); if (tcfg->keepalive_interval <= 0) return; for (i = 1; i < tcfg->number_endpoints; ++i) { struct mgcp_endpoint *endp = &tcfg->endpoints[i]; if (endp->conn_mode == MGCP_CONN_RECV_ONLY) send_dummy(endp); } LOGP(DMGCP, LOGL_DEBUG, "Rescheduling trunk %d keepalive timer.\n", tcfg->trunk_nr); osmo_timer_schedule(&tcfg->keepalive_timer, tcfg->keepalive_interval, 0); } void mgcp_trunk_set_keepalive(struct mgcp_trunk_config *tcfg, int interval) { tcfg->keepalive_interval = interval; tcfg->keepalive_timer.data = tcfg; tcfg->keepalive_timer.cb = mgcp_keepalive_timer_cb; if (interval <= 0) osmo_timer_del(&tcfg->keepalive_timer); else osmo_timer_schedule(&tcfg->keepalive_timer, tcfg->keepalive_interval, 0); } struct mgcp_config *mgcp_config_alloc(void) { struct mgcp_config *cfg; cfg = talloc_zero(NULL, struct mgcp_config); if (!cfg) { LOGP(DMGCP, LOGL_FATAL, "Failed to allocate config.\n"); return NULL; } cfg->source_port = 2427; cfg->source_addr = talloc_strdup(cfg, "0.0.0.0"); cfg->transcoder_remote_base = 4000; cfg->bts_ports.base_port = RTP_PORT_DEFAULT; cfg->net_ports.base_port = RTP_PORT_NET_DEFAULT; cfg->rtp_processing_cb = &mgcp_rtp_processing_default; cfg->setup_rtp_processing_cb = &mgcp_setup_rtp_processing_default; cfg->get_net_downlink_format_cb = &mgcp_get_net_downlink_format_default; /* default trunk handling */ cfg->trunk.cfg = cfg; cfg->trunk.trunk_nr = 0; cfg->trunk.trunk_type = MGCP_TRUNK_VIRTUAL; cfg->trunk.audio_name = talloc_strdup(cfg, "AMR/8000"); cfg->trunk.audio_payload = 126; cfg->trunk.audio_send_ptime = 1; cfg->trunk.audio_send_name = 1; cfg->trunk.omit_rtcp = 0; mgcp_trunk_set_keepalive(&cfg->trunk, MGCP_KEEPALIVE_ONCE); INIT_LLIST_HEAD(&cfg->trunks); return cfg; } struct mgcp_trunk_config *mgcp_trunk_alloc(struct mgcp_config *cfg, int nr) { struct mgcp_trunk_config *trunk; trunk = talloc_zero(cfg, struct mgcp_trunk_config); if (!trunk) { LOGP(DMGCP, LOGL_ERROR, "Failed to allocate.\n"); return NULL; } trunk->cfg = cfg; trunk->trunk_type = MGCP_TRUNK_E1; trunk->trunk_nr = nr; trunk->audio_name = talloc_strdup(cfg, "AMR/8000"); trunk->audio_payload = 126; trunk->audio_send_ptime = 1; trunk->audio_send_name = 1; trunk->number_endpoints = 33; trunk->omit_rtcp = 0; mgcp_trunk_set_keepalive(trunk, MGCP_KEEPALIVE_ONCE); llist_add_tail(&trunk->entry, &cfg->trunks); return trunk; } struct mgcp_trunk_config *mgcp_trunk_num(struct mgcp_config *cfg, int index) { struct mgcp_trunk_config *trunk; llist_for_each_entry(trunk, &cfg->trunks, entry) if (trunk->trunk_nr == index) return trunk; return NULL; } static void mgcp_rtp_codec_reset(struct mgcp_rtp_codec *codec) { codec->payload_type = -1; talloc_free(codec->subtype_name); codec->subtype_name = NULL; talloc_free(codec->audio_name); codec->audio_name = NULL; codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM; codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN; codec->rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE; codec->channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS; } static void mgcp_rtp_end_reset(struct mgcp_rtp_end *end) { if (end->local_alloc == PORT_ALLOC_DYNAMIC) { mgcp_free_rtp_port(end); end->local_port = 0; } end->packets = 0; end->octets = 0; end->dropped_packets = 0; memset(&end->addr, 0, sizeof(end->addr)); end->rtp_port = end->rtcp_port = 0; end->local_alloc = -1; talloc_free(end->fmtp_extra); end->fmtp_extra = NULL; talloc_free(end->rtp_process_data); end->rtp_process_data = NULL; /* Set default values */ end->frames_per_packet = 0; /* unknown */ end->packet_duration_ms = DEFAULT_RTP_AUDIO_PACKET_DURATION_MS; end->output_enabled = 0; mgcp_rtp_codec_reset(&end->codec); mgcp_rtp_codec_reset(&end->alt_codec); } static void mgcp_rtp_end_init(struct mgcp_rtp_end *end) { mgcp_rtp_end_reset(end); end->rtp.fd = -1; end->rtcp.fd = -1; } int mgcp_endpoints_allocate(struct mgcp_trunk_config *tcfg) { int i; /* Initialize all endpoints */ tcfg->endpoints = _talloc_zero_array(tcfg->cfg, sizeof(struct mgcp_endpoint), tcfg->number_endpoints, "endpoints"); if (!tcfg->endpoints) return -1; for (i = 0; i < tcfg->number_endpoints; ++i) { tcfg->endpoints[i].osmux.allocated_cid = -1; tcfg->endpoints[i].ci = CI_UNUSED; tcfg->endpoints[i].cfg = tcfg->cfg; tcfg->endpoints[i].tcfg = tcfg; mgcp_rtp_end_init(&tcfg->endpoints[i].net_end); mgcp_rtp_end_init(&tcfg->endpoints[i].bts_end); mgcp_rtp_end_init(&tcfg->endpoints[i].trans_net); mgcp_rtp_end_init(&tcfg->endpoints[i].trans_bts); } return 0; } void mgcp_release_endp(struct mgcp_endpoint *endp) { LOGP(DMGCP, LOGL_DEBUG, "Releasing endpoint on: 0x%x\n", ENDPOINT_NUMBER(endp)); endp->ci = CI_UNUSED; endp->allocated = 0; talloc_free(endp->callid); endp->callid = NULL; talloc_free(endp->local_options.string); endp->local_options.string = NULL; talloc_free(endp->local_options.codec); endp->local_options.codec = NULL; mgcp_rtp_end_reset(&endp->bts_end); mgcp_rtp_end_reset(&endp->net_end); mgcp_rtp_end_reset(&endp->trans_net); mgcp_rtp_end_reset(&endp->trans_bts); endp->type = MGCP_RTP_DEFAULT; memset(&endp->net_state, 0, sizeof(endp->net_state)); memset(&endp->bts_state, 0, sizeof(endp->bts_state)); endp->conn_mode = endp->orig_mode = MGCP_CONN_NONE; if (endp->osmux.state == OSMUX_STATE_ENABLED) osmux_disable_endpoint(endp); /* release the circuit ID if it had been allocated */ osmux_release_cid(endp); memset(&endp->taps, 0, sizeof(endp->taps)); } void mgcp_initialize_endp(struct mgcp_endpoint *endp) { return mgcp_release_endp(endp); } static int send_trans(struct mgcp_config *cfg, const char *buf, int len) { struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr = cfg->transcoder_in; addr.sin_port = htons(2427); return sendto(cfg->gw_fd.bfd.fd, buf, len, 0, (struct sockaddr *) &addr, sizeof(addr)); } static void send_msg(struct mgcp_endpoint *endp, int endpoint, int port, const char *msg, const char *mode) { char buf[2096]; int len; int nchars; /* hardcoded to AMR right now, we do not know the real type at this point */ len = snprintf(buf, sizeof(buf), "%s 42 %x@mgw MGCP 1.0\r\n" "C: 4256\r\n" "M: %s\r\n" "\r\n", msg, endpoint, mode); if (len < 0) return; nchars = write_response_sdp(endp, buf + len, sizeof(buf) + len - 1, NULL); if (nchars < 0) return; len += nchars; buf[sizeof(buf) - 1] = '\0'; send_trans(endp->cfg, buf, len); } static void send_dlcx(struct mgcp_endpoint *endp, int endpoint) { char buf[2096]; int len; len = snprintf(buf, sizeof(buf), "DLCX 43 %x@mgw MGCP 1.0\r\n" "C: 4256\r\n" , endpoint); if (len < 0) return; buf[sizeof(buf) - 1] = '\0'; send_trans(endp->cfg, buf, len); } static int send_agent(struct mgcp_config *cfg, const char *buf, int len) { return write(cfg->gw_fd.bfd.fd, buf, len); } int mgcp_send_reset_all(struct mgcp_config *cfg) { static const char mgcp_reset[] = { "RSIP 1 *@mgw MGCP 1.0\r\n" }; return send_agent(cfg, mgcp_reset, sizeof mgcp_reset -1); } int mgcp_send_reset_ep(struct mgcp_endpoint *endp, int endpoint) { char buf[128]; int len; len = snprintf(buf, sizeof(buf), "RSIP 39 %x@mgw MGCP 1.0\r\n" , endpoint); if (len < 0) return len; buf[sizeof(buf) - 1] = '\0'; return send_agent(endp->cfg, buf, len); } static int setup_rtp_processing(struct mgcp_endpoint *endp) { int rc = 0; struct mgcp_config *cfg = endp->cfg; if (endp->type != MGCP_RTP_DEFAULT) return 0; if (endp->conn_mode == MGCP_CONN_LOOPBACK) return 0; if (endp->conn_mode & MGCP_CONN_SEND_ONLY) rc |= cfg->setup_rtp_processing_cb(endp, &endp->net_end, &endp->bts_end); else rc |= cfg->setup_rtp_processing_cb(endp, &endp->net_end, NULL); if (endp->conn_mode & MGCP_CONN_RECV_ONLY) rc |= cfg->setup_rtp_processing_cb(endp, &endp->bts_end, &endp->net_end); else rc |= cfg->setup_rtp_processing_cb(endp, &endp->bts_end, NULL); return rc; } static void create_transcoder(struct mgcp_endpoint *endp) { int port; int in_endp = ENDPOINT_NUMBER(endp); int out_endp = endp_back_channel(in_endp); if (endp->type != MGCP_RTP_TRANSCODED) return; send_msg(endp, in_endp, endp->trans_bts.local_port, "CRCX", "sendrecv"); send_msg(endp, in_endp, endp->trans_bts.local_port, "MDCX", "sendrecv"); send_msg(endp, out_endp, endp->trans_net.local_port, "CRCX", "sendrecv"); send_msg(endp, out_endp, endp->trans_net.local_port, "MDCX", "sendrecv"); port = rtp_calculate_port(in_endp, endp->cfg->transcoder_remote_base); endp->trans_bts.rtp_port = htons(port); endp->trans_bts.rtcp_port = htons(port + 1); port = rtp_calculate_port(out_endp, endp->cfg->transcoder_remote_base); endp->trans_net.rtp_port = htons(port); endp->trans_net.rtcp_port = htons(port + 1); } static void delete_transcoder(struct mgcp_endpoint *endp) { int in_endp = ENDPOINT_NUMBER(endp); int out_endp = endp_back_channel(in_endp); if (endp->type != MGCP_RTP_TRANSCODED) return; send_dlcx(endp, in_endp); send_dlcx(endp, out_endp); } int mgcp_reset_transcoder(struct mgcp_config *cfg) { if (!cfg->transcoder_ip) return 0; static const char mgcp_reset[] = { "RSIP 1 13@mgw MGCP 1.0\r\n" }; return send_trans(cfg, mgcp_reset, sizeof mgcp_reset -1); } void mgcp_format_stats(struct mgcp_endpoint *endp, char *msg, size_t size) { uint32_t expected, jitter; int ploss; int nchars; mgcp_state_calc_loss(&endp->net_state, &endp->net_end, &expected, &ploss); jitter = mgcp_state_calc_jitter(&endp->net_state); nchars = snprintf(msg, size, "\r\nP: PS=%u, OS=%u, PR=%u, OR=%u, PL=%d, JI=%u", endp->bts_end.packets, endp->bts_end.octets, endp->net_end.packets, endp->net_end.octets, ploss, jitter); if (nchars < 0 || nchars >= size) goto truncate; msg += nchars; size -= nchars; /* Error Counter */ nchars = snprintf(msg, size, "\r\nX-Osmo-CP: EC TIS=%u, TOS=%u, TIR=%u, TOR=%u", endp->net_state.in_stream.err_ts_counter, endp->net_state.out_stream.err_ts_counter, endp->bts_state.in_stream.err_ts_counter, endp->bts_state.out_stream.err_ts_counter); if (nchars < 0 || nchars >= size) goto truncate; msg += nchars; size -= nchars; if (endp->osmux.state == OSMUX_STATE_ENABLED) { snprintf(msg, size, "\r\nX-Osmux-ST: CR=%u, BR=%u", endp->osmux.stats.chunks, endp->osmux.stats.octets); } truncate: msg[size - 1] = '\0'; } int mgcp_parse_stats(struct msgb *msg, uint32_t *ps, uint32_t *os, uint32_t *pr, uint32_t *_or, int *loss, uint32_t *jitter) { char *line, *save; int rc; /* initialize with bad values */ *ps = *os = *pr = *_or = *jitter = UINT_MAX; *loss = INT_MAX; line = strtok_r((char *) msg->l2h, "\r\n", &save); if (!line) return -1; /* this can only parse the message that is created above... */ for_each_non_empty_line(line, save) { switch (line[0]) { case 'P': rc = sscanf(line, "P: PS=%u, OS=%u, PR=%u, OR=%u, PL=%d, JI=%u", ps, os, pr, _or, loss, jitter); return rc == 6 ? 0 : -1; } } return -1; } openbsc-0.15.0/openbsc/src/libmgcp/mgcp_sdp.c000066400000000000000000000170711265565154000210420ustar00rootroot00000000000000/* * Some SDP file parsing... * * (C) 2009-2015 by Holger Hans Peter Freyther * (C) 2009-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 . * */ #include #include #include struct sdp_rtp_map { /* the type */ int payload_type; /* null, static or later dynamic codec name */ char *codec_name; /* A pointer to the original line for later parsing */ char *map_line; int rate; int channels; }; int mgcp_set_audio_info(void *ctx, struct mgcp_rtp_codec *codec, int payload_type, const char *audio_name) { int rate = codec->rate; int channels = codec->channels; char audio_codec[64]; talloc_free(codec->subtype_name); codec->subtype_name = NULL; talloc_free(codec->audio_name); codec->audio_name = NULL; if (payload_type != PTYPE_UNDEFINED) codec->payload_type = payload_type; if (!audio_name) { switch (payload_type) { case 0: audio_name = "PCMU/8000/1"; break; case 3: audio_name = "GSM/8000/1"; break; case 8: audio_name = "PCMA/8000/1"; break; case 18: audio_name = "G729/8000/1"; break; default: /* Payload type is unknown, don't change rate and * channels. */ /* TODO: return value? */ return 0; } } if (sscanf(audio_name, "%63[^/]/%d/%d", audio_codec, &rate, &channels) < 1) return -EINVAL; codec->rate = rate; codec->channels = channels; codec->subtype_name = talloc_strdup(ctx, audio_codec); codec->audio_name = talloc_strdup(ctx, audio_name); if (!strcmp(audio_codec, "G729")) { codec->frame_duration_num = 10; codec->frame_duration_den = 1000; } else { codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM; codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN; } if (payload_type < 0) { payload_type = 96; if (rate == 8000 && channels == 1) { if (!strcmp(audio_codec, "GSM")) payload_type = 3; else if (!strcmp(audio_codec, "PCMA")) payload_type = 8; else if (!strcmp(audio_codec, "PCMU")) payload_type = 0; else if (!strcmp(audio_codec, "G729")) payload_type = 18; } codec->payload_type = payload_type; } if (channels != 1) LOGP(DMGCP, LOGL_NOTICE, "Channels != 1 in SDP: '%s'\n", audio_name); return 0; } void codecs_initialize(void *ctx, struct sdp_rtp_map *codecs, int used) { int i; for (i = 0; i < used; ++i) { switch (codecs[i].payload_type) { case 0: codecs[i].codec_name = "PCMU"; codecs[i].rate = 8000; codecs[i].channels = 1; break; case 3: codecs[i].codec_name = "GSM"; codecs[i].rate = 8000; codecs[i].channels = 1; break; case 8: codecs[i].codec_name = "PCMA"; codecs[i].rate = 8000; codecs[i].channels = 1; break; case 18: codecs[i].codec_name = "G729"; codecs[i].rate = 8000; codecs[i].channels = 1; break; } } } void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used, int payload, char *audio_name) { int i; for (i = 0; i < used; ++i) { char audio_codec[64]; int rate = -1; int channels = -1; if (codecs[i].payload_type != payload) continue; if (sscanf(audio_name, "%63[^/]/%d/%d", audio_codec, &rate, &channels) < 1) { LOGP(DMGCP, LOGL_ERROR, "Failed to parse '%s'\n", audio_name); continue; } codecs[i].map_line = talloc_strdup(ctx, audio_name); codecs[i].codec_name = talloc_strdup(ctx, audio_codec); codecs[i].rate = rate; codecs[i].channels = channels; return; } LOGP(DMGCP, LOGL_ERROR, "Unconfigured PT(%d) with %s\n", payload, audio_name); } int is_codec_compatible(struct mgcp_endpoint *endp, struct sdp_rtp_map *codec) { char *bts_codec; char audio_codec[64]; if (!codec->codec_name) return 0; /* * GSM, GSM/8000 and GSM/8000/1 should all be compatible.. let's go * by name first. */ bts_codec = endp->tcfg->audio_name; if (sscanf(bts_codec, "%63[^/]/%*d/%*d", audio_codec) < 1) return 0; return strcasecmp(audio_codec, codec->codec_name) == 0; } int mgcp_parse_sdp_data(struct mgcp_endpoint *endp, struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p) { struct sdp_rtp_map codecs[10]; int codecs_used = 0; char *line; int maxptime = -1; int i; int codecs_assigned = 0; void *tmp_ctx = talloc_new(NULL); memset(&codecs, 0, sizeof(codecs)); for_each_line(line, p->save) { switch (line[0]) { case 'o': case 's': case 't': case 'v': /* skip these SDP attributes */ break; case 'a': { int payload; int ptime, ptime2 = 0; char audio_name[64]; if (sscanf(line, "a=rtpmap:%d %63s", &payload, audio_name) == 2) { codecs_update(tmp_ctx, codecs, codecs_used, payload, audio_name); } else if (sscanf(line, "a=ptime:%d-%d", &ptime, &ptime2) >= 1) { if (ptime2 > 0 && ptime2 != ptime) rtp->packet_duration_ms = 0; else rtp->packet_duration_ms = ptime; } else if (sscanf(line, "a=maxptime:%d", &ptime2) == 1) { maxptime = ptime2; } break; } case 'm': { int port, rc; rc = sscanf(line, "m=audio %d RTP/AVP %d %d %d %d %d %d %d %d %d %d", &port, &codecs[0].payload_type, &codecs[1].payload_type, &codecs[2].payload_type, &codecs[3].payload_type, &codecs[4].payload_type, &codecs[5].payload_type, &codecs[6].payload_type, &codecs[7].payload_type, &codecs[8].payload_type, &codecs[9].payload_type); if (rc >= 2) { rtp->rtp_port = htons(port); rtp->rtcp_port = htons(port + 1); codecs_used = rc - 1; codecs_initialize(tmp_ctx, codecs, codecs_used); } break; } case 'c': { char ipv4[16]; if (sscanf(line, "c=IN IP4 %15s", ipv4) == 1) { inet_aton(ipv4, &rtp->addr); } break; } default: if (p->endp) LOGP(DMGCP, LOGL_NOTICE, "Unhandled SDP option: '%c'/%d on 0x%x\n", line[0], line[0], ENDPOINT_NUMBER(p->endp)); else LOGP(DMGCP, LOGL_NOTICE, "Unhandled SDP option: '%c'/%d\n", line[0], line[0]); break; } } /* Now select the primary and alt_codec */ for (i = 0; i < codecs_used && codecs_assigned < 2; ++i) { struct mgcp_rtp_codec *codec = codecs_assigned == 0 ? &rtp->codec : &rtp->alt_codec; if (endp->tcfg->no_audio_transcoding && !is_codec_compatible(endp, &codecs[i])) { LOGP(DMGCP, LOGL_NOTICE, "Skipping codec %s\n", codecs[i].codec_name); continue; } mgcp_set_audio_info(p->cfg, codec, codecs[i].payload_type, codecs[i].map_line); codecs_assigned += 1; } if (codecs_assigned > 0) { /* TODO/XXX: Store this per codec and derive it on use */ if (maxptime >= 0 && maxptime * rtp->codec.frame_duration_den > rtp->codec.frame_duration_num * 1500) { /* more than 1 frame */ rtp->packet_duration_ms = 0; } LOGP(DMGCP, LOGL_NOTICE, "Got media info via SDP: port %d, payload %d (%s), " "duration %d, addr %s\n", ntohs(rtp->rtp_port), rtp->codec.payload_type, rtp->codec.subtype_name ? rtp->codec.subtype_name : "unknown", rtp->packet_duration_ms, inet_ntoa(rtp->addr)); } talloc_free(tmp_ctx); return codecs_assigned > 0; } openbsc-0.15.0/openbsc/src/libmgcp/mgcp_transcode.c000066400000000000000000000376721265565154000222470ustar00rootroot00000000000000/* * (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 . * */ #include #include #include #include "g711common.h" #include #include #include #include #include #include int mgcp_transcoding_get_frame_size(void *state_, int nsamples, int dst) { struct mgcp_process_rtp_state *state = state_; if (dst) return (nsamples >= 0 ? nsamples / state->dst_samples_per_frame : 1) * state->dst_frame_size; else return (nsamples >= 0 ? nsamples / state->src_samples_per_frame : 1) * state->src_frame_size; } static enum audio_format get_audio_format(const struct mgcp_rtp_codec *codec) { if (codec->subtype_name) { if (!strcasecmp("GSM", codec->subtype_name)) return AF_GSM; if (!strcasecmp("PCMA", codec->subtype_name)) return AF_PCMA; if (!strcasecmp("PCMU", codec->subtype_name)) return AF_PCMU; #ifdef HAVE_BCG729 if (!strcasecmp("G729", codec->subtype_name)) return AF_G729; #endif if (!strcasecmp("L16", codec->subtype_name)) return AF_L16; } switch (codec->payload_type) { case 0 /* PCMU */: return AF_PCMU; case 3 /* GSM */: return AF_GSM; case 8 /* PCMA */: return AF_PCMA; #ifdef HAVE_BCG729 case 18 /* G.729 */: return AF_G729; #endif case 11 /* L16 */: return AF_L16; default: return AF_INVALID; } } static void l16_encode(short *sample, unsigned char *buf, size_t n) { for (; n > 0; --n, ++sample, buf += 2) { buf[0] = sample[0] >> 8; buf[1] = sample[0] & 0xff; } } static void l16_decode(unsigned char *buf, short *sample, size_t n) { for (; n > 0; --n, ++sample, buf += 2) sample[0] = ((short)buf[0] << 8) | buf[1]; } static void alaw_encode(short *sample, unsigned char *buf, size_t n) { for (; n > 0; --n) *(buf++) = s16_to_alaw(*(sample++)); } static void alaw_decode(unsigned char *buf, short *sample, size_t n) { for (; n > 0; --n) *(sample++) = alaw_to_s16(*(buf++)); } static void ulaw_encode(short *sample, unsigned char *buf, size_t n) { for (; n > 0; --n) *(buf++) = s16_to_ulaw(*(sample++)); } static void ulaw_decode(unsigned char *buf, short *sample, size_t n) { for (; n > 0; --n) *(sample++) = ulaw_to_s16(*(buf++)); } static int processing_state_destructor(struct mgcp_process_rtp_state *state) { switch (state->src_fmt) { case AF_GSM: if (state->src.gsm_handle) gsm_destroy(state->src.gsm_handle); break; #ifdef HAVE_BCG729 case AF_G729: if (state->src.g729_dec) closeBcg729DecoderChannel(state->src.g729_dec); break; #endif default: break; } switch (state->dst_fmt) { case AF_GSM: if (state->dst.gsm_handle) gsm_destroy(state->dst.gsm_handle); break; #ifdef HAVE_BCG729 case AF_G729: if (state->dst.g729_enc) closeBcg729EncoderChannel(state->dst.g729_enc); break; #endif default: break; } return 0; } int mgcp_transcoding_setup(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end, struct mgcp_rtp_end *src_end) { struct mgcp_process_rtp_state *state; enum audio_format src_fmt, dst_fmt; const struct mgcp_rtp_codec *dst_codec = &dst_end->codec; /* cleanup first */ if (dst_end->rtp_process_data) { talloc_free(dst_end->rtp_process_data); dst_end->rtp_process_data = NULL; } if (!src_end) return 0; const struct mgcp_rtp_codec *src_codec = &src_end->codec; if (endp->tcfg->no_audio_transcoding) { LOGP(DMGCP, LOGL_NOTICE, "Transcoding disabled on endpoint 0x%x\n", ENDPOINT_NUMBER(endp)); return 0; } src_fmt = get_audio_format(src_codec); dst_fmt = get_audio_format(dst_codec); LOGP(DMGCP, LOGL_ERROR, "Checking transcoding: %s (%d) -> %s (%d)\n", src_codec->subtype_name, src_codec->payload_type, dst_codec->subtype_name, dst_codec->payload_type); if (src_fmt == AF_INVALID || dst_fmt == AF_INVALID) { if (!src_codec->subtype_name || !dst_codec->subtype_name) /* Not enough info, do nothing */ return 0; if (strcasecmp(src_codec->subtype_name, dst_codec->subtype_name) == 0) /* Nothing to do */ return 0; LOGP(DMGCP, LOGL_ERROR, "Cannot transcode: %s codec not supported (%s -> %s).\n", src_fmt != AF_INVALID ? "destination" : "source", src_codec->audio_name, dst_codec->audio_name); return -EINVAL; } if (src_codec->rate && dst_codec->rate && src_codec->rate != dst_codec->rate) { LOGP(DMGCP, LOGL_ERROR, "Cannot transcode: rate conversion (%d -> %d) not supported.\n", src_codec->rate, dst_codec->rate); return -EINVAL; } state = talloc_zero(endp->tcfg->cfg, struct mgcp_process_rtp_state); talloc_set_destructor(state, processing_state_destructor); dst_end->rtp_process_data = state; state->src_fmt = src_fmt; switch (state->src_fmt) { case AF_L16: case AF_S16: state->src_frame_size = 80 * sizeof(short); state->src_samples_per_frame = 80; break; case AF_GSM: state->src_frame_size = sizeof(gsm_frame); state->src_samples_per_frame = 160; state->src.gsm_handle = gsm_create(); if (!state->src.gsm_handle) { LOGP(DMGCP, LOGL_ERROR, "Failed to initialize GSM decoder.\n"); return -EINVAL; } break; #ifdef HAVE_BCG729 case AF_G729: state->src_frame_size = 10; state->src_samples_per_frame = 80; state->src.g729_dec = initBcg729DecoderChannel(); if (!state->src.g729_dec) { LOGP(DMGCP, LOGL_ERROR, "Failed to initialize G.729 decoder.\n"); return -EINVAL; } break; #endif case AF_PCMU: case AF_PCMA: state->src_frame_size = 80; state->src_samples_per_frame = 80; break; default: break; } state->dst_fmt = dst_fmt; switch (state->dst_fmt) { case AF_L16: case AF_S16: state->dst_frame_size = 80*sizeof(short); state->dst_samples_per_frame = 80; break; case AF_GSM: state->dst_frame_size = sizeof(gsm_frame); state->dst_samples_per_frame = 160; state->dst.gsm_handle = gsm_create(); if (!state->dst.gsm_handle) { LOGP(DMGCP, LOGL_ERROR, "Failed to initialize GSM encoder.\n"); return -EINVAL; } break; #ifdef HAVE_BCG729 case AF_G729: state->dst_frame_size = 10; state->dst_samples_per_frame = 80; state->dst.g729_enc = initBcg729EncoderChannel(); if (!state->dst.g729_enc) { LOGP(DMGCP, LOGL_ERROR, "Failed to initialize G.729 decoder.\n"); return -EINVAL; } break; #endif case AF_PCMU: case AF_PCMA: state->dst_frame_size = 80; state->dst_samples_per_frame = 80; break; default: break; } if (dst_end->force_output_ptime) state->dst_packet_duration = mgcp_rtp_packet_duration(endp, dst_end); LOGP(DMGCP, LOGL_INFO, "Initialized RTP processing on: 0x%x " "conv: %d (%d, %d, %s) -> %d (%d, %d, %s)\n", ENDPOINT_NUMBER(endp), src_fmt, src_codec->payload_type, src_codec->rate, src_end->fmtp_extra, dst_fmt, dst_codec->payload_type, dst_codec->rate, dst_end->fmtp_extra); return 0; } void mgcp_transcoding_net_downlink_format(struct mgcp_endpoint *endp, int *payload_type, const char**audio_name, const char**fmtp_extra) { struct mgcp_process_rtp_state *state = endp->net_end.rtp_process_data; struct mgcp_rtp_codec *net_codec = &endp->net_end.codec; struct mgcp_rtp_codec *bts_codec = &endp->bts_end.codec; if (!state || net_codec->payload_type < 0) { *payload_type = bts_codec->payload_type; *audio_name = bts_codec->audio_name; *fmtp_extra = endp->bts_end.fmtp_extra; return; } *payload_type = net_codec->payload_type; *audio_name = net_codec->audio_name; *fmtp_extra = endp->net_end.fmtp_extra; } static int decode_audio(struct mgcp_process_rtp_state *state, uint8_t **src, size_t *nbytes) { while (*nbytes >= state->src_frame_size) { if (state->sample_cnt + state->src_samples_per_frame > ARRAY_SIZE(state->samples)) { LOGP(DMGCP, LOGL_ERROR, "Sample buffer too small: %d > %d.\n", state->sample_cnt + state->src_samples_per_frame, ARRAY_SIZE(state->samples)); return -ENOSPC; } switch (state->src_fmt) { case AF_GSM: if (gsm_decode(state->src.gsm_handle, (gsm_byte *)*src, state->samples + state->sample_cnt) < 0) { LOGP(DMGCP, LOGL_ERROR, "Failed to decode GSM.\n"); return -EINVAL; } break; #ifdef HAVE_BCG729 case AF_G729: bcg729Decoder(state->src.g729_dec, *src, 0, state->samples + state->sample_cnt); break; #endif case AF_PCMU: ulaw_decode(*src, state->samples + state->sample_cnt, state->src_samples_per_frame); break; case AF_PCMA: alaw_decode(*src, state->samples + state->sample_cnt, state->src_samples_per_frame); break; case AF_S16: memmove(state->samples + state->sample_cnt, *src, state->src_frame_size); break; case AF_L16: l16_decode(*src, state->samples + state->sample_cnt, state->src_samples_per_frame); break; default: break; } *src += state->src_frame_size; *nbytes -= state->src_frame_size; state->sample_cnt += state->src_samples_per_frame; } return 0; } static int encode_audio(struct mgcp_process_rtp_state *state, uint8_t *dst, size_t buf_size, size_t max_samples) { int nbytes = 0; size_t nsamples = 0; /* Encode samples into dst */ while (nsamples + state->dst_samples_per_frame <= max_samples) { if (nbytes + state->dst_frame_size > buf_size) { if (nbytes > 0) break; /* Not even one frame fits into the buffer */ LOGP(DMGCP, LOGL_INFO, "Encoding (RTP) buffer too small: %d > %d.\n", nbytes + state->dst_frame_size, buf_size); return -ENOSPC; } switch (state->dst_fmt) { case AF_GSM: gsm_encode(state->dst.gsm_handle, state->samples + state->sample_offs, dst); break; #ifdef HAVE_BCG729 case AF_G729: bcg729Encoder(state->dst.g729_enc, state->samples + state->sample_offs, dst); break; #endif case AF_PCMU: ulaw_encode(state->samples + state->sample_offs, dst, state->src_samples_per_frame); break; case AF_PCMA: alaw_encode(state->samples + state->sample_offs, dst, state->src_samples_per_frame); break; case AF_S16: memmove(dst, state->samples + state->sample_offs, state->dst_frame_size); break; case AF_L16: l16_encode(state->samples + state->sample_offs, dst, state->src_samples_per_frame); break; default: break; } dst += state->dst_frame_size; nbytes += state->dst_frame_size; state->sample_offs += state->dst_samples_per_frame; nsamples += state->dst_samples_per_frame; } state->sample_cnt -= nsamples; return nbytes; } static struct mgcp_rtp_end *source_for_dest(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end) { if (&endp->bts_end == dst_end) return &endp->net_end; else if (&endp->net_end == dst_end) return &endp->bts_end; OSMO_ASSERT(0); } /* * With some modems we get offered multiple codecs * and we have selected one of them. It might not * be the right one and we need to detect this with * the first audio packets. One difficulty is that * we patch the rtp payload type in place, so we * need to discuss this. */ struct mgcp_process_rtp_state *check_transcode_state( struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end, struct rtp_hdr *rtp_hdr) { struct mgcp_rtp_end *src_end; /* Only deal with messages from net to bts */ if (&endp->bts_end != dst_end) goto done; src_end = source_for_dest(endp, dst_end); /* Already patched */ if (rtp_hdr->payload_type == dst_end->codec.payload_type) goto done; /* The payload we expect */ if (rtp_hdr->payload_type == src_end->codec.payload_type) goto done; /* The matching alternate payload type? Then switch */ if (rtp_hdr->payload_type == src_end->alt_codec.payload_type) { struct mgcp_config *cfg = endp->cfg; struct mgcp_rtp_codec tmp_codec = src_end->alt_codec; src_end->alt_codec = src_end->codec; src_end->codec = tmp_codec; cfg->setup_rtp_processing_cb(endp, &endp->net_end, &endp->bts_end); cfg->setup_rtp_processing_cb(endp, &endp->bts_end, &endp->net_end); } done: return dst_end->rtp_process_data; } int mgcp_transcoding_process_rtp(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end, char *data, int *len, int buf_size) { struct mgcp_process_rtp_state *state; const size_t rtp_hdr_size = sizeof(struct rtp_hdr); struct rtp_hdr *rtp_hdr = (struct rtp_hdr *) data; char *payload_data = (char *) &rtp_hdr->data[0]; int payload_len = *len - rtp_hdr_size; uint8_t *src = (uint8_t *)payload_data; uint8_t *dst = (uint8_t *)payload_data; size_t nbytes = payload_len; size_t nsamples; size_t max_samples; uint32_t ts_no; int rc; state = check_transcode_state(endp, dst_end, rtp_hdr); if (!state) return 0; if (state->src_fmt == state->dst_fmt) { if (!state->dst_packet_duration) return 0; /* TODO: repackage without transcoding */ } /* If the remaining samples do not fit into a fixed ptime, * a) discard them, if the next packet is much later * b) add silence and * send it, if the current packet is not * yet too late * c) append the sample data, if the timestamp matches exactly */ /* TODO: check payload type (-> G.711 comfort noise) */ if (payload_len > 0) { ts_no = ntohl(rtp_hdr->timestamp); if (!state->is_running) { state->next_seq = ntohs(rtp_hdr->sequence); state->next_time = ts_no; state->is_running = 1; } if (state->sample_cnt > 0) { int32_t delta = ts_no - state->next_time; /* TODO: check sequence? reordering? packet loss? */ if (delta > state->sample_cnt) { /* There is a time gap between the last packet * and the current one. Just discard the * partial data that is left in the buffer. * TODO: This can be improved by adding silence * instead if the delta is small enough. */ LOGP(DMGCP, LOGL_NOTICE, "0x%x dropping sample buffer due delta=%d sample_cnt=%d\n", ENDPOINT_NUMBER(endp), delta, state->sample_cnt); state->sample_cnt = 0; state->next_time = ts_no; } else if (delta < 0) { LOGP(DMGCP, LOGL_NOTICE, "RTP time jumps backwards, delta = %d, " "discarding buffered samples\n", delta); state->sample_cnt = 0; state->sample_offs = 0; return -EAGAIN; } /* Make sure the samples start without offset */ if (state->sample_offs && state->sample_cnt) memmove(&state->samples[0], &state->samples[state->sample_offs], state->sample_cnt * sizeof(state->samples[0])); } state->sample_offs = 0; /* Append decoded audio to samples */ decode_audio(state, &src, &nbytes); if (nbytes > 0) LOGP(DMGCP, LOGL_NOTICE, "Skipped audio frame in RTP packet: %d octets\n", nbytes); } else ts_no = state->next_time; if (state->sample_cnt < state->dst_packet_duration) return -EAGAIN; max_samples = state->dst_packet_duration ? state->dst_packet_duration : state->sample_cnt; nsamples = state->sample_cnt; rc = encode_audio(state, dst, buf_size, max_samples); /* * There were no samples to encode? * TODO: how does this work for comfort noise? */ if (rc == 0) return -ENOMSG; /* Any other error during the encoding */ if (rc < 0) return rc; nsamples -= state->sample_cnt; *len = rtp_hdr_size + rc; rtp_hdr->sequence = htons(state->next_seq); rtp_hdr->timestamp = htonl(ts_no); state->next_seq += 1; state->next_time = ts_no + nsamples; /* * XXX: At this point we should always have consumed * samples. So doing OSMO_ASSERT(nsamples > 0) and returning * rtp_hdr_size should be fine. */ return nsamples ? rtp_hdr_size : 0; } openbsc-0.15.0/openbsc/src/libmgcp/mgcp_vty.c000066400000000000000000001266751265565154000211110ustar00rootroot00000000000000/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */ /* The protocol implementation */ /* * (C) 2009-2014 by Holger Hans Peter Freyther * (C) 2009-2011 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 . * */ #include #include #include #include #include #define RTCP_OMIT_STR "Drop RTCP packets in both directions\n" #define RTP_PATCH_STR "Modify RTP packet header in both directions\n" #define RTP_KEEPALIVE_STR "Send dummy UDP packet to net RTP destination\n" static struct mgcp_config *g_cfg = NULL; static struct mgcp_trunk_config *find_trunk(struct mgcp_config *cfg, int nr) { struct mgcp_trunk_config *trunk; if (nr == 0) trunk = &cfg->trunk; else trunk = mgcp_trunk_num(cfg, nr); return trunk; } /* * vty code for mgcp below */ struct cmd_node mgcp_node = { MGCP_NODE, "%s(config-mgcp)# ", 1, }; struct cmd_node trunk_node = { TRUNK_NODE, "%s(config-mgcp-trunk)# ", 1, }; static int config_write_mgcp(struct vty *vty) { vty_out(vty, "mgcp%s", VTY_NEWLINE); if (g_cfg->local_ip) vty_out(vty, " local ip %s%s", g_cfg->local_ip, VTY_NEWLINE); if (g_cfg->bts_ip && strlen(g_cfg->bts_ip) != 0) vty_out(vty, " bts ip %s%s", g_cfg->bts_ip, VTY_NEWLINE); vty_out(vty, " bind ip %s%s", g_cfg->source_addr, VTY_NEWLINE); vty_out(vty, " bind port %u%s", g_cfg->source_port, VTY_NEWLINE); if (g_cfg->bts_ports.mode == PORT_ALLOC_STATIC) vty_out(vty, " rtp bts-base %u%s", g_cfg->bts_ports.base_port, VTY_NEWLINE); else vty_out(vty, " rtp bts-range %u %u%s", g_cfg->bts_ports.range_start, g_cfg->bts_ports.range_end, VTY_NEWLINE); if (g_cfg->bts_ports.bind_addr) vty_out(vty, " rtp bts-bind-ip %s%s", g_cfg->bts_ports.bind_addr, VTY_NEWLINE); if (g_cfg->net_ports.mode == PORT_ALLOC_STATIC) vty_out(vty, " rtp net-base %u%s", g_cfg->net_ports.base_port, VTY_NEWLINE); else vty_out(vty, " rtp net-range %u %u%s", g_cfg->net_ports.range_start, g_cfg->net_ports.range_end, VTY_NEWLINE); if (g_cfg->net_ports.bind_addr) vty_out(vty, " rtp net-bind-ip %s%s", g_cfg->net_ports.bind_addr, VTY_NEWLINE); vty_out(vty, " rtp ip-dscp %d%s", g_cfg->endp_dscp, VTY_NEWLINE); if (g_cfg->trunk.keepalive_interval == MGCP_KEEPALIVE_ONCE) vty_out(vty, " rtp keep-alive once%s", VTY_NEWLINE); else if (g_cfg->trunk.keepalive_interval) vty_out(vty, " rtp keep-alive %d%s", g_cfg->trunk.keepalive_interval, VTY_NEWLINE); else vty_out(vty, " no rtp keep-alive%s", VTY_NEWLINE); if (g_cfg->trunk.omit_rtcp) vty_out(vty, " rtcp-omit%s", VTY_NEWLINE); else vty_out(vty, " no rtcp-omit%s", VTY_NEWLINE); if (g_cfg->trunk.force_constant_ssrc || g_cfg->trunk.force_aligned_timing) { vty_out(vty, " %srtp-patch ssrc%s", g_cfg->trunk.force_constant_ssrc ? "" : "no ", VTY_NEWLINE); vty_out(vty, " %srtp-patch timestamp%s", g_cfg->trunk.force_aligned_timing ? "" : "no ", VTY_NEWLINE); } else vty_out(vty, " no rtp-patch%s", VTY_NEWLINE); if (g_cfg->trunk.audio_payload != -1) vty_out(vty, " sdp audio-payload number %d%s", g_cfg->trunk.audio_payload, VTY_NEWLINE); if (g_cfg->trunk.audio_name) vty_out(vty, " sdp audio-payload name %s%s", g_cfg->trunk.audio_name, VTY_NEWLINE); if (g_cfg->trunk.audio_fmtp_extra) vty_out(vty, " sdp audio fmtp-extra %s%s", g_cfg->trunk.audio_fmtp_extra, VTY_NEWLINE); vty_out(vty, " %ssdp audio-payload send-ptime%s", g_cfg->trunk.audio_send_ptime ? "" : "no ", VTY_NEWLINE); vty_out(vty, " %ssdp audio-payload send-name%s", g_cfg->trunk.audio_send_name ? "" : "no ", VTY_NEWLINE); vty_out(vty, " loop %u%s", !!g_cfg->trunk.audio_loop, VTY_NEWLINE); vty_out(vty, " number endpoints %u%s", g_cfg->trunk.number_endpoints - 1, VTY_NEWLINE); vty_out(vty, " %sallow-transcoding%s", g_cfg->trunk.no_audio_transcoding ? "no " : "", VTY_NEWLINE); if (g_cfg->call_agent_addr) vty_out(vty, " call-agent ip %s%s", g_cfg->call_agent_addr, VTY_NEWLINE); if (g_cfg->transcoder_ip) vty_out(vty, " transcoder-mgw %s%s", g_cfg->transcoder_ip, VTY_NEWLINE); if (g_cfg->transcoder_ports.mode == PORT_ALLOC_STATIC) vty_out(vty, " rtp transcoder-base %u%s", g_cfg->transcoder_ports.base_port, VTY_NEWLINE); else vty_out(vty, " rtp transcoder-range %u %u%s", g_cfg->transcoder_ports.range_start, g_cfg->transcoder_ports.range_end, VTY_NEWLINE); if (g_cfg->bts_force_ptime > 0) vty_out(vty, " rtp force-ptime %d%s", g_cfg->bts_force_ptime, VTY_NEWLINE); vty_out(vty, " transcoder-remote-base %u%s", g_cfg->transcoder_remote_base, VTY_NEWLINE); switch (g_cfg->osmux) { case OSMUX_USAGE_ON: vty_out(vty, " osmux on%s", VTY_NEWLINE); break; case OSMUX_USAGE_ONLY: vty_out(vty, " osmux only%s", VTY_NEWLINE); break; case OSMUX_USAGE_OFF: default: vty_out(vty, " osmux off%s", VTY_NEWLINE); break; } if (g_cfg->osmux) { vty_out(vty, " osmux batch-factor %d%s", g_cfg->osmux_batch, VTY_NEWLINE); vty_out(vty, " osmux batch-size %u%s", g_cfg->osmux_batch_size, VTY_NEWLINE); vty_out(vty, " osmux port %u%s", g_cfg->osmux_port, VTY_NEWLINE); vty_out(vty, " osmux dummy %s%s", g_cfg->osmux_dummy ? "on" : "off", VTY_NEWLINE); } return CMD_SUCCESS; } static void dump_rtp_end(const char *end_name, struct vty *vty, struct mgcp_rtp_state *state, struct mgcp_rtp_end *end) { struct mgcp_rtp_codec *codec = &end->codec; vty_out(vty, " %s%s" " Timestamp Errs: %d->%d%s" " Dropped Packets: %d%s" " Payload Type: %d Rate: %u Channels: %d %s" " Frame Duration: %u Frame Denominator: %u%s" " FPP: %d Packet Duration: %u%s" " FMTP-Extra: %s Audio-Name: %s Sub-Type: %s%s" " Output-Enabled: %d Force-PTIME: %d%s", end_name, VTY_NEWLINE, state->in_stream.err_ts_counter, state->out_stream.err_ts_counter, VTY_NEWLINE, end->dropped_packets, VTY_NEWLINE, codec->payload_type, codec->rate, codec->channels, VTY_NEWLINE, codec->frame_duration_num, codec->frame_duration_den, VTY_NEWLINE, end->frames_per_packet, end->packet_duration_ms, VTY_NEWLINE, end->fmtp_extra, codec->audio_name, codec->subtype_name, VTY_NEWLINE, end->output_enabled, end->force_output_ptime, VTY_NEWLINE); } static void dump_trunk(struct vty *vty, struct mgcp_trunk_config *cfg, int verbose) { int i; vty_out(vty, "%s trunk nr %d with %d endpoints:%s", cfg->trunk_type == MGCP_TRUNK_VIRTUAL ? "Virtual" : "E1", cfg->trunk_nr, cfg->number_endpoints - 1, VTY_NEWLINE); if (!cfg->endpoints) { vty_out(vty, "No endpoints allocated yet.%s", VTY_NEWLINE); return; } for (i = 1; i < cfg->number_endpoints; ++i) { struct mgcp_endpoint *endp = &cfg->endpoints[i]; vty_out(vty, " Endpoint 0x%.2x: CI: %d net: %u/%u bts: %u/%u on %s " "traffic received bts: %u remote: %u transcoder: %u/%u%s", i, endp->ci, ntohs(endp->net_end.rtp_port), ntohs(endp->net_end.rtcp_port), ntohs(endp->bts_end.rtp_port), ntohs(endp->bts_end.rtcp_port), inet_ntoa(endp->bts_end.addr), endp->bts_end.packets, endp->net_end.packets, endp->trans_net.packets, endp->trans_bts.packets, VTY_NEWLINE); if (verbose && endp->allocated) { dump_rtp_end("Net->BTS", vty, &endp->bts_state, &endp->bts_end); dump_rtp_end("BTS->Net", vty, &endp->net_state, &endp->net_end); } } } DEFUN(show_mcgp, show_mgcp_cmd, "show mgcp [stats]", SHOW_STR "Display information about the MGCP Media Gateway\n" "Include Statistics\n") { struct mgcp_trunk_config *trunk; int show_stats = argc >= 1; dump_trunk(vty, &g_cfg->trunk, show_stats); llist_for_each_entry(trunk, &g_cfg->trunks, entry) dump_trunk(vty, trunk, show_stats); if (g_cfg->osmux) vty_out(vty, "Osmux used CID: %d%s", osmux_used_cid(), VTY_NEWLINE); return CMD_SUCCESS; } DEFUN(cfg_mgcp, cfg_mgcp_cmd, "mgcp", "Configure the MGCP") { vty->node = MGCP_NODE; return CMD_SUCCESS; } DEFUN(cfg_mgcp_local_ip, cfg_mgcp_local_ip_cmd, "local ip A.B.C.D", "Local options for the SDP record\n" IP_STR "IPv4 Address to use in SDP record\n") { bsc_replace_string(g_cfg, &g_cfg->local_ip, argv[0]); return CMD_SUCCESS; } DEFUN(cfg_mgcp_bts_ip, cfg_mgcp_bts_ip_cmd, "bts ip A.B.C.D", "BTS Audio source/destination options\n" IP_STR "IPv4 Address of the BTS\n") { bsc_replace_string(g_cfg, &g_cfg->bts_ip, argv[0]); inet_aton(g_cfg->bts_ip, &g_cfg->bts_in); return CMD_SUCCESS; } #define BIND_STR "Listen/Bind related socket option\n" DEFUN(cfg_mgcp_bind_ip, cfg_mgcp_bind_ip_cmd, "bind ip A.B.C.D", BIND_STR IP_STR "IPv4 Address to bind to\n") { bsc_replace_string(g_cfg, &g_cfg->source_addr, argv[0]); return CMD_SUCCESS; } DEFUN(cfg_mgcp_bind_port, cfg_mgcp_bind_port_cmd, "bind port <0-65534>", BIND_STR "Port information\n" "UDP port to listen for MGCP messages\n") { unsigned int port = atoi(argv[0]); g_cfg->source_port = port; return CMD_SUCCESS; } DEFUN(cfg_mgcp_bind_early, cfg_mgcp_bind_early_cmd, "bind early (0|1)", BIND_STR "Bind local ports on start up\n" "Bind on demand\n" "Bind on startup\n") { vty_out(vty, "bind early is deprecated, remove it from the config.\n"); return CMD_WARNING; } static void parse_base(struct mgcp_port_range *range, const char **argv) { unsigned int port = atoi(argv[0]); range->mode = PORT_ALLOC_STATIC; range->base_port = port; } static void parse_range(struct mgcp_port_range *range, const char **argv) { range->mode = PORT_ALLOC_DYNAMIC; range->range_start = atoi(argv[0]); range->range_end = atoi(argv[1]); range->last_port = g_cfg->bts_ports.range_start; } #define RTP_STR "RTP configuration\n" #define BTS_START_STR "First UDP port allocated for the BTS side\n" #define NET_START_STR "First UDP port allocated for the NET side\n" #define UDP_PORT_STR "UDP Port number\n" DEFUN(cfg_mgcp_rtp_bts_base_port, cfg_mgcp_rtp_bts_base_port_cmd, "rtp bts-base <0-65534>", RTP_STR BTS_START_STR UDP_PORT_STR) { parse_base(&g_cfg->bts_ports, argv); return CMD_SUCCESS; } #define RANGE_START_STR "Start of the range of ports\n" #define RANGE_END_STR "End of the range of ports\n" DEFUN(cfg_mgcp_rtp_bts_range, cfg_mgcp_rtp_bts_range_cmd, "rtp bts-range <0-65534> <0-65534>", RTP_STR "Range of ports to use for the BTS side\n" RANGE_START_STR RANGE_END_STR) { parse_range(&g_cfg->bts_ports, argv); return CMD_SUCCESS; } DEFUN(cfg_mgcp_rtp_net_range, cfg_mgcp_rtp_net_range_cmd, "rtp net-range <0-65534> <0-65534>", RTP_STR "Range of ports to use for the NET side\n" RANGE_START_STR RANGE_END_STR) { parse_range(&g_cfg->net_ports, argv); return CMD_SUCCESS; } DEFUN(cfg_mgcp_rtp_net_base_port, cfg_mgcp_rtp_net_base_port_cmd, "rtp net-base <0-65534>", RTP_STR NET_START_STR UDP_PORT_STR) { parse_base(&g_cfg->net_ports, argv); return CMD_SUCCESS; } ALIAS_DEPRECATED(cfg_mgcp_rtp_bts_base_port, cfg_mgcp_rtp_base_port_cmd, "rtp base <0-65534>", RTP_STR BTS_START_STR UDP_PORT_STR) DEFUN(cfg_mgcp_rtp_transcoder_range, cfg_mgcp_rtp_transcoder_range_cmd, "rtp transcoder-range <0-65534> <0-65534>", RTP_STR "Range of ports to use for the Transcoder\n" RANGE_START_STR RANGE_END_STR) { parse_range(&g_cfg->transcoder_ports, argv); return CMD_SUCCESS; } DEFUN(cfg_mgcp_rtp_transcoder_base, cfg_mgcp_rtp_transcoder_base_cmd, "rtp transcoder-base <0-65534>", RTP_STR "First UDP port allocated for the Transcoder side\n" UDP_PORT_STR) { parse_base(&g_cfg->transcoder_ports, argv); return CMD_SUCCESS; } DEFUN(cfg_mgcp_rtp_bts_bind_ip, cfg_mgcp_rtp_bts_bind_ip_cmd, "rtp bts-bind-ip A.B.C.D", RTP_STR "Bind endpoints facing the BTS\n" "Address to bind to\n") { bsc_replace_string(g_cfg, &g_cfg->bts_ports.bind_addr, argv[0]); return CMD_SUCCESS; } DEFUN(cfg_mgcp_rtp_no_bts_bind_ip, cfg_mgcp_rtp_no_bts_bind_ip_cmd, "no rtp bts-bind-ip", NO_STR RTP_STR "Bind endpoints facing the BTS\n" "Address to bind to\n") { talloc_free(g_cfg->bts_ports.bind_addr); g_cfg->bts_ports.bind_addr = NULL; return CMD_SUCCESS; } DEFUN(cfg_mgcp_rtp_net_bind_ip, cfg_mgcp_rtp_net_bind_ip_cmd, "rtp net-bind-ip A.B.C.D", RTP_STR "Bind endpoints facing the Network\n" "Address to bind to\n") { bsc_replace_string(g_cfg, &g_cfg->net_ports.bind_addr, argv[0]); return CMD_SUCCESS; } DEFUN(cfg_mgcp_rtp_no_net_bind_ip, cfg_mgcp_rtp_no_net_bind_ip_cmd, "no rtp net-bind-ip", NO_STR RTP_STR "Bind endpoints facing the Network\n" "Address to bind to\n") { talloc_free(g_cfg->net_ports.bind_addr); g_cfg->net_ports.bind_addr = NULL; return CMD_SUCCESS; } DEFUN(cfg_mgcp_rtp_ip_dscp, cfg_mgcp_rtp_ip_dscp_cmd, "rtp ip-dscp <0-255>", RTP_STR "Apply IP_TOS to the audio stream (including Osmux)\n" "The DSCP value\n") { int dscp = atoi(argv[0]); g_cfg->endp_dscp = dscp; return CMD_SUCCESS; } ALIAS_DEPRECATED(cfg_mgcp_rtp_ip_dscp, cfg_mgcp_rtp_ip_tos_cmd, "rtp ip-tos <0-255>", RTP_STR "Apply IP_TOS to the audio stream\n" "The DSCP value\n") #define FORCE_PTIME_STR "Force a fixed ptime for packets sent to the BTS" DEFUN(cfg_mgcp_rtp_force_ptime, cfg_mgcp_rtp_force_ptime_cmd, "rtp force-ptime (10|20|40)", RTP_STR FORCE_PTIME_STR "The required ptime (packet duration) in ms\n" "10 ms\n20 ms\n40 ms\n") { g_cfg->bts_force_ptime = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_mgcp_no_rtp_force_ptime, cfg_mgcp_no_rtp_force_ptime_cmd, "no rtp force-ptime", NO_STR RTP_STR FORCE_PTIME_STR) { g_cfg->bts_force_ptime = 0; return CMD_SUCCESS; } DEFUN(cfg_mgcp_sdp_fmtp_extra, cfg_mgcp_sdp_fmtp_extra_cmd, "sdp audio fmtp-extra .NAME", "Add extra fmtp for the SDP file\n" "Audio\n" "Fmtp-extra\n" "Extra Information\n") { char *txt = argv_concat(argv, argc, 0); if (!txt) return CMD_WARNING; bsc_replace_string(g_cfg, &g_cfg->trunk.audio_fmtp_extra, txt); talloc_free(txt); return CMD_SUCCESS; } DEFUN(cfg_mgcp_allow_transcoding, cfg_mgcp_allow_transcoding_cmd, "allow-transcoding", "Allow transcoding\n") { g_cfg->trunk.no_audio_transcoding = 0; return CMD_SUCCESS; } DEFUN(cfg_mgcp_no_allow_transcoding, cfg_mgcp_no_allow_transcoding_cmd, "no allow-transcoding", NO_STR "Allow transcoding\n") { g_cfg->trunk.no_audio_transcoding = 1; return CMD_SUCCESS; } #define SDP_STR "SDP File related options\n" #define AUDIO_STR "Audio payload options\n" DEFUN(cfg_mgcp_sdp_payload_number, cfg_mgcp_sdp_payload_number_cmd, "sdp audio-payload number <0-255>", SDP_STR AUDIO_STR "Number\n" "Payload number\n") { unsigned int payload = atoi(argv[0]); g_cfg->trunk.audio_payload = payload; return CMD_SUCCESS; } ALIAS_DEPRECATED(cfg_mgcp_sdp_payload_number, cfg_mgcp_sdp_payload_number_cmd_old, "sdp audio payload number <0-255>", SDP_STR AUDIO_STR AUDIO_STR "Number\n" "Payload number\n") DEFUN(cfg_mgcp_sdp_payload_name, cfg_mgcp_sdp_payload_name_cmd, "sdp audio-payload name NAME", SDP_STR AUDIO_STR "Name\n" "Payload name\n") { bsc_replace_string(g_cfg, &g_cfg->trunk.audio_name, argv[0]); return CMD_SUCCESS; } ALIAS_DEPRECATED(cfg_mgcp_sdp_payload_name, cfg_mgcp_sdp_payload_name_cmd_old, "sdp audio payload name NAME", SDP_STR AUDIO_STR AUDIO_STR "Name\n" "Payload name\n") DEFUN(cfg_mgcp_sdp_payload_send_ptime, cfg_mgcp_sdp_payload_send_ptime_cmd, "sdp audio-payload send-ptime", SDP_STR AUDIO_STR "Send SDP ptime (packet duration) attribute\n") { g_cfg->trunk.audio_send_ptime = 1; return CMD_SUCCESS; } DEFUN(cfg_mgcp_no_sdp_payload_send_ptime, cfg_mgcp_no_sdp_payload_send_ptime_cmd, "no sdp audio-payload send-ptime", NO_STR SDP_STR AUDIO_STR "Send SDP ptime (packet duration) attribute\n") { g_cfg->trunk.audio_send_ptime = 0; return CMD_SUCCESS; } DEFUN(cfg_mgcp_sdp_payload_send_name, cfg_mgcp_sdp_payload_send_name_cmd, "sdp audio-payload send-name", SDP_STR AUDIO_STR "Send SDP rtpmap with the audio name\n") { g_cfg->trunk.audio_send_name = 1; return CMD_SUCCESS; } DEFUN(cfg_mgcp_no_sdp_payload_send_name, cfg_mgcp_no_sdp_payload_send_name_cmd, "no sdp audio-payload send-name", NO_STR SDP_STR AUDIO_STR "Send SDP rtpmap with the audio name\n") { g_cfg->trunk.audio_send_name = 0; return CMD_SUCCESS; } DEFUN(cfg_mgcp_loop, cfg_mgcp_loop_cmd, "loop (0|1)", "Loop audio for all endpoints on main trunk\n" "Don't Loop\n" "Loop\n") { if (g_cfg->osmux) { vty_out(vty, "Cannot use `loop' with `osmux'.%s", VTY_NEWLINE); return CMD_WARNING; } g_cfg->trunk.audio_loop = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_mgcp_number_endp, cfg_mgcp_number_endp_cmd, "number endpoints <0-65534>", "Number options\n" "Endpoints available\n" "Number endpoints\n") { /* + 1 as we start counting at one */ g_cfg->trunk.number_endpoints = atoi(argv[0]) + 1; return CMD_SUCCESS; } DEFUN(cfg_mgcp_omit_rtcp, cfg_mgcp_omit_rtcp_cmd, "rtcp-omit", RTCP_OMIT_STR) { g_cfg->trunk.omit_rtcp = 1; return CMD_SUCCESS; } DEFUN(cfg_mgcp_no_omit_rtcp, cfg_mgcp_no_omit_rtcp_cmd, "no rtcp-omit", NO_STR RTCP_OMIT_STR) { g_cfg->trunk.omit_rtcp = 0; return CMD_SUCCESS; } DEFUN(cfg_mgcp_patch_rtp_ssrc, cfg_mgcp_patch_rtp_ssrc_cmd, "rtp-patch ssrc", RTP_PATCH_STR "Force a fixed SSRC\n" ) { g_cfg->trunk.force_constant_ssrc = 1; return CMD_SUCCESS; } DEFUN(cfg_mgcp_no_patch_rtp_ssrc, cfg_mgcp_no_patch_rtp_ssrc_cmd, "no rtp-patch ssrc", NO_STR RTP_PATCH_STR "Force a fixed SSRC\n" ) { g_cfg->trunk.force_constant_ssrc = 0; return CMD_SUCCESS; } DEFUN(cfg_mgcp_patch_rtp_ts, cfg_mgcp_patch_rtp_ts_cmd, "rtp-patch timestamp", RTP_PATCH_STR "Adjust RTP timestamp\n" ) { g_cfg->trunk.force_aligned_timing = 1; return CMD_SUCCESS; } DEFUN(cfg_mgcp_no_patch_rtp_ts, cfg_mgcp_no_patch_rtp_ts_cmd, "no rtp-patch timestamp", NO_STR RTP_PATCH_STR "Adjust RTP timestamp\n" ) { g_cfg->trunk.force_aligned_timing = 0; return CMD_SUCCESS; } DEFUN(cfg_mgcp_no_patch_rtp, cfg_mgcp_no_patch_rtp_cmd, "no rtp-patch", NO_STR RTP_PATCH_STR) { g_cfg->trunk.force_constant_ssrc = 0; g_cfg->trunk.force_aligned_timing = 0; return CMD_SUCCESS; } DEFUN(cfg_mgcp_rtp_keepalive, cfg_mgcp_rtp_keepalive_cmd, "rtp keep-alive <1-120>", RTP_STR RTP_KEEPALIVE_STR "Keep alive interval in secs\n" ) { mgcp_trunk_set_keepalive(&g_cfg->trunk, atoi(argv[0])); return CMD_SUCCESS; } DEFUN(cfg_mgcp_rtp_keepalive_once, cfg_mgcp_rtp_keepalive_once_cmd, "rtp keep-alive once", RTP_STR RTP_KEEPALIVE_STR "Send dummy packet only once after CRCX/MDCX\n" ) { mgcp_trunk_set_keepalive(&g_cfg->trunk, MGCP_KEEPALIVE_ONCE); return CMD_SUCCESS; } DEFUN(cfg_mgcp_no_rtp_keepalive, cfg_mgcp_no_rtp_keepalive_cmd, "no rtp keep-alive", NO_STR RTP_STR RTP_KEEPALIVE_STR ) { mgcp_trunk_set_keepalive(&g_cfg->trunk, 0); return CMD_SUCCESS; } #define CALL_AGENT_STR "Callagent information\n" DEFUN(cfg_mgcp_agent_addr, cfg_mgcp_agent_addr_cmd, "call-agent ip A.B.C.D", CALL_AGENT_STR IP_STR "IPv4 Address of the callagent\n") { bsc_replace_string(g_cfg, &g_cfg->call_agent_addr, argv[0]); return CMD_SUCCESS; } ALIAS_DEPRECATED(cfg_mgcp_agent_addr, cfg_mgcp_agent_addr_cmd_old, "call agent ip A.B.C.D", CALL_AGENT_STR CALL_AGENT_STR IP_STR "IPv4 Address of the callagent\n") DEFUN(cfg_mgcp_transcoder, cfg_mgcp_transcoder_cmd, "transcoder-mgw A.B.C.D", "Use a MGW to detranscoder RTP\n" "The IP address of the MGW") { bsc_replace_string(g_cfg, &g_cfg->transcoder_ip, argv[0]); inet_aton(g_cfg->transcoder_ip, &g_cfg->transcoder_in); return CMD_SUCCESS; } DEFUN(cfg_mgcp_no_transcoder, cfg_mgcp_no_transcoder_cmd, "no transcoder-mgw", NO_STR "Disable the transcoding\n") { if (g_cfg->transcoder_ip) { LOGP(DMGCP, LOGL_NOTICE, "Disabling transcoding on future calls.\n"); talloc_free(g_cfg->transcoder_ip); g_cfg->transcoder_ip = NULL; } return CMD_SUCCESS; } DEFUN(cfg_mgcp_transcoder_remote_base, cfg_mgcp_transcoder_remote_base_cmd, "transcoder-remote-base <0-65534>", "Set the base port for the transcoder\n" "The RTP base port on the transcoder") { g_cfg->transcoder_remote_base = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_mgcp_trunk, cfg_mgcp_trunk_cmd, "trunk <1-64>", "Configure a SS7 trunk\n" "Trunk Nr\n") { struct mgcp_trunk_config *trunk; int index = atoi(argv[0]); trunk = mgcp_trunk_num(g_cfg, index); if (!trunk) trunk = mgcp_trunk_alloc(g_cfg, index); if (!trunk) { vty_out(vty, "%%Unable to allocate trunk %u.%s", index, VTY_NEWLINE); return CMD_WARNING; } vty->node = TRUNK_NODE; vty->index = trunk; return CMD_SUCCESS; } static int config_write_trunk(struct vty *vty) { struct mgcp_trunk_config *trunk; llist_for_each_entry(trunk, &g_cfg->trunks, entry) { vty_out(vty, " trunk %d%s", trunk->trunk_nr, VTY_NEWLINE); vty_out(vty, " sdp audio-payload number %d%s", trunk->audio_payload, VTY_NEWLINE); vty_out(vty, " sdp audio-payload name %s%s", trunk->audio_name, VTY_NEWLINE); vty_out(vty, " %ssdp audio-payload send-ptime%s", trunk->audio_send_ptime ? "" : "no ", VTY_NEWLINE); vty_out(vty, " %ssdp audio-payload send-name%s", trunk->audio_send_name ? "" : "no ", VTY_NEWLINE); if (trunk->keepalive_interval == MGCP_KEEPALIVE_ONCE) vty_out(vty, " rtp keep-alive once%s", VTY_NEWLINE); else if (trunk->keepalive_interval) vty_out(vty, " rtp keep-alive %d%s", trunk->keepalive_interval, VTY_NEWLINE); else vty_out(vty, " no rtp keep-alive%s", VTY_NEWLINE); vty_out(vty, " loop %d%s", trunk->audio_loop, VTY_NEWLINE); if (trunk->omit_rtcp) vty_out(vty, " rtcp-omit%s", VTY_NEWLINE); else vty_out(vty, " no rtcp-omit%s", VTY_NEWLINE); if (trunk->force_constant_ssrc || trunk->force_aligned_timing) { vty_out(vty, " %srtp-patch ssrc%s", trunk->force_constant_ssrc ? "" : "no ", VTY_NEWLINE); vty_out(vty, " %srtp-patch timestamp%s", trunk->force_aligned_timing ? "" : "no ", VTY_NEWLINE); } else vty_out(vty, " no rtp-patch%s", VTY_NEWLINE); if (trunk->audio_fmtp_extra) vty_out(vty, " sdp audio fmtp-extra %s%s", trunk->audio_fmtp_extra, VTY_NEWLINE); vty_out(vty, " %sallow-transcoding%s", trunk->no_audio_transcoding ? "no " : "", VTY_NEWLINE); } return CMD_SUCCESS; } DEFUN(cfg_trunk_sdp_fmtp_extra, cfg_trunk_sdp_fmtp_extra_cmd, "sdp audio fmtp-extra .NAME", "Add extra fmtp for the SDP file\n" "Audio\n" "Fmtp-extra\n" "Extra Information\n") { struct mgcp_trunk_config *trunk = vty->index; char *txt = argv_concat(argv, argc, 0); if (!txt) return CMD_WARNING; bsc_replace_string(g_cfg, &trunk->audio_fmtp_extra, txt); talloc_free(txt); return CMD_SUCCESS; } DEFUN(cfg_trunk_payload_number, cfg_trunk_payload_number_cmd, "sdp audio-payload number <0-255>", SDP_STR AUDIO_STR "Number\n" "Payload Number\n") { struct mgcp_trunk_config *trunk = vty->index; unsigned int payload = atoi(argv[0]); trunk->audio_payload = payload; return CMD_SUCCESS; } ALIAS_DEPRECATED(cfg_trunk_payload_number, cfg_trunk_payload_number_cmd_old, "sdp audio payload number <0-255>", SDP_STR AUDIO_STR AUDIO_STR "Number\n" "Payload Number\n") DEFUN(cfg_trunk_payload_name, cfg_trunk_payload_name_cmd, "sdp audio-payload name NAME", SDP_STR AUDIO_STR "Payload\n" "Payload Name\n") { struct mgcp_trunk_config *trunk = vty->index; bsc_replace_string(g_cfg, &trunk->audio_name, argv[0]); return CMD_SUCCESS; } ALIAS_DEPRECATED(cfg_trunk_payload_name, cfg_trunk_payload_name_cmd_old, "sdp audio payload name NAME", SDP_STR AUDIO_STR AUDIO_STR "Payload\n" "Payload Name\n") DEFUN(cfg_trunk_loop, cfg_trunk_loop_cmd, "loop (0|1)", "Loop audio for all endpoints on this trunk\n" "Don't Loop\n" "Loop\n") { struct mgcp_trunk_config *trunk = vty->index; if (g_cfg->osmux) { vty_out(vty, "Cannot use `loop' with `osmux'.%s", VTY_NEWLINE); return CMD_WARNING; } trunk->audio_loop = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_trunk_sdp_payload_send_ptime, cfg_trunk_sdp_payload_send_ptime_cmd, "sdp audio-payload send-ptime", SDP_STR AUDIO_STR "Send SDP ptime (packet duration) attribute\n") { struct mgcp_trunk_config *trunk = vty->index; trunk->audio_send_ptime = 1; return CMD_SUCCESS; } DEFUN(cfg_trunk_no_sdp_payload_send_ptime, cfg_trunk_no_sdp_payload_send_ptime_cmd, "no sdp audio-payload send-ptime", NO_STR SDP_STR AUDIO_STR "Send SDP ptime (packet duration) attribute\n") { struct mgcp_trunk_config *trunk = vty->index; trunk->audio_send_ptime = 0; return CMD_SUCCESS; } DEFUN(cfg_trunk_sdp_payload_send_name, cfg_trunk_sdp_payload_send_name_cmd, "sdp audio-payload send-name", SDP_STR AUDIO_STR "Send SDP rtpmap with the audio name\n") { struct mgcp_trunk_config *trunk = vty->index; trunk->audio_send_name = 1; return CMD_SUCCESS; } DEFUN(cfg_trunk_no_sdp_payload_send_name, cfg_trunk_no_sdp_payload_send_name_cmd, "no sdp audio-payload send-name", NO_STR SDP_STR AUDIO_STR "Send SDP rtpmap with the audio name\n") { struct mgcp_trunk_config *trunk = vty->index; trunk->audio_send_name = 0; return CMD_SUCCESS; } DEFUN(cfg_trunk_omit_rtcp, cfg_trunk_omit_rtcp_cmd, "rtcp-omit", RTCP_OMIT_STR) { struct mgcp_trunk_config *trunk = vty->index; trunk->omit_rtcp = 1; return CMD_SUCCESS; } DEFUN(cfg_trunk_no_omit_rtcp, cfg_trunk_no_omit_rtcp_cmd, "no rtcp-omit", NO_STR RTCP_OMIT_STR) { struct mgcp_trunk_config *trunk = vty->index; trunk->omit_rtcp = 0; return CMD_SUCCESS; } DEFUN(cfg_trunk_patch_rtp_ssrc, cfg_trunk_patch_rtp_ssrc_cmd, "rtp-patch ssrc", RTP_PATCH_STR "Force a fixed SSRC\n" ) { struct mgcp_trunk_config *trunk = vty->index; trunk->force_constant_ssrc = 1; return CMD_SUCCESS; } DEFUN(cfg_trunk_no_patch_rtp_ssrc, cfg_trunk_no_patch_rtp_ssrc_cmd, "no rtp-patch ssrc", NO_STR RTP_PATCH_STR "Force a fixed SSRC\n" ) { struct mgcp_trunk_config *trunk = vty->index; trunk->force_constant_ssrc = 0; return CMD_SUCCESS; } DEFUN(cfg_trunk_patch_rtp_ts, cfg_trunk_patch_rtp_ts_cmd, "rtp-patch timestamp", RTP_PATCH_STR "Adjust RTP timestamp\n" ) { struct mgcp_trunk_config *trunk = vty->index; trunk->force_aligned_timing = 1; return CMD_SUCCESS; } DEFUN(cfg_trunk_no_patch_rtp_ts, cfg_trunk_no_patch_rtp_ts_cmd, "no rtp-patch timestamp", NO_STR RTP_PATCH_STR "Adjust RTP timestamp\n" ) { struct mgcp_trunk_config *trunk = vty->index; trunk->force_aligned_timing = 0; return CMD_SUCCESS; } DEFUN(cfg_trunk_no_patch_rtp, cfg_trunk_no_patch_rtp_cmd, "no rtp-patch", NO_STR RTP_PATCH_STR) { struct mgcp_trunk_config *trunk = vty->index; trunk->force_constant_ssrc = 0; trunk->force_aligned_timing = 0; return CMD_SUCCESS; } DEFUN(cfg_trunk_rtp_keepalive, cfg_trunk_rtp_keepalive_cmd, "rtp keep-alive <1-120>", RTP_STR RTP_KEEPALIVE_STR "Keep-alive interval in secs\n" ) { struct mgcp_trunk_config *trunk = vty->index; mgcp_trunk_set_keepalive(trunk, atoi(argv[0])); return CMD_SUCCESS; } DEFUN(cfg_trunk_rtp_keepalive_once, cfg_trunk_rtp_keepalive_once_cmd, "rtp keep-alive once", RTP_STR RTP_KEEPALIVE_STR "Send dummy packet only once after CRCX/MDCX\n" ) { struct mgcp_trunk_config *trunk = vty->index; mgcp_trunk_set_keepalive(trunk, MGCP_KEEPALIVE_ONCE); return CMD_SUCCESS; } DEFUN(cfg_trunk_no_rtp_keepalive, cfg_trunk_no_rtp_keepalive_cmd, "no rtp keep-alive", NO_STR RTP_STR RTP_KEEPALIVE_STR ) { struct mgcp_trunk_config *trunk = vty->index; mgcp_trunk_set_keepalive(trunk, 0); return CMD_SUCCESS; } DEFUN(cfg_trunk_allow_transcoding, cfg_trunk_allow_transcoding_cmd, "allow-transcoding", "Allow transcoding\n") { struct mgcp_trunk_config *trunk = vty->index; trunk->no_audio_transcoding = 0; return CMD_SUCCESS; } DEFUN(cfg_trunk_no_allow_transcoding, cfg_trunk_no_allow_transcoding_cmd, "no allow-transcoding", NO_STR "Allow transcoding\n") { struct mgcp_trunk_config *trunk = vty->index; trunk->no_audio_transcoding = 1; return CMD_SUCCESS; } DEFUN(loop_endp, loop_endp_cmd, "loop-endpoint <0-64> NAME (0|1)", "Loop a given endpoint\n" "Trunk number\n" "The name in hex of the endpoint\n" "Disable the loop\n" "Enable the loop\n") { struct mgcp_trunk_config *trunk; struct mgcp_endpoint *endp; trunk = find_trunk(g_cfg, atoi(argv[0])); if (!trunk) { vty_out(vty, "%%Trunk %d not found in the config.%s", atoi(argv[0]), VTY_NEWLINE); return CMD_WARNING; } if (!trunk->endpoints) { vty_out(vty, "%%Trunk %d has no endpoints allocated.%s", trunk->trunk_nr, VTY_NEWLINE); return CMD_WARNING; } int endp_no = strtoul(argv[1], NULL, 16); if (endp_no < 1 || endp_no >= trunk->number_endpoints) { vty_out(vty, "Loopback number %s/%d is invalid.%s", argv[1], endp_no, VTY_NEWLINE); return CMD_WARNING; } endp = &trunk->endpoints[endp_no]; int loop = atoi(argv[2]); if (loop) endp->conn_mode = MGCP_CONN_LOOPBACK; else endp->conn_mode = endp->orig_mode; /* Handle it like a MDCX, switch on SSRC patching if enabled */ mgcp_rtp_end_config(endp, 1, &endp->bts_end); mgcp_rtp_end_config(endp, 1, &endp->net_end); return CMD_SUCCESS; } DEFUN(tap_call, tap_call_cmd, "tap-call <0-64> ENDPOINT (bts-in|bts-out|net-in|net-out) A.B.C.D <0-65534>", "Forward data on endpoint to a different system\n" "Trunk number\n" "The endpoint in hex\n" "Forward the data coming from the bts\n" "Forward the data coming from the bts leaving to the network\n" "Forward the data coming from the net\n" "Forward the data coming from the net leaving to the bts\n" "destination IP of the data\n" "destination port\n") { struct mgcp_rtp_tap *tap; struct mgcp_trunk_config *trunk; struct mgcp_endpoint *endp; int port = 0; trunk = find_trunk(g_cfg, atoi(argv[0])); if (!trunk) { vty_out(vty, "%%Trunk %d not found in the config.%s", atoi(argv[0]), VTY_NEWLINE); return CMD_WARNING; } if (!trunk->endpoints) { vty_out(vty, "%%Trunk %d has no endpoints allocated.%s", trunk->trunk_nr, VTY_NEWLINE); return CMD_WARNING; } int endp_no = strtoul(argv[1], NULL, 16); if (endp_no < 1 || endp_no >= trunk->number_endpoints) { vty_out(vty, "Endpoint number %s/%d is invalid.%s", argv[1], endp_no, VTY_NEWLINE); return CMD_WARNING; } endp = &trunk->endpoints[endp_no]; if (strcmp(argv[2], "bts-in") == 0) { port = MGCP_TAP_BTS_IN; } else if (strcmp(argv[2], "bts-out") == 0) { port = MGCP_TAP_BTS_OUT; } else if (strcmp(argv[2], "net-in") == 0) { port = MGCP_TAP_NET_IN; } else if (strcmp(argv[2], "net-out") == 0) { port = MGCP_TAP_NET_OUT; } else { vty_out(vty, "Unknown mode... tricked vty?%s", VTY_NEWLINE); return CMD_WARNING; } tap = &endp->taps[port]; memset(&tap->forward, 0, sizeof(tap->forward)); inet_aton(argv[3], &tap->forward.sin_addr); tap->forward.sin_port = htons(atoi(argv[4])); tap->enabled = 1; return CMD_SUCCESS; } DEFUN(free_endp, free_endp_cmd, "free-endpoint <0-64> NUMBER", "Free the given endpoint\n" "Trunk number\n" "Endpoint number in hex.\n") { struct mgcp_trunk_config *trunk; struct mgcp_endpoint *endp; trunk = find_trunk(g_cfg, atoi(argv[0])); if (!trunk) { vty_out(vty, "%%Trunk %d not found in the config.%s", atoi(argv[0]), VTY_NEWLINE); return CMD_WARNING; } if (!trunk->endpoints) { vty_out(vty, "%%Trunk %d has no endpoints allocated.%s", trunk->trunk_nr, VTY_NEWLINE); return CMD_WARNING; } int endp_no = strtoul(argv[1], NULL, 16); if (endp_no < 1 || endp_no >= trunk->number_endpoints) { vty_out(vty, "Endpoint number %s/%d is invalid.%s", argv[1], endp_no, VTY_NEWLINE); return CMD_WARNING; } endp = &trunk->endpoints[endp_no]; mgcp_release_endp(endp); return CMD_SUCCESS; } DEFUN(reset_endp, reset_endp_cmd, "reset-endpoint <0-64> NUMBER", "Reset the given endpoint\n" "Trunk number\n" "Endpoint number in hex.\n") { struct mgcp_trunk_config *trunk; struct mgcp_endpoint *endp; int endp_no, rc; trunk = find_trunk(g_cfg, atoi(argv[0])); if (!trunk) { vty_out(vty, "%%Trunk %d not found in the config.%s", atoi(argv[0]), VTY_NEWLINE); return CMD_WARNING; } if (!trunk->endpoints) { vty_out(vty, "%%Trunk %d has no endpoints allocated.%s", trunk->trunk_nr, VTY_NEWLINE); return CMD_WARNING; } endp_no = strtoul(argv[1], NULL, 16); if (endp_no < 1 || endp_no >= trunk->number_endpoints) { vty_out(vty, "Endpoint number %s/%d is invalid.%s", argv[1], endp_no, VTY_NEWLINE); return CMD_WARNING; } endp = &trunk->endpoints[endp_no]; rc = mgcp_send_reset_ep(endp, ENDPOINT_NUMBER(endp)); if (rc < 0) { vty_out(vty, "Error %d sending reset.%s", rc, VTY_NEWLINE); return CMD_WARNING; } return CMD_SUCCESS; } DEFUN(reset_all_endp, reset_all_endp_cmd, "reset-all-endpoints", "Reset all endpoints\n") { int rc; rc = mgcp_send_reset_all(g_cfg); if (rc < 0) { vty_out(vty, "Error %d during endpoint reset.%s", rc, VTY_NEWLINE); return CMD_WARNING; } return CMD_SUCCESS; } #define OSMUX_STR "RTP multiplexing\n" DEFUN(cfg_mgcp_osmux, cfg_mgcp_osmux_cmd, "osmux (on|off|only)", OSMUX_STR "Enable OSMUX\n" "Disable OSMUX\n" "Only use OSMUX\n") { if (strcmp(argv[0], "off") == 0) { g_cfg->osmux = OSMUX_USAGE_OFF; return CMD_SUCCESS; } if (strcmp(argv[0], "on") == 0) g_cfg->osmux = OSMUX_USAGE_ON; else if (strcmp(argv[0], "only") == 0) g_cfg->osmux = OSMUX_USAGE_ONLY; if (g_cfg->trunk.audio_loop) { vty_out(vty, "Cannot use `loop' with `osmux'.%s", VTY_NEWLINE); return CMD_WARNING; } return CMD_SUCCESS; } DEFUN(cfg_mgcp_osmux_batch_factor, cfg_mgcp_osmux_batch_factor_cmd, "osmux batch-factor <1-8>", OSMUX_STR "Batching factor\n" "Number of messages in the batch\n") { g_cfg->osmux_batch = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_mgcp_osmux_batch_size, cfg_mgcp_osmux_batch_size_cmd, "osmux batch-size <1-65535>", OSMUX_STR "batch size\n" "Batch size in bytes\n") { g_cfg->osmux_batch_size = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_mgcp_osmux_port, cfg_mgcp_osmux_port_cmd, "osmux port <1-65535>", OSMUX_STR "port\n" "UDP port\n") { g_cfg->osmux_port = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_mgcp_osmux_dummy, cfg_mgcp_osmux_dummy_cmd, "osmux dummy (on|off)", OSMUX_STR "Dummy padding\n" "Enable dummy padding\n" "Disable dummy padding\n") { if (strcmp(argv[0], "on") == 0) g_cfg->osmux_dummy = 1; else if (strcmp(argv[0], "off") == 0) g_cfg->osmux_dummy = 0; return CMD_SUCCESS; } int mgcp_vty_init(void) { install_element_ve(&show_mgcp_cmd); install_element(ENABLE_NODE, &loop_endp_cmd); install_element(ENABLE_NODE, &tap_call_cmd); install_element(ENABLE_NODE, &free_endp_cmd); install_element(ENABLE_NODE, &reset_endp_cmd); install_element(ENABLE_NODE, &reset_all_endp_cmd); install_element(CONFIG_NODE, &cfg_mgcp_cmd); install_node(&mgcp_node, config_write_mgcp); vty_install_default(MGCP_NODE); install_element(MGCP_NODE, &cfg_mgcp_local_ip_cmd); install_element(MGCP_NODE, &cfg_mgcp_bts_ip_cmd); install_element(MGCP_NODE, &cfg_mgcp_bind_ip_cmd); install_element(MGCP_NODE, &cfg_mgcp_bind_port_cmd); install_element(MGCP_NODE, &cfg_mgcp_bind_early_cmd); install_element(MGCP_NODE, &cfg_mgcp_rtp_base_port_cmd); install_element(MGCP_NODE, &cfg_mgcp_rtp_bts_base_port_cmd); install_element(MGCP_NODE, &cfg_mgcp_rtp_net_base_port_cmd); install_element(MGCP_NODE, &cfg_mgcp_rtp_bts_range_cmd); install_element(MGCP_NODE, &cfg_mgcp_rtp_bts_bind_ip_cmd); install_element(MGCP_NODE, &cfg_mgcp_rtp_no_bts_bind_ip_cmd); install_element(MGCP_NODE, &cfg_mgcp_rtp_net_range_cmd); install_element(MGCP_NODE, &cfg_mgcp_rtp_net_bind_ip_cmd); install_element(MGCP_NODE, &cfg_mgcp_rtp_no_net_bind_ip_cmd); install_element(MGCP_NODE, &cfg_mgcp_rtp_transcoder_range_cmd); install_element(MGCP_NODE, &cfg_mgcp_rtp_transcoder_base_cmd); install_element(MGCP_NODE, &cfg_mgcp_rtp_ip_dscp_cmd); install_element(MGCP_NODE, &cfg_mgcp_rtp_ip_tos_cmd); install_element(MGCP_NODE, &cfg_mgcp_rtp_force_ptime_cmd); install_element(MGCP_NODE, &cfg_mgcp_no_rtp_force_ptime_cmd); install_element(MGCP_NODE, &cfg_mgcp_rtp_keepalive_cmd); install_element(MGCP_NODE, &cfg_mgcp_rtp_keepalive_once_cmd); install_element(MGCP_NODE, &cfg_mgcp_no_rtp_keepalive_cmd); install_element(MGCP_NODE, &cfg_mgcp_agent_addr_cmd); install_element(MGCP_NODE, &cfg_mgcp_agent_addr_cmd_old); install_element(MGCP_NODE, &cfg_mgcp_transcoder_cmd); install_element(MGCP_NODE, &cfg_mgcp_no_transcoder_cmd); install_element(MGCP_NODE, &cfg_mgcp_transcoder_remote_base_cmd); install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_number_cmd); install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_name_cmd); install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_number_cmd_old); install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_name_cmd_old); install_element(MGCP_NODE, &cfg_mgcp_loop_cmd); install_element(MGCP_NODE, &cfg_mgcp_number_endp_cmd); install_element(MGCP_NODE, &cfg_mgcp_omit_rtcp_cmd); install_element(MGCP_NODE, &cfg_mgcp_no_omit_rtcp_cmd); install_element(MGCP_NODE, &cfg_mgcp_patch_rtp_ssrc_cmd); install_element(MGCP_NODE, &cfg_mgcp_no_patch_rtp_ssrc_cmd); install_element(MGCP_NODE, &cfg_mgcp_patch_rtp_ts_cmd); install_element(MGCP_NODE, &cfg_mgcp_no_patch_rtp_ts_cmd); install_element(MGCP_NODE, &cfg_mgcp_no_patch_rtp_cmd); install_element(MGCP_NODE, &cfg_mgcp_sdp_fmtp_extra_cmd); install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_send_ptime_cmd); install_element(MGCP_NODE, &cfg_mgcp_no_sdp_payload_send_ptime_cmd); install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_send_name_cmd); install_element(MGCP_NODE, &cfg_mgcp_no_sdp_payload_send_name_cmd); install_element(MGCP_NODE, &cfg_mgcp_osmux_cmd); install_element(MGCP_NODE, &cfg_mgcp_osmux_batch_factor_cmd); install_element(MGCP_NODE, &cfg_mgcp_osmux_batch_size_cmd); install_element(MGCP_NODE, &cfg_mgcp_osmux_port_cmd); install_element(MGCP_NODE, &cfg_mgcp_osmux_dummy_cmd); install_element(MGCP_NODE, &cfg_mgcp_allow_transcoding_cmd); install_element(MGCP_NODE, &cfg_mgcp_no_allow_transcoding_cmd); install_element(MGCP_NODE, &cfg_mgcp_trunk_cmd); install_node(&trunk_node, config_write_trunk); vty_install_default(TRUNK_NODE); install_element(TRUNK_NODE, &cfg_trunk_rtp_keepalive_cmd); install_element(TRUNK_NODE, &cfg_trunk_rtp_keepalive_once_cmd); install_element(TRUNK_NODE, &cfg_trunk_no_rtp_keepalive_cmd); install_element(TRUNK_NODE, &cfg_trunk_payload_number_cmd); install_element(TRUNK_NODE, &cfg_trunk_payload_name_cmd); install_element(TRUNK_NODE, &cfg_trunk_payload_number_cmd_old); install_element(TRUNK_NODE, &cfg_trunk_payload_name_cmd_old); install_element(TRUNK_NODE, &cfg_trunk_loop_cmd); install_element(TRUNK_NODE, &cfg_trunk_omit_rtcp_cmd); install_element(TRUNK_NODE, &cfg_trunk_no_omit_rtcp_cmd); install_element(TRUNK_NODE, &cfg_trunk_patch_rtp_ssrc_cmd); install_element(TRUNK_NODE, &cfg_trunk_no_patch_rtp_ssrc_cmd); install_element(TRUNK_NODE, &cfg_trunk_patch_rtp_ts_cmd); install_element(TRUNK_NODE, &cfg_trunk_no_patch_rtp_ts_cmd); install_element(TRUNK_NODE, &cfg_trunk_no_patch_rtp_cmd); install_element(TRUNK_NODE, &cfg_trunk_sdp_fmtp_extra_cmd); install_element(TRUNK_NODE, &cfg_trunk_sdp_payload_send_ptime_cmd); install_element(TRUNK_NODE, &cfg_trunk_no_sdp_payload_send_ptime_cmd); install_element(TRUNK_NODE, &cfg_trunk_sdp_payload_send_name_cmd); install_element(TRUNK_NODE, &cfg_trunk_no_sdp_payload_send_name_cmd); install_element(TRUNK_NODE, &cfg_trunk_allow_transcoding_cmd); install_element(TRUNK_NODE, &cfg_trunk_no_allow_transcoding_cmd); return 0; } static int allocate_trunk(struct mgcp_trunk_config *trunk) { int i; struct mgcp_config *cfg = trunk->cfg; if (mgcp_endpoints_allocate(trunk) != 0) { LOGP(DMGCP, LOGL_ERROR, "Failed to allocate %d endpoints on trunk %d.\n", trunk->number_endpoints, trunk->trunk_nr); return -1; } /* early bind */ for (i = 1; i < trunk->number_endpoints; ++i) { struct mgcp_endpoint *endp = &trunk->endpoints[i]; if (cfg->bts_ports.mode == PORT_ALLOC_STATIC) { cfg->last_bts_port += 2; if (mgcp_bind_bts_rtp_port(endp, cfg->last_bts_port) != 0) { LOGP(DMGCP, LOGL_FATAL, "Failed to bind: %d\n", cfg->last_bts_port); return -1; } endp->bts_end.local_alloc = PORT_ALLOC_STATIC; } if (cfg->net_ports.mode == PORT_ALLOC_STATIC) { cfg->last_net_port += 2; if (mgcp_bind_net_rtp_port(endp, cfg->last_net_port) != 0) { LOGP(DMGCP, LOGL_FATAL, "Failed to bind: %d\n", cfg->last_net_port); return -1; } endp->net_end.local_alloc = PORT_ALLOC_STATIC; } if (trunk->trunk_type == MGCP_TRUNK_VIRTUAL && cfg->transcoder_ip && cfg->transcoder_ports.mode == PORT_ALLOC_STATIC) { int rtp_port; /* network side */ rtp_port = rtp_calculate_port(ENDPOINT_NUMBER(endp), cfg->transcoder_ports.base_port); if (mgcp_bind_trans_net_rtp_port(endp, rtp_port) != 0) { LOGP(DMGCP, LOGL_FATAL, "Failed to bind: %d\n", rtp_port); return -1; } endp->trans_net.local_alloc = PORT_ALLOC_STATIC; /* bts side */ rtp_port = rtp_calculate_port(endp_back_channel(ENDPOINT_NUMBER(endp)), cfg->transcoder_ports.base_port); if (mgcp_bind_trans_bts_rtp_port(endp, rtp_port) != 0) { LOGP(DMGCP, LOGL_FATAL, "Failed to bind: %d\n", rtp_port); return -1; } endp->trans_bts.local_alloc = PORT_ALLOC_STATIC; } } return 0; } int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg, enum mgcp_role role) { int rc; struct mgcp_trunk_config *trunk; cfg->osmux_port = OSMUX_PORT; cfg->osmux_batch = 4; cfg->osmux_batch_size = OSMUX_BATCH_DEFAULT_MAX; 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; } if (!g_cfg->bts_ip) fprintf(stderr, "No BTS ip address specified. This will allow everyone to connect.\n"); if (!g_cfg->source_addr) { fprintf(stderr, "You need to specify a bind address.\n"); return -1; } /* initialize the last ports */ g_cfg->last_bts_port = rtp_calculate_port(0, g_cfg->bts_ports.base_port); g_cfg->last_net_port = rtp_calculate_port(0, g_cfg->net_ports.base_port); if (allocate_trunk(&g_cfg->trunk) != 0) { LOGP(DMGCP, LOGL_ERROR, "Failed to initialize the virtual trunk.\n"); return -1; } llist_for_each_entry(trunk, &g_cfg->trunks, entry) { if (allocate_trunk(trunk) != 0) { LOGP(DMGCP, LOGL_ERROR, "Failed to initialize E1 trunk %d.\n", trunk->trunk_nr); return -1; } } cfg->role = role; return 0; } openbsc-0.15.0/openbsc/src/libmsc/000077500000000000000000000000001265565154000167305ustar00rootroot00000000000000openbsc-0.15.0/openbsc/src/libmsc/Makefile.am000066400000000000000000000013041265565154000207620ustar00rootroot00000000000000AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) \ $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) $(LIBCRYPTO_CFLAGS) noinst_HEADERS = meas_feed.h noinst_LIBRARIES = libmsc.a libmsc_a_SOURCES = auth.c \ db.c \ gsm_04_08.c gsm_04_11.c gsm_04_11_helper.c \ gsm_04_80.c \ gsm_subscriber.c \ mncc.c mncc_builtin.c mncc_sock.c \ rrlp.c \ silent_call.c \ sms_queue.c \ token_auth.c \ ussd.c \ vty_interface_layer3.c \ transaction.c \ osmo_msc.c ctrl_commands.c meas_feed.c if BUILD_SMPP noinst_HEADERS += smpp_smsc.h libmsc_a_SOURCES += smpp_smsc.c smpp_openbsc.c smpp_vty.c smpp_utils.c endif openbsc-0.15.0/openbsc/src/libmsc/auth.c000066400000000000000000000070241265565154000200400ustar00rootroot00000000000000/* Authentication related functions */ /* * (C) 2010 by Sylvain Munaut * * 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 . * */ #include #include #include #include #include #include #include static int _use_xor(struct gsm_auth_info *ainfo, struct gsm_auth_tuple *atuple) { int i, l = ainfo->a3a8_ki_len; if ((l > A38_XOR_MAX_KEY_LEN) || (l < A38_XOR_MIN_KEY_LEN)) { LOGP(DMM, LOGL_ERROR, "Invalid XOR key (len=%d) %s\n", ainfo->a3a8_ki_len, osmo_hexdump(ainfo->a3a8_ki, ainfo->a3a8_ki_len)); return -1; } for (i=0; i<4; i++) atuple->sres[i] = atuple->rand[i] ^ ainfo->a3a8_ki[i]; for (i=4; i<12; i++) atuple->kc[i-4] = atuple->rand[i] ^ ainfo->a3a8_ki[i]; return 0; } static int _use_comp128_v1(struct gsm_auth_info *ainfo, struct gsm_auth_tuple *atuple) { if (ainfo->a3a8_ki_len != A38_COMP128_KEY_LEN) { LOGP(DMM, LOGL_ERROR, "Invalid COMP128v1 key (len=%d) %s\n", ainfo->a3a8_ki_len, osmo_hexdump(ainfo->a3a8_ki, ainfo->a3a8_ki_len)); return -1; } comp128(ainfo->a3a8_ki, atuple->rand, atuple->sres, atuple->kc); return 0; } /* Return values * -1 -> Internal error * 0 -> Not available * 1 -> Tuple returned, need to do auth, then enable cipher * 2 -> Tuple returned, need to enable cipher */ int auth_get_tuple_for_subscr(struct gsm_auth_tuple *atuple, struct gsm_subscriber *subscr, int key_seq) { struct gsm_auth_info ainfo; int rc; /* Get subscriber info (if any) */ rc = db_get_authinfo_for_subscr(&ainfo, subscr); if (rc < 0) { LOGP(DMM, LOGL_NOTICE, "No retrievable Ki for subscriber, skipping auth\n"); return rc == -ENOENT ? AUTH_NOT_AVAIL : -1; } /* If possible, re-use the last tuple and skip auth */ rc = db_get_lastauthtuple_for_subscr(atuple, subscr); if ((rc == 0) && (key_seq != GSM_KEY_SEQ_INVAL) && (atuple->use_count < 3)) { atuple->use_count++; db_sync_lastauthtuple_for_subscr(atuple, subscr); DEBUGP(DMM, "Auth tuple use < 3, just doing ciphering\n"); return AUTH_DO_CIPH; } /* Generate a new one */ atuple->use_count = 1; atuple->key_seq = (atuple->key_seq + 1) % 7; if (RAND_bytes(atuple->rand, sizeof(atuple->rand)) != 1) { LOGP(DMM, LOGL_NOTICE, "RAND_bytes failed, can't generate new auth tuple\n"); return -1; } switch (ainfo.auth_algo) { case AUTH_ALGO_NONE: DEBUGP(DMM, "No authentication for subscriber\n"); return 0; case AUTH_ALGO_XOR: if (_use_xor(&ainfo, atuple)) return 0; break; case AUTH_ALGO_COMP128v1: if (_use_comp128_v1(&ainfo, atuple)) return 0; break; default: DEBUGP(DMM, "Unsupported auth type algo_id=%d\n", ainfo.auth_algo); return 0; } db_sync_lastauthtuple_for_subscr(atuple, subscr); DEBUGP(DMM, "Need to do authentication and ciphering\n"); return AUTH_DO_AUTH_THAN_CIPH; } openbsc-0.15.0/openbsc/src/libmsc/ctrl_commands.c000066400000000000000000000106471265565154000217310ustar00rootroot00000000000000/* * (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 . * */ #include #include #include #include #include static int verify_subscriber_modify(struct ctrl_cmd *cmd, const char *value, void *d) { char *tmp, *imsi, *msisdn, *saveptr = NULL; int rc = 0; tmp = talloc_strdup(cmd, value); if (!tmp) return 1; imsi = strtok_r(tmp, ",", &saveptr); msisdn = strtok_r(NULL, ",", &saveptr); if (!imsi || !msisdn) rc = 1; else if (strlen(imsi) >= GSM_IMSI_LENGTH) rc = 1; else if (strlen(msisdn) >= GSM_EXTENSION_LENGTH) rc = 1; talloc_free(tmp); return rc; } static int get_subscriber_modify(struct ctrl_cmd *cmd, void *data) { cmd->reply = "Set only attribute"; return CTRL_CMD_ERROR; } static int set_subscriber_modify(struct ctrl_cmd *cmd, void *data) { struct gsm_network *net = cmd->node; char *tmp, *imsi, *msisdn, *saveptr = NULL; struct gsm_subscriber* subscr; int rc; tmp = talloc_strdup(cmd, cmd->value); if (!tmp) return 1; imsi = strtok_r(tmp, ",", &saveptr); msisdn = strtok_r(NULL, ",", &saveptr); subscr = subscr_get_by_imsi(net->subscr_group, imsi); if (!subscr) subscr = subscr_create_subscriber(net->subscr_group, imsi); if (!subscr) goto fail; subscr->authorized = 1; strncpy(subscr->extension, msisdn, GSM_EXTENSION_LENGTH - 1); subscr->extension[GSM_EXTENSION_LENGTH-1] = '\0'; /* put it back to the db */ rc = db_sync_subscriber(subscr); db_subscriber_update(subscr); subscr_put(subscr); talloc_free(tmp); if (rc != 0) { cmd->reply = "Failed to store the record in the DB"; return CTRL_CMD_ERROR; } cmd->reply = "OK"; return CTRL_CMD_REPLY; fail: talloc_free(tmp); cmd->reply = "Failed to create subscriber"; return CTRL_CMD_ERROR; } CTRL_CMD_DEFINE(subscriber_modify, "subscriber-modify-v1"); static int verify_subscriber_delete(struct ctrl_cmd *cmd, const char *v, void *d) { return 0; } static int get_subscriber_delete(struct ctrl_cmd *cmd, void *data) { cmd->reply = "Set only attribute"; return CTRL_CMD_ERROR; } static int set_subscriber_delete(struct ctrl_cmd *cmd, void *data) { int was_used = 0; int rc; struct gsm_subscriber *subscr; struct gsm_network *net = cmd->node; subscr = subscr_get_by_imsi(net->subscr_group, cmd->value); if (!subscr) { cmd->reply = "Failed to find subscriber"; return CTRL_CMD_ERROR; } if (subscr->use_count != 1) { LOGP(DCTRL, LOGL_NOTICE, "Going to remove active subscriber.\n"); was_used = 1; } rc = db_subscriber_delete(subscr); subscr_put(subscr); if (rc != 0) { cmd->reply = "Failed to remove subscriber"; return CTRL_CMD_ERROR; } cmd->reply = was_used ? "Removed active subscriber" : "Removed"; return CTRL_CMD_REPLY; } CTRL_CMD_DEFINE(subscriber_delete, "subscriber-delete-v1"); static int verify_subscriber_list(struct ctrl_cmd *cmd, const char *value, void *d) { return 1; } static int set_subscriber_list(struct ctrl_cmd *cmd, void *d) { cmd->reply = "Get only attribute"; return CTRL_CMD_ERROR; } static void list_cb(struct gsm_subscriber *subscr, void *d) { char **data = (char **) d; *data = talloc_asprintf_append(*data, "%s,%s\n", subscr->imsi, subscr->extension); } static int get_subscriber_list(struct ctrl_cmd *cmd, void *d) { cmd->reply = talloc_strdup(cmd, ""); db_subscriber_list_active(list_cb, &cmd->reply); printf("%s\n", cmd->reply); return CTRL_CMD_REPLY; } CTRL_CMD_DEFINE(subscriber_list, "subscriber-list-active-v1"); int msc_ctrl_cmds_install(void) { int rc = 0; rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_subscriber_modify); rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_subscriber_delete); rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_subscriber_list); return rc; } openbsc-0.15.0/openbsc/src/libmsc/db.c000066400000000000000000001257471265565154000175010ustar00rootroot00000000000000/* Simple HLR/VLR database backend using dbi */ /* (C) 2008 by Jan Luebbe * (C) 2009 by Holger Hans Peter Freyther * (C) 2009 by Harald Welte * 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Semi-Private-Interface (SPI) for the subscriber code */ void subscr_direct_free(struct gsm_subscriber *subscr); static char *db_basename = NULL; static char *db_dirname = NULL; static dbi_conn conn; #define SCHEMA_REVISION "4" enum { SCHEMA_META, INSERT_META, SCHEMA_SUBSCRIBER, SCHEMA_AUTH, SCHEMA_EQUIPMENT, SCHEMA_EQUIPMENT_WATCH, SCHEMA_SMS, SCHEMA_VLR, SCHEMA_APDU, SCHEMA_COUNTERS, SCHEMA_RATE, SCHEMA_AUTHKEY, SCHEMA_AUTHLAST, }; static const char *create_stmts[] = { [SCHEMA_META] = "CREATE TABLE IF NOT EXISTS Meta (" "id INTEGER PRIMARY KEY AUTOINCREMENT, " "key TEXT UNIQUE NOT NULL, " "value TEXT NOT NULL" ")", [INSERT_META] = "INSERT OR IGNORE INTO Meta " "(key, value) " "VALUES " "('revision', " SCHEMA_REVISION ")", [SCHEMA_SUBSCRIBER] = "CREATE TABLE IF NOT EXISTS Subscriber (" "id INTEGER PRIMARY KEY AUTOINCREMENT, " "created TIMESTAMP NOT NULL, " "updated TIMESTAMP NOT NULL, " "imsi NUMERIC UNIQUE NOT NULL, " "name TEXT, " "extension TEXT UNIQUE, " "authorized INTEGER NOT NULL DEFAULT 0, " "tmsi TEXT UNIQUE, " "lac INTEGER NOT NULL DEFAULT 0, " "expire_lu TIMESTAMP DEFAULT NULL" ")", [SCHEMA_AUTH] = "CREATE TABLE IF NOT EXISTS AuthToken (" "id INTEGER PRIMARY KEY AUTOINCREMENT, " "subscriber_id INTEGER UNIQUE NOT NULL, " "created TIMESTAMP NOT NULL, " "token TEXT UNIQUE NOT NULL" ")", [SCHEMA_EQUIPMENT] = "CREATE TABLE IF NOT EXISTS Equipment (" "id INTEGER PRIMARY KEY AUTOINCREMENT, " "created TIMESTAMP NOT NULL, " "updated TIMESTAMP NOT NULL, " "name TEXT, " "classmark1 NUMERIC, " "classmark2 BLOB, " "classmark3 BLOB, " "imei NUMERIC UNIQUE NOT NULL" ")", [SCHEMA_EQUIPMENT_WATCH] = "CREATE TABLE IF NOT EXISTS EquipmentWatch (" "id INTEGER PRIMARY KEY AUTOINCREMENT, " "created TIMESTAMP NOT NULL, " "updated TIMESTAMP NOT NULL, " "subscriber_id NUMERIC NOT NULL, " "equipment_id NUMERIC NOT NULL, " "UNIQUE (subscriber_id, equipment_id) " ")", [SCHEMA_SMS] = "CREATE TABLE IF NOT EXISTS SMS (" /* metadata, not part of sms */ "id INTEGER PRIMARY KEY AUTOINCREMENT, " "created TIMESTAMP NOT NULL, " "sent TIMESTAMP, " "deliver_attempts INTEGER NOT NULL DEFAULT 0, " /* data directly copied/derived from SMS */ "valid_until TIMESTAMP, " "reply_path_req INTEGER NOT NULL, " "status_rep_req INTEGER NOT NULL, " "protocol_id INTEGER NOT NULL, " "data_coding_scheme INTEGER NOT NULL, " "ud_hdr_ind INTEGER NOT NULL, " "src_addr TEXT NOT NULL, " "src_ton INTEGER NOT NULL, " "src_npi INTEGER NOT NULL, " "dest_addr TEXT NOT NULL, " "dest_ton INTEGER NOT NULL, " "dest_npi INTEGER NOT NULL, " "user_data BLOB, " /* TP-UD */ /* additional data, interpreted from SMS */ "header BLOB, " /* UD Header */ "text TEXT " /* decoded UD after UDH */ ")", [SCHEMA_VLR] = "CREATE TABLE IF NOT EXISTS VLR (" "id INTEGER PRIMARY KEY AUTOINCREMENT, " "created TIMESTAMP NOT NULL, " "updated TIMESTAMP NOT NULL, " "subscriber_id NUMERIC UNIQUE NOT NULL, " "last_bts NUMERIC NOT NULL " ")", [SCHEMA_APDU] = "CREATE TABLE IF NOT EXISTS ApduBlobs (" "id INTEGER PRIMARY KEY AUTOINCREMENT, " "created TIMESTAMP NOT NULL, " "apdu_id_flags INTEGER NOT NULL, " "subscriber_id INTEGER NOT NULL, " "apdu BLOB " ")", [SCHEMA_COUNTERS] = "CREATE TABLE IF NOT EXISTS Counters (" "id INTEGER PRIMARY KEY AUTOINCREMENT, " "timestamp TIMESTAMP NOT NULL, " "value INTEGER NOT NULL, " "name TEXT NOT NULL " ")", [SCHEMA_RATE] = "CREATE TABLE IF NOT EXISTS RateCounters (" "id INTEGER PRIMARY KEY AUTOINCREMENT, " "timestamp TIMESTAMP NOT NULL, " "value INTEGER NOT NULL, " "name TEXT NOT NULL, " "idx INTEGER NOT NULL " ")", [SCHEMA_AUTHKEY] = "CREATE TABLE IF NOT EXISTS AuthKeys (" "subscriber_id INTEGER PRIMARY KEY, " "algorithm_id INTEGER NOT NULL, " "a3a8_ki BLOB " ")", [SCHEMA_AUTHLAST] = "CREATE TABLE IF NOT EXISTS AuthLastTuples (" "subscriber_id INTEGER PRIMARY KEY, " "issued TIMESTAMP NOT NULL, " "use_count INTEGER NOT NULL DEFAULT 0, " "key_seq INTEGER NOT NULL, " "rand BLOB NOT NULL, " "sres BLOB NOT NULL, " "kc BLOB NOT NULL " ")", }; void db_error_func(dbi_conn conn, void *data) { const char *msg; dbi_conn_error(conn, &msg); LOGP(DDB, LOGL_ERROR, "DBI: %s\n", msg); osmo_log_backtrace(DDB, LOGL_ERROR); } static int update_db_revision_2(void) { dbi_result result; result = dbi_conn_query(conn, "ALTER TABLE Subscriber " "ADD COLUMN expire_lu " "TIMESTAMP DEFAULT NULL"); if (!result) { LOGP(DDB, LOGL_ERROR, "Failed to alter table Subscriber (upgrade from rev 2).\n"); return -EINVAL; } dbi_result_free(result); result = dbi_conn_query(conn, "UPDATE Meta " "SET value = '3' " "WHERE key = 'revision'"); if (!result) { LOGP(DDB, LOGL_ERROR, "Failed to update DB schema revision (upgrade from rev 2).\n"); return -EINVAL; } dbi_result_free(result); return 0; } /** * Copied from the normal sms_from_result_v3 to avoid having * to make sure that the real routine will remain backward * compatible. */ static struct gsm_sms *sms_from_result_v3(dbi_result result) { struct gsm_sms *sms = sms_alloc(); long long unsigned int sender_id; struct gsm_subscriber *sender; const char *text, *daddr; const unsigned char *user_data; char buf[32]; if (!sms) return NULL; sms->id = dbi_result_get_ulonglong(result, "id"); sender_id = dbi_result_get_ulonglong(result, "sender_id"); snprintf(buf, sizeof(buf), "%llu", sender_id); sender = db_get_subscriber(GSM_SUBSCRIBER_ID, buf); OSMO_ASSERT(sender); strncpy(sms->src.addr, sender->extension, sizeof(sms->src.addr)-1); subscr_direct_free(sender); sender = NULL; sms->reply_path_req = dbi_result_get_ulonglong(result, "reply_path_req"); sms->status_rep_req = dbi_result_get_ulonglong(result, "status_rep_req"); sms->ud_hdr_ind = dbi_result_get_ulonglong(result, "ud_hdr_ind"); sms->protocol_id = dbi_result_get_ulonglong(result, "protocol_id"); sms->data_coding_scheme = dbi_result_get_ulonglong(result, "data_coding_scheme"); daddr = dbi_result_get_string(result, "dest_addr"); if (daddr) { strncpy(sms->dst.addr, daddr, sizeof(sms->dst.addr)); sms->dst.addr[sizeof(sms->dst.addr)-1] = '\0'; } sms->user_data_len = dbi_result_get_field_length(result, "user_data"); user_data = dbi_result_get_binary(result, "user_data"); if (sms->user_data_len > sizeof(sms->user_data)) sms->user_data_len = (uint8_t) sizeof(sms->user_data); memcpy(sms->user_data, user_data, sms->user_data_len); text = dbi_result_get_string(result, "text"); if (text) { strncpy(sms->text, text, sizeof(sms->text)); sms->text[sizeof(sms->text)-1] = '\0'; } return sms; } static int update_db_revision_3(void) { dbi_result result; struct gsm_sms *sms; LOGP(DDB, LOGL_NOTICE, "Going to migrate from revision 3\n"); result = dbi_conn_query(conn, "BEGIN EXCLUSIVE TRANSACTION"); if (!result) { LOGP(DDB, LOGL_ERROR, "Failed to begin transaction (upgrade from rev 3)\n"); return -EINVAL; } dbi_result_free(result); /* Rename old SMS table to be able create a new one */ result = dbi_conn_query(conn, "ALTER TABLE SMS RENAME TO SMS_3"); if (!result) { LOGP(DDB, LOGL_ERROR, "Failed to rename the old SMS table (upgrade from rev 3).\n"); goto rollback; } dbi_result_free(result); /* Create new SMS table with all the bells and whistles! */ result = dbi_conn_query(conn, create_stmts[SCHEMA_SMS]); if (!result) { LOGP(DDB, LOGL_ERROR, "Failed to create a new SMS table (upgrade from rev 3).\n"); goto rollback; } dbi_result_free(result); /* Cycle through old messages and convert them to the new format */ result = dbi_conn_query(conn, "SELECT * FROM SMS_3"); if (!result) { LOGP(DDB, LOGL_ERROR, "Failed fetch messages from the old SMS table (upgrade from rev 3).\n"); goto rollback; } while (dbi_result_next_row(result)) { sms = sms_from_result_v3(result); if (db_sms_store(sms) != 0) { LOGP(DDB, LOGL_ERROR, "Failed to store message to the new SMS table(upgrade from rev 3).\n"); sms_free(sms); dbi_result_free(result); goto rollback; } sms_free(sms); } dbi_result_free(result); /* Remove the temporary table */ result = dbi_conn_query(conn, "DROP TABLE SMS_3"); if (!result) { LOGP(DDB, LOGL_ERROR, "Failed to drop the old SMS table (upgrade from rev 3).\n"); goto rollback; } dbi_result_free(result); /* We're done. Bump DB Meta revision to 4 */ result = dbi_conn_query(conn, "UPDATE Meta " "SET value = '4' " "WHERE key = 'revision'"); if (!result) { LOGP(DDB, LOGL_ERROR, "Failed to update DB schema revision (upgrade from rev 3).\n"); goto rollback; } dbi_result_free(result); result = dbi_conn_query(conn, "COMMIT TRANSACTION"); if (!result) { LOGP(DDB, LOGL_ERROR, "Failed to commit the transaction (upgrade from rev 3)\n"); return -EINVAL; } /* Shrink DB file size by actually wiping out SMS_3 table data */ result = dbi_conn_query(conn, "VACUUM"); if (!result) LOGP(DDB, LOGL_ERROR, "VACUUM failed. Ignoring it (upgrade from rev 3).\n"); else dbi_result_free(result); return 0; rollback: result = dbi_conn_query(conn, "ROLLBACK TRANSACTION"); if (!result) LOGP(DDB, LOGL_ERROR, "Rollback failed (upgrade from rev 3).\n"); else dbi_result_free(result); return -EINVAL; } static int check_db_revision(void) { dbi_result result; const char *rev_s; result = dbi_conn_query(conn, "SELECT value FROM Meta WHERE key='revision'"); if (!result) return -EINVAL; if (!dbi_result_next_row(result)) { dbi_result_free(result); return -EINVAL; } rev_s = dbi_result_get_string(result, "value"); if (!rev_s) { dbi_result_free(result); return -EINVAL; } if (!strcmp(rev_s, "2")) { if (update_db_revision_2()) { LOGP(DDB, LOGL_FATAL, "Failed to update database from schema revision '%s'.\n", rev_s); dbi_result_free(result); return -EINVAL; } } else if (!strcmp(rev_s, "3")) { if (update_db_revision_3()) { LOGP(DDB, LOGL_FATAL, "Failed to update database from schema revision '%s'.\n", rev_s); dbi_result_free(result); return -EINVAL; } } else if (!strcmp(rev_s, SCHEMA_REVISION)) { /* everything is fine */ } else { LOGP(DDB, LOGL_FATAL, "Invalid database schema revision '%s'.\n", rev_s); dbi_result_free(result); return -EINVAL; } dbi_result_free(result); return 0; } static int db_configure(void) { dbi_result result; result = dbi_conn_query(conn, "PRAGMA synchronous = FULL"); if (!result) return -EINVAL; dbi_result_free(result); return 0; } int db_init(const char *name) { dbi_initialize(NULL); conn = dbi_conn_new("sqlite3"); if (conn == NULL) { LOGP(DDB, LOGL_FATAL, "Failed to create connection.\n"); return 1; } dbi_conn_error_handler( conn, db_error_func, NULL ); /* MySQL dbi_conn_set_option(conn, "host", "localhost"); dbi_conn_set_option(conn, "username", "your_name"); dbi_conn_set_option(conn, "password", "your_password"); dbi_conn_set_option(conn, "dbname", "your_dbname"); dbi_conn_set_option(conn, "encoding", "UTF-8"); */ /* SqLite 3 */ db_basename = strdup(name); db_dirname = strdup(name); dbi_conn_set_option(conn, "sqlite3_dbdir", dirname(db_dirname)); dbi_conn_set_option(conn, "dbname", basename(db_basename)); if (dbi_conn_connect(conn) < 0) goto out_err; return 0; out_err: free(db_dirname); free(db_basename); db_dirname = db_basename = NULL; return -1; } int db_prepare(void) { dbi_result result; int i; for (i = 0; i < ARRAY_SIZE(create_stmts); i++) { result = dbi_conn_query(conn, create_stmts[i]); if (!result) { LOGP(DDB, LOGL_ERROR, "Failed to create some table.\n"); return 1; } dbi_result_free(result); } if (check_db_revision() < 0) { LOGP(DDB, LOGL_FATAL, "Database schema revision invalid, " "please update your database schema\n"); return -1; } db_configure(); return 0; } int db_fini(void) { dbi_conn_close(conn); dbi_shutdown(); free(db_dirname); free(db_basename); return 0; } struct gsm_subscriber *db_create_subscriber(const char *imsi) { dbi_result result; struct gsm_subscriber *subscr; /* Is this subscriber known in the db? */ subscr = db_get_subscriber(GSM_SUBSCRIBER_IMSI, imsi); if (subscr) { result = dbi_conn_queryf(conn, "UPDATE Subscriber set updated = datetime('now') " "WHERE imsi = %s " , imsi); if (!result) LOGP(DDB, LOGL_ERROR, "failed to update timestamp\n"); else dbi_result_free(result); return subscr; } subscr = subscr_alloc(); if (!subscr) return NULL; subscr->flags |= GSM_SUBSCRIBER_FIRST_CONTACT; result = dbi_conn_queryf(conn, "INSERT INTO Subscriber " "(imsi, created, updated) " "VALUES " "(%s, datetime('now'), datetime('now')) ", imsi ); if (!result) LOGP(DDB, LOGL_ERROR, "Failed to create Subscriber by IMSI.\n"); subscr->id = dbi_conn_sequence_last(conn, NULL); strncpy(subscr->imsi, imsi, GSM_IMSI_LENGTH-1); dbi_result_free(result); LOGP(DDB, LOGL_INFO, "New Subscriber: ID %llu, IMSI %s\n", subscr->id, subscr->imsi); db_subscriber_alloc_exten(subscr); return subscr; } osmo_static_assert(sizeof(unsigned char) == sizeof(struct gsm48_classmark1), classmark1_size); static int get_equipment_by_subscr(struct gsm_subscriber *subscr) { dbi_result result; const char *string; unsigned char cm1; const unsigned char *cm2, *cm3; struct gsm_equipment *equip = &subscr->equipment; result = dbi_conn_queryf(conn, "SELECT Equipment.* " "FROM Equipment JOIN EquipmentWatch ON " "EquipmentWatch.equipment_id=Equipment.id " "WHERE EquipmentWatch.subscriber_id = %llu " "ORDER BY EquipmentWatch.updated DESC", subscr->id); if (!result) return -EIO; if (!dbi_result_next_row(result)) { dbi_result_free(result); return -ENOENT; } equip->id = dbi_result_get_ulonglong(result, "id"); string = dbi_result_get_string(result, "imei"); if (string) strncpy(equip->imei, string, sizeof(equip->imei)-1); string = dbi_result_get_string(result, "classmark1"); if (string) { cm1 = atoi(string) & 0xff; memcpy(&equip->classmark1, &cm1, sizeof(equip->classmark1)); } equip->classmark2_len = dbi_result_get_field_length(result, "classmark2"); cm2 = dbi_result_get_binary(result, "classmark2"); if (equip->classmark2_len > sizeof(equip->classmark2)) equip->classmark2_len = sizeof(equip->classmark2); memcpy(equip->classmark2, cm2, equip->classmark2_len); equip->classmark3_len = dbi_result_get_field_length(result, "classmark3"); cm3 = dbi_result_get_binary(result, "classmark3"); if (equip->classmark3_len > sizeof(equip->classmark3)) equip->classmark3_len = sizeof(equip->classmark3); memcpy(equip->classmark3, cm3, equip->classmark3_len); dbi_result_free(result); return 0; } int db_get_authinfo_for_subscr(struct gsm_auth_info *ainfo, struct gsm_subscriber *subscr) { dbi_result result; const unsigned char *a3a8_ki; result = dbi_conn_queryf(conn, "SELECT * FROM AuthKeys WHERE subscriber_id=%llu", subscr->id); if (!result) return -EIO; if (!dbi_result_next_row(result)) { dbi_result_free(result); return -ENOENT; } ainfo->auth_algo = dbi_result_get_ulonglong(result, "algorithm_id"); ainfo->a3a8_ki_len = dbi_result_get_field_length(result, "a3a8_ki"); a3a8_ki = dbi_result_get_binary(result, "a3a8_ki"); if (ainfo->a3a8_ki_len > sizeof(ainfo->a3a8_ki)) ainfo->a3a8_ki_len = sizeof(ainfo->a3a8_ki); memcpy(ainfo->a3a8_ki, a3a8_ki, ainfo->a3a8_ki_len); dbi_result_free(result); return 0; } int db_sync_authinfo_for_subscr(struct gsm_auth_info *ainfo, struct gsm_subscriber *subscr) { dbi_result result; struct gsm_auth_info ainfo_old; int rc, upd; unsigned char *ki_str; /* Deletion ? */ if (ainfo == NULL) { result = dbi_conn_queryf(conn, "DELETE FROM AuthKeys WHERE subscriber_id=%llu", subscr->id); if (!result) return -EIO; dbi_result_free(result); return 0; } /* Check if already existing */ rc = db_get_authinfo_for_subscr(&ainfo_old, subscr); if (rc && rc != -ENOENT) return rc; upd = rc ? 0 : 1; /* Update / Insert */ dbi_conn_quote_binary_copy(conn, ainfo->a3a8_ki, ainfo->a3a8_ki_len, &ki_str); if (!upd) { result = dbi_conn_queryf(conn, "INSERT INTO AuthKeys " "(subscriber_id, algorithm_id, a3a8_ki) " "VALUES (%llu, %u, %s)", subscr->id, ainfo->auth_algo, ki_str); } else { result = dbi_conn_queryf(conn, "UPDATE AuthKeys " "SET algorithm_id=%u, a3a8_ki=%s " "WHERE subscriber_id=%llu", ainfo->auth_algo, ki_str, subscr->id); } free(ki_str); if (!result) return -EIO; dbi_result_free(result); return 0; } int db_get_lastauthtuple_for_subscr(struct gsm_auth_tuple *atuple, struct gsm_subscriber *subscr) { dbi_result result; int len; const unsigned char *blob; result = dbi_conn_queryf(conn, "SELECT * FROM AuthLastTuples WHERE subscriber_id=%llu", subscr->id); if (!result) return -EIO; if (!dbi_result_next_row(result)) { dbi_result_free(result); return -ENOENT; } memset(atuple, 0, sizeof(*atuple)); atuple->use_count = dbi_result_get_ulonglong(result, "use_count"); atuple->key_seq = dbi_result_get_ulonglong(result, "key_seq"); len = dbi_result_get_field_length(result, "rand"); if (len != sizeof(atuple->rand)) goto err_size; blob = dbi_result_get_binary(result, "rand"); memcpy(atuple->rand, blob, len); len = dbi_result_get_field_length(result, "sres"); if (len != sizeof(atuple->sres)) goto err_size; blob = dbi_result_get_binary(result, "sres"); memcpy(atuple->sres, blob, len); len = dbi_result_get_field_length(result, "kc"); if (len != sizeof(atuple->kc)) goto err_size; blob = dbi_result_get_binary(result, "kc"); memcpy(atuple->kc, blob, len); dbi_result_free(result); return 0; err_size: dbi_result_free(result); return -EIO; } int db_sync_lastauthtuple_for_subscr(struct gsm_auth_tuple *atuple, struct gsm_subscriber *subscr) { dbi_result result; int rc, upd; struct gsm_auth_tuple atuple_old; unsigned char *rand_str, *sres_str, *kc_str; /* Deletion ? */ if (atuple == NULL) { result = dbi_conn_queryf(conn, "DELETE FROM AuthLastTuples WHERE subscriber_id=%llu", subscr->id); if (!result) return -EIO; dbi_result_free(result); return 0; } /* Check if already existing */ rc = db_get_lastauthtuple_for_subscr(&atuple_old, subscr); if (rc && rc != -ENOENT) return rc; upd = rc ? 0 : 1; /* Update / Insert */ dbi_conn_quote_binary_copy(conn, atuple->rand, sizeof(atuple->rand), &rand_str); dbi_conn_quote_binary_copy(conn, atuple->sres, sizeof(atuple->sres), &sres_str); dbi_conn_quote_binary_copy(conn, atuple->kc, sizeof(atuple->kc), &kc_str); if (!upd) { result = dbi_conn_queryf(conn, "INSERT INTO AuthLastTuples " "(subscriber_id, issued, use_count, " "key_seq, rand, sres, kc) " "VALUES (%llu, datetime('now'), %u, " "%u, %s, %s, %s ) ", subscr->id, atuple->use_count, atuple->key_seq, rand_str, sres_str, kc_str); } else { char *issued = atuple->key_seq == atuple_old.key_seq ? "issued" : "datetime('now')"; result = dbi_conn_queryf(conn, "UPDATE AuthLastTuples " "SET issued=%s, use_count=%u, " "key_seq=%u, rand=%s, sres=%s, kc=%s " "WHERE subscriber_id = %llu", issued, atuple->use_count, atuple->key_seq, rand_str, sres_str, kc_str, subscr->id); } free(rand_str); free(sres_str); free(kc_str); if (!result) return -EIO; dbi_result_free(result); return 0; } static void db_set_from_query(struct gsm_subscriber *subscr, dbi_conn result) { const char *string; string = dbi_result_get_string(result, "imsi"); if (string) strncpy(subscr->imsi, string, GSM_IMSI_LENGTH-1); string = dbi_result_get_string(result, "tmsi"); if (string) subscr->tmsi = tmsi_from_string(string); string = dbi_result_get_string(result, "name"); if (string) strncpy(subscr->name, string, GSM_NAME_LENGTH); string = dbi_result_get_string(result, "extension"); if (string) strncpy(subscr->extension, string, GSM_EXTENSION_LENGTH); subscr->lac = dbi_result_get_ulonglong(result, "lac"); if (!dbi_result_field_is_null(result, "expire_lu")) subscr->expire_lu = dbi_result_get_datetime(result, "expire_lu"); else subscr->expire_lu = GSM_SUBSCRIBER_NO_EXPIRATION; subscr->authorized = dbi_result_get_ulonglong(result, "authorized"); } #define BASE_QUERY "SELECT * FROM Subscriber " struct gsm_subscriber *db_get_subscriber(enum gsm_subscriber_field field, const char *id) { dbi_result result; char *quoted; struct gsm_subscriber *subscr; switch (field) { case GSM_SUBSCRIBER_IMSI: dbi_conn_quote_string_copy(conn, id, "ed); result = dbi_conn_queryf(conn, BASE_QUERY "WHERE imsi = %s ", quoted ); free(quoted); break; case GSM_SUBSCRIBER_TMSI: dbi_conn_quote_string_copy(conn, id, "ed); result = dbi_conn_queryf(conn, BASE_QUERY "WHERE tmsi = %s ", quoted ); free(quoted); break; case GSM_SUBSCRIBER_EXTENSION: dbi_conn_quote_string_copy(conn, id, "ed); result = dbi_conn_queryf(conn, BASE_QUERY "WHERE extension = %s ", quoted ); free(quoted); break; case GSM_SUBSCRIBER_ID: dbi_conn_quote_string_copy(conn, id, "ed); result = dbi_conn_queryf(conn, BASE_QUERY "WHERE id = %s ", quoted); free(quoted); break; default: LOGP(DDB, LOGL_NOTICE, "Unknown query selector for Subscriber.\n"); return NULL; } if (!result) { LOGP(DDB, LOGL_ERROR, "Failed to query Subscriber.\n"); return NULL; } if (!dbi_result_next_row(result)) { DEBUGP(DDB, "Failed to find the Subscriber. '%u' '%s'\n", field, id); dbi_result_free(result); return NULL; } subscr = subscr_alloc(); subscr->id = dbi_result_get_ulonglong(result, "id"); db_set_from_query(subscr, result); DEBUGP(DDB, "Found Subscriber: ID %llu, IMSI %s, NAME '%s', TMSI %u, EXTEN '%s', LAC %hu, AUTH %u\n", subscr->id, subscr->imsi, subscr->name, subscr->tmsi, subscr->extension, subscr->lac, subscr->authorized); dbi_result_free(result); get_equipment_by_subscr(subscr); return subscr; } int db_subscriber_update(struct gsm_subscriber *subscr) { char buf[32]; dbi_result result; /* Copy the id to a string as queryf with %llu is failing */ sprintf(buf, "%llu", subscr->id); result = dbi_conn_queryf(conn, BASE_QUERY "WHERE id = %s", buf); if (!result) { LOGP(DDB, LOGL_ERROR, "Failed to query Subscriber: %llu\n", subscr->id); return -EIO; } if (!dbi_result_next_row(result)) { DEBUGP(DDB, "Failed to find the Subscriber. %llu\n", subscr->id); dbi_result_free(result); return -EIO; } db_set_from_query(subscr, result); dbi_result_free(result); get_equipment_by_subscr(subscr); return 0; } int db_sync_subscriber(struct gsm_subscriber *subscriber) { dbi_result result; char tmsi[14]; char *q_tmsi, *q_name, *q_extension; dbi_conn_quote_string_copy(conn, subscriber->name, &q_name); dbi_conn_quote_string_copy(conn, subscriber->extension, &q_extension); if (subscriber->tmsi != GSM_RESERVED_TMSI) { sprintf(tmsi, "%u", subscriber->tmsi); dbi_conn_quote_string_copy(conn, tmsi, &q_tmsi); } else q_tmsi = strdup("NULL"); if (subscriber->expire_lu == GSM_SUBSCRIBER_NO_EXPIRATION) { result = dbi_conn_queryf(conn, "UPDATE Subscriber " "SET updated = datetime('now'), " "name = %s, " "extension = %s, " "authorized = %i, " "tmsi = %s, " "lac = %i, " "expire_lu = NULL " "WHERE imsi = %s ", q_name, q_extension, subscriber->authorized, q_tmsi, subscriber->lac, subscriber->imsi); } else { result = dbi_conn_queryf(conn, "UPDATE Subscriber " "SET updated = datetime('now'), " "name = %s, " "extension = %s, " "authorized = %i, " "tmsi = %s, " "lac = %i, " "expire_lu = datetime(%i, 'unixepoch') " "WHERE imsi = %s ", q_name, q_extension, subscriber->authorized, q_tmsi, subscriber->lac, (int) subscriber->expire_lu, subscriber->imsi); } free(q_tmsi); free(q_name); free(q_extension); if (!result) { LOGP(DDB, LOGL_ERROR, "Failed to update Subscriber (by IMSI).\n"); return 1; } dbi_result_free(result); return 0; } int db_subscriber_delete(struct gsm_subscriber *subscr) { dbi_result result; result = dbi_conn_queryf(conn, "DELETE FROM AuthKeys WHERE subscriber_id=%llu", subscr->id); if (!result) { LOGP(DDB, LOGL_ERROR, "Failed to delete Authkeys for %llu\n", subscr->id); return -1; } dbi_result_free(result); result = dbi_conn_queryf(conn, "DELETE FROM AuthLastTuples WHERE subscriber_id=%llu", subscr->id); if (!result) { LOGP(DDB, LOGL_ERROR, "Failed to delete AuthLastTuples for %llu\n", subscr->id); return -1; } dbi_result_free(result); result = dbi_conn_queryf(conn, "DELETE FROM AuthToken WHERE subscriber_id=%llu", subscr->id); if (!result) { LOGP(DDB, LOGL_ERROR, "Failed to delete AuthToken for %llu\n", subscr->id); return -1; } dbi_result_free(result); result = dbi_conn_queryf(conn, "DELETE FROM EquipmentWatch WHERE subscriber_id=%llu", subscr->id); if (!result) { LOGP(DDB, LOGL_ERROR, "Failed to delete EquipmentWatch for %llu\n", subscr->id); return -1; } dbi_result_free(result); result = dbi_conn_queryf(conn, "DELETE FROM SMS WHERE src_addr=%s OR dest_addr=%s", subscr->extension, subscr->extension); if (!result) { LOGP(DDB, LOGL_ERROR, "Failed to delete SMS for %llu\n", subscr->id); return -1; } dbi_result_free(result); result = dbi_conn_queryf(conn, "DELETE FROM VLR WHERE subscriber_id=%llu", subscr->id); if (!result) { LOGP(DDB, LOGL_ERROR, "Failed to delete VLR for %llu\n", subscr->id); return -1; } dbi_result_free(result); result = dbi_conn_queryf(conn, "DELETE FROM ApduBlobs WHERE subscriber_id=%llu", subscr->id); if (!result) { LOGP(DDB, LOGL_ERROR, "Failed to delete ApduBlobs for %llu\n", subscr->id); return -1; } dbi_result_free(result); result = dbi_conn_queryf(conn, "DELETE FROM Subscriber WHERE id=%llu", subscr->id); if (!result) { LOGP(DDB, LOGL_ERROR, "Failed to delete Subscriber for %llu\n", subscr->id); return -1; } dbi_result_free(result); return 0; } /** * List all the authorized and non-expired subscribers. The callback will * be called one by one. The subscr argument is not fully initialize and * subscr_get/subscr_put must not be called. The passed in pointer will be * deleted after the callback by the database call. */ int db_subscriber_list_active(void (*cb)(struct gsm_subscriber*,void*), void *closure) { dbi_result result; result = dbi_conn_query(conn, "SELECT * from Subscriber WHERE LAC != 0 AND authorized = 1"); if (!result) { LOGP(DDB, LOGL_ERROR, "Failed to list active subscribers\n"); return -1; } while (dbi_result_next_row(result)) { struct gsm_subscriber *subscr; subscr = subscr_alloc(); subscr->id = dbi_result_get_ulonglong(result, "id"); db_set_from_query(subscr, result); cb(subscr, closure); OSMO_ASSERT(subscr->use_count == 1); llist_del(&subscr->entry); talloc_free(subscr); } dbi_result_free(result); return 0; } int db_sync_equipment(struct gsm_equipment *equip) { dbi_result result; unsigned char *cm2, *cm3; char *q_imei; uint8_t classmark1; memcpy(&classmark1, &equip->classmark1, sizeof(classmark1)); DEBUGP(DDB, "Sync Equipment IMEI=%s, classmark1=%02x", equip->imei, classmark1); if (equip->classmark2_len) DEBUGPC(DDB, ", classmark2=%s", osmo_hexdump(equip->classmark2, equip->classmark2_len)); if (equip->classmark3_len) DEBUGPC(DDB, ", classmark3=%s", osmo_hexdump(equip->classmark3, equip->classmark3_len)); DEBUGPC(DDB, "\n"); dbi_conn_quote_binary_copy(conn, equip->classmark2, equip->classmark2_len, &cm2); dbi_conn_quote_binary_copy(conn, equip->classmark3, equip->classmark3_len, &cm3); dbi_conn_quote_string_copy(conn, equip->imei, &q_imei); result = dbi_conn_queryf(conn, "UPDATE Equipment SET " "updated = datetime('now'), " "classmark1 = %u, " "classmark2 = %s, " "classmark3 = %s " "WHERE imei = %s ", classmark1, cm2, cm3, q_imei); free(cm2); free(cm3); free(q_imei); if (!result) { LOGP(DDB, LOGL_ERROR, "Failed to update Equipment\n"); return -EIO; } dbi_result_free(result); return 0; } int db_subscriber_expire(void *priv, void (*callback)(void *priv, long long unsigned int id)) { dbi_result result; result = dbi_conn_query(conn, "SELECT id " "FROM Subscriber " "WHERE lac != 0 AND " "( expire_lu is NOT NULL " "AND expire_lu < datetime('now') ) " "LIMIT 1"); if (!result) { LOGP(DDB, LOGL_ERROR, "Failed to get expired subscribers\n"); return -EIO; } while (dbi_result_next_row(result)) callback(priv, dbi_result_get_ulonglong(result, "id")); dbi_result_free(result); return 0; } int db_subscriber_alloc_tmsi(struct gsm_subscriber *subscriber) { dbi_result result = NULL; char tmsi[14]; char *tmsi_quoted; for (;;) { if (RAND_bytes((uint8_t *) &subscriber->tmsi, sizeof(subscriber->tmsi)) != 1) { LOGP(DDB, LOGL_ERROR, "RAND_bytes failed\n"); return 1; } if (subscriber->tmsi == GSM_RESERVED_TMSI) continue; sprintf(tmsi, "%u", subscriber->tmsi); dbi_conn_quote_string_copy(conn, tmsi, &tmsi_quoted); result = dbi_conn_queryf(conn, "SELECT * FROM Subscriber " "WHERE tmsi = %s ", tmsi_quoted); free(tmsi_quoted); if (!result) { LOGP(DDB, LOGL_ERROR, "Failed to query Subscriber " "while allocating new TMSI.\n"); return 1; } if (dbi_result_get_numrows(result)) { dbi_result_free(result); continue; } if (!dbi_result_next_row(result)) { dbi_result_free(result); DEBUGP(DDB, "Allocated TMSI %u for IMSI %s.\n", subscriber->tmsi, subscriber->imsi); return db_sync_subscriber(subscriber); } dbi_result_free(result); } return 0; } int db_subscriber_alloc_exten(struct gsm_subscriber *subscriber) { dbi_result result = NULL; uint32_t try; for (;;) { try = (rand()%(GSM_MAX_EXTEN-GSM_MIN_EXTEN+1)+GSM_MIN_EXTEN); result = dbi_conn_queryf(conn, "SELECT * FROM Subscriber " "WHERE extension = %i", try ); if (!result) { LOGP(DDB, LOGL_ERROR, "Failed to query Subscriber " "while allocating new extension.\n"); return 1; } if (dbi_result_get_numrows(result)){ dbi_result_free(result); continue; } if (!dbi_result_next_row(result)) { dbi_result_free(result); break; } dbi_result_free(result); } sprintf(subscriber->extension, "%i", try); DEBUGP(DDB, "Allocated extension %i for IMSI %s.\n", try, subscriber->imsi); return db_sync_subscriber(subscriber); } /* * try to allocate a new unique token for this subscriber and return it * via a parameter. if the subscriber already has a token, return * an error. */ int db_subscriber_alloc_token(struct gsm_subscriber *subscriber, uint32_t *token) { dbi_result result; uint32_t try; for (;;) { if (RAND_bytes((uint8_t *) &try, sizeof(try)) != 1) { LOGP(DDB, LOGL_ERROR, "RAND_bytes failed\n"); return 1; } if (!try) /* 0 is an invalid token */ continue; result = dbi_conn_queryf(conn, "SELECT * FROM AuthToken " "WHERE subscriber_id = %llu OR token = \"%08X\" ", subscriber->id, try); if (!result) { LOGP(DDB, LOGL_ERROR, "Failed to query AuthToken " "while allocating new token.\n"); return 1; } if (dbi_result_get_numrows(result)) { dbi_result_free(result); continue; } if (!dbi_result_next_row(result)) { dbi_result_free(result); break; } dbi_result_free(result); } result = dbi_conn_queryf(conn, "INSERT INTO AuthToken " "(subscriber_id, created, token) " "VALUES " "(%llu, datetime('now'), \"%08X\") ", subscriber->id, try); if (!result) { LOGP(DDB, LOGL_ERROR, "Failed to create token %08X for " "IMSI %s.\n", try, subscriber->imsi); return 1; } dbi_result_free(result); *token = try; DEBUGP(DDB, "Allocated token %08X for IMSI %s.\n", try, subscriber->imsi); return 0; } int db_subscriber_assoc_imei(struct gsm_subscriber *subscriber, char imei[GSM_IMEI_LENGTH]) { unsigned long long equipment_id, watch_id; dbi_result result; strncpy(subscriber->equipment.imei, imei, sizeof(subscriber->equipment.imei)-1); result = dbi_conn_queryf(conn, "INSERT OR IGNORE INTO Equipment " "(imei, created, updated) " "VALUES " "(%s, datetime('now'), datetime('now')) ", imei); if (!result) { LOGP(DDB, LOGL_ERROR, "Failed to create Equipment by IMEI.\n"); return 1; } equipment_id = 0; if (dbi_result_get_numrows_affected(result)) { equipment_id = dbi_conn_sequence_last(conn, NULL); } dbi_result_free(result); if (equipment_id) DEBUGP(DDB, "New Equipment: ID %llu, IMEI %s\n", equipment_id, imei); else { result = dbi_conn_queryf(conn, "SELECT id FROM Equipment " "WHERE imei = %s ", imei ); if (!result) { LOGP(DDB, LOGL_ERROR, "Failed to query Equipment by IMEI.\n"); return 1; } if (!dbi_result_next_row(result)) { LOGP(DDB, LOGL_ERROR, "Failed to find the Equipment.\n"); dbi_result_free(result); return 1; } equipment_id = dbi_result_get_ulonglong(result, "id"); dbi_result_free(result); } result = dbi_conn_queryf(conn, "INSERT OR IGNORE INTO EquipmentWatch " "(subscriber_id, equipment_id, created, updated) " "VALUES " "(%llu, %llu, datetime('now'), datetime('now')) ", subscriber->id, equipment_id); if (!result) { LOGP(DDB, LOGL_ERROR, "Failed to create EquipmentWatch.\n"); return 1; } watch_id = 0; if (dbi_result_get_numrows_affected(result)) watch_id = dbi_conn_sequence_last(conn, NULL); dbi_result_free(result); if (watch_id) DEBUGP(DDB, "New EquipmentWatch: ID %llu, IMSI %s, IMEI %s\n", equipment_id, subscriber->imsi, imei); else { result = dbi_conn_queryf(conn, "UPDATE EquipmentWatch " "SET updated = datetime('now') " "WHERE subscriber_id = %llu AND equipment_id = %llu ", subscriber->id, equipment_id); if (!result) { LOGP(DDB, LOGL_ERROR, "Failed to update EquipmentWatch.\n"); return 1; } dbi_result_free(result); DEBUGP(DDB, "Updated EquipmentWatch: ID %llu, IMSI %s, IMEI %s\n", equipment_id, subscriber->imsi, imei); } return 0; } /* store an [unsent] SMS to the database */ int db_sms_store(struct gsm_sms *sms) { dbi_result result; char *q_text, *q_daddr, *q_saddr; unsigned char *q_udata; char *validity_timestamp = "2222-2-2"; /* FIXME: generate validity timestamp based on validity_minutes */ dbi_conn_quote_string_copy(conn, (char *)sms->text, &q_text); dbi_conn_quote_string_copy(conn, (char *)sms->dst.addr, &q_daddr); dbi_conn_quote_string_copy(conn, (char *)sms->src.addr, &q_saddr); dbi_conn_quote_binary_copy(conn, sms->user_data, sms->user_data_len, &q_udata); /* FIXME: correct validity period */ result = dbi_conn_queryf(conn, "INSERT INTO SMS " "(created, valid_until, " "reply_path_req, status_rep_req, protocol_id, " "data_coding_scheme, ud_hdr_ind, " "user_data, text, " "dest_addr, dest_ton, dest_npi, " "src_addr, src_ton, src_npi) VALUES " "(datetime('now'), %u, " "%u, %u, %u, " "%u, %u, " "%s, %s, " "%s, %u, %u, " "%s, %u, %u)", validity_timestamp, sms->reply_path_req, sms->status_rep_req, sms->protocol_id, sms->data_coding_scheme, sms->ud_hdr_ind, q_udata, q_text, q_daddr, sms->dst.ton, sms->dst.npi, q_saddr, sms->src.ton, sms->src.npi); free(q_text); free(q_udata); free(q_daddr); free(q_saddr); if (!result) return -EIO; dbi_result_free(result); return 0; } static struct gsm_sms *sms_from_result(struct gsm_network *net, dbi_result result) { struct gsm_sms *sms = sms_alloc(); const char *text, *daddr, *saddr; const unsigned char *user_data; if (!sms) return NULL; sms->id = dbi_result_get_ulonglong(result, "id"); /* FIXME: validity */ /* FIXME: those should all be get_uchar, but sqlite3 is braindead */ sms->reply_path_req = dbi_result_get_ulonglong(result, "reply_path_req"); sms->status_rep_req = dbi_result_get_ulonglong(result, "status_rep_req"); sms->ud_hdr_ind = dbi_result_get_ulonglong(result, "ud_hdr_ind"); sms->protocol_id = dbi_result_get_ulonglong(result, "protocol_id"); sms->data_coding_scheme = dbi_result_get_ulonglong(result, "data_coding_scheme"); /* sms->msg_ref is temporary and not stored in DB */ sms->dst.npi = dbi_result_get_ulonglong(result, "dest_npi"); sms->dst.ton = dbi_result_get_ulonglong(result, "dest_ton"); daddr = dbi_result_get_string(result, "dest_addr"); if (daddr) { strncpy(sms->dst.addr, daddr, sizeof(sms->dst.addr)); sms->dst.addr[sizeof(sms->dst.addr)-1] = '\0'; } sms->receiver = subscr_get_by_extension(net->subscr_group, sms->dst.addr); sms->src.npi = dbi_result_get_ulonglong(result, "src_npi"); sms->src.ton = dbi_result_get_ulonglong(result, "src_ton"); saddr = dbi_result_get_string(result, "src_addr"); if (saddr) { strncpy(sms->src.addr, saddr, sizeof(sms->src.addr)); sms->src.addr[sizeof(sms->src.addr)-1] = '\0'; } sms->user_data_len = dbi_result_get_field_length(result, "user_data"); user_data = dbi_result_get_binary(result, "user_data"); if (sms->user_data_len > sizeof(sms->user_data)) sms->user_data_len = (uint8_t) sizeof(sms->user_data); memcpy(sms->user_data, user_data, sms->user_data_len); text = dbi_result_get_string(result, "text"); if (text) { strncpy(sms->text, text, sizeof(sms->text)); sms->text[sizeof(sms->text)-1] = '\0'; } return sms; } struct gsm_sms *db_sms_get(struct gsm_network *net, unsigned long long id) { dbi_result result; struct gsm_sms *sms; result = dbi_conn_queryf(conn, "SELECT * FROM SMS WHERE SMS.id = %llu", id); if (!result) return NULL; if (!dbi_result_next_row(result)) { dbi_result_free(result); return NULL; } sms = sms_from_result(net, result); dbi_result_free(result); return sms; } /* retrieve the next unsent SMS with ID >= min_id */ struct gsm_sms *db_sms_get_unsent(struct gsm_network *net, unsigned long long min_id) { dbi_result result; struct gsm_sms *sms; result = dbi_conn_queryf(conn, "SELECT SMS.* " "FROM SMS JOIN Subscriber ON " "SMS.dest_addr = Subscriber.extension " "WHERE SMS.id >= %llu AND SMS.sent IS NULL " "AND Subscriber.lac > 0 " "ORDER BY SMS.id LIMIT 1", min_id); if (!result) return NULL; if (!dbi_result_next_row(result)) { dbi_result_free(result); return NULL; } sms = sms_from_result(net, result); dbi_result_free(result); return sms; } struct gsm_sms *db_sms_get_unsent_by_subscr(struct gsm_network *net, unsigned long long min_subscr_id, unsigned int failed) { dbi_result result; struct gsm_sms *sms; result = dbi_conn_queryf(conn, "SELECT SMS.* " "FROM SMS JOIN Subscriber ON " "SMS.dest_addr = Subscriber.extension " "WHERE Subscriber.id >= %llu AND SMS.sent IS NULL " "AND Subscriber.lac > 0 AND SMS.deliver_attempts < %u " "ORDER BY Subscriber.id, SMS.id LIMIT 1", min_subscr_id, failed); if (!result) return NULL; if (!dbi_result_next_row(result)) { dbi_result_free(result); return NULL; } sms = sms_from_result(net, result); dbi_result_free(result); return sms; } /* retrieve the next unsent SMS for a given subscriber */ struct gsm_sms *db_sms_get_unsent_for_subscr(struct gsm_subscriber *subscr) { dbi_result result; struct gsm_sms *sms; result = dbi_conn_queryf(conn, "SELECT SMS.* " "FROM SMS JOIN Subscriber ON " "SMS.dest_addr = Subscriber.extension " "WHERE Subscriber.id = %llu AND SMS.sent IS NULL " "AND Subscriber.lac > 0 " "ORDER BY SMS.id LIMIT 1", subscr->id); if (!result) return NULL; if (!dbi_result_next_row(result)) { dbi_result_free(result); return NULL; } sms = sms_from_result(subscr->group->net, result); dbi_result_free(result); return sms; } /* mark a given SMS as delivered */ int db_sms_mark_delivered(struct gsm_sms *sms) { dbi_result result; result = dbi_conn_queryf(conn, "UPDATE SMS " "SET sent = datetime('now') " "WHERE id = %llu", sms->id); if (!result) { LOGP(DDB, LOGL_ERROR, "Failed to mark SMS %llu as sent.\n", sms->id); return 1; } dbi_result_free(result); return 0; } /* increase the number of attempted deliveries */ int db_sms_inc_deliver_attempts(struct gsm_sms *sms) { dbi_result result; result = dbi_conn_queryf(conn, "UPDATE SMS " "SET deliver_attempts = deliver_attempts + 1 " "WHERE id = %llu", sms->id); if (!result) { LOGP(DDB, LOGL_ERROR, "Failed to inc deliver attempts for " "SMS %llu.\n", sms->id); return 1; } dbi_result_free(result); return 0; } int db_apdu_blob_store(struct gsm_subscriber *subscr, uint8_t apdu_id_flags, uint8_t len, uint8_t *apdu) { dbi_result result; unsigned char *q_apdu; dbi_conn_quote_binary_copy(conn, apdu, len, &q_apdu); result = dbi_conn_queryf(conn, "INSERT INTO ApduBlobs " "(created,subscriber_id,apdu_id_flags,apdu) VALUES " "(datetime('now'),%llu,%u,%s)", subscr->id, apdu_id_flags, q_apdu); free(q_apdu); if (!result) return -EIO; dbi_result_free(result); return 0; } int db_store_counter(struct osmo_counter *ctr) { dbi_result result; char *q_name; dbi_conn_quote_string_copy(conn, ctr->name, &q_name); result = dbi_conn_queryf(conn, "INSERT INTO Counters " "(timestamp,name,value) VALUES " "(datetime('now'),%s,%lu)", q_name, ctr->value); free(q_name); if (!result) return -EIO; dbi_result_free(result); return 0; } static int db_store_rate_ctr(struct rate_ctr_group *ctrg, unsigned int num, char *q_prefix) { dbi_result result; char *q_name; dbi_conn_quote_string_copy(conn, ctrg->desc->ctr_desc[num].name, &q_name); result = dbi_conn_queryf(conn, "Insert INTO RateCounters " "(timestamp,name,idx,value) VALUES " "(datetime('now'),%s.%s,%u,%"PRIu64")", q_prefix, q_name, ctrg->idx, ctrg->ctr[num].current); free(q_name); if (!result) return -EIO; dbi_result_free(result); return 0; } int db_store_rate_ctr_group(struct rate_ctr_group *ctrg) { unsigned int i; char *q_prefix; dbi_conn_quote_string_copy(conn, ctrg->desc->group_name_prefix, &q_prefix); for (i = 0; i < ctrg->desc->num_ctr; i++) db_store_rate_ctr(ctrg, i, q_prefix); free(q_prefix); return 0; } openbsc-0.15.0/openbsc/src/libmsc/gsm_04_08.c000066400000000000000000003141601265565154000205010ustar00rootroot00000000000000/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */ /* (C) 2008-2009 by Harald Welte * (C) 2008-2012 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 . * */ #include #include #include #include #include #include #include "bscconfig.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void *tall_locop_ctx; void *tall_authciphop_ctx; static int tch_rtp_signal(struct gsm_lchan *lchan, int signal); static int gsm0408_loc_upd_acc(struct gsm_subscriber_connection *conn); static int gsm48_tx_simple(struct gsm_subscriber_connection *conn, uint8_t pdisc, uint8_t msg_type); static void schedule_reject(struct gsm_subscriber_connection *conn); static void release_anchor(struct gsm_subscriber_connection *conn); struct gsm_lai { uint16_t mcc; uint16_t mnc; uint16_t lac; }; static int apply_codec_restrictions(struct gsm_bts *bts, struct gsm_mncc_bearer_cap *bcap) { int i, j; /* remove unsupported speech versions from list */ for (i = 0, j = 0; bcap->speech_ver[i] >= 0; i++) { if (bcap->speech_ver[i] == GSM48_BCAP_SV_FR) bcap->speech_ver[j++] = GSM48_BCAP_SV_FR; if (bcap->speech_ver[i] == GSM48_BCAP_SV_EFR && bts->codec.efr) bcap->speech_ver[j++] = GSM48_BCAP_SV_EFR; if (bcap->speech_ver[i] == GSM48_BCAP_SV_AMR_F && bts->codec.amr) bcap->speech_ver[j++] = GSM48_BCAP_SV_AMR_F; if (bcap->speech_ver[i] == GSM48_BCAP_SV_HR && bts->codec.hr) bcap->speech_ver[j++] = GSM48_BCAP_SV_HR; if (bcap->speech_ver[i] == GSM48_BCAP_SV_AMR_H && bts->codec.amr) bcap->speech_ver[j++] = GSM48_BCAP_SV_AMR_H; } bcap->speech_ver[j] = -1; return 0; } static uint32_t new_callref = 0x80000001; void cc_tx_to_mncc(struct gsm_network *net, struct msgb *msg) { net->mncc_recv(net, msg); } static int gsm48_conn_sendmsg(struct msgb *msg, struct gsm_subscriber_connection *conn, struct gsm_trans *trans) { struct gsm48_hdr *gh = (struct gsm48_hdr *) msg->data; /* if we get passed a transaction reference, do some common * work that the caller no longer has to do */ if (trans) { gh->proto_discr = trans->protocol | (trans->transaction_id << 4); msg->lchan = trans->conn->lchan; } if (msg->lchan) { struct e1inp_sign_link *sign_link = msg->lchan->ts->trx->rsl_link; msg->dst = sign_link; if ((gh->proto_discr & GSM48_PDISC_MASK) == GSM48_PDISC_CC) DEBUGP(DCC, "(bts %d trx %d ts %d ti %02x) " "Sending '%s' to MS.\n", sign_link->trx->bts->nr, sign_link->trx->nr, msg->lchan->ts->nr, gh->proto_discr & 0xf0, gsm48_cc_msg_name(gh->msg_type)); else DEBUGP(DCC, "(bts %d trx %d ts %d pd %02x) " "Sending 0x%02x to MS.\n", sign_link->trx->bts->nr, sign_link->trx->nr, msg->lchan->ts->nr, gh->proto_discr, gh->msg_type); } return gsm0808_submit_dtap(conn, msg, 0, 0); } int gsm48_cc_tx_notify_ss(struct gsm_trans *trans, const char *message) { struct gsm48_hdr *gh; struct msgb *ss_notify; ss_notify = gsm0480_create_notifySS(message); if (!ss_notify) return -1; gsm0480_wrap_invoke(ss_notify, GSM0480_OP_CODE_NOTIFY_SS, 0); uint8_t *data = msgb_push(ss_notify, 1); data[0] = ss_notify->len - 1; gh = (struct gsm48_hdr *) msgb_push(ss_notify, sizeof(*gh)); gh->msg_type = GSM48_MT_CC_FACILITY; return gsm48_conn_sendmsg(ss_notify, trans->conn, trans); } void release_security_operation(struct gsm_subscriber_connection *conn) { if (!conn->sec_operation) return; talloc_free(conn->sec_operation); conn->sec_operation = NULL; msc_release_connection(conn); } void allocate_security_operation(struct gsm_subscriber_connection *conn) { conn->sec_operation = talloc_zero(tall_authciphop_ctx, struct gsm_security_operation); } int gsm48_secure_channel(struct gsm_subscriber_connection *conn, int key_seq, gsm_cbfn *cb, void *cb_data) { struct gsm_network *net = conn->bts->network; struct gsm_subscriber *subscr = conn->subscr; struct gsm_security_operation *op; struct gsm_auth_tuple atuple; int status = -1, rc; /* Check if we _can_ enable encryption. Cases where we can't: * - Encryption disabled in config * - Channel already secured (nothing to do) * - Subscriber equipment doesn't support configured encryption */ if (!net->a5_encryption) { status = GSM_SECURITY_NOAVAIL; } else if (conn->lchan->encr.alg_id > RSL_ENC_ALG_A5(0)) { DEBUGP(DMM, "Requesting to secure an already secure channel"); status = GSM_SECURITY_ALREADY; } else if (!ms_cm2_a5n_support(subscr->equipment.classmark2, net->a5_encryption)) { DEBUGP(DMM, "Subscriber equipment doesn't support requested encryption"); status = GSM_SECURITY_NOAVAIL; } /* If not done yet, try to get info for this user */ if (status < 0) { rc = auth_get_tuple_for_subscr(&atuple, subscr, key_seq); if (rc <= 0) status = GSM_SECURITY_NOAVAIL; } /* Are we done yet ? */ if (status >= 0) return cb ? cb(GSM_HOOK_RR_SECURITY, status, NULL, conn, cb_data) : 0; /* Start an operation (can't have more than one pending !!!) */ if (conn->sec_operation) return -EBUSY; allocate_security_operation(conn); op = conn->sec_operation; op->cb = cb; op->cb_data = cb_data; memcpy(&op->atuple, &atuple, sizeof(struct gsm_auth_tuple)); /* FIXME: Should start a timer for completion ... */ /* Then do whatever is needed ... */ if (rc == AUTH_DO_AUTH_THAN_CIPH) { /* Start authentication */ return gsm48_tx_mm_auth_req(conn, op->atuple.rand, op->atuple.key_seq); } else if (rc == AUTH_DO_CIPH) { /* Start ciphering directly */ return gsm0808_cipher_mode(conn, net->a5_encryption, op->atuple.kc, 8, 0); } return -EINVAL; /* not reached */ } static int authorize_subscriber(struct gsm_loc_updating_operation *loc, struct gsm_subscriber *subscriber) { if (!subscriber) return 0; /* * Do not send accept yet as more information should arrive. Some * phones will not send us the information and we will have to check * what we want to do with that. */ if (loc && (loc->waiting_for_imsi || loc->waiting_for_imei)) return 0; switch (subscriber->group->net->auth_policy) { case GSM_AUTH_POLICY_CLOSED: return subscriber->authorized; case GSM_AUTH_POLICY_TOKEN: if (subscriber->authorized) return subscriber->authorized; return (subscriber->flags & GSM_SUBSCRIBER_FIRST_CONTACT); case GSM_AUTH_POLICY_ACCEPT_ALL: return 1; default: return 0; } } static void release_loc_updating_req(struct gsm_subscriber_connection *conn, int release) { if (!conn->loc_operation) return; /* No need to keep the connection up */ release_anchor(conn); osmo_timer_del(&conn->loc_operation->updating_timer); talloc_free(conn->loc_operation); conn->loc_operation = NULL; if (release) msc_release_connection(conn); } static void allocate_loc_updating_req(struct gsm_subscriber_connection *conn) { if (conn->loc_operation) LOGP(DMM, LOGL_ERROR, "Connection already had operation.\n"); release_loc_updating_req(conn, 0); conn->loc_operation = talloc_zero(tall_locop_ctx, struct gsm_loc_updating_operation); } static int finish_lu(struct gsm_subscriber_connection *conn) { int rc = 0; int avoid_tmsi = conn->bts->network->avoid_tmsi; /* We're all good */ if (avoid_tmsi) { conn->subscr->tmsi = GSM_RESERVED_TMSI; db_sync_subscriber(conn->subscr); } else { db_subscriber_alloc_tmsi(conn->subscr); } rc = gsm0408_loc_upd_acc(conn); if (conn->bts->network->send_mm_info) { /* send MM INFO with network name */ rc = gsm48_tx_mm_info(conn); } /* call subscr_update after putting the loc_upd_acc * in the transmit queue, since S_SUBSCR_ATTACHED might * trigger further action like SMS delivery */ subscr_update(conn->subscr, conn->bts, GSM_SUBSCRIBER_UPDATE_ATTACHED); /* * The gsm0408_loc_upd_acc sends a MI with the TMSI. The * MS needs to respond with a TMSI REALLOCATION COMPLETE * (even if the TMSI is the same). */ if (avoid_tmsi) release_loc_updating_req(conn, 1); return rc; } static int _gsm0408_authorize_sec_cb(unsigned int hooknum, unsigned int event, struct msgb *msg, void *data, void *param) { struct gsm_subscriber_connection *conn = data; int rc = 0; switch (event) { case GSM_SECURITY_AUTH_FAILED: release_loc_updating_req(conn, 1); break; case GSM_SECURITY_ALREADY: LOGP(DMM, LOGL_ERROR, "We don't expect LOCATION " "UPDATING after CM SERVICE REQUEST\n"); /* fall through */ case GSM_SECURITY_NOAVAIL: case GSM_SECURITY_SUCCEEDED: rc = finish_lu(conn); break; default: rc = -EINVAL; }; return rc; } static int gsm0408_authorize(struct gsm_subscriber_connection *conn, struct msgb *msg) { if (!conn->loc_operation) return 0; if (authorize_subscriber(conn->loc_operation, conn->subscr)) return gsm48_secure_channel(conn, conn->loc_operation->key_seq, _gsm0408_authorize_sec_cb, NULL); return 0; } void gsm0408_clear_request(struct gsm_subscriber_connection *conn, uint32_t cause) { struct gsm_trans *trans, *temp; /* avoid someone issuing a clear */ conn->in_release = 1; /* * Cancel any outstanding location updating request * operation taking place on the subscriber connection. */ release_loc_updating_req(conn, 1); /* We might need to cancel the paging response or such. */ if (conn->sec_operation && conn->sec_operation->cb) { conn->sec_operation->cb(GSM_HOOK_RR_SECURITY, GSM_SECURITY_AUTH_FAILED, NULL, conn, conn->sec_operation->cb_data); } release_security_operation(conn); release_anchor(conn); /* * Free all transactions that are associated with the released * connection. The transaction code will inform the CC or SMS * facilities that will send the release indications. As part of * the CC REL_IND the remote leg might be released and this will * trigger the call to trans_free. This is something the llist * macro can not handle and we will need to re-iterate the list. * * TODO: Move the trans_list into the subscriber connection and * create a pending list for MT transactions. These exist before * we have a subscriber connection. */ restart: llist_for_each_entry_safe(trans, temp, &conn->bts->network->trans_list, entry) { if (trans->conn == conn) { trans_free(trans); goto restart; } } } void gsm0408_clear_all_trans(struct gsm_network *net, int protocol) { struct gsm_trans *trans, *temp; LOGP(DCC, LOGL_NOTICE, "Clearing all currently active transactions!!!\n"); llist_for_each_entry_safe(trans, temp, &net->trans_list, entry) { if (trans->protocol == protocol) { trans->callref = 0; trans_free(trans); } } } /* Chapter 9.2.14 : Send LOCATION UPDATING REJECT */ int gsm0408_loc_upd_rej(struct gsm_subscriber_connection *conn, uint8_t cause) { struct gsm_bts *bts = conn->bts; struct msgb *msg; osmo_counter_inc(bts->network->stats.loc_upd_resp.reject); msg = gsm48_create_loc_upd_rej(cause); if (!msg) { LOGP(DMM, LOGL_ERROR, "Failed to create msg for LOCATION UPDATING REJECT.\n"); return -1; } msg->lchan = conn->lchan; LOGP(DMM, LOGL_INFO, "Subscriber %s: LOCATION UPDATING REJECT " "LAC=%u BTS=%u\n", conn->subscr ? subscr_name(conn->subscr) : "unknown", bts->location_area_code, bts->nr); return gsm48_conn_sendmsg(msg, conn, NULL); } /* Chapter 9.2.13 : Send LOCATION UPDATE ACCEPT */ static int gsm0408_loc_upd_acc(struct gsm_subscriber_connection *conn) { struct gsm_bts *bts = conn->bts; struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh; struct gsm48_loc_area_id *lai; uint8_t *mid; msg->lchan = conn->lchan; gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->proto_discr = GSM48_PDISC_MM; gh->msg_type = GSM48_MT_MM_LOC_UPD_ACCEPT; lai = (struct gsm48_loc_area_id *) msgb_put(msg, sizeof(*lai)); gsm48_generate_lai(lai, bts->network->country_code, bts->network->network_code, bts->location_area_code); if (conn->subscr->tmsi == GSM_RESERVED_TMSI) { uint8_t mi[10]; int len; len = gsm48_generate_mid_from_imsi(mi, conn->subscr->imsi); mid = msgb_put(msg, len); memcpy(mid, mi, len); } else { mid = msgb_put(msg, GSM48_MID_TMSI_LEN); gsm48_generate_mid_from_tmsi(mid, conn->subscr->tmsi); } DEBUGP(DMM, "-> LOCATION UPDATE ACCEPT\n"); osmo_counter_inc(bts->network->stats.loc_upd_resp.accept); return gsm48_conn_sendmsg(msg, conn, NULL); } /* Transmit Chapter 9.2.10 Identity Request */ static int mm_tx_identity_req(struct gsm_subscriber_connection *conn, uint8_t id_type) { struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh; msg->lchan = conn->lchan; gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); gh->proto_discr = GSM48_PDISC_MM; gh->msg_type = GSM48_MT_MM_ID_REQ; gh->data[0] = id_type; return gsm48_conn_sendmsg(msg, conn, NULL); } /* Parse Chapter 9.2.11 Identity Response */ static int mm_rx_id_resp(struct gsm_subscriber_connection *conn, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); struct gsm_lchan *lchan = msg->lchan; struct gsm_bts *bts = lchan->ts->trx->bts; struct gsm_network *net = bts->network; uint8_t mi_type = gh->data[1] & GSM_MI_TYPE_MASK; char mi_string[GSM48_MI_SIZE]; gsm48_mi_to_string(mi_string, sizeof(mi_string), &gh->data[1], gh->data[0]); DEBUGP(DMM, "IDENTITY RESPONSE: MI(%s)=%s\n", gsm48_mi_type_name(mi_type), mi_string); osmo_signal_dispatch(SS_SUBSCR, S_SUBSCR_IDENTITY, gh->data); switch (mi_type) { case GSM_MI_TYPE_IMSI: /* look up subscriber based on IMSI, create if not found */ if (!conn->subscr) { conn->subscr = subscr_get_by_imsi(net->subscr_group, mi_string); if (!conn->subscr && net->create_subscriber) conn->subscr = subscr_create_subscriber( net->subscr_group, mi_string); } if (!conn->subscr && conn->loc_operation) { gsm0408_loc_upd_rej(conn, bts->network->reject_cause); release_loc_updating_req(conn, 1); return 0; } if (conn->loc_operation) conn->loc_operation->waiting_for_imsi = 0; break; case GSM_MI_TYPE_IMEI: case GSM_MI_TYPE_IMEISV: /* update subscribe <-> IMEI mapping */ if (conn->subscr) { db_subscriber_assoc_imei(conn->subscr, mi_string); db_sync_equipment(&conn->subscr->equipment); } if (conn->loc_operation) conn->loc_operation->waiting_for_imei = 0; break; } /* Check if we can let the mobile station enter */ return gsm0408_authorize(conn, msg); } static void loc_upd_rej_cb(void *data) { struct gsm_subscriber_connection *conn = data; struct gsm_lchan *lchan = conn->lchan; struct gsm_bts *bts = lchan->ts->trx->bts; LOGP(DMM, LOGL_DEBUG, "Location Updating Request procedure timedout.\n"); gsm0408_loc_upd_rej(conn, bts->network->reject_cause); release_loc_updating_req(conn, 1); } static void schedule_reject(struct gsm_subscriber_connection *conn) { conn->loc_operation->updating_timer.cb = loc_upd_rej_cb; conn->loc_operation->updating_timer.data = conn; osmo_timer_schedule(&conn->loc_operation->updating_timer, 5, 0); } static const struct value_string lupd_names[] = { { GSM48_LUPD_NORMAL, "NORMAL" }, { GSM48_LUPD_PERIODIC, "PERIODIC" }, { GSM48_LUPD_IMSI_ATT, "IMSI ATTACH" }, { 0, NULL } }; /* Chapter 9.2.15: Receive Location Updating Request */ static int mm_rx_loc_upd_req(struct gsm_subscriber_connection *conn, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); struct gsm48_loc_upd_req *lu; struct gsm_subscriber *subscr = NULL; struct gsm_bts *bts = conn->bts; uint8_t mi_type; char mi_string[GSM48_MI_SIZE]; lu = (struct gsm48_loc_upd_req *) gh->data; mi_type = lu->mi[0] & GSM_MI_TYPE_MASK; gsm48_mi_to_string(mi_string, sizeof(mi_string), lu->mi, lu->mi_len); DEBUGPC(DMM, "MI(%s)=%s type=%s ", gsm48_mi_type_name(mi_type), mi_string, get_value_string(lupd_names, lu->type)); osmo_signal_dispatch(SS_SUBSCR, S_SUBSCR_IDENTITY, &lu->mi_len); switch (lu->type) { case GSM48_LUPD_NORMAL: osmo_counter_inc(bts->network->stats.loc_upd_type.normal); break; case GSM48_LUPD_IMSI_ATT: osmo_counter_inc(bts->network->stats.loc_upd_type.attach); break; case GSM48_LUPD_PERIODIC: osmo_counter_inc(bts->network->stats.loc_upd_type.periodic); break; } /* * Pseudo Spoof detection: Just drop a second/concurrent * location updating request. */ if (conn->loc_operation) { DEBUGPC(DMM, "ignoring request due an existing one: %p.\n", conn->loc_operation); gsm0408_loc_upd_rej(conn, GSM48_REJECT_PROTOCOL_ERROR); return 0; } allocate_loc_updating_req(conn); conn->loc_operation->key_seq = lu->key_seq; switch (mi_type) { case GSM_MI_TYPE_IMSI: DEBUGPC(DMM, "\n"); /* we always want the IMEI, too */ mm_tx_identity_req(conn, GSM_MI_TYPE_IMEI); conn->loc_operation->waiting_for_imei = 1; /* look up subscriber based on IMSI, create if not found */ subscr = subscr_get_by_imsi(bts->network->subscr_group, mi_string); if (!subscr && bts->network->create_subscriber) { subscr = subscr_create_subscriber( bts->network->subscr_group, mi_string); } if (!subscr) { gsm0408_loc_upd_rej(conn, bts->network->reject_cause); release_loc_updating_req(conn, 0); return 0; } break; case GSM_MI_TYPE_TMSI: DEBUGPC(DMM, "\n"); /* look up the subscriber based on TMSI, request IMSI if it fails */ subscr = subscr_get_by_tmsi(bts->network->subscr_group, tmsi_from_string(mi_string)); if (!subscr) { /* send IDENTITY REQUEST message to get IMSI */ mm_tx_identity_req(conn, GSM_MI_TYPE_IMSI); conn->loc_operation->waiting_for_imsi = 1; } /* we always want the IMEI, too */ mm_tx_identity_req(conn, GSM_MI_TYPE_IMEI); conn->loc_operation->waiting_for_imei = 1; break; case GSM_MI_TYPE_IMEI: case GSM_MI_TYPE_IMEISV: /* no sim card... FIXME: what to do ? */ DEBUGPC(DMM, "unimplemented mobile identity type\n"); break; default: DEBUGPC(DMM, "unknown mobile identity type\n"); break; } /* schedule the reject timer */ schedule_reject(conn); if (!subscr) { DEBUGPC(DRR, "<- Can't find any subscriber for this ID\n"); /* FIXME: request id? close channel? */ return -EINVAL; } conn->subscr = subscr; conn->subscr->equipment.classmark1 = lu->classmark1; /* check if we can let the subscriber into our network immediately * or if we need to wait for identity responses. */ return gsm0408_authorize(conn, msg); } /* Turn int into semi-octet representation: 98 => 0x89 */ static uint8_t bcdify(uint8_t value) { uint8_t ret; ret = value / 10; ret |= (value % 10) << 4; return ret; } /* Section 9.2.15a */ int gsm48_tx_mm_info(struct gsm_subscriber_connection *conn) { struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh; struct gsm_network *net = conn->bts->network; struct gsm_bts *bts = conn->bts; uint8_t *ptr8; int name_len, name_pad; time_t cur_t; struct tm* gmt_time; struct tm* local_time; int tzunits; int dst = 0; msg->lchan = conn->lchan; gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->proto_discr = GSM48_PDISC_MM; gh->msg_type = GSM48_MT_MM_INFO; if (net->name_long) { #if 0 name_len = strlen(net->name_long); /* 10.5.3.5a */ ptr8 = msgb_put(msg, 3); ptr8[0] = GSM48_IE_NAME_LONG; ptr8[1] = name_len*2 +1; ptr8[2] = 0x90; /* UCS2, no spare bits, no CI */ ptr16 = (uint16_t *) msgb_put(msg, name_len*2); for (i = 0; i < name_len; i++) ptr16[i] = htons(net->name_long[i]); /* FIXME: Use Cell Broadcast, not UCS-2, since * UCS-2 is only supported by later revisions of the spec */ #endif name_len = (strlen(net->name_long)*7)/8; name_pad = (8 - strlen(net->name_long)*7)%8; if (name_pad > 0) name_len++; /* 10.5.3.5a */ ptr8 = msgb_put(msg, 3); ptr8[0] = GSM48_IE_NAME_LONG; ptr8[1] = name_len +1; ptr8[2] = 0x80 | name_pad; /* Cell Broadcast DCS, no CI */ ptr8 = msgb_put(msg, name_len); gsm_7bit_encode_n(ptr8, name_len, net->name_long, NULL); } if (net->name_short) { #if 0 name_len = strlen(net->name_short); /* 10.5.3.5a */ ptr8 = (uint8_t *) msgb_put(msg, 3); ptr8[0] = GSM48_IE_NAME_SHORT; ptr8[1] = name_len*2 + 1; ptr8[2] = 0x90; /* UCS2, no spare bits, no CI */ ptr16 = (uint16_t *) msgb_put(msg, name_len*2); for (i = 0; i < name_len; i++) ptr16[i] = htons(net->name_short[i]); #endif name_len = (strlen(net->name_short)*7)/8; name_pad = (8 - strlen(net->name_short)*7)%8; if (name_pad > 0) name_len++; /* 10.5.3.5a */ ptr8 = (uint8_t *) msgb_put(msg, 3); ptr8[0] = GSM48_IE_NAME_SHORT; ptr8[1] = name_len +1; ptr8[2] = 0x80 | name_pad; /* Cell Broadcast DCS, no CI */ ptr8 = msgb_put(msg, name_len); gsm_7bit_encode_n(ptr8, name_len, net->name_short, NULL); } /* Section 10.5.3.9 */ cur_t = time(NULL); gmt_time = gmtime(&cur_t); ptr8 = msgb_put(msg, 8); ptr8[0] = GSM48_IE_NET_TIME_TZ; ptr8[1] = bcdify(gmt_time->tm_year % 100); ptr8[2] = bcdify(gmt_time->tm_mon + 1); ptr8[3] = bcdify(gmt_time->tm_mday); ptr8[4] = bcdify(gmt_time->tm_hour); ptr8[5] = bcdify(gmt_time->tm_min); ptr8[6] = bcdify(gmt_time->tm_sec); if (bts->tz.override) { /* Convert tz.hr and tz.mn to units */ if (bts->tz.hr < 0) { tzunits = ((bts->tz.hr/-1)*4); tzunits = tzunits + (bts->tz.mn/15); ptr8[7] = bcdify(tzunits); /* Set negative time */ ptr8[7] |= 0x08; } else { tzunits = bts->tz.hr*4; tzunits = tzunits + (bts->tz.mn/15); ptr8[7] = bcdify(tzunits); } /* Convert DST value */ if (bts->tz.dst >= 0 && bts->tz.dst <= 2) dst = bts->tz.dst; } else { /* Need to get GSM offset and convert into 15 min units */ /* This probably breaks if gmtoff returns a value not evenly divisible by 15? */ local_time = localtime(&cur_t); #ifdef HAVE_TM_GMTOFF_IN_TM tzunits = (local_time->tm_gmtoff/60)/15; #else #warning find a portable way to obtain the timezone offset tzunits = 0; #endif if (tzunits < 0) { tzunits = tzunits/-1; ptr8[7] = bcdify(tzunits); /* Flip it to negative */ ptr8[7] |= 0x08; } else ptr8[7] = bcdify(tzunits); /* Does not support DST +2 */ if (local_time->tm_isdst) dst = 1; } if (dst) { ptr8 = msgb_put(msg, 3); ptr8[0] = GSM48_IE_NET_DST; ptr8[1] = 1; ptr8[2] = dst; } DEBUGP(DMM, "-> MM INFO\n"); return gsm48_conn_sendmsg(msg, conn, NULL); } /* Section 9.2.2 */ int gsm48_tx_mm_auth_req(struct gsm_subscriber_connection *conn, uint8_t *rand, int key_seq) { struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); struct gsm48_auth_req *ar = (struct gsm48_auth_req *) msgb_put(msg, sizeof(*ar)); DEBUGP(DMM, "-> AUTH REQ (rand = %s)\n", osmo_hexdump(rand, 16)); msg->lchan = conn->lchan; gh->proto_discr = GSM48_PDISC_MM; gh->msg_type = GSM48_MT_MM_AUTH_REQ; ar->key_seq = key_seq; /* 16 bytes RAND parameters */ if (rand) memcpy(ar->rand, rand, 16); return gsm48_conn_sendmsg(msg, conn, NULL); } /* Section 9.2.1 */ int gsm48_tx_mm_auth_rej(struct gsm_subscriber_connection *conn) { DEBUGP(DMM, "-> AUTH REJECT\n"); return gsm48_tx_simple(conn, GSM48_PDISC_MM, GSM48_MT_MM_AUTH_REJ); } /* * At the 30C3 phones miss their periodic update * interval a lot and then remain unreachable. In case * we still know the TMSI we can just attach it again. */ static void implit_attach(struct gsm_subscriber_connection *conn) { if (conn->subscr->lac != GSM_LAC_RESERVED_DETACHED) return; subscr_update(conn->subscr, conn->bts, GSM_SUBSCRIBER_UPDATE_ATTACHED); } static int _gsm48_rx_mm_serv_req_sec_cb( unsigned int hooknum, unsigned int event, struct msgb *msg, void *data, void *param) { struct gsm_subscriber_connection *conn = data; int rc = 0; /* auth failed or succeeded, the timer was stopped */ conn->expire_timer_stopped = 1; switch (event) { case GSM_SECURITY_AUTH_FAILED: /* Nothing to do */ break; case GSM_SECURITY_NOAVAIL: case GSM_SECURITY_ALREADY: rc = gsm48_tx_mm_serv_ack(conn); implit_attach(conn); break; case GSM_SECURITY_SUCCEEDED: /* nothing to do. CIPHER MODE COMMAND is * implicit CM SERV ACK */ implit_attach(conn); break; default: rc = -EINVAL; }; return rc; } /* * Handle CM Service Requests * a) Verify that the packet is long enough to contain the information * we require otherwsie reject with INCORRECT_MESSAGE * b) Try to parse the TMSI. If we do not have one reject * c) Check that we know the subscriber with the TMSI otherwise reject * with a HLR cause * d) Set the subscriber on the gsm_lchan and accept */ static int gsm48_rx_mm_serv_req(struct gsm_subscriber_connection *conn, struct msgb *msg) { uint8_t mi_type; char mi_string[GSM48_MI_SIZE]; struct gsm_bts *bts = conn->bts; struct gsm_subscriber *subscr; struct gsm48_hdr *gh = msgb_l3(msg); struct gsm48_service_request *req = (struct gsm48_service_request *)gh->data; /* unfortunately in Phase1 the classmark2 length is variable */ uint8_t classmark2_len = gh->data[1]; uint8_t *classmark2 = gh->data+2; uint8_t mi_len = *(classmark2 + classmark2_len); uint8_t *mi = (classmark2 + classmark2_len + 1); DEBUGP(DMM, "<- CM SERVICE REQUEST "); if (msg->data_len < sizeof(struct gsm48_service_request*)) { DEBUGPC(DMM, "wrong sized message\n"); return gsm48_tx_mm_serv_rej(conn, GSM48_REJECT_INCORRECT_MESSAGE); } if (msg->data_len < req->mi_len + 6) { DEBUGPC(DMM, "does not fit in packet\n"); return gsm48_tx_mm_serv_rej(conn, GSM48_REJECT_INCORRECT_MESSAGE); } gsm48_mi_to_string(mi_string, sizeof(mi_string), mi, mi_len); mi_type = mi[0] & GSM_MI_TYPE_MASK; if (mi_type == GSM_MI_TYPE_IMSI) { DEBUGPC(DMM, "serv_type=0x%02x MI(%s)=%s\n", req->cm_service_type, gsm48_mi_type_name(mi_type), mi_string); subscr = subscr_get_by_imsi(bts->network->subscr_group, mi_string); } else if (mi_type == GSM_MI_TYPE_TMSI) { DEBUGPC(DMM, "serv_type=0x%02x MI(%s)=%s\n", req->cm_service_type, gsm48_mi_type_name(mi_type), mi_string); subscr = subscr_get_by_tmsi(bts->network->subscr_group, tmsi_from_string(mi_string)); } else { DEBUGPC(DMM, "mi_type is not expected: %d\n", mi_type); return gsm48_tx_mm_serv_rej(conn, GSM48_REJECT_INCORRECT_MESSAGE); } osmo_signal_dispatch(SS_SUBSCR, S_SUBSCR_IDENTITY, (classmark2 + classmark2_len)); if (is_siemens_bts(bts)) send_siemens_mrpci(msg->lchan, classmark2-1); /* FIXME: if we don't know the TMSI, inquire abit IMSI and allocate new TMSI */ if (!subscr) return gsm48_tx_mm_serv_rej(conn, GSM48_REJECT_IMSI_UNKNOWN_IN_VLR); if (!conn->subscr) conn->subscr = subscr; else if (conn->subscr == subscr) subscr_put(subscr); /* lchan already has a ref, don't need another one */ else { DEBUGP(DMM, "<- CM Channel already owned by someone else?\n"); subscr_put(subscr); } subscr->equipment.classmark2_len = classmark2_len; memcpy(subscr->equipment.classmark2, classmark2, classmark2_len); db_sync_equipment(&subscr->equipment); /* we will send a MM message soon */ conn->expire_timer_stopped = 1; return gsm48_secure_channel(conn, req->cipher_key_seq, _gsm48_rx_mm_serv_req_sec_cb, NULL); } static int gsm48_rx_mm_imsi_detach_ind(struct gsm_subscriber_connection *conn, struct msgb *msg) { struct gsm_bts *bts = conn->bts; struct gsm48_hdr *gh = msgb_l3(msg); struct gsm48_imsi_detach_ind *idi = (struct gsm48_imsi_detach_ind *) gh->data; uint8_t mi_type = idi->mi[0] & GSM_MI_TYPE_MASK; char mi_string[GSM48_MI_SIZE]; struct gsm_subscriber *subscr = NULL; gsm48_mi_to_string(mi_string, sizeof(mi_string), idi->mi, idi->mi_len); DEBUGP(DMM, "IMSI DETACH INDICATION: MI(%s)=%s", gsm48_mi_type_name(mi_type), mi_string); osmo_counter_inc(bts->network->stats.loc_upd_type.detach); switch (mi_type) { case GSM_MI_TYPE_TMSI: DEBUGPC(DMM, "\n"); subscr = subscr_get_by_tmsi(bts->network->subscr_group, tmsi_from_string(mi_string)); break; case GSM_MI_TYPE_IMSI: DEBUGPC(DMM, "\n"); subscr = subscr_get_by_imsi(bts->network->subscr_group, mi_string); break; case GSM_MI_TYPE_IMEI: case GSM_MI_TYPE_IMEISV: /* no sim card... FIXME: what to do ? */ DEBUGPC(DMM, ": unimplemented mobile identity type\n"); break; default: DEBUGPC(DMM, ": unknown mobile identity type\n"); break; } if (subscr) { subscr_update(subscr, bts, GSM_SUBSCRIBER_UPDATE_DETACHED); DEBUGP(DMM, "Subscriber: %s\n", subscr_name(subscr)); subscr->equipment.classmark1 = idi->classmark1; db_sync_equipment(&subscr->equipment); subscr_put(subscr); } else DEBUGP(DMM, "Unknown Subscriber ?!?\n"); /* FIXME: iterate over all transactions and release them, * imagine an IMSI DETACH happening during an active call! */ release_anchor(conn); return 0; } static int gsm48_rx_mm_status(struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); DEBUGP(DMM, "MM STATUS (reject cause 0x%02x)\n", gh->data[0]); return 0; } /* Chapter 9.2.3: Authentication Response */ static int gsm48_rx_mm_auth_resp(struct gsm_subscriber_connection *conn, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); struct gsm48_auth_resp *ar = (struct gsm48_auth_resp*) gh->data; struct gsm_network *net = conn->bts->network; DEBUGP(DMM, "MM AUTHENTICATION RESPONSE (sres = %s): ", osmo_hexdump(ar->sres, 4)); /* Safety check */ if (!conn->sec_operation) { DEBUGP(DMM, "No authentication/cipher operation in progress !!!\n"); return -EIO; } /* Validate SRES */ if (memcmp(conn->sec_operation->atuple.sres, ar->sres,4)) { int rc; gsm_cbfn *cb = conn->sec_operation->cb; DEBUGPC(DMM, "Invalid (expected %s)\n", osmo_hexdump(conn->sec_operation->atuple.sres, 4)); if (cb) cb(GSM_HOOK_RR_SECURITY, GSM_SECURITY_AUTH_FAILED, NULL, conn, conn->sec_operation->cb_data); rc = gsm48_tx_mm_auth_rej(conn); release_security_operation(conn); return rc; } DEBUGPC(DMM, "OK\n"); /* Start ciphering */ return gsm0808_cipher_mode(conn, net->a5_encryption, conn->sec_operation->atuple.kc, 8, 0); } /* Receive a GSM 04.08 Mobility Management (MM) message */ static int gsm0408_rcv_mm(struct gsm_subscriber_connection *conn, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); int rc = 0; switch (gh->msg_type & 0xbf) { case GSM48_MT_MM_LOC_UPD_REQUEST: DEBUGP(DMM, "LOCATION UPDATING REQUEST: "); rc = mm_rx_loc_upd_req(conn, msg); break; case GSM48_MT_MM_ID_RESP: rc = mm_rx_id_resp(conn, msg); break; case GSM48_MT_MM_CM_SERV_REQ: rc = gsm48_rx_mm_serv_req(conn, msg); break; case GSM48_MT_MM_STATUS: rc = gsm48_rx_mm_status(msg); break; case GSM48_MT_MM_TMSI_REALL_COMPL: DEBUGP(DMM, "TMSI Reallocation Completed. Subscriber: %s\n", conn->subscr ? subscr_name(conn->subscr) : "unknown subscriber"); release_loc_updating_req(conn, 1); break; case GSM48_MT_MM_IMSI_DETACH_IND: rc = gsm48_rx_mm_imsi_detach_ind(conn, msg); break; case GSM48_MT_MM_CM_REEST_REQ: DEBUGP(DMM, "CM REESTABLISH REQUEST: Not implemented\n"); break; case GSM48_MT_MM_AUTH_RESP: rc = gsm48_rx_mm_auth_resp(conn, msg); break; default: LOGP(DMM, LOGL_NOTICE, "Unknown GSM 04.08 MM msg type 0x%02x\n", gh->msg_type); break; } return rc; } /* Receive a PAGING RESPONSE message from the MS */ static int gsm48_rx_rr_pag_resp(struct gsm_subscriber_connection *conn, struct msgb *msg) { struct gsm_bts *bts = conn->bts; struct gsm48_hdr *gh = msgb_l3(msg); struct gsm48_pag_resp *resp; uint8_t *classmark2_lv = gh->data + 1; uint8_t mi_type; char mi_string[GSM48_MI_SIZE]; struct gsm_subscriber *subscr = NULL; int rc = 0; resp = (struct gsm48_pag_resp *) &gh->data[0]; gsm48_paging_extract_mi(resp, msgb_l3len(msg) - sizeof(*gh), mi_string, &mi_type); DEBUGP(DRR, "PAGING RESPONSE: MI(%s)=%s\n", gsm48_mi_type_name(mi_type), mi_string); switch (mi_type) { case GSM_MI_TYPE_TMSI: subscr = subscr_get_by_tmsi(bts->network->subscr_group, tmsi_from_string(mi_string)); break; case GSM_MI_TYPE_IMSI: subscr = subscr_get_by_imsi(bts->network->subscr_group, mi_string); break; } if (!subscr) { DEBUGP(DRR, "<- Can't find any subscriber for this ID\n"); /* FIXME: request id? close channel? */ return -EINVAL; } log_set_context(BSC_CTX_SUBSCR, subscr); DEBUGP(DRR, "<- Channel was requested by %s\n", subscr->name && strlen(subscr->name) ? subscr->name : subscr->imsi); subscr->equipment.classmark2_len = *classmark2_lv; memcpy(subscr->equipment.classmark2, classmark2_lv+1, *classmark2_lv); db_sync_equipment(&subscr->equipment); /* We received a paging */ conn->expire_timer_stopped = 1; rc = gsm48_handle_paging_resp(conn, msg, subscr); return rc; } static int gsm48_rx_rr_app_info(struct gsm_subscriber_connection *conn, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); uint8_t apdu_id_flags; uint8_t apdu_len; uint8_t *apdu_data; apdu_id_flags = gh->data[0]; apdu_len = gh->data[1]; apdu_data = gh->data+2; DEBUGP(DRR, "RX APPLICATION INFO id/flags=0x%02x apdu_len=%u apdu=%s", apdu_id_flags, apdu_len, osmo_hexdump(apdu_data, apdu_len)); return db_apdu_blob_store(conn->subscr, apdu_id_flags, apdu_len, apdu_data); } /* Receive a GSM 04.08 Radio Resource (RR) message */ static int gsm0408_rcv_rr(struct gsm_subscriber_connection *conn, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); int rc = 0; switch (gh->msg_type) { case GSM48_MT_RR_PAG_RESP: rc = gsm48_rx_rr_pag_resp(conn, msg); break; case GSM48_MT_RR_APP_INFO: rc = gsm48_rx_rr_app_info(conn, msg); break; default: LOGP(DRR, LOGL_NOTICE, "MSC: Unimplemented " "GSM 04.08 RR msg type 0x%02x\n", gh->msg_type); break; } return rc; } int gsm48_send_rr_app_info(struct gsm_subscriber_connection *conn, uint8_t apdu_id, uint8_t apdu_len, const uint8_t *apdu) { struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh; msg->lchan = conn->lchan; DEBUGP(DRR, "TX APPLICATION INFO id=0x%02x, len=%u\n", apdu_id, apdu_len); gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 2 + apdu_len); gh->proto_discr = GSM48_PDISC_RR; gh->msg_type = GSM48_MT_RR_APP_INFO; gh->data[0] = apdu_id; gh->data[1] = apdu_len; memcpy(gh->data+2, apdu, apdu_len); return gsm48_conn_sendmsg(msg, conn, NULL); } /* Call Control */ /* The entire call control code is written in accordance with Figure 7.10c * for 'very early assignment', i.e. we allocate a TCH/F during IMMEDIATE * ASSIGN, then first use that TCH/F for signalling and later MODE MODIFY * it for voice */ static void new_cc_state(struct gsm_trans *trans, int state) { if (state > 31 || state < 0) return; DEBUGP(DCC, "new state %s -> %s\n", gsm48_cc_state_name(trans->cc.state), gsm48_cc_state_name(state)); trans->cc.state = state; } static int gsm48_cc_tx_status(struct gsm_trans *trans, void *arg) { struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); uint8_t *cause, *call_state; gh->msg_type = GSM48_MT_CC_STATUS; cause = msgb_put(msg, 3); cause[0] = 2; cause[1] = GSM48_CAUSE_CS_GSM | GSM48_CAUSE_LOC_USER; cause[2] = 0x80 | 30; /* response to status inquiry */ call_state = msgb_put(msg, 1); call_state[0] = 0xc0 | 0x00; return gsm48_conn_sendmsg(msg, trans->conn, trans); } static int gsm48_tx_simple(struct gsm_subscriber_connection *conn, uint8_t pdisc, uint8_t msg_type) { struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); msg->lchan = conn->lchan; gh->proto_discr = pdisc; gh->msg_type = msg_type; return gsm48_conn_sendmsg(msg, conn, NULL); } static void gsm48_stop_cc_timer(struct gsm_trans *trans) { if (osmo_timer_pending(&trans->cc.timer)) { DEBUGP(DCC, "stopping pending timer T%x\n", trans->cc.Tcurrent); osmo_timer_del(&trans->cc.timer); trans->cc.Tcurrent = 0; } } static int mncc_recvmsg(struct gsm_network *net, struct gsm_trans *trans, int msg_type, struct gsm_mncc *mncc) { struct msgb *msg; unsigned char *data; if (trans) if (trans->conn && trans->conn->lchan) DEBUGP(DCC, "(bts %d trx %d ts %d ti %x sub %s) " "Sending '%s' to MNCC.\n", trans->conn->lchan->ts->trx->bts->nr, trans->conn->lchan->ts->trx->nr, trans->conn->lchan->ts->nr, trans->transaction_id, (trans->subscr)?(trans->subscr->extension):"-", get_mncc_name(msg_type)); else DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) " "Sending '%s' to MNCC.\n", (trans->subscr)?(trans->subscr->extension):"-", get_mncc_name(msg_type)); else DEBUGP(DCC, "(bts - trx - ts - ti -- sub -) " "Sending '%s' to MNCC.\n", get_mncc_name(msg_type)); mncc->msg_type = msg_type; msg = msgb_alloc(sizeof(struct gsm_mncc), "MNCC"); if (!msg) return -ENOMEM; data = msgb_put(msg, sizeof(struct gsm_mncc)); memcpy(data, mncc, sizeof(struct gsm_mncc)); cc_tx_to_mncc(net, msg); return 0; } int mncc_release_ind(struct gsm_network *net, struct gsm_trans *trans, uint32_t callref, int location, int value) { struct gsm_mncc rel; memset(&rel, 0, sizeof(rel)); rel.callref = callref; mncc_set_cause(&rel, location, value); if (trans && trans->cc.state == GSM_CSTATE_RELEASE_REQ) return mncc_recvmsg(net, trans, MNCC_REL_CNF, &rel); return mncc_recvmsg(net, trans, MNCC_REL_IND, &rel); } /* Call Control Specific transaction release. * gets called by trans_free, DO NOT CALL YOURSELF! */ void _gsm48_cc_trans_free(struct gsm_trans *trans) { gsm48_stop_cc_timer(trans); /* send release to L4, if callref still exists */ if (trans->callref) { /* Ressource unavailable */ mncc_release_ind(trans->net, trans, trans->callref, GSM48_CAUSE_LOC_PRN_S_LU, GSM48_CC_CAUSE_RESOURCE_UNAVAIL); } if (trans->cc.state != GSM_CSTATE_NULL) new_cc_state(trans, GSM_CSTATE_NULL); if (trans->conn) trau_mux_unmap(&trans->conn->lchan->ts->e1_link, trans->callref); } static int gsm48_cc_tx_setup(struct gsm_trans *trans, void *arg); /* call-back from paging the B-end of the connection */ static int setup_trig_pag_evt(unsigned int hooknum, unsigned int event, struct msgb *msg, void *_conn, void *_transt) { struct gsm_subscriber_connection *conn = _conn; struct gsm_trans *transt = _transt; OSMO_ASSERT(!transt->conn); /* check all tranactions (without lchan) for subscriber */ switch (event) { case GSM_PAGING_SUCCEEDED: DEBUGP(DCC, "Paging subscr %s succeeded!\n", transt->subscr->extension); OSMO_ASSERT(conn); /* Assign lchan */ transt->conn = conn; /* send SETUP request to called party */ gsm48_cc_tx_setup(transt, &transt->cc.msg); break; case GSM_PAGING_EXPIRED: case GSM_PAGING_BUSY: DEBUGP(DCC, "Paging subscr %s expired!\n", transt->subscr->extension); /* Temporarily out of order */ mncc_release_ind(transt->net, transt, transt->callref, GSM48_CAUSE_LOC_PRN_S_LU, GSM48_CC_CAUSE_DEST_OOO); transt->callref = 0; transt->paging_request = NULL; trans_free(transt); break; default: LOGP(DCC, LOGL_ERROR, "Unknown paging event %d\n", event); break; } transt->paging_request = NULL; return 0; } static int tch_recv_mncc(struct gsm_network *net, uint32_t callref, int enable); /* handle audio path for handover */ static int switch_for_handover(struct gsm_lchan *old_lchan, struct gsm_lchan *new_lchan) { struct rtp_socket *old_rs, *new_rs, *other_rs; /* Ask the new socket to send to the already known port. */ if (new_lchan->conn->mncc_rtp_bridge) { LOGP(DHO, LOGL_DEBUG, "Forwarding RTP\n"); rsl_ipacc_mdcx(new_lchan, old_lchan->abis_ip.connect_ip, old_lchan->abis_ip.connect_port, 0); return 0; } if (ipacc_rtp_direct) { LOGP(DHO, LOGL_ERROR, "unable to handover in direct RTP mode\n"); return 0; } /* RTP Proxy mode */ new_rs = new_lchan->abis_ip.rtp_socket; old_rs = old_lchan->abis_ip.rtp_socket; if (!new_rs) { LOGP(DHO, LOGL_ERROR, "no RTP socket for new_lchan\n"); return -EIO; } rsl_ipacc_mdcx_to_rtpsock(new_lchan); if (!old_rs) { LOGP(DHO, LOGL_ERROR, "no RTP socket for old_lchan\n"); return -EIO; } /* copy rx_action and reference to other sock */ new_rs->rx_action = old_rs->rx_action; new_rs->tx_action = old_rs->tx_action; new_rs->transmit = old_rs->transmit; switch (old_lchan->abis_ip.rtp_socket->rx_action) { case RTP_PROXY: other_rs = old_rs->proxy.other_sock; rtp_socket_proxy(new_rs, other_rs); /* delete reference to other end socket to prevent * rtp_socket_free() from removing the inverse reference */ old_rs->proxy.other_sock = NULL; break; case RTP_RECV_UPSTREAM: new_rs->receive = old_rs->receive; break; case RTP_NONE: break; } return 0; } static void maybe_switch_for_handover(struct gsm_lchan *lchan) { struct gsm_lchan *old_lchan; old_lchan = bsc_handover_pending(lchan); if (old_lchan) switch_for_handover(old_lchan, lchan); } /* some other part of the code sends us a signal */ static int handle_abisip_signal(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct gsm_lchan *lchan = signal_data; int rc; struct gsm_network *net; struct gsm_trans *trans; if (subsys != SS_ABISIP) return 0; /* RTP bridge handling */ if (lchan->conn && lchan->conn->mncc_rtp_bridge) return tch_rtp_signal(lchan, signal); /* in case we use direct BTS-to-BTS RTP */ if (ipacc_rtp_direct) return 0; switch (signal) { case S_ABISIP_CRCX_ACK: /* in case we don't use direct BTS-to-BTS RTP */ /* the BTS has successfully bound a TCH to a local ip/port, * which means we can connect our UDP socket to it */ if (lchan->abis_ip.rtp_socket) { rtp_socket_free(lchan->abis_ip.rtp_socket); lchan->abis_ip.rtp_socket = NULL; } lchan->abis_ip.rtp_socket = rtp_socket_create(); if (!lchan->abis_ip.rtp_socket) return -EIO; rc = rtp_socket_connect(lchan->abis_ip.rtp_socket, lchan->abis_ip.bound_ip, lchan->abis_ip.bound_port); if (rc < 0) return -EIO; /* check if any transactions on this lchan still have * a tch_recv_mncc request pending */ net = lchan->ts->trx->bts->network; llist_for_each_entry(trans, &net->trans_list, entry) { if (trans->conn && trans->conn->lchan == lchan && trans->tch_recv) { DEBUGP(DCC, "pending tch_recv_mncc request\n"); tch_recv_mncc(net, trans->callref, 1); } } /* * TODO: this appears to be too early? Why not until after * the handover detect or the handover complete? * * Do we have a handover pending for this new lchan? In that * case re-route the audio from the old channel to the new one. */ maybe_switch_for_handover(lchan); break; case S_ABISIP_DLCX_IND: /* the BTS tells us a RTP stream has been disconnected */ if (lchan->abis_ip.rtp_socket) { rtp_socket_free(lchan->abis_ip.rtp_socket); lchan->abis_ip.rtp_socket = NULL; } break; } return 0; } /* map two ipaccess RTP streams onto each other */ static int tch_map(struct gsm_lchan *lchan, struct gsm_lchan *remote_lchan) { struct gsm_bts *bts = lchan->ts->trx->bts; struct gsm_bts *remote_bts = remote_lchan->ts->trx->bts; int rc; DEBUGP(DCC, "Setting up TCH map between (bts=%u,trx=%u,ts=%u) and (bts=%u,trx=%u,ts=%u)\n", bts->nr, lchan->ts->trx->nr, lchan->ts->nr, remote_bts->nr, remote_lchan->ts->trx->nr, remote_lchan->ts->nr); if (bts->type != remote_bts->type) { LOGP(DCC, LOGL_ERROR, "Cannot switch calls between different BTS types yet\n"); return -EINVAL; } // todo: map between different bts types switch (bts->type) { case GSM_BTS_TYPE_NANOBTS: case GSM_BTS_TYPE_OSMO_SYSMO: if (!ipacc_rtp_direct) { if (!lchan->abis_ip.rtp_socket) { LOGP(DHO, LOGL_ERROR, "no RTP socket for " "lchan\n"); return -EIO; } if (!remote_lchan->abis_ip.rtp_socket) { LOGP(DHO, LOGL_ERROR, "no RTP socket for " "remote_lchan\n"); return -EIO; } /* connect the TCH's to our RTP proxy */ rc = rsl_ipacc_mdcx_to_rtpsock(lchan); if (rc < 0) return rc; rc = rsl_ipacc_mdcx_to_rtpsock(remote_lchan); if (rc < 0) return rc; /* connect them with each other */ rtp_socket_proxy(lchan->abis_ip.rtp_socket, remote_lchan->abis_ip.rtp_socket); } else { /* directly connect TCH RTP streams to each other */ rc = rsl_ipacc_mdcx(lchan, remote_lchan->abis_ip.bound_ip, remote_lchan->abis_ip.bound_port, remote_lchan->abis_ip.rtp_payload2); if (rc < 0) return rc; rc = rsl_ipacc_mdcx(remote_lchan, lchan->abis_ip.bound_ip, lchan->abis_ip.bound_port, lchan->abis_ip.rtp_payload2); } break; case GSM_BTS_TYPE_BS11: case GSM_BTS_TYPE_RBS2000: case GSM_BTS_TYPE_NOKIA_SITE: trau_mux_map_lchan(lchan, remote_lchan); break; default: LOGP(DCC, LOGL_ERROR, "Unknown BTS type %u\n", bts->type); return -EINVAL; } return 0; } /* bridge channels of two transactions */ static int tch_bridge(struct gsm_network *net, uint32_t *refs) { struct gsm_trans *trans1 = trans_find_by_callref(net, refs[0]); struct gsm_trans *trans2 = trans_find_by_callref(net, refs[1]); if (!trans1 || !trans2) return -EIO; if (!trans1->conn || !trans2->conn) return -EIO; /* Which subscriber do we want to track trans1 or trans2? */ log_set_context(BSC_CTX_SUBSCR, trans1->subscr); /* through-connect channel */ return tch_map(trans1->conn->lchan, trans2->conn->lchan); } /* enable receive of channels to MNCC upqueue */ static int tch_recv_mncc(struct gsm_network *net, uint32_t callref, int enable) { struct gsm_trans *trans; struct gsm_lchan *lchan; struct gsm_bts *bts; int rc; /* Find callref */ trans = trans_find_by_callref(net, callref); if (!trans) return -EIO; if (!trans->conn) return 0; log_set_context(BSC_CTX_SUBSCR, trans->subscr); lchan = trans->conn->lchan; bts = lchan->ts->trx->bts; /* store receive state */ trans->tch_recv = enable; switch (bts->type) { case GSM_BTS_TYPE_NANOBTS: case GSM_BTS_TYPE_OSMO_SYSMO: if (ipacc_rtp_direct) { LOGP(DCC, LOGL_ERROR, "Error: RTP proxy is disabled\n"); return -EINVAL; } /* In case, we don't have a RTP socket to the BTS yet, the BTS * will not be connected to our RTP proxy and the socket will * not be assigned to the application interface. This method * will be called again, once the audio socket is created and * connected. */ if (!lchan->abis_ip.rtp_socket) { DEBUGP(DCC, "queue tch_recv_mncc request (%d)\n", enable); return 0; } if (enable) { /* connect the TCH's to our RTP proxy */ rc = rsl_ipacc_mdcx_to_rtpsock(lchan); if (rc < 0) return rc; /* assign socket to application interface */ rtp_socket_upstream(lchan->abis_ip.rtp_socket, net, callref); } else rtp_socket_upstream(lchan->abis_ip.rtp_socket, net, 0); break; case GSM_BTS_TYPE_BS11: case GSM_BTS_TYPE_RBS2000: case GSM_BTS_TYPE_NOKIA_SITE: /* In case we don't have a TCH with correct mode, the TRAU muxer * will not be asigned to the application interface. This is * performed by switch_trau_mux() after successful handover or * assignment. */ if (lchan->tch_mode == GSM48_CMODE_SIGN) { DEBUGP(DCC, "queue tch_recv_mncc request (%d)\n", enable); return 0; } if (enable) return trau_recv_lchan(lchan, callref); return trau_mux_unmap(NULL, callref); break; default: LOGP(DCC, LOGL_ERROR, "Unknown BTS type %u\n", bts->type); return -EINVAL; } return 0; } static int gsm48_cc_rx_status_enq(struct gsm_trans *trans, struct msgb *msg) { DEBUGP(DCC, "-> STATUS ENQ\n"); return gsm48_cc_tx_status(trans, msg); } static int gsm48_cc_tx_release(struct gsm_trans *trans, void *arg); static int gsm48_cc_tx_disconnect(struct gsm_trans *trans, void *arg); static void gsm48_cc_timeout(void *arg) { struct gsm_trans *trans = arg; int disconnect = 0, release = 0; int mo_cause = GSM48_CC_CAUSE_RECOVERY_TIMER; int mo_location = GSM48_CAUSE_LOC_USER; int l4_cause = GSM48_CC_CAUSE_NORMAL_UNSPEC; int l4_location = GSM48_CAUSE_LOC_PRN_S_LU; struct gsm_mncc mo_rel, l4_rel; memset(&mo_rel, 0, sizeof(struct gsm_mncc)); mo_rel.callref = trans->callref; memset(&l4_rel, 0, sizeof(struct gsm_mncc)); l4_rel.callref = trans->callref; switch(trans->cc.Tcurrent) { case 0x303: release = 1; l4_cause = GSM48_CC_CAUSE_USER_NOTRESPOND; break; case 0x310: disconnect = 1; l4_cause = GSM48_CC_CAUSE_USER_NOTRESPOND; break; case 0x313: disconnect = 1; /* unknown, did not find it in the specs */ break; case 0x301: disconnect = 1; l4_cause = GSM48_CC_CAUSE_USER_NOTRESPOND; break; case 0x308: if (!trans->cc.T308_second) { /* restart T308 a second time */ gsm48_cc_tx_release(trans, &trans->cc.msg); trans->cc.T308_second = 1; break; /* stay in release state */ } trans_free(trans); return; // release = 1; // l4_cause = 14; // break; case 0x306: release = 1; mo_cause = trans->cc.msg.cause.value; mo_location = trans->cc.msg.cause.location; break; case 0x323: disconnect = 1; break; default: release = 1; } if (release && trans->callref) { /* process release towards layer 4 */ mncc_release_ind(trans->net, trans, trans->callref, l4_location, l4_cause); trans->callref = 0; } if (disconnect && trans->callref) { /* process disconnect towards layer 4 */ mncc_set_cause(&l4_rel, l4_location, l4_cause); mncc_recvmsg(trans->net, trans, MNCC_DISC_IND, &l4_rel); } /* process disconnect towards mobile station */ if (disconnect || release) { mncc_set_cause(&mo_rel, mo_location, mo_cause); mo_rel.cause.diag[0] = ((trans->cc.Tcurrent & 0xf00) >> 8) + '0'; mo_rel.cause.diag[1] = ((trans->cc.Tcurrent & 0x0f0) >> 4) + '0'; mo_rel.cause.diag[2] = (trans->cc.Tcurrent & 0x00f) + '0'; mo_rel.cause.diag_len = 3; if (disconnect) gsm48_cc_tx_disconnect(trans, &mo_rel); if (release) gsm48_cc_tx_release(trans, &mo_rel); } } static void gsm48_start_cc_timer(struct gsm_trans *trans, int current, int sec, int micro) { DEBUGP(DCC, "starting timer T%x with %d seconds\n", current, sec); trans->cc.timer.cb = gsm48_cc_timeout; trans->cc.timer.data = trans; osmo_timer_schedule(&trans->cc.timer, sec, micro); trans->cc.Tcurrent = current; } static int gsm48_cc_rx_setup(struct gsm_trans *trans, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); uint8_t msg_type = gh->msg_type & 0xbf; unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); struct tlv_parsed tp; struct gsm_mncc setup; memset(&setup, 0, sizeof(struct gsm_mncc)); setup.callref = trans->callref; setup.lchan_type = trans->conn->lchan->type; tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); /* emergency setup is identified by msg_type */ if (msg_type == GSM48_MT_CC_EMERG_SETUP) setup.emergency = 1; /* use subscriber as calling party number */ setup.fields |= MNCC_F_CALLING; strncpy(setup.calling.number, trans->subscr->extension, sizeof(setup.calling.number)-1); strncpy(setup.imsi, trans->subscr->imsi, sizeof(setup.imsi)-1); /* bearer capability */ if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { setup.fields |= MNCC_F_BEARER_CAP; gsm48_decode_bearer_cap(&setup.bearer_cap, TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); apply_codec_restrictions(trans->conn->bts, &setup.bearer_cap); } /* facility */ if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { setup.fields |= MNCC_F_FACILITY; gsm48_decode_facility(&setup.facility, TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); } /* called party bcd number */ if (TLVP_PRESENT(&tp, GSM48_IE_CALLED_BCD)) { setup.fields |= MNCC_F_CALLED; gsm48_decode_called(&setup.called, TLVP_VAL(&tp, GSM48_IE_CALLED_BCD)-1); } /* user-user */ if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { setup.fields |= MNCC_F_USERUSER; gsm48_decode_useruser(&setup.useruser, TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); } /* ss-version */ if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { setup.fields |= MNCC_F_SSVERSION; gsm48_decode_ssversion(&setup.ssversion, TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); } /* CLIR suppression */ if (TLVP_PRESENT(&tp, GSM48_IE_CLIR_SUPP)) setup.clir.sup = 1; /* CLIR invocation */ if (TLVP_PRESENT(&tp, GSM48_IE_CLIR_INVOC)) setup.clir.inv = 1; /* cc cap */ if (TLVP_PRESENT(&tp, GSM48_IE_CC_CAP)) { setup.fields |= MNCC_F_CCCAP; gsm48_decode_cccap(&setup.cccap, TLVP_VAL(&tp, GSM48_IE_CC_CAP)-1); } new_cc_state(trans, GSM_CSTATE_INITIATED); LOGP(DCC, LOGL_INFO, "Subscriber %s (%s) sends SETUP to %s\n", subscr_name(trans->subscr), trans->subscr->extension, setup.called.number); osmo_counter_inc(trans->net->stats.call.mo_setup); /* indicate setup to MNCC */ mncc_recvmsg(trans->net, trans, MNCC_SETUP_IND, &setup); /* MNCC code will modify the channel asynchronously, we should * ipaccess-bind only after the modification has been made to the * lchan->tch_mode */ return 0; } static int gsm48_cc_tx_setup(struct gsm_trans *trans, void *arg) { struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh; struct gsm_mncc *setup = arg; int rc, trans_id; gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); /* transaction id must not be assigned */ if (trans->transaction_id != 0xff) { /* unasssigned */ DEBUGP(DCC, "TX Setup with assigned transaction. " "This is not allowed!\n"); /* Temporarily out of order */ rc = mncc_release_ind(trans->net, trans, trans->callref, GSM48_CAUSE_LOC_PRN_S_LU, GSM48_CC_CAUSE_RESOURCE_UNAVAIL); trans->callref = 0; trans_free(trans); return rc; } /* Get free transaction_id */ trans_id = trans_assign_trans_id(trans->net, trans->subscr, GSM48_PDISC_CC, 0); if (trans_id < 0) { /* no free transaction ID */ rc = mncc_release_ind(trans->net, trans, trans->callref, GSM48_CAUSE_LOC_PRN_S_LU, GSM48_CC_CAUSE_RESOURCE_UNAVAIL); trans->callref = 0; trans_free(trans); return rc; } trans->transaction_id = trans_id; gh->msg_type = GSM48_MT_CC_SETUP; gsm48_start_cc_timer(trans, 0x303, GSM48_T303); /* bearer capability */ if (setup->fields & MNCC_F_BEARER_CAP) gsm48_encode_bearer_cap(msg, 0, &setup->bearer_cap); /* facility */ if (setup->fields & MNCC_F_FACILITY) gsm48_encode_facility(msg, 0, &setup->facility); /* progress */ if (setup->fields & MNCC_F_PROGRESS) gsm48_encode_progress(msg, 0, &setup->progress); /* calling party BCD number */ if (setup->fields & MNCC_F_CALLING) gsm48_encode_calling(msg, &setup->calling); /* called party BCD number */ if (setup->fields & MNCC_F_CALLED) gsm48_encode_called(msg, &setup->called); /* user-user */ if (setup->fields & MNCC_F_USERUSER) gsm48_encode_useruser(msg, 0, &setup->useruser); /* redirecting party BCD number */ if (setup->fields & MNCC_F_REDIRECTING) gsm48_encode_redirecting(msg, &setup->redirecting); /* signal */ if (setup->fields & MNCC_F_SIGNAL) gsm48_encode_signal(msg, setup->signal); new_cc_state(trans, GSM_CSTATE_CALL_PRESENT); osmo_counter_inc(trans->net->stats.call.mt_setup); return gsm48_conn_sendmsg(msg, trans->conn, trans); } static int gsm48_cc_rx_call_conf(struct gsm_trans *trans, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); struct tlv_parsed tp; struct gsm_mncc call_conf; gsm48_stop_cc_timer(trans); gsm48_start_cc_timer(trans, 0x310, GSM48_T310); memset(&call_conf, 0, sizeof(struct gsm_mncc)); call_conf.callref = trans->callref; call_conf.lchan_type = trans->conn->lchan->type; tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); #if 0 /* repeat */ if (TLVP_PRESENT(&tp, GSM48_IE_REPEAT_CIR)) call_conf.repeat = 1; if (TLVP_PRESENT(&tp, GSM48_IE_REPEAT_SEQ)) call_conf.repeat = 2; #endif /* bearer capability */ if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { call_conf.fields |= MNCC_F_BEARER_CAP; gsm48_decode_bearer_cap(&call_conf.bearer_cap, TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); apply_codec_restrictions(trans->conn->bts, &call_conf.bearer_cap); } /* cause */ if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { call_conf.fields |= MNCC_F_CAUSE; gsm48_decode_cause(&call_conf.cause, TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); } /* cc cap */ if (TLVP_PRESENT(&tp, GSM48_IE_CC_CAP)) { call_conf.fields |= MNCC_F_CCCAP; gsm48_decode_cccap(&call_conf.cccap, TLVP_VAL(&tp, GSM48_IE_CC_CAP)-1); } /* IMSI of called subscriber */ strncpy(call_conf.imsi, trans->subscr->imsi, sizeof(call_conf.imsi)-1); new_cc_state(trans, GSM_CSTATE_MO_TERM_CALL_CONF); return mncc_recvmsg(trans->net, trans, MNCC_CALL_CONF_IND, &call_conf); } static int gsm48_cc_tx_call_proc(struct gsm_trans *trans, void *arg) { struct gsm_mncc *proceeding = arg; struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->msg_type = GSM48_MT_CC_CALL_PROC; new_cc_state(trans, GSM_CSTATE_MO_CALL_PROC); /* bearer capability */ if (proceeding->fields & MNCC_F_BEARER_CAP) gsm48_encode_bearer_cap(msg, 0, &proceeding->bearer_cap); /* facility */ if (proceeding->fields & MNCC_F_FACILITY) gsm48_encode_facility(msg, 0, &proceeding->facility); /* progress */ if (proceeding->fields & MNCC_F_PROGRESS) gsm48_encode_progress(msg, 0, &proceeding->progress); return gsm48_conn_sendmsg(msg, trans->conn, trans); } static int gsm48_cc_rx_alerting(struct gsm_trans *trans, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); struct tlv_parsed tp; struct gsm_mncc alerting; gsm48_stop_cc_timer(trans); gsm48_start_cc_timer(trans, 0x301, GSM48_T301); memset(&alerting, 0, sizeof(struct gsm_mncc)); alerting.callref = trans->callref; tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); /* facility */ if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { alerting.fields |= MNCC_F_FACILITY; gsm48_decode_facility(&alerting.facility, TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); } /* progress */ if (TLVP_PRESENT(&tp, GSM48_IE_PROGR_IND)) { alerting.fields |= MNCC_F_PROGRESS; gsm48_decode_progress(&alerting.progress, TLVP_VAL(&tp, GSM48_IE_PROGR_IND)-1); } /* ss-version */ if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { alerting.fields |= MNCC_F_SSVERSION; gsm48_decode_ssversion(&alerting.ssversion, TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); } new_cc_state(trans, GSM_CSTATE_CALL_RECEIVED); return mncc_recvmsg(trans->net, trans, MNCC_ALERT_IND, &alerting); } static int gsm48_cc_tx_alerting(struct gsm_trans *trans, void *arg) { struct gsm_mncc *alerting = arg; struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->msg_type = GSM48_MT_CC_ALERTING; /* facility */ if (alerting->fields & MNCC_F_FACILITY) gsm48_encode_facility(msg, 0, &alerting->facility); /* progress */ if (alerting->fields & MNCC_F_PROGRESS) gsm48_encode_progress(msg, 0, &alerting->progress); /* user-user */ if (alerting->fields & MNCC_F_USERUSER) gsm48_encode_useruser(msg, 0, &alerting->useruser); new_cc_state(trans, GSM_CSTATE_CALL_DELIVERED); return gsm48_conn_sendmsg(msg, trans->conn, trans); } static int gsm48_cc_tx_progress(struct gsm_trans *trans, void *arg) { struct gsm_mncc *progress = arg; struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->msg_type = GSM48_MT_CC_PROGRESS; /* progress */ gsm48_encode_progress(msg, 1, &progress->progress); /* user-user */ if (progress->fields & MNCC_F_USERUSER) gsm48_encode_useruser(msg, 0, &progress->useruser); return gsm48_conn_sendmsg(msg, trans->conn, trans); } static int gsm48_cc_tx_connect(struct gsm_trans *trans, void *arg) { struct gsm_mncc *connect = arg; struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->msg_type = GSM48_MT_CC_CONNECT; gsm48_stop_cc_timer(trans); gsm48_start_cc_timer(trans, 0x313, GSM48_T313); /* facility */ if (connect->fields & MNCC_F_FACILITY) gsm48_encode_facility(msg, 0, &connect->facility); /* progress */ if (connect->fields & MNCC_F_PROGRESS) gsm48_encode_progress(msg, 0, &connect->progress); /* connected number */ if (connect->fields & MNCC_F_CONNECTED) gsm48_encode_connected(msg, &connect->connected); /* user-user */ if (connect->fields & MNCC_F_USERUSER) gsm48_encode_useruser(msg, 0, &connect->useruser); new_cc_state(trans, GSM_CSTATE_CONNECT_IND); return gsm48_conn_sendmsg(msg, trans->conn, trans); } static int gsm48_cc_rx_connect(struct gsm_trans *trans, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); struct tlv_parsed tp; struct gsm_mncc connect; gsm48_stop_cc_timer(trans); memset(&connect, 0, sizeof(struct gsm_mncc)); connect.callref = trans->callref; tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); /* use subscriber as connected party number */ connect.fields |= MNCC_F_CONNECTED; strncpy(connect.connected.number, trans->subscr->extension, sizeof(connect.connected.number)-1); strncpy(connect.imsi, trans->subscr->imsi, sizeof(connect.imsi)-1); /* facility */ if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { connect.fields |= MNCC_F_FACILITY; gsm48_decode_facility(&connect.facility, TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); } /* user-user */ if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { connect.fields |= MNCC_F_USERUSER; gsm48_decode_useruser(&connect.useruser, TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); } /* ss-version */ if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { connect.fields |= MNCC_F_SSVERSION; gsm48_decode_ssversion(&connect.ssversion, TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); } new_cc_state(trans, GSM_CSTATE_CONNECT_REQUEST); osmo_counter_inc(trans->net->stats.call.mt_connect); return mncc_recvmsg(trans->net, trans, MNCC_SETUP_CNF, &connect); } static int gsm48_cc_rx_connect_ack(struct gsm_trans *trans, struct msgb *msg) { struct gsm_mncc connect_ack; gsm48_stop_cc_timer(trans); new_cc_state(trans, GSM_CSTATE_ACTIVE); osmo_counter_inc(trans->net->stats.call.mo_connect_ack); memset(&connect_ack, 0, sizeof(struct gsm_mncc)); connect_ack.callref = trans->callref; return mncc_recvmsg(trans->net, trans, MNCC_SETUP_COMPL_IND, &connect_ack); } static int gsm48_cc_tx_connect_ack(struct gsm_trans *trans, void *arg) { struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->msg_type = GSM48_MT_CC_CONNECT_ACK; new_cc_state(trans, GSM_CSTATE_ACTIVE); return gsm48_conn_sendmsg(msg, trans->conn, trans); } static int gsm48_cc_rx_disconnect(struct gsm_trans *trans, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); struct tlv_parsed tp; struct gsm_mncc disc; gsm48_stop_cc_timer(trans); new_cc_state(trans, GSM_CSTATE_DISCONNECT_REQ); memset(&disc, 0, sizeof(struct gsm_mncc)); disc.callref = trans->callref; tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_CAUSE, 0); /* cause */ if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { disc.fields |= MNCC_F_CAUSE; gsm48_decode_cause(&disc.cause, TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); } /* facility */ if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { disc.fields |= MNCC_F_FACILITY; gsm48_decode_facility(&disc.facility, TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); } /* user-user */ if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { disc.fields |= MNCC_F_USERUSER; gsm48_decode_useruser(&disc.useruser, TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); } /* ss-version */ if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { disc.fields |= MNCC_F_SSVERSION; gsm48_decode_ssversion(&disc.ssversion, TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); } return mncc_recvmsg(trans->net, trans, MNCC_DISC_IND, &disc); } static struct gsm_mncc_cause default_cause = { .location = GSM48_CAUSE_LOC_PRN_S_LU, .coding = 0, .rec = 0, .rec_val = 0, .value = GSM48_CC_CAUSE_NORMAL_UNSPEC, .diag_len = 0, .diag = { 0 }, }; static int gsm48_cc_tx_disconnect(struct gsm_trans *trans, void *arg) { struct gsm_mncc *disc = arg; struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->msg_type = GSM48_MT_CC_DISCONNECT; gsm48_stop_cc_timer(trans); gsm48_start_cc_timer(trans, 0x306, GSM48_T306); /* cause */ if (disc->fields & MNCC_F_CAUSE) gsm48_encode_cause(msg, 1, &disc->cause); else gsm48_encode_cause(msg, 1, &default_cause); /* facility */ if (disc->fields & MNCC_F_FACILITY) gsm48_encode_facility(msg, 0, &disc->facility); /* progress */ if (disc->fields & MNCC_F_PROGRESS) gsm48_encode_progress(msg, 0, &disc->progress); /* user-user */ if (disc->fields & MNCC_F_USERUSER) gsm48_encode_useruser(msg, 0, &disc->useruser); /* store disconnect cause for T306 expiry */ memcpy(&trans->cc.msg, disc, sizeof(struct gsm_mncc)); new_cc_state(trans, GSM_CSTATE_DISCONNECT_IND); return gsm48_conn_sendmsg(msg, trans->conn, trans); } static int gsm48_cc_rx_release(struct gsm_trans *trans, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); struct tlv_parsed tp; struct gsm_mncc rel; int rc; gsm48_stop_cc_timer(trans); memset(&rel, 0, sizeof(struct gsm_mncc)); rel.callref = trans->callref; tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); /* cause */ if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { rel.fields |= MNCC_F_CAUSE; gsm48_decode_cause(&rel.cause, TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); } /* facility */ if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { rel.fields |= MNCC_F_FACILITY; gsm48_decode_facility(&rel.facility, TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); } /* user-user */ if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { rel.fields |= MNCC_F_USERUSER; gsm48_decode_useruser(&rel.useruser, TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); } /* ss-version */ if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { rel.fields |= MNCC_F_SSVERSION; gsm48_decode_ssversion(&rel.ssversion, TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); } if (trans->cc.state == GSM_CSTATE_RELEASE_REQ) { /* release collision 5.4.5 */ rc = mncc_recvmsg(trans->net, trans, MNCC_REL_CNF, &rel); } else { rc = gsm48_tx_simple(trans->conn, GSM48_PDISC_CC | (trans->transaction_id << 4), GSM48_MT_CC_RELEASE_COMPL); rc = mncc_recvmsg(trans->net, trans, MNCC_REL_IND, &rel); } new_cc_state(trans, GSM_CSTATE_NULL); trans->callref = 0; trans_free(trans); return rc; } static int gsm48_cc_tx_release(struct gsm_trans *trans, void *arg) { struct gsm_mncc *rel = arg; struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->msg_type = GSM48_MT_CC_RELEASE; gsm48_stop_cc_timer(trans); gsm48_start_cc_timer(trans, 0x308, GSM48_T308); /* cause */ if (rel->fields & MNCC_F_CAUSE) gsm48_encode_cause(msg, 0, &rel->cause); /* facility */ if (rel->fields & MNCC_F_FACILITY) gsm48_encode_facility(msg, 0, &rel->facility); /* user-user */ if (rel->fields & MNCC_F_USERUSER) gsm48_encode_useruser(msg, 0, &rel->useruser); trans->cc.T308_second = 0; memcpy(&trans->cc.msg, rel, sizeof(struct gsm_mncc)); if (trans->cc.state != GSM_CSTATE_RELEASE_REQ) new_cc_state(trans, GSM_CSTATE_RELEASE_REQ); return gsm48_conn_sendmsg(msg, trans->conn, trans); } static int gsm48_cc_rx_release_compl(struct gsm_trans *trans, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); struct tlv_parsed tp; struct gsm_mncc rel; int rc = 0; gsm48_stop_cc_timer(trans); memset(&rel, 0, sizeof(struct gsm_mncc)); rel.callref = trans->callref; tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); /* cause */ if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { rel.fields |= MNCC_F_CAUSE; gsm48_decode_cause(&rel.cause, TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); } /* facility */ if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { rel.fields |= MNCC_F_FACILITY; gsm48_decode_facility(&rel.facility, TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); } /* user-user */ if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { rel.fields |= MNCC_F_USERUSER; gsm48_decode_useruser(&rel.useruser, TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); } /* ss-version */ if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { rel.fields |= MNCC_F_SSVERSION; gsm48_decode_ssversion(&rel.ssversion, TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); } if (trans->callref) { switch (trans->cc.state) { case GSM_CSTATE_CALL_PRESENT: rc = mncc_recvmsg(trans->net, trans, MNCC_REJ_IND, &rel); break; case GSM_CSTATE_RELEASE_REQ: rc = mncc_recvmsg(trans->net, trans, MNCC_REL_CNF, &rel); break; default: rc = mncc_recvmsg(trans->net, trans, MNCC_REL_IND, &rel); } } trans->callref = 0; trans_free(trans); return rc; } static int gsm48_cc_tx_release_compl(struct gsm_trans *trans, void *arg) { struct gsm_mncc *rel = arg; struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); int ret; gh->msg_type = GSM48_MT_CC_RELEASE_COMPL; trans->callref = 0; gsm48_stop_cc_timer(trans); /* cause */ if (rel->fields & MNCC_F_CAUSE) gsm48_encode_cause(msg, 0, &rel->cause); /* facility */ if (rel->fields & MNCC_F_FACILITY) gsm48_encode_facility(msg, 0, &rel->facility); /* user-user */ if (rel->fields & MNCC_F_USERUSER) gsm48_encode_useruser(msg, 0, &rel->useruser); ret = gsm48_conn_sendmsg(msg, trans->conn, trans); trans_free(trans); return ret; } static int gsm48_cc_rx_facility(struct gsm_trans *trans, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); struct tlv_parsed tp; struct gsm_mncc fac; memset(&fac, 0, sizeof(struct gsm_mncc)); fac.callref = trans->callref; tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_FACILITY, 0); /* facility */ if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { fac.fields |= MNCC_F_FACILITY; gsm48_decode_facility(&fac.facility, TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); } /* ss-version */ if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { fac.fields |= MNCC_F_SSVERSION; gsm48_decode_ssversion(&fac.ssversion, TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); } return mncc_recvmsg(trans->net, trans, MNCC_FACILITY_IND, &fac); } static int gsm48_cc_tx_facility(struct gsm_trans *trans, void *arg) { struct gsm_mncc *fac = arg; struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->msg_type = GSM48_MT_CC_FACILITY; /* facility */ gsm48_encode_facility(msg, 1, &fac->facility); return gsm48_conn_sendmsg(msg, trans->conn, trans); } static int gsm48_cc_rx_hold(struct gsm_trans *trans, struct msgb *msg) { struct gsm_mncc hold; memset(&hold, 0, sizeof(struct gsm_mncc)); hold.callref = trans->callref; return mncc_recvmsg(trans->net, trans, MNCC_HOLD_IND, &hold); } static int gsm48_cc_tx_hold_ack(struct gsm_trans *trans, void *arg) { struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->msg_type = GSM48_MT_CC_HOLD_ACK; return gsm48_conn_sendmsg(msg, trans->conn, trans); } static int gsm48_cc_tx_hold_rej(struct gsm_trans *trans, void *arg) { struct gsm_mncc *hold_rej = arg; struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->msg_type = GSM48_MT_CC_HOLD_REJ; /* cause */ if (hold_rej->fields & MNCC_F_CAUSE) gsm48_encode_cause(msg, 1, &hold_rej->cause); else gsm48_encode_cause(msg, 1, &default_cause); return gsm48_conn_sendmsg(msg, trans->conn, trans); } static int gsm48_cc_rx_retrieve(struct gsm_trans *trans, struct msgb *msg) { struct gsm_mncc retrieve; memset(&retrieve, 0, sizeof(struct gsm_mncc)); retrieve.callref = trans->callref; return mncc_recvmsg(trans->net, trans, MNCC_RETRIEVE_IND, &retrieve); } static int gsm48_cc_tx_retrieve_ack(struct gsm_trans *trans, void *arg) { struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->msg_type = GSM48_MT_CC_RETR_ACK; return gsm48_conn_sendmsg(msg, trans->conn, trans); } static int gsm48_cc_tx_retrieve_rej(struct gsm_trans *trans, void *arg) { struct gsm_mncc *retrieve_rej = arg; struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->msg_type = GSM48_MT_CC_RETR_REJ; /* cause */ if (retrieve_rej->fields & MNCC_F_CAUSE) gsm48_encode_cause(msg, 1, &retrieve_rej->cause); else gsm48_encode_cause(msg, 1, &default_cause); return gsm48_conn_sendmsg(msg, trans->conn, trans); } static int gsm48_cc_rx_start_dtmf(struct gsm_trans *trans, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); struct tlv_parsed tp; struct gsm_mncc dtmf; memset(&dtmf, 0, sizeof(struct gsm_mncc)); dtmf.callref = trans->callref; tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); /* keypad facility */ if (TLVP_PRESENT(&tp, GSM48_IE_KPD_FACILITY)) { dtmf.fields |= MNCC_F_KEYPAD; gsm48_decode_keypad(&dtmf.keypad, TLVP_VAL(&tp, GSM48_IE_KPD_FACILITY)-1); } return mncc_recvmsg(trans->net, trans, MNCC_START_DTMF_IND, &dtmf); } static int gsm48_cc_tx_start_dtmf_ack(struct gsm_trans *trans, void *arg) { struct gsm_mncc *dtmf = arg; struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->msg_type = GSM48_MT_CC_START_DTMF_ACK; /* keypad */ if (dtmf->fields & MNCC_F_KEYPAD) gsm48_encode_keypad(msg, dtmf->keypad); return gsm48_conn_sendmsg(msg, trans->conn, trans); } static int gsm48_cc_tx_start_dtmf_rej(struct gsm_trans *trans, void *arg) { struct gsm_mncc *dtmf = arg; struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->msg_type = GSM48_MT_CC_START_DTMF_REJ; /* cause */ if (dtmf->fields & MNCC_F_CAUSE) gsm48_encode_cause(msg, 1, &dtmf->cause); else gsm48_encode_cause(msg, 1, &default_cause); return gsm48_conn_sendmsg(msg, trans->conn, trans); } static int gsm48_cc_tx_stop_dtmf_ack(struct gsm_trans *trans, void *arg) { struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->msg_type = GSM48_MT_CC_STOP_DTMF_ACK; return gsm48_conn_sendmsg(msg, trans->conn, trans); } static int gsm48_cc_rx_stop_dtmf(struct gsm_trans *trans, struct msgb *msg) { struct gsm_mncc dtmf; memset(&dtmf, 0, sizeof(struct gsm_mncc)); dtmf.callref = trans->callref; return mncc_recvmsg(trans->net, trans, MNCC_STOP_DTMF_IND, &dtmf); } static int gsm48_cc_rx_modify(struct gsm_trans *trans, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); struct tlv_parsed tp; struct gsm_mncc modify; memset(&modify, 0, sizeof(struct gsm_mncc)); modify.callref = trans->callref; tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_BEARER_CAP, 0); /* bearer capability */ if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { modify.fields |= MNCC_F_BEARER_CAP; gsm48_decode_bearer_cap(&modify.bearer_cap, TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); apply_codec_restrictions(trans->conn->bts, &modify.bearer_cap); } new_cc_state(trans, GSM_CSTATE_MO_ORIG_MODIFY); return mncc_recvmsg(trans->net, trans, MNCC_MODIFY_IND, &modify); } static int gsm48_cc_tx_modify(struct gsm_trans *trans, void *arg) { struct gsm_mncc *modify = arg; struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->msg_type = GSM48_MT_CC_MODIFY; gsm48_start_cc_timer(trans, 0x323, GSM48_T323); /* bearer capability */ gsm48_encode_bearer_cap(msg, 1, &modify->bearer_cap); new_cc_state(trans, GSM_CSTATE_MO_TERM_MODIFY); return gsm48_conn_sendmsg(msg, trans->conn, trans); } static int gsm48_cc_rx_modify_complete(struct gsm_trans *trans, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); struct tlv_parsed tp; struct gsm_mncc modify; gsm48_stop_cc_timer(trans); memset(&modify, 0, sizeof(struct gsm_mncc)); modify.callref = trans->callref; tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_BEARER_CAP, 0); /* bearer capability */ if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { modify.fields |= MNCC_F_BEARER_CAP; gsm48_decode_bearer_cap(&modify.bearer_cap, TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); apply_codec_restrictions(trans->conn->bts, &modify.bearer_cap); } new_cc_state(trans, GSM_CSTATE_ACTIVE); return mncc_recvmsg(trans->net, trans, MNCC_MODIFY_CNF, &modify); } static int gsm48_cc_tx_modify_complete(struct gsm_trans *trans, void *arg) { struct gsm_mncc *modify = arg; struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->msg_type = GSM48_MT_CC_MODIFY_COMPL; /* bearer capability */ gsm48_encode_bearer_cap(msg, 1, &modify->bearer_cap); new_cc_state(trans, GSM_CSTATE_ACTIVE); return gsm48_conn_sendmsg(msg, trans->conn, trans); } static int gsm48_cc_rx_modify_reject(struct gsm_trans *trans, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); struct tlv_parsed tp; struct gsm_mncc modify; gsm48_stop_cc_timer(trans); memset(&modify, 0, sizeof(struct gsm_mncc)); modify.callref = trans->callref; tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_BEARER_CAP, GSM48_IE_CAUSE); /* bearer capability */ if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { modify.fields |= GSM48_IE_BEARER_CAP; gsm48_decode_bearer_cap(&modify.bearer_cap, TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); apply_codec_restrictions(trans->conn->bts, &modify.bearer_cap); } /* cause */ if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { modify.fields |= MNCC_F_CAUSE; gsm48_decode_cause(&modify.cause, TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); } new_cc_state(trans, GSM_CSTATE_ACTIVE); return mncc_recvmsg(trans->net, trans, MNCC_MODIFY_REJ, &modify); } static int gsm48_cc_tx_modify_reject(struct gsm_trans *trans, void *arg) { struct gsm_mncc *modify = arg; struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->msg_type = GSM48_MT_CC_MODIFY_REJECT; /* bearer capability */ gsm48_encode_bearer_cap(msg, 1, &modify->bearer_cap); /* cause */ gsm48_encode_cause(msg, 1, &modify->cause); new_cc_state(trans, GSM_CSTATE_ACTIVE); return gsm48_conn_sendmsg(msg, trans->conn, trans); } static int gsm48_cc_tx_notify(struct gsm_trans *trans, void *arg) { struct gsm_mncc *notify = arg; struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->msg_type = GSM48_MT_CC_NOTIFY; /* notify */ gsm48_encode_notify(msg, notify->notify); return gsm48_conn_sendmsg(msg, trans->conn, trans); } static int gsm48_cc_rx_notify(struct gsm_trans *trans, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); // struct tlv_parsed tp; struct gsm_mncc notify; memset(¬ify, 0, sizeof(struct gsm_mncc)); notify.callref = trans->callref; // tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len); if (payload_len >= 1) gsm48_decode_notify(¬ify.notify, gh->data); return mncc_recvmsg(trans->net, trans, MNCC_NOTIFY_IND, ¬ify); } static int gsm48_cc_tx_userinfo(struct gsm_trans *trans, void *arg) { struct gsm_mncc *user = arg; struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); gh->msg_type = GSM48_MT_CC_USER_INFO; /* user-user */ if (user->fields & MNCC_F_USERUSER) gsm48_encode_useruser(msg, 1, &user->useruser); /* more data */ if (user->more) gsm48_encode_more(msg); return gsm48_conn_sendmsg(msg, trans->conn, trans); } static int gsm48_cc_rx_userinfo(struct gsm_trans *trans, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); struct tlv_parsed tp; struct gsm_mncc user; memset(&user, 0, sizeof(struct gsm_mncc)); user.callref = trans->callref; tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_USER_USER, 0); /* user-user */ if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { user.fields |= MNCC_F_USERUSER; gsm48_decode_useruser(&user.useruser, TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); } /* more data */ if (TLVP_PRESENT(&tp, GSM48_IE_MORE_DATA)) user.more = 1; return mncc_recvmsg(trans->net, trans, MNCC_USERINFO_IND, &user); } static int _gsm48_lchan_modify(struct gsm_trans *trans, void *arg) { struct gsm_mncc *mode = arg; struct gsm_lchan *lchan = trans->conn->lchan; /* * We were forced to make an assignment a lot earlier and * we should avoid sending another assignment that might * even lead to a different kind of lchan (TCH/F vs. TCH/H). * In case of rtp-bridge it is too late to change things * here. */ if (trans->conn->mncc_rtp_bridge && lchan->tch_mode != GSM48_CMODE_SIGN) return 0; return gsm0808_assign_req(trans->conn, mode->lchan_mode, trans->conn->lchan->type != GSM_LCHAN_TCH_H); } static void mncc_recv_rtp(struct gsm_network *net, uint32_t callref, int cmd, uint32_t addr, uint16_t port, uint32_t payload_type, uint32_t payload_msg_type) { uint8_t data[sizeof(struct gsm_mncc)]; struct gsm_mncc_rtp *rtp; memset(&data, 0, sizeof(data)); rtp = (struct gsm_mncc_rtp *) &data[0]; rtp->callref = callref; rtp->msg_type = cmd; rtp->ip = addr; rtp->port = port; rtp->payload_type = payload_type; rtp->payload_msg_type = payload_msg_type; mncc_recvmsg(net, NULL, cmd, (struct gsm_mncc *)data); } static void mncc_recv_rtp_sock(struct gsm_network *net, struct gsm_trans *trans, int cmd) { struct gsm_lchan *lchan; int msg_type; lchan = trans->conn->lchan; switch (lchan->abis_ip.rtp_payload) { case RTP_PT_GSM_FULL: msg_type = GSM_TCHF_FRAME; break; case RTP_PT_GSM_EFR: msg_type = GSM_TCHF_FRAME_EFR; break; case RTP_PT_GSM_HALF: msg_type = GSM_TCHH_FRAME; break; case RTP_PT_AMR: msg_type = GSM_TCH_FRAME_AMR; break; default: LOGP(DMNCC, LOGL_ERROR, "%s unknown payload type %d\n", gsm_lchan_name(lchan), lchan->abis_ip.rtp_payload); msg_type = 0; break; } return mncc_recv_rtp(net, trans->callref, cmd, lchan->abis_ip.bound_ip, lchan->abis_ip.bound_port, lchan->abis_ip.rtp_payload, msg_type); } static void mncc_recv_rtp_err(struct gsm_network *net, uint32_t callref, int cmd) { return mncc_recv_rtp(net, callref, cmd, 0, 0, 0, 0); } static int tch_rtp_create(struct gsm_network *net, uint32_t callref) { struct gsm_bts *bts; struct gsm_lchan *lchan; struct gsm_trans *trans; /* Find callref */ trans = trans_find_by_callref(net, callref); if (!trans) { LOGP(DMNCC, LOGL_ERROR, "RTP create for non-existing trans\n"); mncc_recv_rtp_err(net, callref, MNCC_RTP_CREATE); return -EIO; } log_set_context(BSC_CTX_SUBSCR, trans->subscr); if (!trans->conn) { LOGP(DMNCC, LOGL_NOTICE, "RTP create for trans without conn\n"); mncc_recv_rtp_err(net, callref, MNCC_RTP_CREATE); return 0; } lchan = trans->conn->lchan; bts = lchan->ts->trx->bts; if (!is_ipaccess_bts(bts)) { /* * I want this to be straight forward and have no audio flow * through the nitb/osmo-mss system. This currently means that * this will not work with BS11/Nokia type BTS. We would need * to have a trau<->rtp bridge for these but still preferable * in another process. */ LOGP(DMNCC, LOGL_ERROR, "RTP create only works with IP systems\n"); mncc_recv_rtp_err(net, callref, MNCC_RTP_CREATE); return -EINVAL; } trans->conn->mncc_rtp_bridge = 1; /* * *sigh* we need to pick a codec now. Pick the most generic one * right now and hope we could fix that later on. This is very * similiar to the routine above. * Fallback to the internal MNCC mode to select a route. */ if (lchan->tch_mode == GSM48_CMODE_SIGN) { trans->conn->mncc_rtp_create_pending = 1; return gsm0808_assign_req(trans->conn, mncc_codec_for_mode(lchan->type), lchan->type != GSM_LCHAN_TCH_H); } mncc_recv_rtp_sock(trans->net, trans, MNCC_RTP_CREATE); return 0; } static int tch_rtp_connect(struct gsm_network *net, void *arg) { struct gsm_lchan *lchan; struct gsm_trans *trans; struct gsm_mncc_rtp *rtp = arg; /* Find callref */ trans = trans_find_by_callref(net, rtp->callref); if (!trans) { LOGP(DMNCC, LOGL_ERROR, "RTP connect for non-existing trans\n"); mncc_recv_rtp_err(net, rtp->callref, MNCC_RTP_CONNECT); return -EIO; } log_set_context(BSC_CTX_SUBSCR, trans->subscr); if (!trans->conn) { LOGP(DMNCC, LOGL_ERROR, "RTP connect for trans without conn\n"); mncc_recv_rtp_err(net, rtp->callref, MNCC_RTP_CONNECT); return 0; } lchan = trans->conn->lchan; /* TODO: Check if payload_msg_type is compatible with what we have */ if (rtp->payload_type != lchan->abis_ip.rtp_payload) { LOGP(DMNCC, LOGL_ERROR, "RTP connect with different RTP payload\n"); mncc_recv_rtp_err(net, rtp->callref, MNCC_RTP_CONNECT); } /* * FIXME: payload2 can't be sent with MDCX as the osmo-bts code * complains about both rtp and rtp payload2 being present in the * same package! */ trans->conn->mncc_rtp_connect_pending = 1; return rsl_ipacc_mdcx(lchan, rtp->ip, rtp->port, 0); } static int tch_rtp_signal(struct gsm_lchan *lchan, int signal) { struct gsm_network *net; struct gsm_trans *tmp, *trans = NULL; net = lchan->ts->trx->bts->network; llist_for_each_entry(tmp, &net->trans_list, entry) { if (!tmp->conn) continue; if (tmp->conn->lchan != lchan && tmp->conn->ho_lchan != lchan) continue; trans = tmp; break; } if (!trans) { LOGP(DMNCC, LOGL_ERROR, "%s IPA abis signal but no transaction.\n", gsm_lchan_name(lchan)); return 0; } switch (signal) { case S_ABISIP_CRCX_ACK: if (lchan->conn->mncc_rtp_create_pending) { lchan->conn->mncc_rtp_create_pending = 0; LOGP(DMNCC, LOGL_NOTICE, "%s sending pending RTP create ind.\n", gsm_lchan_name(lchan)); mncc_recv_rtp_sock(net, trans, MNCC_RTP_CREATE); } /* * TODO: this appears to be too early? Why not until after * the handover detect or the handover complete? */ maybe_switch_for_handover(lchan); break; case S_ABISIP_MDCX_ACK: if (lchan->conn->mncc_rtp_connect_pending) { lchan->conn->mncc_rtp_connect_pending = 0; LOGP(DMNCC, LOGL_NOTICE, "%s sending pending RTP connect ind.\n", gsm_lchan_name(lchan)); mncc_recv_rtp_sock(net, trans, MNCC_RTP_CONNECT); } break; } return 0; } static struct downstate { uint32_t states; int type; int (*rout) (struct gsm_trans *trans, void *arg); } downstatelist[] = { /* mobile originating call establishment */ {SBIT(GSM_CSTATE_INITIATED), /* 5.2.1.2 */ MNCC_CALL_PROC_REQ, gsm48_cc_tx_call_proc}, {SBIT(GSM_CSTATE_INITIATED) | SBIT(GSM_CSTATE_MO_CALL_PROC), /* 5.2.1.2 | 5.2.1.5 */ MNCC_ALERT_REQ, gsm48_cc_tx_alerting}, {SBIT(GSM_CSTATE_INITIATED) | SBIT(GSM_CSTATE_MO_CALL_PROC) | SBIT(GSM_CSTATE_CALL_DELIVERED), /* 5.2.1.2 | 5.2.1.6 | 5.2.1.6 */ MNCC_SETUP_RSP, gsm48_cc_tx_connect}, {SBIT(GSM_CSTATE_MO_CALL_PROC), /* 5.2.1.4.2 */ MNCC_PROGRESS_REQ, gsm48_cc_tx_progress}, /* mobile terminating call establishment */ {SBIT(GSM_CSTATE_NULL), /* 5.2.2.1 */ MNCC_SETUP_REQ, gsm48_cc_tx_setup}, {SBIT(GSM_CSTATE_CONNECT_REQUEST), MNCC_SETUP_COMPL_REQ, gsm48_cc_tx_connect_ack}, /* signalling during call */ {SBIT(GSM_CSTATE_ACTIVE), MNCC_NOTIFY_REQ, gsm48_cc_tx_notify}, {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_RELEASE_REQ), MNCC_FACILITY_REQ, gsm48_cc_tx_facility}, {ALL_STATES, MNCC_START_DTMF_RSP, gsm48_cc_tx_start_dtmf_ack}, {ALL_STATES, MNCC_START_DTMF_REJ, gsm48_cc_tx_start_dtmf_rej}, {ALL_STATES, MNCC_STOP_DTMF_RSP, gsm48_cc_tx_stop_dtmf_ack}, {SBIT(GSM_CSTATE_ACTIVE), MNCC_HOLD_CNF, gsm48_cc_tx_hold_ack}, {SBIT(GSM_CSTATE_ACTIVE), MNCC_HOLD_REJ, gsm48_cc_tx_hold_rej}, {SBIT(GSM_CSTATE_ACTIVE), MNCC_RETRIEVE_CNF, gsm48_cc_tx_retrieve_ack}, {SBIT(GSM_CSTATE_ACTIVE), MNCC_RETRIEVE_REJ, gsm48_cc_tx_retrieve_rej}, {SBIT(GSM_CSTATE_ACTIVE), MNCC_MODIFY_REQ, gsm48_cc_tx_modify}, {SBIT(GSM_CSTATE_MO_ORIG_MODIFY), MNCC_MODIFY_RSP, gsm48_cc_tx_modify_complete}, {SBIT(GSM_CSTATE_MO_ORIG_MODIFY), MNCC_MODIFY_REJ, gsm48_cc_tx_modify_reject}, {SBIT(GSM_CSTATE_ACTIVE), MNCC_USERINFO_REQ, gsm48_cc_tx_userinfo}, /* clearing */ {SBIT(GSM_CSTATE_INITIATED), MNCC_REJ_REQ, gsm48_cc_tx_release_compl}, {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_DISCONNECT_IND) - SBIT(GSM_CSTATE_RELEASE_REQ) - SBIT(GSM_CSTATE_DISCONNECT_REQ), /* 5.4.4 */ MNCC_DISC_REQ, gsm48_cc_tx_disconnect}, {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_RELEASE_REQ), /* 5.4.3.2 */ MNCC_REL_REQ, gsm48_cc_tx_release}, /* special */ {ALL_STATES, MNCC_LCHAN_MODIFY, _gsm48_lchan_modify}, }; #define DOWNSLLEN \ (sizeof(downstatelist) / sizeof(struct downstate)) int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg) { int i, rc = 0; struct gsm_trans *trans = NULL, *transt; struct gsm_subscriber_connection *conn = NULL; struct gsm_bts *bts = NULL; struct gsm_mncc *data = arg, rel; DEBUGP(DMNCC, "receive message %s\n", get_mncc_name(msg_type)); /* handle special messages */ switch(msg_type) { case MNCC_BRIDGE: return tch_bridge(net, arg); case MNCC_FRAME_DROP: return tch_recv_mncc(net, data->callref, 0); case MNCC_FRAME_RECV: return tch_recv_mncc(net, data->callref, 1); case MNCC_RTP_CREATE: return tch_rtp_create(net, data->callref); case MNCC_RTP_CONNECT: return tch_rtp_connect(net, arg); case MNCC_RTP_FREE: /* unused right now */ return -EIO; case GSM_TCHF_FRAME: case GSM_TCHF_FRAME_EFR: case GSM_TCHH_FRAME: case GSM_TCH_FRAME_AMR: /* Find callref */ trans = trans_find_by_callref(net, data->callref); if (!trans) { LOGP(DMNCC, LOGL_ERROR, "TCH frame for non-existing trans\n"); return -EIO; } log_set_context(BSC_CTX_SUBSCR, trans->subscr); if (!trans->conn) { LOGP(DMNCC, LOGL_NOTICE, "TCH frame for trans without conn\n"); return 0; } if (!trans->conn->lchan) { LOGP(DMNCC, LOGL_NOTICE, "TCH frame for trans without lchan\n"); return 0; } if (trans->conn->lchan->type != GSM_LCHAN_TCH_F && trans->conn->lchan->type != GSM_LCHAN_TCH_H) { /* This should be LOGL_ERROR or NOTICE, but * unfortuantely it happens for a couple of frames at * the beginning of every RTP connection */ LOGP(DMNCC, LOGL_DEBUG, "TCH frame for lchan != TCH_F/TCH_H\n"); return 0; } bts = trans->conn->lchan->ts->trx->bts; switch (bts->type) { case GSM_BTS_TYPE_NANOBTS: case GSM_BTS_TYPE_OSMO_SYSMO: if (!trans->conn->lchan->abis_ip.rtp_socket) { DEBUGP(DMNCC, "TCH frame to lchan without RTP connection\n"); return 0; } return rtp_send_frame(trans->conn->lchan->abis_ip.rtp_socket, arg); case GSM_BTS_TYPE_BS11: case GSM_BTS_TYPE_RBS2000: case GSM_BTS_TYPE_NOKIA_SITE: return trau_send_frame(trans->conn->lchan, arg); default: LOGP(DCC, LOGL_ERROR, "Unknown BTS type %u\n", bts->type); } return -EINVAL; } memset(&rel, 0, sizeof(struct gsm_mncc)); rel.callref = data->callref; /* Find callref */ trans = trans_find_by_callref(net, data->callref); /* Callref unknown */ if (!trans) { struct gsm_subscriber *subscr; if (msg_type != MNCC_SETUP_REQ) { DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) " "Received '%s' from MNCC with " "unknown callref %d\n", data->called.number, get_mncc_name(msg_type), data->callref); /* Invalid call reference */ return mncc_release_ind(net, NULL, data->callref, GSM48_CAUSE_LOC_PRN_S_LU, GSM48_CC_CAUSE_INVAL_TRANS_ID); } if (!data->called.number[0] && !data->imsi[0]) { DEBUGP(DCC, "(bts - trx - ts - ti) " "Received '%s' from MNCC with " "no number or IMSI\n", get_mncc_name(msg_type)); /* Invalid number */ return mncc_release_ind(net, NULL, data->callref, GSM48_CAUSE_LOC_PRN_S_LU, GSM48_CC_CAUSE_INV_NR_FORMAT); } /* New transaction due to setup, find subscriber */ if (data->called.number[0]) subscr = subscr_get_by_extension(net->subscr_group, data->called.number); else subscr = subscr_get_by_imsi(net->subscr_group, data->imsi); /* update the subscriber we deal with */ log_set_context(BSC_CTX_SUBSCR, subscr); /* If subscriber is not found */ if (!subscr) { DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) " "Received '%s' from MNCC with " "unknown subscriber %s\n", data->called.number, get_mncc_name(msg_type), data->called.number); /* Unknown subscriber */ return mncc_release_ind(net, NULL, data->callref, GSM48_CAUSE_LOC_PRN_S_LU, GSM48_CC_CAUSE_UNASSIGNED_NR); } /* If subscriber is not "attached" */ if (!subscr->lac) { DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) " "Received '%s' from MNCC with " "detached subscriber %s\n", data->called.number, get_mncc_name(msg_type), data->called.number); subscr_put(subscr); /* Temporarily out of order */ return mncc_release_ind(net, NULL, data->callref, GSM48_CAUSE_LOC_PRN_S_LU, GSM48_CC_CAUSE_DEST_OOO); } /* Create transaction */ trans = trans_alloc(net, subscr, GSM48_PDISC_CC, 0xff, data->callref); if (!trans) { DEBUGP(DCC, "No memory for trans.\n"); subscr_put(subscr); /* Ressource unavailable */ mncc_release_ind(net, NULL, data->callref, GSM48_CAUSE_LOC_PRN_S_LU, GSM48_CC_CAUSE_RESOURCE_UNAVAIL); return -ENOMEM; } /* Find lchan */ conn = connection_for_subscr(subscr); /* If subscriber has no lchan */ if (!conn) { /* find transaction with this subscriber already paging */ llist_for_each_entry(transt, &net->trans_list, entry) { /* Transaction of our lchan? */ if (transt == trans || transt->subscr != subscr) continue; DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) " "Received '%s' from MNCC with " "unallocated channel, paging already " "started for lac %d.\n", data->called.number, get_mncc_name(msg_type), subscr->lac); subscr_put(subscr); trans_free(trans); return 0; } /* store setup informations until paging was successfull */ memcpy(&trans->cc.msg, data, sizeof(struct gsm_mncc)); /* Request a channel */ trans->paging_request = subscr_request_channel(subscr, RSL_CHANNEED_TCH_F, setup_trig_pag_evt, trans); if (!trans->paging_request) { LOGP(DCC, LOGL_ERROR, "Failed to allocate paging token.\n"); subscr_put(subscr); trans_free(trans); return 0; } subscr_put(subscr); return 0; } /* Assign lchan */ trans->conn = conn; subscr_put(subscr); } else { /* update the subscriber we deal with */ log_set_context(BSC_CTX_SUBSCR, trans->subscr); } if (trans->conn) conn = trans->conn; /* if paging did not respond yet */ if (!conn) { DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) " "Received '%s' from MNCC in paging state\n", (trans->subscr)?(trans->subscr->extension):"-", get_mncc_name(msg_type)); mncc_set_cause(&rel, GSM48_CAUSE_LOC_PRN_S_LU, GSM48_CC_CAUSE_NORM_CALL_CLEAR); if (msg_type == MNCC_REL_REQ) rc = mncc_recvmsg(net, trans, MNCC_REL_CNF, &rel); else rc = mncc_recvmsg(net, trans, MNCC_REL_IND, &rel); trans->callref = 0; trans_free(trans); return rc; } DEBUGP(DCC, "(bts %d trx %d ts %d ti %02x sub %s) " "Received '%s' from MNCC in state %d (%s)\n", conn->bts->nr, conn->lchan->ts->trx->nr, conn->lchan->ts->nr, trans->transaction_id, (trans->conn->subscr)?(trans->conn->subscr->extension):"-", get_mncc_name(msg_type), trans->cc.state, gsm48_cc_state_name(trans->cc.state)); /* Find function for current state and message */ for (i = 0; i < DOWNSLLEN; i++) if ((msg_type == downstatelist[i].type) && ((1 << trans->cc.state) & downstatelist[i].states)) break; if (i == DOWNSLLEN) { DEBUGP(DCC, "Message unhandled at this state.\n"); return 0; } rc = downstatelist[i].rout(trans, arg); return rc; } static struct datastate { uint32_t states; int type; int (*rout) (struct gsm_trans *trans, struct msgb *msg); } datastatelist[] = { /* mobile originating call establishment */ {SBIT(GSM_CSTATE_NULL), /* 5.2.1.2 */ GSM48_MT_CC_SETUP, gsm48_cc_rx_setup}, {SBIT(GSM_CSTATE_NULL), /* 5.2.1.2 */ GSM48_MT_CC_EMERG_SETUP, gsm48_cc_rx_setup}, {SBIT(GSM_CSTATE_CONNECT_IND), /* 5.2.1.2 */ GSM48_MT_CC_CONNECT_ACK, gsm48_cc_rx_connect_ack}, /* mobile terminating call establishment */ {SBIT(GSM_CSTATE_CALL_PRESENT), /* 5.2.2.3.2 */ GSM48_MT_CC_CALL_CONF, gsm48_cc_rx_call_conf}, {SBIT(GSM_CSTATE_CALL_PRESENT) | SBIT(GSM_CSTATE_MO_TERM_CALL_CONF), /* ???? | 5.2.2.3.2 */ GSM48_MT_CC_ALERTING, gsm48_cc_rx_alerting}, {SBIT(GSM_CSTATE_CALL_PRESENT) | SBIT(GSM_CSTATE_MO_TERM_CALL_CONF) | SBIT(GSM_CSTATE_CALL_RECEIVED), /* (5.2.2.6) | 5.2.2.6 | 5.2.2.6 */ GSM48_MT_CC_CONNECT, gsm48_cc_rx_connect}, /* signalling during call */ {ALL_STATES - SBIT(GSM_CSTATE_NULL), GSM48_MT_CC_FACILITY, gsm48_cc_rx_facility}, {SBIT(GSM_CSTATE_ACTIVE), GSM48_MT_CC_NOTIFY, gsm48_cc_rx_notify}, {ALL_STATES, GSM48_MT_CC_START_DTMF, gsm48_cc_rx_start_dtmf}, {ALL_STATES, GSM48_MT_CC_STOP_DTMF, gsm48_cc_rx_stop_dtmf}, {ALL_STATES, GSM48_MT_CC_STATUS_ENQ, gsm48_cc_rx_status_enq}, {SBIT(GSM_CSTATE_ACTIVE), GSM48_MT_CC_HOLD, gsm48_cc_rx_hold}, {SBIT(GSM_CSTATE_ACTIVE), GSM48_MT_CC_RETR, gsm48_cc_rx_retrieve}, {SBIT(GSM_CSTATE_ACTIVE), GSM48_MT_CC_MODIFY, gsm48_cc_rx_modify}, {SBIT(GSM_CSTATE_MO_TERM_MODIFY), GSM48_MT_CC_MODIFY_COMPL, gsm48_cc_rx_modify_complete}, {SBIT(GSM_CSTATE_MO_TERM_MODIFY), GSM48_MT_CC_MODIFY_REJECT, gsm48_cc_rx_modify_reject}, {SBIT(GSM_CSTATE_ACTIVE), GSM48_MT_CC_USER_INFO, gsm48_cc_rx_userinfo}, /* clearing */ {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_RELEASE_REQ), /* 5.4.3.2 */ GSM48_MT_CC_DISCONNECT, gsm48_cc_rx_disconnect}, {ALL_STATES - SBIT(GSM_CSTATE_NULL), /* 5.4.4.1.2.2 */ GSM48_MT_CC_RELEASE, gsm48_cc_rx_release}, {ALL_STATES, /* 5.4.3.4 */ GSM48_MT_CC_RELEASE_COMPL, gsm48_cc_rx_release_compl}, }; #define DATASLLEN \ (sizeof(datastatelist) / sizeof(struct datastate)) static int gsm0408_rcv_cc(struct gsm_subscriber_connection *conn, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); uint8_t msg_type = gh->msg_type & 0xbf; uint8_t transaction_id = ((gh->proto_discr & 0xf0) ^ 0x80) >> 4; /* flip */ struct gsm_trans *trans = NULL; int i, rc = 0; if (msg_type & 0x80) { DEBUGP(DCC, "MSG 0x%2x not defined for PD error\n", msg_type); return -EINVAL; } /* Find transaction */ trans = trans_find_by_id(conn, GSM48_PDISC_CC, transaction_id); DEBUGP(DCC, "(bts %d trx %d ts %d ti %x sub %s) " "Received '%s' from MS in state %d (%s)\n", conn->bts->nr, conn->lchan->ts->trx->nr, conn->lchan->ts->nr, transaction_id, (conn->subscr)?(conn->subscr->extension):"-", gsm48_cc_msg_name(msg_type), trans?(trans->cc.state):0, gsm48_cc_state_name(trans?(trans->cc.state):0)); /* Create transaction */ if (!trans) { DEBUGP(DCC, "Unknown transaction ID %x, " "creating new trans.\n", transaction_id); /* Create transaction */ trans = trans_alloc(conn->bts->network, conn->subscr, GSM48_PDISC_CC, transaction_id, new_callref++); if (!trans) { DEBUGP(DCC, "No memory for trans.\n"); rc = gsm48_tx_simple(conn, GSM48_PDISC_CC | (transaction_id << 4), GSM48_MT_CC_RELEASE_COMPL); return -ENOMEM; } /* Assign transaction */ trans->conn = conn; } /* find function for current state and message */ for (i = 0; i < DATASLLEN; i++) if ((msg_type == datastatelist[i].type) && ((1 << trans->cc.state) & datastatelist[i].states)) break; if (i == DATASLLEN) { DEBUGP(DCC, "Message unhandled at this state.\n"); return 0; } assert(trans->subscr); rc = datastatelist[i].rout(trans, msg); return rc; } /* Create a dummy to wait five seconds */ static void release_anchor(struct gsm_subscriber_connection *conn) { if (!conn->anch_operation) return; osmo_timer_del(&conn->anch_operation->timeout); talloc_free(conn->anch_operation); conn->anch_operation = NULL; } static void anchor_timeout(void *_data) { struct gsm_subscriber_connection *con = _data; release_anchor(con); msc_release_connection(con); } int gsm0408_new_conn(struct gsm_subscriber_connection *conn) { conn->anch_operation = talloc_zero(conn, struct gsm_anchor_operation); if (!conn->anch_operation) return -1; conn->anch_operation->timeout.data = conn; conn->anch_operation->timeout.cb = anchor_timeout; osmo_timer_schedule(&conn->anch_operation->timeout, 5, 0); return 0; } /* here we get data from the BSC level... */ int gsm0408_dispatch(struct gsm_subscriber_connection *conn, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); uint8_t pdisc = gh->proto_discr & 0x0f; int rc = 0; LOGP(DRLL, LOGL_DEBUG, "Dispatching 04.08 message, pdisc=%d\n", pdisc); if (silent_call_reroute(conn, msg)) return silent_call_rx(conn, msg); switch (pdisc) { case GSM48_PDISC_CC: release_anchor(conn); rc = gsm0408_rcv_cc(conn, msg); break; case GSM48_PDISC_MM: rc = gsm0408_rcv_mm(conn, msg); break; case GSM48_PDISC_RR: rc = gsm0408_rcv_rr(conn, msg); break; case GSM48_PDISC_SMS: release_anchor(conn); rc = gsm0411_rcv_sms(conn, msg); break; case GSM48_PDISC_MM_GPRS: case GSM48_PDISC_SM_GPRS: LOGP(DRLL, LOGL_NOTICE, "Unimplemented " "GSM 04.08 discriminator 0x%02x\n", pdisc); break; case GSM48_PDISC_NC_SS: release_anchor(conn); rc = handle_rcv_ussd(conn, msg); break; default: LOGP(DRLL, LOGL_NOTICE, "Unknown " "GSM 04.08 discriminator 0x%02x\n", pdisc); break; } return rc; } /* * This will be ran by the linker when loading the DSO. We use it to * do system initialization, e.g. registration of signal handlers. */ static __attribute__((constructor)) void on_dso_load_0408(void) { osmo_signal_register_handler(SS_ABISIP, handle_abisip_signal, NULL); } openbsc-0.15.0/openbsc/src/libmsc/gsm_04_11.c000066400000000000000000000672101265565154000204740ustar00rootroot00000000000000/* Point-to-Point (PP) Short Message Service (SMS) * Support on Mobile Radio Interface * 3GPP TS 04.11 version 7.1.0 Release 1998 / ETSI TS 100 942 V7.1.0 */ /* (C) 2008 by Daniel Willmann * (C) 2009 by Harald Welte * (C) 2010-2012 by Holger Hans Peter Freyther * (C) 2010 by On-Waves * (C) 2011 by Andreas Eversberg * * 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 . * */ #include #include #include #include #include #include #include "bscconfig.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef BUILD_SMPP #include "smpp_smsc.h" #endif void *tall_gsms_ctx; static uint32_t new_callref = 0x40000001; struct gsm_sms *sms_alloc(void) { return talloc_zero(tall_gsms_ctx, struct gsm_sms); } void sms_free(struct gsm_sms *sms) { /* drop references to subscriber structure */ if (sms->receiver) subscr_put(sms->receiver); #ifdef BUILD_SMPP if (sms->smpp.esme) smpp_esme_put(sms->smpp.esme); #endif talloc_free(sms); } struct gsm_sms *sms_from_text(struct gsm_subscriber *receiver, struct gsm_subscriber *sender, int dcs, const char *text) { struct gsm_sms *sms = sms_alloc(); if (!sms) return NULL; sms->receiver = subscr_get(receiver); strncpy(sms->text, text, sizeof(sms->text)-1); strncpy(sms->src.addr, sender->extension, sizeof(sms->src.addr)-1); sms->reply_path_req = 0; sms->status_rep_req = 0; sms->ud_hdr_ind = 0; sms->protocol_id = 0; /* implicit */ sms->data_coding_scheme = dcs; strncpy(sms->dst.addr, receiver->extension, sizeof(sms->dst.addr)-1); /* Generate user_data */ sms->user_data_len = gsm_7bit_encode_n(sms->user_data, sizeof(sms->user_data), sms->text, NULL); return sms; } static void send_signal(int sig_no, struct gsm_trans *trans, struct gsm_sms *sms, int paging_result) { struct sms_signal_data sig; sig.trans = trans; sig.sms = sms; sig.paging_result = paging_result; osmo_signal_dispatch(SS_SMS, sig_no, &sig); } static int gsm411_sendmsg(struct gsm_subscriber_connection *conn, struct msgb *msg) { DEBUGP(DLSMS, "GSM4.11 TX %s\n", osmo_hexdump(msg->data, msg->len)); msg->l3h = msg->data; return gsm0808_submit_dtap(conn, msg, UM_SAPI_SMS, 1); } /* Prefix msg with a 04.08/04.11 CP header */ static int gsm411_cp_sendmsg(struct msgb *msg, struct gsm_trans *trans, uint8_t msg_type) { struct gsm48_hdr *gh; gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh)); /* Outgoing needs the highest bit set */ gh->proto_discr = trans->protocol | (trans->transaction_id<<4); gh->msg_type = msg_type; DEBUGP(DLSMS, "sending CP message (trans=%x)\n", trans->transaction_id); return gsm411_sendmsg(trans->conn, msg); } /* mm_send: receive MMCCSMS sap message from SMC */ static int gsm411_mm_send(struct gsm411_smc_inst *inst, int msg_type, struct msgb *msg, int cp_msg_type) { struct gsm_trans *trans = container_of(inst, struct gsm_trans, sms.smc_inst); int rc = 0; switch (msg_type) { case GSM411_MMSMS_EST_REQ: /* recycle msg */ rc = gsm411_smc_recv(inst, GSM411_MMSMS_EST_CNF, msg, 0); msgb_free(msg); /* upper layer does not free msg */ break; case GSM411_MMSMS_DATA_REQ: rc = gsm411_cp_sendmsg(msg, trans, cp_msg_type); break; case GSM411_MMSMS_REL_REQ: DEBUGP(DLSMS, "Got MMSMS_REL_REQ, destroying transaction.\n"); msgb_free(msg); trans_free(trans); break; default: LOGP(DLSMS, LOGL_NOTICE, "Unhandled MMCCSMS msg 0x%x\n", msg_type); msgb_free(msg); rc = -EINVAL; } return rc; } /* mm_send: receive MNCCSMS sap message from SMR */ int gsm411_mn_send(struct gsm411_smr_inst *inst, int msg_type, struct msgb *msg) { struct gsm_trans *trans = container_of(inst, struct gsm_trans, sms.smr_inst); /* forward to SMC */ return gsm411_smc_send(&trans->sms.smc_inst, msg_type, msg); } static int gsm340_rx_sms_submit(struct msgb *msg, struct gsm_sms *gsms) { if (db_sms_store(gsms) != 0) { LOGP(DLSMS, LOGL_ERROR, "Failed to store SMS in Database\n"); return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER; } /* dispatch a signal to tell higher level about it */ send_signal(S_SMS_SUBMITTED, NULL, gsms, 0); return 0; } /* generate a TPDU address field compliant with 03.40 sec. 9.1.2.5 */ static int gsm340_gen_oa_sub(uint8_t *oa, unsigned int oa_len, const struct gsm_sms_addr *src) { /* network specific, private numbering plan */ return gsm340_gen_oa(oa, oa_len, src->ton, src->npi, src->addr); } /* generate a msgb containing an 03.40 9.2.2.1 SMS-DELIVER TPDU derived from * struct gsm_sms, returns total size of TPDU */ static int gsm340_gen_sms_deliver_tpdu(struct msgb *msg, struct gsm_sms *sms) { uint8_t *smsp; uint8_t oa[12]; /* max len per 03.40 */ uint8_t oa_len = 0; uint8_t octet_len; unsigned int old_msg_len = msg->len; /* generate first octet with masked bits */ smsp = msgb_put(msg, 1); /* TP-MTI (message type indicator) */ *smsp = GSM340_SMS_DELIVER_SC2MS; /* TP-MMS (more messages to send) */ if (0 /* FIXME */) *smsp |= 0x04; /* TP-SRI(deliver)/SRR(submit) */ if (sms->status_rep_req) *smsp |= 0x20; /* TP-UDHI (indicating TP-UD contains a header) */ if (sms->ud_hdr_ind) *smsp |= 0x40; /* generate originator address */ oa_len = gsm340_gen_oa_sub(oa, sizeof(oa), &sms->src); smsp = msgb_put(msg, oa_len); memcpy(smsp, oa, oa_len); /* generate TP-PID */ smsp = msgb_put(msg, 1); *smsp = sms->protocol_id; /* generate TP-DCS */ smsp = msgb_put(msg, 1); *smsp = sms->data_coding_scheme; /* generate TP-SCTS */ smsp = msgb_put(msg, 7); gsm340_gen_scts(smsp, time(NULL)); /* generate TP-UDL */ smsp = msgb_put(msg, 1); *smsp = sms->user_data_len; /* generate TP-UD */ switch (gsm338_get_sms_alphabet(sms->data_coding_scheme)) { case DCS_7BIT_DEFAULT: octet_len = sms->user_data_len*7/8; if (sms->user_data_len*7%8 != 0) octet_len++; /* Warning, user_data_len indicates the amount of septets * (characters), we need amount of octets occupied */ smsp = msgb_put(msg, octet_len); memcpy(smsp, sms->user_data, octet_len); break; case DCS_UCS2: case DCS_8BIT_DATA: smsp = msgb_put(msg, sms->user_data_len); memcpy(smsp, sms->user_data, sms->user_data_len); break; default: LOGP(DLSMS, LOGL_NOTICE, "Unhandled Data Coding Scheme: 0x%02X\n", sms->data_coding_scheme); break; } return msg->len - old_msg_len; } int sms_route_mt_sms(struct gsm_subscriber_connection *conn, struct msgb *msg, struct gsm_sms *gsms, uint8_t sms_mti) { int rc; #ifdef BUILD_SMPP int smpp_first = smpp_route_smpp_first(gsms, conn); /* * Route through SMPP first before going to the local database. In case * of a unroutable message and no local subscriber, SMPP will be tried * twice. In case of an unknown subscriber continue with the normal * delivery of the SMS. */ if (smpp_first) { rc = smpp_try_deliver(gsms, conn); if (rc == 1) goto try_local; if (rc < 0) { rc = 21; /* cause 21: short message transfer rejected */ /* FIXME: handle the error somehow? */ } return rc; } try_local: #endif /* determine gsms->receiver based on dialled number */ gsms->receiver = subscr_get_by_extension(conn->bts->network->subscr_group, gsms->dst.addr); if (!gsms->receiver) { #ifdef BUILD_SMPP /* Avoid a second look-up */ if (smpp_first) return 1; /* cause 1: unknown subscriber */ rc = smpp_try_deliver(gsms, conn); if (rc == 1) { rc = 1; /* cause 1: unknown subscriber */ osmo_counter_inc(conn->bts->network->stats.sms.no_receiver); } else if (rc < 0) { rc = 21; /* cause 21: short message transfer rejected */ /* FIXME: handle the error somehow? */ } #else rc = 1; /* cause 1: unknown subscriber */ osmo_counter_inc(conn->bts->network->stats.sms.no_receiver); #endif return rc; } switch (sms_mti) { case GSM340_SMS_SUBMIT_MS2SC: /* MS is submitting a SMS */ rc = gsm340_rx_sms_submit(msg, gsms); break; case GSM340_SMS_COMMAND_MS2SC: case GSM340_SMS_DELIVER_REP_MS2SC: LOGP(DLSMS, LOGL_NOTICE, "Unimplemented MTI 0x%02x\n", sms_mti); rc = GSM411_RP_CAUSE_IE_NOTEXIST; break; default: LOGP(DLSMS, LOGL_NOTICE, "Undefined MTI 0x%02x\n", sms_mti); rc = GSM411_RP_CAUSE_IE_NOTEXIST; break; } if (!rc && !gsms->receiver) rc = GSM411_RP_CAUSE_MO_NUM_UNASSIGNED; return rc; } /* process an incoming TPDU (called from RP-DATA) * return value > 0: RP CAUSE for ERROR; < 0: silent error; 0 = success */ static int gsm340_rx_tpdu(struct gsm_subscriber_connection *conn, struct msgb *msg) { uint8_t *smsp = msgb_sms(msg); struct gsm_sms *gsms; unsigned int sms_alphabet; uint8_t sms_mti, sms_mms, sms_vpf, sms_rp; uint8_t *sms_vp; uint8_t da_len_bytes; uint8_t address_lv[12]; /* according to 03.40 / 9.1.2.5 */ int rc = 0; osmo_counter_inc(conn->bts->network->stats.sms.submitted); gsms = sms_alloc(); if (!gsms) return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER; /* invert those fields where 0 means active/present */ sms_mti = *smsp & 0x03; sms_mms = !!(*smsp & 0x04); sms_vpf = (*smsp & 0x18) >> 3; gsms->status_rep_req = (*smsp & 0x20); gsms->ud_hdr_ind = (*smsp & 0x40); sms_rp = (*smsp & 0x80); smsp++; gsms->msg_ref = *smsp++; /* length in bytes of the destination address */ da_len_bytes = 2 + *smsp/2 + *smsp%2; if (da_len_bytes > 12) { LOGP(DLSMS, LOGL_ERROR, "Destination Address > 12 bytes ?!?\n"); rc = GSM411_RP_CAUSE_SEMANT_INC_MSG; goto out; } else if (da_len_bytes < 4) { LOGP(DLSMS, LOGL_ERROR, "Destination Address < 4 bytes ?!?\n"); rc = GSM411_RP_CAUSE_SEMANT_INC_MSG; goto out; } memset(address_lv, 0, sizeof(address_lv)); memcpy(address_lv, smsp, da_len_bytes); /* mangle first byte to reflect length in bytes, not digits */ address_lv[0] = da_len_bytes - 1; gsms->dst.ton = (address_lv[1] >> 4) & 7; gsms->dst.npi = address_lv[1] & 0xF; /* convert to real number */ gsm48_decode_bcd_number(gsms->dst.addr, sizeof(gsms->dst.addr), address_lv, 1); smsp += da_len_bytes; gsms->protocol_id = *smsp++; gsms->data_coding_scheme = *smsp++; sms_alphabet = gsm338_get_sms_alphabet(gsms->data_coding_scheme); if (sms_alphabet == 0xffffffff) { rc = GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER; goto out; } switch (sms_vpf) { case GSM340_TP_VPF_RELATIVE: sms_vp = smsp++; break; case GSM340_TP_VPF_ABSOLUTE: case GSM340_TP_VPF_ENHANCED: sms_vp = smsp; /* the additional functionality indicator... */ if (sms_vpf == GSM340_TP_VPF_ENHANCED && *smsp & (1<<7)) smsp++; smsp += 7; break; case GSM340_TP_VPF_NONE: sms_vp = 0; break; default: LOGP(DLSMS, LOGL_NOTICE, "SMS Validity period not implemented: 0x%02x\n", sms_vpf); rc = GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER; goto out; } gsms->user_data_len = *smsp++; if (gsms->user_data_len) { memcpy(gsms->user_data, smsp, gsms->user_data_len); switch (sms_alphabet) { case DCS_7BIT_DEFAULT: gsm_7bit_decode_n(gsms->text, sizeof(gsms->text), smsp, gsms->user_data_len); break; case DCS_8BIT_DATA: case DCS_UCS2: case DCS_NONE: break; } } strncpy(gsms->src.addr, conn->subscr->extension, sizeof(gsms->src.addr)-1); LOGP(DLSMS, LOGL_INFO, "RX SMS: Sender: %s, MTI: 0x%02x, VPF: 0x%02x, " "MR: 0x%02x PID: 0x%02x, DCS: 0x%02x, DA: %s, " "UserDataLength: 0x%02x, UserData: \"%s\"\n", subscr_name(conn->subscr), sms_mti, sms_vpf, gsms->msg_ref, gsms->protocol_id, gsms->data_coding_scheme, gsms->dst.addr, gsms->user_data_len, sms_alphabet == DCS_7BIT_DEFAULT ? gsms->text : osmo_hexdump(gsms->user_data, gsms->user_data_len)); gsms->validity_minutes = gsm340_validity_period(sms_vpf, sms_vp); /* FIXME: This looks very wrong */ send_signal(0, NULL, gsms, 0); rc = sms_route_mt_sms(conn, msg, gsms, sms_mti); out: sms_free(gsms); return rc; } /* Prefix msg with a RP-DATA header and send as SMR DATA */ static int gsm411_rp_sendmsg(struct gsm411_smr_inst *inst, struct msgb *msg, uint8_t rp_msg_type, uint8_t rp_msg_ref, int rl_msg_type) { struct gsm411_rp_hdr *rp; uint8_t len = msg->len; /* GSM 04.11 RP-DATA header */ rp = (struct gsm411_rp_hdr *)msgb_push(msg, sizeof(*rp)); rp->len = len + 2; rp->msg_type = rp_msg_type; rp->msg_ref = rp_msg_ref; return gsm411_smr_send(inst, rl_msg_type, msg); } static int gsm411_send_rp_ack(struct gsm_trans *trans, uint8_t msg_ref) { struct msgb *msg = gsm411_msgb_alloc(); DEBUGP(DLSMS, "TX: SMS RP ACK\n"); return gsm411_rp_sendmsg(&trans->sms.smr_inst, msg, GSM411_MT_RP_ACK_MT, msg_ref, GSM411_SM_RL_REPORT_REQ); } static int gsm411_send_rp_error(struct gsm_trans *trans, uint8_t msg_ref, uint8_t cause) { struct msgb *msg = gsm411_msgb_alloc(); msgb_tv_put(msg, 1, cause); LOGP(DLSMS, LOGL_NOTICE, "TX: SMS RP ERROR, cause %d (%s)\n", cause, get_value_string(gsm411_rp_cause_strs, cause)); return gsm411_rp_sendmsg(&trans->sms.smr_inst, msg, GSM411_MT_RP_ERROR_MT, msg_ref, GSM411_SM_RL_REPORT_REQ); } /* Receive a 04.11 TPDU inside RP-DATA / user data */ static int gsm411_rx_rp_ud(struct msgb *msg, struct gsm_trans *trans, struct gsm411_rp_hdr *rph, uint8_t src_len, uint8_t *src, uint8_t dst_len, uint8_t *dst, uint8_t tpdu_len, uint8_t *tpdu) { int rc = 0; if (src_len && src) LOGP(DLSMS, LOGL_ERROR, "RP-DATA (MO) with SRC ?!?\n"); if (!dst_len || !dst || !tpdu_len || !tpdu) { LOGP(DLSMS, LOGL_ERROR, "RP-DATA (MO) without DST or TPDU ?!?\n"); gsm411_send_rp_error(trans, rph->msg_ref, GSM411_RP_CAUSE_INV_MAND_INF); return -EIO; } msg->l4h = tpdu; DEBUGP(DLSMS, "DST(%u,%s)\n", dst_len, osmo_hexdump(dst, dst_len)); rc = gsm340_rx_tpdu(trans->conn, msg); if (rc == 0) return gsm411_send_rp_ack(trans, rph->msg_ref); else if (rc > 0) return gsm411_send_rp_error(trans, rph->msg_ref, rc); else return rc; } /* Receive a 04.11 RP-DATA message in accordance with Section 7.3.1.2 */ static int gsm411_rx_rp_data(struct msgb *msg, struct gsm_trans *trans, struct gsm411_rp_hdr *rph) { uint8_t src_len, dst_len, rpud_len; uint8_t *src = NULL, *dst = NULL , *rp_ud = NULL; /* in the MO case, this should always be zero length */ src_len = rph->data[0]; if (src_len) src = &rph->data[1]; dst_len = rph->data[1+src_len]; if (dst_len) dst = &rph->data[1+src_len+1]; rpud_len = rph->data[1+src_len+1+dst_len]; if (rpud_len) rp_ud = &rph->data[1+src_len+1+dst_len+1]; DEBUGP(DLSMS, "RX_RP-DATA: src_len=%u, dst_len=%u ud_len=%u\n", src_len, dst_len, rpud_len); return gsm411_rx_rp_ud(msg, trans, rph, src_len, src, dst_len, dst, rpud_len, rp_ud); } /* Receive a 04.11 RP-ACK message (response to RP-DATA from us) */ static int gsm411_rx_rp_ack(struct msgb *msg, struct gsm_trans *trans, struct gsm411_rp_hdr *rph) { struct gsm_sms *sms = trans->sms.sms; /* Acnkowledgement to MT RP_DATA, i.e. the MS confirms it * successfully received a SMS. We can now safely mark it as * transmitted */ if (!sms) { LOGP(DLSMS, LOGL_ERROR, "RX RP-ACK but no sms in transaction?!?\n"); return gsm411_send_rp_error(trans, rph->msg_ref, GSM411_RP_CAUSE_PROTOCOL_ERR); } /* mark this SMS as sent in database */ db_sms_mark_delivered(sms); send_signal(S_SMS_DELIVERED, trans, sms, 0); sms_free(sms); trans->sms.sms = NULL; return 0; } static int gsm411_rx_rp_error(struct msgb *msg, struct gsm_trans *trans, struct gsm411_rp_hdr *rph) { struct gsm_network *net = trans->conn->bts->network; struct gsm_sms *sms = trans->sms.sms; uint8_t cause_len = rph->data[0]; uint8_t cause = rph->data[1]; /* Error in response to MT RP_DATA, i.e. the MS did not * successfully receive the SMS. We need to investigate * the cause and take action depending on it */ LOGP(DLSMS, LOGL_NOTICE, "%s: RX SMS RP-ERROR, cause %d:%d (%s)\n", subscr_name(trans->conn->subscr), cause_len, cause, get_value_string(gsm411_rp_cause_strs, cause)); if (!sms) { LOGP(DLSMS, LOGL_ERROR, "RX RP-ERR, but no sms in transaction?!?\n"); return -EINVAL; #if 0 return gsm411_send_rp_error(trans, rph->msg_ref, GSM411_RP_CAUSE_PROTOCOL_ERR); #endif } if (cause == GSM411_RP_CAUSE_MT_MEM_EXCEEDED) { /* MS has not enough memory to store the message. We need * to store this in our database and wait for a SMMA message */ /* FIXME */ send_signal(S_SMS_MEM_EXCEEDED, trans, sms, 0); osmo_counter_inc(net->stats.sms.rp_err_mem); } else { send_signal(S_SMS_UNKNOWN_ERROR, trans, sms, 0); osmo_counter_inc(net->stats.sms.rp_err_other); } sms_free(sms); trans->sms.sms = NULL; return 0; } static int gsm411_rx_rp_smma(struct msgb *msg, struct gsm_trans *trans, struct gsm411_rp_hdr *rph) { int rc; rc = gsm411_send_rp_ack(trans, rph->msg_ref); /* MS tells us that it has memory for more SMS, we need * to check if we have any pending messages for it and then * transfer those */ send_signal(S_SMS_SMMA, trans, NULL, 0); return rc; } /* receive RL DATA */ static int gsm411_rx_rl_data(struct msgb *msg, struct gsm48_hdr *gh, struct gsm_trans *trans) { struct gsm411_rp_hdr *rp_data = (struct gsm411_rp_hdr*)&gh->data; uint8_t msg_type = rp_data->msg_type & 0x07; int rc = 0; switch (msg_type) { case GSM411_MT_RP_DATA_MO: DEBUGP(DLSMS, "RX SMS RP-DATA (MO)\n"); rc = gsm411_rx_rp_data(msg, trans, rp_data); break; case GSM411_MT_RP_SMMA_MO: DEBUGP(DLSMS, "RX SMS RP-SMMA\n"); rc = gsm411_rx_rp_smma(msg, trans, rp_data); break; default: LOGP(DLSMS, LOGL_NOTICE, "Invalid RP type 0x%02x\n", msg_type); rc = -EINVAL; break; } return rc; } /* receive RL REPORT */ static int gsm411_rx_rl_report(struct msgb *msg, struct gsm48_hdr *gh, struct gsm_trans *trans) { struct gsm411_rp_hdr *rp_data = (struct gsm411_rp_hdr*)&gh->data; uint8_t msg_type = rp_data->msg_type & 0x07; int rc = 0; switch (msg_type) { case GSM411_MT_RP_ACK_MO: DEBUGP(DLSMS, "RX SMS RP-ACK (MO)\n"); rc = gsm411_rx_rp_ack(msg, trans, rp_data); break; case GSM411_MT_RP_ERROR_MO: DEBUGP(DLSMS, "RX SMS RP-ERROR (MO)\n"); rc = gsm411_rx_rp_error(msg, trans, rp_data); break; default: LOGP(DLSMS, LOGL_NOTICE, "Invalid RP type 0x%02x\n", msg_type); rc = -EINVAL; break; } return rc; } /* receive SM-RL sap message from SMR * NOTE: Message is freed by sender */ int gsm411_rl_recv(struct gsm411_smr_inst *inst, int msg_type, struct msgb *msg) { struct gsm_trans *trans = container_of(inst, struct gsm_trans, sms.smr_inst); struct gsm48_hdr *gh = msgb_l3(msg); int rc = 0; switch (msg_type) { case GSM411_SM_RL_DATA_IND: rc = gsm411_rx_rl_data(msg, gh, trans); break; case GSM411_SM_RL_REPORT_IND: if (gh) rc = gsm411_rx_rl_report(msg, gh, trans); break; default: LOGP(DLSMS, LOGL_NOTICE, "Unhandled SM-RL message 0x%x\n", msg_type); rc = -EINVAL; } return rc; } /* receive MNCCSMS sap message from SMC * NOTE: Message is freed by sender */ static int gsm411_mn_recv(struct gsm411_smc_inst *inst, int msg_type, struct msgb *msg) { struct gsm_trans *trans = container_of(inst, struct gsm_trans, sms.smc_inst); struct gsm48_hdr *gh = msgb_l3(msg); int rc = 0; switch (msg_type) { case GSM411_MNSMS_EST_IND: case GSM411_MNSMS_DATA_IND: DEBUGP(DLSMS, "MNSMS-DATA/EST-IND\n"); rc = gsm411_smr_recv(&trans->sms.smr_inst, msg_type, msg); break; case GSM411_MNSMS_ERROR_IND: if (gh) DEBUGP(DLSMS, "MNSMS-ERROR-IND, cause %d (%s)\n", gh->data[0], get_value_string(gsm411_cp_cause_strs, gh->data[0])); else DEBUGP(DLSMS, "MNSMS-ERROR-IND, no cause\n"); rc = gsm411_smr_recv(&trans->sms.smr_inst, msg_type, msg); break; default: LOGP(DLSMS, LOGL_NOTICE, "Unhandled MNCCSMS msg 0x%x\n", msg_type); rc = -EINVAL; } return rc; } /* Entry point for incoming GSM48_PDISC_SMS from abis_rsl.c */ int gsm0411_rcv_sms(struct gsm_subscriber_connection *conn, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); uint8_t msg_type = gh->msg_type; uint8_t transaction_id = ((gh->proto_discr >> 4) ^ 0x8); /* flip */ struct gsm_trans *trans; int new_trans = 0; int rc = 0; if (!conn->subscr) return -EIO; /* FIXME: send some error message */ DEBUGP(DLSMS, "receiving data (trans_id=%x)\n", transaction_id); trans = trans_find_by_id(conn, GSM48_PDISC_SMS, transaction_id); /* * A transaction we created but don't know about? */ if (!trans && (transaction_id & 0x8) == 0) { LOGP(DLSMS, LOGL_ERROR, "trans_id=%x allocated by us but known " "to us anymore. We are ignoring it, maybe a CP-ERROR " "from a MS?\n", transaction_id); return -EINVAL; } if (!trans) { DEBUGP(DLSMS, " -> (new transaction)\n"); trans = trans_alloc(conn->bts->network, conn->subscr, GSM48_PDISC_SMS, transaction_id, new_callref++); if (!trans) { DEBUGP(DLSMS, " -> No memory for trans\n"); /* FIXME: send some error message */ return -ENOMEM; } gsm411_smc_init(&trans->sms.smc_inst, 0, 1, gsm411_mn_recv, gsm411_mm_send); gsm411_smr_init(&trans->sms.smr_inst, 0, 1, gsm411_rl_recv, gsm411_mn_send); trans->conn = conn; new_trans = 1; } /* 5.4: For MO, if a CP-DATA is received for a new * transaction, equals reception of an implicit * last CP-ACK for previous transaction */ if (trans->sms.smc_inst.cp_state == GSM411_CPS_IDLE && msg_type == GSM411_MT_CP_DATA) { int i; struct gsm_trans *ptrans; /* Scan through all remote initiated transactions */ for (i=8; i<15; i++) { if (i == transaction_id) continue; ptrans = trans_find_by_id(conn, GSM48_PDISC_SMS, i); if (!ptrans) continue; DEBUGP(DLSMS, "Implicit CP-ACK for trans_id=%x\n", i); /* Finish it for good */ trans_free(ptrans); } } gsm411_smc_recv(&trans->sms.smc_inst, (new_trans) ? GSM411_MMSMS_EST_IND : GSM411_MMSMS_DATA_IND, msg, msg_type); return rc; } /* Take a SMS in gsm_sms structure and send it through an already * existing lchan. We also assume that the caller ensured this lchan already * has a SAPI3 RLL connection! */ int gsm411_send_sms(struct gsm_subscriber_connection *conn, struct gsm_sms *sms) { struct msgb *msg = gsm411_msgb_alloc(); struct gsm_trans *trans; uint8_t *data, *rp_ud_len; uint8_t msg_ref = sms_next_rp_msg_ref(conn); int transaction_id; int rc; transaction_id = trans_assign_trans_id(conn->bts->network, conn->subscr, GSM48_PDISC_SMS, 0); if (transaction_id == -1) { LOGP(DLSMS, LOGL_ERROR, "No available transaction ids\n"); send_signal(S_SMS_UNKNOWN_ERROR, NULL, sms, 0); sms_free(sms); msgb_free(msg); return -EBUSY; } DEBUGP(DLSMS, "send_sms_lchan()\n"); /* FIXME: allocate transaction with message reference */ trans = trans_alloc(conn->bts->network, conn->subscr, GSM48_PDISC_SMS, transaction_id, new_callref++); if (!trans) { LOGP(DLSMS, LOGL_ERROR, "No memory for trans\n"); send_signal(S_SMS_UNKNOWN_ERROR, NULL, sms, 0); sms_free(sms); msgb_free(msg); /* FIXME: send some error message */ return -ENOMEM; } gsm411_smc_init(&trans->sms.smc_inst, sms->id, 1, gsm411_mn_recv, gsm411_mm_send); gsm411_smr_init(&trans->sms.smr_inst, sms->id, 1, gsm411_rl_recv, gsm411_mn_send); trans->sms.sms = sms; trans->conn = conn; /* Hardcode SMSC Originating Address for now */ data = (uint8_t *)msgb_put(msg, 8); data[0] = 0x07; /* originator length == 7 */ data[1] = 0x91; /* type of number: international, ISDN */ data[2] = 0x44; /* 447785016005 */ data[3] = 0x77; data[4] = 0x58; data[5] = 0x10; data[6] = 0x06; data[7] = 0x50; /* Hardcoded Destination Address */ data = (uint8_t *)msgb_put(msg, 1); data[0] = 0; /* destination length == 0 */ /* obtain a pointer for the rp_ud_len, so we can fill it later */ rp_ud_len = (uint8_t *)msgb_put(msg, 1); /* generate the 03.40 SMS-DELIVER TPDU */ rc = gsm340_gen_sms_deliver_tpdu(msg, sms); if (rc < 0) { send_signal(S_SMS_UNKNOWN_ERROR, trans, sms, 0); sms_free(sms); trans->sms.sms = NULL; trans_free(trans); msgb_free(msg); return rc; } *rp_ud_len = rc; DEBUGP(DLSMS, "TX: SMS DELIVER\n"); osmo_counter_inc(conn->bts->network->stats.sms.delivered); db_sms_inc_deliver_attempts(trans->sms.sms); return gsm411_rp_sendmsg(&trans->sms.smr_inst, msg, GSM411_MT_RP_DATA_MT, msg_ref, GSM411_SM_RL_DATA_REQ); } /* paging callback. Here we get called if paging a subscriber has * succeeded or failed. */ static int paging_cb_send_sms(unsigned int hooknum, unsigned int event, struct msgb *msg, void *_conn, void *_sms) { struct gsm_subscriber_connection *conn = _conn; struct gsm_sms *sms = _sms; int rc = 0; DEBUGP(DLSMS, "paging_cb_send_sms(hooknum=%u, event=%u, msg=%p," "conn=%p, sms=%p/id: %llu)\n", hooknum, event, msg, conn, sms, sms->id); if (hooknum != GSM_HOOK_RR_PAGING) return -EINVAL; switch (event) { case GSM_PAGING_SUCCEEDED: gsm411_send_sms(conn, sms); break; case GSM_PAGING_EXPIRED: case GSM_PAGING_OOM: case GSM_PAGING_BUSY: send_signal(S_SMS_UNKNOWN_ERROR, NULL, sms, event); sms_free(sms); rc = -ETIMEDOUT; break; default: LOGP(DLSMS, LOGL_ERROR, "Unhandled paging event: %d\n", event); } return rc; } /* high-level function to send a SMS to a given subscriber. The function * will take care of paging the subscriber, establishing the RLL SAPI3 * connection, etc. */ int gsm411_send_sms_subscr(struct gsm_subscriber *subscr, struct gsm_sms *sms) { struct gsm_subscriber_connection *conn; void *res; /* check if we already have an open lchan to the subscriber. * if yes, send the SMS this way */ conn = connection_for_subscr(subscr); if (conn) { return gsm411_send_sms(conn, sms); } /* if not, we have to start paging */ res = subscr_request_channel(subscr, RSL_CHANNEED_SDCCH, paging_cb_send_sms, sms); if (!res) { send_signal(S_SMS_UNKNOWN_ERROR, NULL, sms, GSM_PAGING_BUSY); sms_free(sms); } return 0; } void _gsm411_sms_trans_free(struct gsm_trans *trans) { /* cleanup SMS instance */ gsm411_smr_clear(&trans->sms.smr_inst); trans->sms.smr_inst.rl_recv = NULL; trans->sms.smr_inst.mn_send = NULL; gsm411_smc_clear(&trans->sms.smc_inst); trans->sms.smc_inst.mn_recv = NULL; trans->sms.smc_inst.mm_send = NULL; if (trans->sms.sms) { LOGP(DLSMS, LOGL_ERROR, "Transaction contains SMS.\n"); send_signal(S_SMS_UNKNOWN_ERROR, trans, trans->sms.sms, 0); sms_free(trans->sms.sms); trans->sms.sms = NULL; } } void gsm411_sapi_n_reject(struct gsm_subscriber_connection *conn) { struct gsm_network *net; struct gsm_trans *trans, *tmp; net = conn->bts->network; llist_for_each_entry_safe(trans, tmp, &net->trans_list, entry) { struct gsm_sms *sms; if (trans->conn != conn) continue; if (trans->protocol != GSM48_PDISC_SMS) continue; sms = trans->sms.sms; if (!sms) { LOGP(DLSMS, LOGL_ERROR, "SAPI Reject but no SMS.\n"); continue; } send_signal(S_SMS_UNKNOWN_ERROR, trans, sms, 0); sms_free(sms); trans->sms.sms = NULL; trans_free(trans); } } openbsc-0.15.0/openbsc/src/libmsc/gsm_04_11_helper.c000066400000000000000000000022311265565154000220230ustar00rootroot00000000000000/* Helpers for SMS/GSM 04.11 */ /* * (C) 2014 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 . * */ #include #include uint8_t sms_next_rp_msg_ref(struct gsm_subscriber_connection *conn) { const uint8_t rp_msg_ref = conn->next_rp_ref; /* * This should wrap as the valid range is 0 to 255. We only * transfer one SMS at a time so we don't need to check if * the id has been already assigned. */ conn->next_rp_ref += 1; return rp_msg_ref; } openbsc-0.15.0/openbsc/src/libmsc/gsm_04_80.c000066400000000000000000000121731265565154000205000ustar00rootroot00000000000000/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */ /* (C) 2008-2009 by Harald Welte * (C) 2008, 2009, 2010 by Holger Hans Peter Freyther * (C) 2009 by Mike Haben * * 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include static inline unsigned char *msgb_wrap_with_TL(struct msgb *msgb, uint8_t tag) { uint8_t *data = msgb_push(msgb, 2); data[0] = tag; data[1] = msgb->len - 2; return data; } static inline unsigned char *msgb_push_TLV1(struct msgb *msgb, uint8_t tag, uint8_t value) { uint8_t *data = msgb_push(msgb, 3); data[0] = tag; data[1] = 1; data[2] = value; return data; } /* Send response to a mobile-originated ProcessUnstructuredSS-Request */ int gsm0480_send_ussd_response(struct gsm_subscriber_connection *conn, const struct msgb *in_msg, const char *response_text, const struct ussd_request *req) { struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh; uint8_t *ptr8; int response_len; /* First put the payload text into the message */ ptr8 = msgb_put(msg, 0); gsm_7bit_encode_n_ussd(ptr8, msgb_tailroom(msg), response_text, &response_len); msgb_put(msg, response_len); /* Then wrap it as an Octet String */ msgb_wrap_with_TL(msg, ASN1_OCTET_STRING_TAG); /* Pre-pend the DCS octet string */ msgb_push_TLV1(msg, ASN1_OCTET_STRING_TAG, 0x0F); /* Then wrap these as a Sequence */ msgb_wrap_with_TL(msg, GSM_0480_SEQUENCE_TAG); /* Pre-pend the operation code */ msgb_push_TLV1(msg, GSM0480_OPERATION_CODE, GSM0480_OP_CODE_PROCESS_USS_REQ); /* Wrap the operation code and IA5 string as a sequence */ msgb_wrap_with_TL(msg, GSM_0480_SEQUENCE_TAG); /* Pre-pend the invoke ID */ msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, req->invoke_id); /* Wrap this up as a Return Result component */ msgb_wrap_with_TL(msg, GSM0480_CTYPE_RETURN_RESULT); /* Wrap the component in a Facility message */ msgb_wrap_with_TL(msg, GSM0480_IE_FACILITY); /* And finally pre-pend the L3 header */ gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh)); gh->proto_discr = GSM48_PDISC_NC_SS | req->transaction_id | (1<<7); /* TI direction = 1 */ gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE; return gsm0808_submit_dtap(conn, msg, 0, 0); } int gsm0480_send_ussd_reject(struct gsm_subscriber_connection *conn, const struct msgb *in_msg, const struct ussd_request *req) { struct msgb *msg = gsm48_msgb_alloc(); struct gsm48_hdr *gh; /* First insert the problem code */ msgb_push_TLV1(msg, GSM_0480_PROBLEM_CODE_TAG_GENERAL, GSM_0480_GEN_PROB_CODE_UNRECOGNISED); /* Before it insert the invoke ID */ msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, req->invoke_id); /* Wrap this up as a Reject component */ msgb_wrap_with_TL(msg, GSM0480_CTYPE_REJECT); /* Wrap the component in a Facility message */ msgb_wrap_with_TL(msg, GSM0480_IE_FACILITY); /* And finally pre-pend the L3 header */ gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh)); gh->proto_discr = GSM48_PDISC_NC_SS; gh->proto_discr |= req->transaction_id | (1<<7); /* TI direction = 1 */ gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE; return gsm0808_submit_dtap(conn, msg, 0, 0); } int gsm0480_send_ussdNotify(struct gsm_subscriber_connection *conn, int level, const char *text) { struct gsm48_hdr *gh; struct msgb *msg; msg = gsm0480_create_unstructuredSS_Notify(level, text); if (!msg) return -1; gsm0480_wrap_invoke(msg, GSM0480_OP_CODE_USS_NOTIFY, 0); gsm0480_wrap_facility(msg); /* And finally pre-pend the L3 header */ gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh)); gh->proto_discr = GSM48_PDISC_NC_SS; gh->msg_type = GSM0480_MTYPE_REGISTER; return gsm0808_submit_dtap(conn, msg, 0, 0); } int gsm0480_send_releaseComplete(struct gsm_subscriber_connection *conn) { struct gsm48_hdr *gh; struct msgb *msg; msg = gsm48_msgb_alloc(); if (!msg) return -1; gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh)); gh->proto_discr = GSM48_PDISC_NC_SS; gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE; return gsm0808_submit_dtap(conn, msg, 0, 0); } openbsc-0.15.0/openbsc/src/libmsc/gsm_subscriber.c000066400000000000000000000242051265565154000221100ustar00rootroot00000000000000/* The concept of a subscriber for the MSC, roughly HLR/VLR functionality */ /* (C) 2008 by Harald Welte * (C) 2009,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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void *tall_sub_req_ctx; extern struct llist_head *subscr_bsc_active_subscribers(void); int gsm48_secure_channel(struct gsm_subscriber_connection *conn, int key_seq, gsm_cbfn *cb, void *cb_data); /* * Struct for pending channel requests. This is managed in the * llist_head requests of each subscriber. The reference counting * should work in such a way that a subscriber with a pending request * remains in memory. */ struct subscr_request { struct llist_head entry; /* the callback data */ gsm_cbfn *cbfn; void *param; }; static struct gsm_subscriber *get_subscriber(struct gsm_subscriber_group *sgrp, int type, const char *ident) { struct gsm_subscriber *subscr = db_get_subscriber(type, ident); if (subscr) subscr->group = sgrp; return subscr; } /* * We got the channel assigned and can now hand this channel * over to one of our callbacks. */ static int subscr_paging_dispatch(unsigned int hooknum, unsigned int event, struct msgb *msg, void *data, void *param) { struct subscr_request *request, *tmp; struct gsm_subscriber_connection *conn = data; struct gsm_subscriber *subscr = param; struct paging_signal_data sig_data; OSMO_ASSERT(subscr->is_paging); /* * Stop paging on all other BTS. E.g. if this is * the first timeout on a BTS then the others will * timeout soon as well. Let's just stop everything * and forget we wanted to page. */ paging_request_stop(NULL, subscr, NULL, NULL); /* Inform parts of the system we don't know */ sig_data.subscr = subscr; sig_data.bts = conn ? conn->bts : NULL; sig_data.conn = conn; sig_data.paging_result = event; osmo_signal_dispatch( SS_PAGING, event == GSM_PAGING_SUCCEEDED ? S_PAGING_SUCCEEDED : S_PAGING_EXPIRED, &sig_data ); llist_for_each_entry_safe(request, tmp, &subscr->requests, entry) { llist_del(&request->entry); request->cbfn(hooknum, event, msg, data, request->param); talloc_free(request); } /* balanced with the moment we start paging */ subscr->is_paging = 0; subscr_put(subscr); return 0; } static int subscr_paging_sec_cb(unsigned int hooknum, unsigned int event, struct msgb *msg, void *data, void *param) { int rc; switch (event) { case GSM_SECURITY_AUTH_FAILED: /* Dispatch as paging failure */ rc = subscr_paging_dispatch( GSM_HOOK_RR_PAGING, GSM_PAGING_EXPIRED, msg, data, param); break; case GSM_SECURITY_NOAVAIL: case GSM_SECURITY_SUCCEEDED: /* Dispatch as paging failure */ rc = subscr_paging_dispatch( GSM_HOOK_RR_PAGING, GSM_PAGING_SUCCEEDED, msg, data, param); break; default: rc = -EINVAL; } return rc; } static int subscr_paging_cb(unsigned int hooknum, unsigned int event, struct msgb *msg, void *data, void *param) { struct gsm_subscriber_connection *conn = data; struct gsm48_hdr *gh; struct gsm48_pag_resp *pr; /* Other cases mean problem, dispatch direclty */ if (event != GSM_PAGING_SUCCEEDED) return subscr_paging_dispatch(hooknum, event, msg, data, param); /* Get paging response */ gh = msgb_l3(msg); pr = (struct gsm48_pag_resp *)gh->data; /* We _really_ have a channel, secure it now ! */ return gsm48_secure_channel(conn, pr->key_seq, subscr_paging_sec_cb, param); } struct subscr_request *subscr_request_channel(struct gsm_subscriber *subscr, int channel_type, gsm_cbfn *cbfn, void *param) { int rc; struct subscr_request *request; /* Start paging.. we know it is async so we can do it before */ if (!subscr->is_paging) { LOGP(DMM, LOGL_DEBUG, "Subscriber %s not paged yet.\n", subscr_name(subscr)); rc = paging_request(subscr->group->net, subscr, channel_type, subscr_paging_cb, subscr); if (rc <= 0) { LOGP(DMM, LOGL_ERROR, "Subscriber %s paging failed: %d\n", subscr_name(subscr), rc); return NULL; } /* reduced on the first paging callback */ subscr_get(subscr); subscr->is_paging = 1; } /* TODO: Stop paging in case of memory allocation failure */ request = talloc_zero(subscr, struct subscr_request); if (!request) return NULL; request->cbfn = cbfn; request->param = param; llist_add_tail(&request->entry, &subscr->requests); return request; } void subscr_remove_request(struct subscr_request *request) { llist_del(&request->entry); talloc_free(request); } struct gsm_subscriber *subscr_create_subscriber(struct gsm_subscriber_group *sgrp, const char *imsi) { struct gsm_subscriber *subscr = db_create_subscriber(imsi); if (subscr) subscr->group = sgrp; return subscr; } struct gsm_subscriber *subscr_get_by_tmsi(struct gsm_subscriber_group *sgrp, uint32_t tmsi) { char tmsi_string[14]; struct gsm_subscriber *subscr; /* we might have a record in memory already */ llist_for_each_entry(subscr, subscr_bsc_active_subscribers(), entry) { if (tmsi == subscr->tmsi) return subscr_get(subscr); } sprintf(tmsi_string, "%u", tmsi); return get_subscriber(sgrp, GSM_SUBSCRIBER_TMSI, tmsi_string); } struct gsm_subscriber *subscr_get_by_imsi(struct gsm_subscriber_group *sgrp, const char *imsi) { struct gsm_subscriber *subscr; llist_for_each_entry(subscr, subscr_bsc_active_subscribers(), entry) { if (strcmp(subscr->imsi, imsi) == 0) return subscr_get(subscr); } return get_subscriber(sgrp, GSM_SUBSCRIBER_IMSI, imsi); } struct gsm_subscriber *subscr_get_by_extension(struct gsm_subscriber_group *sgrp, const char *ext) { struct gsm_subscriber *subscr; llist_for_each_entry(subscr, subscr_bsc_active_subscribers(), entry) { if (strcmp(subscr->extension, ext) == 0) return subscr_get(subscr); } return get_subscriber(sgrp, GSM_SUBSCRIBER_EXTENSION, ext); } struct gsm_subscriber *subscr_get_by_id(struct gsm_subscriber_group *sgrp, unsigned long long id) { struct gsm_subscriber *subscr; char buf[32]; sprintf(buf, "%llu", id); llist_for_each_entry(subscr, subscr_bsc_active_subscribers(), entry) { if (subscr->id == id) return subscr_get(subscr); } return get_subscriber(sgrp, GSM_SUBSCRIBER_ID, buf); } int subscr_update_expire_lu(struct gsm_subscriber *s, struct gsm_bts *bts) { int rc; /* Table 10.5.33: The T3212 timeout value field is coded as the * binary representation of the timeout value for * periodic updating in decihours. Mark the subscriber as * inactive if it missed two consecutive location updates. * Timeout is twice the t3212 value plus one minute */ /* Is expiration handling enabled? */ if (bts->si_common.chan_desc.t3212 == 0) s->expire_lu = GSM_SUBSCRIBER_NO_EXPIRATION; else s->expire_lu = time(NULL) + (bts->si_common.chan_desc.t3212 * 60 * 6 * 2) + 60; rc = db_sync_subscriber(s); db_subscriber_update(s); return rc; } int subscr_update(struct gsm_subscriber *s, struct gsm_bts *bts, int reason) { int rc; /* FIXME: Migrate pending requests from one BSC to another */ switch (reason) { case GSM_SUBSCRIBER_UPDATE_ATTACHED: s->group = bts->network->subscr_group; /* Indicate "attached to LAC" */ s->lac = bts->location_area_code; LOGP(DMM, LOGL_INFO, "Subscriber %s ATTACHED LAC=%u\n", subscr_name(s), s->lac); /* * The below will set a new expire_lu but as a side-effect * the new lac will be saved in the database. */ rc = subscr_update_expire_lu(s, bts); osmo_signal_dispatch(SS_SUBSCR, S_SUBSCR_ATTACHED, s); break; case GSM_SUBSCRIBER_UPDATE_DETACHED: /* Only detach if we are currently in this area */ if (bts->location_area_code == s->lac) s->lac = GSM_LAC_RESERVED_DETACHED; LOGP(DMM, LOGL_INFO, "Subscriber %s DETACHED\n", subscr_name(s)); rc = db_sync_subscriber(s); db_subscriber_update(s); osmo_signal_dispatch(SS_SUBSCR, S_SUBSCR_DETACHED, s); break; default: fprintf(stderr, "subscr_update with unknown reason: %d\n", reason); rc = db_sync_subscriber(s); db_subscriber_update(s); break; }; return rc; } void subscr_update_from_db(struct gsm_subscriber *sub) { db_subscriber_update(sub); } static void subscr_expire_callback(void *data, long long unsigned int id) { struct gsm_network *net = data; struct gsm_subscriber *s = subscr_get_by_id(net->subscr_group, id); struct gsm_subscriber_connection *conn = connection_for_subscr(s); /* * The subscriber is active and the phone stopped the timer. As * we don't want to periodically update the database for active * subscribers we will just do it when the subscriber was selected * for expiration. This way on the next around another subscriber * will be selected. */ if (conn && conn->expire_timer_stopped) { LOGP(DMM, LOGL_DEBUG, "Not expiring subscriber %s (ID %llu)\n", subscr_name(s), id); subscr_update_expire_lu(s, conn->bts); subscr_put(s); return; } LOGP(DMM, LOGL_NOTICE, "Expiring inactive subscriber %s (ID %llu)\n", subscr_name(s), id); s->lac = GSM_LAC_RESERVED_DETACHED; db_sync_subscriber(s); subscr_put(s); } void subscr_expire(struct gsm_subscriber_group *sgrp) { db_subscriber_expire(sgrp->net, subscr_expire_callback); } openbsc-0.15.0/openbsc/src/libmsc/meas_feed.c000066400000000000000000000077211265565154000210130ustar00rootroot00000000000000/* UDP-Feed of measurement reports */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "meas_feed.h" struct meas_feed_state { struct osmo_wqueue wqueue; char scenario[31+1]; char *dst_host; uint16_t dst_port; }; static struct meas_feed_state g_mfs; static int process_meas_rep(struct gsm_meas_rep *mr) { struct msgb *msg; struct meas_feed_meas *mfm; struct gsm_subscriber *subscr; /* ignore measurements as long as we don't know who it is */ if (!mr->lchan || !mr->lchan->conn || !mr->lchan->conn->subscr) return 0; subscr = mr->lchan->conn->subscr; msg = msgb_alloc(sizeof(struct meas_feed_meas), "Meas. Feed"); if (!msg) return 0; /* fill in the header */ mfm = (struct meas_feed_meas *) msgb_put(msg, sizeof(*mfm)); mfm->hdr.msg_type = MEAS_FEED_MEAS; mfm->hdr.version = MEAS_FEED_VERSION; /* fill in MEAS_FEED_MEAS specific header */ strncpy(mfm->imsi, subscr->imsi, sizeof(mfm->imsi)-1); mfm->imsi[sizeof(mfm->imsi)-1] = '\0'; strncpy(mfm->name, subscr->name, sizeof(mfm->name)-1); mfm->name[sizeof(mfm->name)-1] = '\0'; strncpy(mfm->scenario, g_mfs.scenario, sizeof(mfm->scenario)); mfm->scenario[sizeof(mfm->scenario)-1] = '\0'; /* copy the entire measurement report */ memcpy(&mfm->mr, mr, sizeof(mfm->mr)); /* copy channel information */ /* we assume that the measurement report always belong to some timeslot */ mfm->lchan_type = (uint8_t)mr->lchan->type; mfm->pchan_type = (uint8_t)mr->lchan->ts->pchan; mfm->bts_nr = mr->lchan->ts->trx->bts->nr; mfm->trx_nr = mr->lchan->ts->trx->nr; mfm->ts_nr = mr->lchan->ts->nr; mfm->ss_nr = mr->lchan->nr; /* and send it to the socket */ if (osmo_wqueue_enqueue(&g_mfs.wqueue, msg) != 0) msgb_free(msg); return 0; } static int meas_feed_sig_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct lchan_signal_data *sdata = signal_data; if (subsys != SS_LCHAN) return 0; if (signal == S_LCHAN_MEAS_REP) process_meas_rep(sdata->mr); return 0; } static int feed_write_cb(struct osmo_fd *ofd, struct msgb *msg) { return write(ofd->fd, msgb_data(msg), msgb_length(msg)); } static int feed_read_cb(struct osmo_fd *ofd) { int rc; char buf[256]; rc = read(ofd->fd, buf, sizeof(buf)); ofd->fd &= ~BSC_FD_READ; return rc; } int meas_feed_cfg_set(const char *dst_host, uint16_t dst_port) { int rc; int already_initialized = 0; if (g_mfs.wqueue.bfd.fd) already_initialized = 1; if (already_initialized && !strcmp(dst_host, g_mfs.dst_host) && dst_port == g_mfs.dst_port) return 0; if (!already_initialized) { osmo_wqueue_init(&g_mfs.wqueue, 10); g_mfs.wqueue.write_cb = feed_write_cb; g_mfs.wqueue.read_cb = feed_read_cb; osmo_signal_register_handler(SS_LCHAN, meas_feed_sig_cb, NULL); } if (already_initialized) { osmo_wqueue_clear(&g_mfs.wqueue); osmo_fd_unregister(&g_mfs.wqueue.bfd); close(g_mfs.wqueue.bfd.fd); /* don't set to zero, as that would mean 'not yet initialized' */ g_mfs.wqueue.bfd.fd = -1; } rc = osmo_sock_init_ofd(&g_mfs.wqueue.bfd, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, dst_host, dst_port, OSMO_SOCK_F_CONNECT); if (rc < 0) return rc; g_mfs.wqueue.bfd.when &= ~BSC_FD_READ; if (g_mfs.dst_host) talloc_free(g_mfs.dst_host); g_mfs.dst_host = talloc_strdup(NULL, dst_host); g_mfs.dst_port = dst_port; return 0; } void meas_feed_cfg_get(char **host, uint16_t *port) { *port = g_mfs.dst_port; *host = g_mfs.dst_host; } void meas_feed_scenario_set(const char *name) { strncpy(g_mfs.scenario, name, sizeof(g_mfs.scenario)-1); g_mfs.scenario[sizeof(g_mfs.scenario)-1] = '\0'; } const char *meas_feed_scenario_get(void) { return g_mfs.scenario; } openbsc-0.15.0/openbsc/src/libmsc/meas_feed.h000066400000000000000000000004671265565154000210200ustar00rootroot00000000000000#ifndef _INT_MEAS_FEED_H #define _INT_MEAS_FEED_H #include int meas_feed_cfg_set(const char *dst_host, uint16_t dst_port); void meas_feed_cfg_get(char **host, uint16_t *port); void meas_feed_scenario_set(const char *name); const char *meas_feed_scenario_get(void); #endif /* _INT_MEAS_FEED_H */ openbsc-0.15.0/openbsc/src/libmsc/mncc.c000066400000000000000000000062431265565154000200210ustar00rootroot00000000000000/* mncc.c - utility routines for the MNCC API between the 04.08 * message parsing and the actual Call Control logic */ /* (C) 2008-2009 by Harald Welte * (C) 2009 by Andreas Eversberg * 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 . * */ #include #include #include #include #include #include #include #include #include #include #include static struct mncc_names { char *name; int value; } mncc_names[] = { {"MNCC_SETUP_REQ", 0x0101}, {"MNCC_SETUP_IND", 0x0102}, {"MNCC_SETUP_RSP", 0x0103}, {"MNCC_SETUP_CNF", 0x0104}, {"MNCC_SETUP_COMPL_REQ",0x0105}, {"MNCC_SETUP_COMPL_IND",0x0106}, {"MNCC_CALL_CONF_IND", 0x0107}, {"MNCC_CALL_PROC_REQ", 0x0108}, {"MNCC_PROGRESS_REQ", 0x0109}, {"MNCC_ALERT_REQ", 0x010a}, {"MNCC_ALERT_IND", 0x010b}, {"MNCC_NOTIFY_REQ", 0x010c}, {"MNCC_NOTIFY_IND", 0x010d}, {"MNCC_DISC_REQ", 0x010e}, {"MNCC_DISC_IND", 0x010f}, {"MNCC_REL_REQ", 0x0110}, {"MNCC_REL_IND", 0x0111}, {"MNCC_REL_CNF", 0x0112}, {"MNCC_FACILITY_REQ", 0x0113}, {"MNCC_FACILITY_IND", 0x0114}, {"MNCC_START_DTMF_IND", 0x0115}, {"MNCC_START_DTMF_RSP", 0x0116}, {"MNCC_START_DTMF_REJ", 0x0117}, {"MNCC_STOP_DTMF_IND", 0x0118}, {"MNCC_STOP_DTMF_RSP", 0x0119}, {"MNCC_MODIFY_REQ", 0x011a}, {"MNCC_MODIFY_IND", 0x011b}, {"MNCC_MODIFY_RSP", 0x011c}, {"MNCC_MODIFY_CNF", 0x011d}, {"MNCC_MODIFY_REJ", 0x011e}, {"MNCC_HOLD_IND", 0x011f}, {"MNCC_HOLD_CNF", 0x0120}, {"MNCC_HOLD_REJ", 0x0121}, {"MNCC_RETRIEVE_IND", 0x0122}, {"MNCC_RETRIEVE_CNF", 0x0123}, {"MNCC_RETRIEVE_REJ", 0x0124}, {"MNCC_USERINFO_REQ", 0x0125}, {"MNCC_USERINFO_IND", 0x0126}, {"MNCC_REJ_REQ", 0x0127}, {"MNCC_REJ_IND", 0x0128}, {"MNCC_BRIDGE", 0x0200}, {"MNCC_FRAME_RECV", 0x0201}, {"MNCC_FRAME_DROP", 0x0202}, {"MNCC_LCHAN_MODIFY", 0x0203}, {"MNCC_RTP_CREATE", 0x0204}, {"MNCC_RTP_CONNECT", 0x0205}, {"MNCC_RTP_FREE", 0x0206}, {"GSM_TCHF_FRAME", 0x0300}, {"GSM_TCHF_FRAME_EFR", 0x0301}, {"GSM_TCHH_FRAME", 0x0302}, {"GSM_TCH_FRAME_AMR", 0x0303}, {"GSM_BAD_FRAME", 0x03ff}, {NULL, 0} }; char *get_mncc_name(int value) { int i; for (i = 0; mncc_names[i].name; i++) { if (mncc_names[i].value == value) return mncc_names[i].name; } return "MNCC_Unknown"; } void mncc_set_cause(struct gsm_mncc *data, int loc, int val) { data->fields |= MNCC_F_CAUSE; data->cause.location = loc; data->cause.value = val; } openbsc-0.15.0/openbsc/src/libmsc/mncc_builtin.c000066400000000000000000000274261265565154000215550ustar00rootroot00000000000000/* mncc_builtin.c - default, minimal built-in MNCC Application for * standalone bsc_hack (netowrk-in-the-box mode) */ /* (C) 2008-2010 by Harald Welte * (C) 2009 by Andreas Eversberg * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 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 General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ #include #include #include #include #include #include #include #include #include #include #include #include void *tall_call_ctx; static LLIST_HEAD(call_list); static uint32_t new_callref = 0x00000001; struct mncc_int mncc_int = { .def_codec = { GSM48_CMODE_SPEECH_V1, GSM48_CMODE_SPEECH_V1 }, }; static void free_call(struct gsm_call *call) { llist_del(&call->entry); DEBUGP(DMNCC, "(call %x) Call removed.\n", call->callref); talloc_free(call); } static struct gsm_call *get_call_ref(uint32_t callref) { struct gsm_call *callt; llist_for_each_entry(callt, &call_list, entry) { if (callt->callref == callref) return callt; } return NULL; } uint8_t mncc_codec_for_mode(int lchan_type) { /* FIXME: check codec capabilities of the phone */ if (lchan_type != GSM_LCHAN_TCH_H) return mncc_int.def_codec[0]; else return mncc_int.def_codec[1]; } static uint8_t determine_lchan_mode(struct gsm_mncc *setup) { return mncc_codec_for_mode(setup->lchan_type); } /* on incoming call, look up database and send setup to remote subscr. */ static int mncc_setup_ind(struct gsm_call *call, int msg_type, struct gsm_mncc *setup) { struct gsm_mncc mncc; struct gsm_call *remote; memset(&mncc, 0, sizeof(struct gsm_mncc)); mncc.callref = call->callref; /* already have remote call */ if (call->remote_ref) return 0; /* transfer mode 1 would be packet mode, which was never specified */ if (setup->bearer_cap.mode != 0) { LOGP(DMNCC, LOGL_NOTICE, "(call %x) We don't support " "packet mode\n", call->callref); mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU, GSM48_CC_CAUSE_BEARER_CA_UNAVAIL); goto out_reject; } /* we currently only do speech */ if (setup->bearer_cap.transfer != GSM_MNCC_BCAP_SPEECH) { LOGP(DMNCC, LOGL_NOTICE, "(call %x) We only support " "voice calls\n", call->callref); mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU, GSM48_CC_CAUSE_BEARER_CA_UNAVAIL); goto out_reject; } /* create remote call */ if (!(remote = talloc_zero(tall_call_ctx, struct gsm_call))) { mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU, GSM48_CC_CAUSE_RESOURCE_UNAVAIL); goto out_reject; } llist_add_tail(&remote->entry, &call_list); remote->net = call->net; remote->callref = new_callref++; DEBUGP(DMNCC, "(call %x) Creating new remote instance %x.\n", call->callref, remote->callref); /* link remote call */ call->remote_ref = remote->callref; remote->remote_ref = call->callref; /* send call proceeding */ memset(&mncc, 0, sizeof(struct gsm_mncc)); mncc.callref = call->callref; DEBUGP(DMNCC, "(call %x) Accepting call.\n", call->callref); mncc_tx_to_cc(call->net, MNCC_CALL_PROC_REQ, &mncc); /* modify mode */ memset(&mncc, 0, sizeof(struct gsm_mncc)); mncc.callref = call->callref; mncc.lchan_mode = determine_lchan_mode(setup); DEBUGP(DMNCC, "(call %x) Modify channel mode.\n", call->callref); mncc_tx_to_cc(call->net, MNCC_LCHAN_MODIFY, &mncc); /* send setup to remote */ // setup->fields |= MNCC_F_SIGNAL; // setup->signal = GSM48_SIGNAL_DIALTONE; setup->callref = remote->callref; DEBUGP(DMNCC, "(call %x) Forwarding SETUP to remote.\n", call->callref); return mncc_tx_to_cc(remote->net, MNCC_SETUP_REQ, setup); out_reject: mncc_tx_to_cc(call->net, MNCC_REJ_REQ, &mncc); free_call(call); return 0; } static int mncc_alert_ind(struct gsm_call *call, int msg_type, struct gsm_mncc *alert) { struct gsm_call *remote; /* send alerting to remote */ if (!(remote = get_call_ref(call->remote_ref))) return 0; alert->callref = remote->callref; DEBUGP(DMNCC, "(call %x) Forwarding ALERT to remote.\n", call->callref); return mncc_tx_to_cc(remote->net, MNCC_ALERT_REQ, alert); } static int mncc_notify_ind(struct gsm_call *call, int msg_type, struct gsm_mncc *notify) { struct gsm_call *remote; /* send notify to remote */ if (!(remote = get_call_ref(call->remote_ref))) return 0; notify->callref = remote->callref; DEBUGP(DMNCC, "(call %x) Forwarding NOTIF to remote.\n", call->callref); return mncc_tx_to_cc(remote->net, MNCC_NOTIFY_REQ, notify); } static int mncc_setup_cnf(struct gsm_call *call, int msg_type, struct gsm_mncc *connect) { struct gsm_mncc connect_ack, frame_recv; struct gsm_network *net = call->net; struct gsm_call *remote; uint32_t refs[2]; /* acknowledge connect */ memset(&connect_ack, 0, sizeof(struct gsm_mncc)); connect_ack.callref = call->callref; DEBUGP(DMNCC, "(call %x) Acknowledge SETUP.\n", call->callref); mncc_tx_to_cc(call->net, MNCC_SETUP_COMPL_REQ, &connect_ack); /* send connect message to remote */ if (!(remote = get_call_ref(call->remote_ref))) return 0; connect->callref = remote->callref; DEBUGP(DMNCC, "(call %x) Sending CONNECT to remote.\n", call->callref); mncc_tx_to_cc(remote->net, MNCC_SETUP_RSP, connect); /* bridge tch */ refs[0] = call->callref; refs[1] = call->remote_ref; DEBUGP(DMNCC, "(call %x) Bridging with remote.\n", call->callref); /* in direct mode, we always have to bridge the channels */ if (ipacc_rtp_direct) return mncc_tx_to_cc(call->net, MNCC_BRIDGE, refs); /* proxy mode */ if (!net->handover.active) { /* in the no-handover case, we can bridge, i.e. use * the old RTP proxy code */ return mncc_tx_to_cc(call->net, MNCC_BRIDGE, refs); } else { /* in case of handover, we need to re-write the RTP * SSRC, sequence and timestamp values and thus * need to enable RTP receive for both directions */ memset(&frame_recv, 0, sizeof(struct gsm_mncc)); frame_recv.callref = call->callref; mncc_tx_to_cc(call->net, MNCC_FRAME_RECV, &frame_recv); frame_recv.callref = call->remote_ref; return mncc_tx_to_cc(call->net, MNCC_FRAME_RECV, &frame_recv); } } static int mncc_disc_ind(struct gsm_call *call, int msg_type, struct gsm_mncc *disc) { struct gsm_call *remote; /* send release */ DEBUGP(DMNCC, "(call %x) Releasing call with cause %d\n", call->callref, disc->cause.value); mncc_tx_to_cc(call->net, MNCC_REL_REQ, disc); /* send disc to remote */ if (!(remote = get_call_ref(call->remote_ref))) { return 0; } disc->callref = remote->callref; DEBUGP(DMNCC, "(call %x) Disconnecting remote with cause %d\n", remote->callref, disc->cause.value); return mncc_tx_to_cc(remote->net, MNCC_DISC_REQ, disc); } static int mncc_rel_ind(struct gsm_call *call, int msg_type, struct gsm_mncc *rel) { struct gsm_call *remote; /* send release to remote */ if (!(remote = get_call_ref(call->remote_ref))) { free_call(call); return 0; } rel->callref = remote->callref; DEBUGP(DMNCC, "(call %x) Releasing remote with cause %d\n", call->callref, rel->cause.value); /* * Release this side of the call right now. Otherwise we end up * in this method for the other call and will also try to release * it and then we will end up with a double free and a crash */ free_call(call); mncc_tx_to_cc(remote->net, MNCC_REL_REQ, rel); return 0; } static int mncc_rel_cnf(struct gsm_call *call, int msg_type, struct gsm_mncc *rel) { free_call(call); return 0; } /* receiving a (speech) traffic frame from the BSC code */ static int mncc_rcv_data(struct gsm_call *call, int msg_type, struct gsm_data_frame *dfr) { struct gsm_trans *remote_trans; remote_trans = trans_find_by_callref(call->net, call->remote_ref); /* this shouldn't really happen */ if (!remote_trans || !remote_trans->conn) { LOGP(DMNCC, LOGL_ERROR, "No transaction or transaction without lchan?!?\n"); return -EIO; } /* RTP socket of remote end has meanwhile died */ if (!remote_trans->conn->lchan->abis_ip.rtp_socket) return -EIO; return rtp_send_frame(remote_trans->conn->lchan->abis_ip.rtp_socket, dfr); } /* Internal MNCC handler input function (from CC -> MNCC -> here) */ int int_mncc_recv(struct gsm_network *net, struct msgb *msg) { void *arg = msgb_data(msg); struct gsm_mncc *data = arg; int msg_type = data->msg_type; int callref; struct gsm_call *call = NULL, *callt; int rc = 0; /* Special messages */ switch(msg_type) { } /* find callref */ callref = data->callref; llist_for_each_entry(callt, &call_list, entry) { if (callt->callref == callref) { call = callt; break; } } /* create callref, if setup is received */ if (!call) { if (msg_type != MNCC_SETUP_IND) goto out_free; /* drop */ /* create call */ if (!(call = talloc_zero(tall_call_ctx, struct gsm_call))) { struct gsm_mncc rel; memset(&rel, 0, sizeof(struct gsm_mncc)); rel.callref = callref; mncc_set_cause(&rel, GSM48_CAUSE_LOC_PRN_S_LU, GSM48_CC_CAUSE_RESOURCE_UNAVAIL); mncc_tx_to_cc(net, MNCC_REL_REQ, &rel); goto out_free; } llist_add_tail(&call->entry, &call_list); call->net = net; call->callref = callref; DEBUGP(DMNCC, "(call %x) Call created.\n", call->callref); } if (mncc_is_data_frame(msg_type)) { rc = mncc_rcv_data(call, msg_type, arg); goto out_free; } DEBUGP(DMNCC, "(call %x) Received message %s\n", call->callref, get_mncc_name(msg_type)); switch(msg_type) { case MNCC_SETUP_IND: rc = mncc_setup_ind(call, msg_type, arg); break; case MNCC_SETUP_CNF: rc = mncc_setup_cnf(call, msg_type, arg); break; case MNCC_SETUP_COMPL_IND: break; case MNCC_CALL_CONF_IND: /* we now need to MODIFY the channel */ data->lchan_mode = determine_lchan_mode(data); mncc_tx_to_cc(call->net, MNCC_LCHAN_MODIFY, data); break; case MNCC_ALERT_IND: rc = mncc_alert_ind(call, msg_type, arg); break; case MNCC_NOTIFY_IND: rc = mncc_notify_ind(call, msg_type, arg); break; case MNCC_DISC_IND: rc = mncc_disc_ind(call, msg_type, arg); break; case MNCC_REL_IND: case MNCC_REJ_IND: rc = mncc_rel_ind(call, msg_type, arg); break; case MNCC_REL_CNF: rc = mncc_rel_cnf(call, msg_type, arg); break; case MNCC_FACILITY_IND: break; case MNCC_START_DTMF_IND: break; case MNCC_STOP_DTMF_IND: break; case MNCC_MODIFY_IND: mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU, GSM48_CC_CAUSE_SERV_OPT_UNIMPL); DEBUGP(DMNCC, "(call %x) Rejecting MODIFY with cause %d\n", call->callref, data->cause.value); rc = mncc_tx_to_cc(net, MNCC_MODIFY_REJ, data); break; case MNCC_MODIFY_CNF: break; case MNCC_HOLD_IND: mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU, GSM48_CC_CAUSE_SERV_OPT_UNIMPL); DEBUGP(DMNCC, "(call %x) Rejecting HOLD with cause %d\n", call->callref, data->cause.value); rc = mncc_tx_to_cc(net, MNCC_HOLD_REJ, data); break; case MNCC_RETRIEVE_IND: mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU, GSM48_CC_CAUSE_SERV_OPT_UNIMPL); DEBUGP(DMNCC, "(call %x) Rejecting RETRIEVE with cause %d\n", call->callref, data->cause.value); rc = mncc_tx_to_cc(net, MNCC_RETRIEVE_REJ, data); break; default: LOGP(DMNCC, LOGL_NOTICE, "(call %x) Message unhandled\n", callref); break; } out_free: msgb_free(msg); return rc; } openbsc-0.15.0/openbsc/src/libmsc/mncc_sock.c000066400000000000000000000217461265565154000210450ustar00rootroot00000000000000/* mncc_sock.c: Tie the MNCC interface to a unix domain socket */ /* (C) 2008-2010 by Harald Welte * (C) 2009 by Andreas Eversberg * (C) 2012 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 General Public License as published by * the Free Software Foundation; either version 2 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 General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct mncc_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 */ }; /* input from CC code into mncc_sock */ int mncc_sock_from_cc(struct gsm_network *net, struct msgb *msg) { struct gsm_mncc *mncc_in = (struct gsm_mncc *) msgb_data(msg); int msg_type = mncc_in->msg_type; /* Check if we currently have a MNCC handler connected */ if (net->mncc_state->conn_bfd.fd < 0) { LOGP(DMNCC, LOGL_ERROR, "mncc_sock receives %s for external CC app " "but socket is gone\n", get_mncc_name(msg_type)); if (!mncc_is_data_frame(msg_type)) { /* release the request */ struct gsm_mncc mncc_out; memset(&mncc_out, 0, sizeof(mncc_out)); mncc_out.callref = mncc_in->callref; mncc_set_cause(&mncc_out, GSM48_CAUSE_LOC_PRN_S_LU, GSM48_CC_CAUSE_TEMP_FAILURE); mncc_tx_to_cc(net, MNCC_REL_REQ, &mncc_out); } /* free the original message */ msgb_free(msg); return -1; } /* FIXME: check for some maximum queue depth? */ /* Actually enqueue the message and mark socket write need */ msgb_enqueue(&net->upqueue, msg); net->mncc_state->conn_bfd.when |= BSC_FD_WRITE; return 0; } /* FIXME: move this to libosmocore */ int osmo_unixsock_listen(struct osmo_fd *bfd, int type, const char *path); static void mncc_sock_close(struct mncc_sock_state *state) { struct osmo_fd *bfd = &state->conn_bfd; LOGP(DMNCC, LOGL_NOTICE, "MNCC Socket has LOST connection\n"); close(bfd->fd); bfd->fd = -1; osmo_fd_unregister(bfd); /* re-enable the generation of ACCEPT for new connections */ state->listen_bfd.when |= BSC_FD_READ; /* release all exisitng calls */ gsm0408_clear_all_trans(state->net, GSM48_PDISC_CC); /* flush the queue */ while (!llist_empty(&state->net->upqueue)) { struct msgb *msg = msgb_dequeue(&state->net->upqueue); msgb_free(msg); } } static int mncc_sock_read(struct osmo_fd *bfd) { struct mncc_sock_state *state = (struct mncc_sock_state *)bfd->data; struct gsm_mncc *mncc_prim; struct msgb *msg; int rc; msg = msgb_alloc(sizeof(*mncc_prim)+256, "mncc_sock_rx"); if (!msg) return -ENOMEM; mncc_prim = (struct gsm_mncc *) msg->tail; rc = recv(bfd->fd, msg->tail, msgb_tailroom(msg), 0); if (rc == 0) goto close; if (rc < 0) { if (errno == EAGAIN) return 0; goto close; } rc = mncc_tx_to_cc(state->net, mncc_prim->msg_type, mncc_prim); /* as we always synchronously process the message in mncc_send() and * its callbacks, we can free the message here. */ msgb_free(msg); return rc; close: msgb_free(msg); mncc_sock_close(state); return -1; } static int mncc_sock_write(struct osmo_fd *bfd) { struct mncc_sock_state *state = bfd->data; struct gsm_network *net = state->net; int rc; while (!llist_empty(&net->upqueue)) { struct msgb *msg, *msg2; struct gsm_mncc *mncc_prim; /* peek at the beginning of the queue */ msg = llist_entry(net->upqueue.next, struct msgb, list); mncc_prim = (struct gsm_mncc *)msg->data; bfd->when &= ~BSC_FD_WRITE; /* bug hunter 8-): maybe someone forgot msgb_put(...) ? */ if (!msgb_length(msg)) { LOGP(DMNCC, LOGL_ERROR, "message type (%d) with ZERO " "bytes!\n", mncc_prim->msg_type); goto dontsend; } /* try to send it over the socket */ rc = write(bfd->fd, msgb_data(msg), msgb_length(msg)); if (rc == 0) goto close; if (rc < 0) { if (errno == EAGAIN) { bfd->when |= BSC_FD_WRITE; break; } goto close; } dontsend: /* _after_ we send it, we can deueue */ msg2 = msgb_dequeue(&net->upqueue); assert(msg == msg2); msgb_free(msg); } return 0; close: mncc_sock_close(state); return -1; } static int mncc_sock_cb(struct osmo_fd *bfd, unsigned int flags) { int rc = 0; if (flags & BSC_FD_READ) rc = mncc_sock_read(bfd); if (rc < 0) return rc; if (flags & BSC_FD_WRITE) rc = mncc_sock_write(bfd); return rc; } /** * Send a version indication to the remote. */ static void queue_hello(struct mncc_sock_state *mncc) { struct gsm_mncc_hello *hello; struct msgb *msg; msg = msgb_alloc(512, "mncc hello"); if (!msg) { LOGP(DMNCC, LOGL_ERROR, "Failed to allocate hello.\n"); mncc_sock_close(mncc); return; } hello = (struct gsm_mncc_hello *) msgb_put(msg, sizeof(*hello)); hello->msg_type = MNCC_SOCKET_HELLO; hello->version = MNCC_SOCK_VERSION; hello->mncc_size = sizeof(struct gsm_mncc); hello->data_frame_size = sizeof(struct gsm_data_frame); hello->called_offset = offsetof(struct gsm_mncc, called); hello->signal_offset = offsetof(struct gsm_mncc, signal); hello->emergency_offset = offsetof(struct gsm_mncc, emergency); hello->lchan_type_offset = offsetof(struct gsm_mncc, lchan_type); msgb_enqueue(&mncc->net->upqueue, msg); mncc->conn_bfd.when |= BSC_FD_WRITE; } /* accept a new connection */ static int mncc_sock_accept(struct osmo_fd *bfd, unsigned int flags) { struct mncc_sock_state *state = (struct mncc_sock_state *)bfd->data; struct osmo_fd *conn_bfd = &state->conn_bfd; struct sockaddr_un un_addr; socklen_t len; int rc; len = sizeof(un_addr); rc = accept(bfd->fd, (struct sockaddr *) &un_addr, &len); if (rc < 0) { LOGP(DMNCC, LOGL_ERROR, "Failed to accept a new connection\n"); return -1; } if (conn_bfd->fd >= 0) { LOGP(DMNCC, LOGL_NOTICE, "MNCC app connects but we already have " "another active connection ?!?\n"); /* We already have one MNCC app connected, this is all we support */ state->listen_bfd.when &= ~BSC_FD_READ; close(rc); return 0; } conn_bfd->fd = rc; conn_bfd->when = BSC_FD_READ; conn_bfd->cb = mncc_sock_cb; conn_bfd->data = state; if (osmo_fd_register(conn_bfd) != 0) { LOGP(DMNCC, LOGL_ERROR, "Failed to register new connection fd\n"); close(conn_bfd->fd); conn_bfd->fd = -1; return -1; } LOGP(DMNCC, LOGL_NOTICE, "MNCC Socket has connection with external " "call control application\n"); queue_hello(state); return 0; } int mncc_sock_init(struct gsm_network *net) { struct mncc_sock_state *state; struct osmo_fd *bfd; int rc; state = talloc_zero(tall_bsc_ctx, struct mncc_sock_state); if (!state) return -ENOMEM; state->net = net; state->conn_bfd.fd = -1; bfd = &state->listen_bfd; rc = osmo_unixsock_listen(bfd, SOCK_SEQPACKET, "/tmp/bsc_mncc"); if (rc < 0) { LOGP(DMNCC, LOGL_ERROR, "Could not create unix socket: %s\n", strerror(errno)); talloc_free(state); return rc; } bfd->when = BSC_FD_READ; bfd->cb = mncc_sock_accept; bfd->data = state; rc = osmo_fd_register(bfd); if (rc < 0) { LOGP(DMNCC, LOGL_ERROR, "Could not register listen fd: %d\n", rc); close(bfd->fd); talloc_free(state); return rc; } net->mncc_state = state; return 0; } /* FIXME: move this to libosmocore */ int osmo_unixsock_listen(struct osmo_fd *bfd, int type, const char *path) { struct sockaddr_un local; unsigned int namelen; int rc; bfd->fd = socket(AF_UNIX, type, 0); if (bfd->fd < 0) { fprintf(stderr, "Failed to create Unix Domain Socket.\n"); return -1; } local.sun_family = AF_UNIX; strncpy(local.sun_path, path, sizeof(local.sun_path)); local.sun_path[sizeof(local.sun_path) - 1] = '\0'; unlink(local.sun_path); /* we use the same magic that X11 uses in Xtranssock.c for * calculating the proper length of the sockaddr */ #if defined(BSD44SOCKETS) || defined(__UNIXWARE__) local.sun_len = strlen(local.sun_path); #endif #if defined(BSD44SOCKETS) || defined(SUN_LEN) namelen = SUN_LEN(&local); #else namelen = strlen(local.sun_path) + offsetof(struct sockaddr_un, sun_path); #endif rc = bind(bfd->fd, (struct sockaddr *) &local, namelen); if (rc != 0) { fprintf(stderr, "Failed to bind the unix domain socket. '%s'\n", local.sun_path); return -1; } if (listen(bfd->fd, 0) != 0) { fprintf(stderr, "Failed to listen.\n"); return -1; } return 0; } openbsc-0.15.0/openbsc/src/libmsc/osmo_msc.c000066400000000000000000000111121265565154000207070ustar00rootroot00000000000000/* main MSC management code... */ /* * (C) 2010,2013 by Holger Hans Peter Freyther * (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 . * */ #include #include #include #include #include static void msc_sapi_n_reject(struct gsm_subscriber_connection *conn, int dlci) { int sapi = dlci & 0x7; if (sapi == UM_SAPI_SMS) gsm411_sapi_n_reject(conn); } static int msc_clear_request(struct gsm_subscriber_connection *conn, uint32_t cause) { gsm0408_clear_request(conn, cause); return 1; } static int msc_compl_l3(struct gsm_subscriber_connection *conn, struct msgb *msg, uint16_t chosen_channel) { gsm0408_new_conn(conn); gsm0408_dispatch(conn, msg); /* TODO: do better */ if (conn->silent_call) return BSC_API_CONN_POL_ACCEPT; if (conn->loc_operation || conn->sec_operation || conn->anch_operation) return BSC_API_CONN_POL_ACCEPT; if (trans_has_conn(conn)) return BSC_API_CONN_POL_ACCEPT; LOGP(DRR, LOGL_INFO, "MSC Complete L3: Rejecting connection.\n"); return BSC_API_CONN_POL_REJECT; } static void msc_dtap(struct gsm_subscriber_connection *conn, uint8_t link_id, struct msgb *msg) { gsm0408_dispatch(conn, msg); } static void msc_assign_compl(struct gsm_subscriber_connection *conn, uint8_t rr_cause, uint8_t chosen_channel, uint8_t encr_alg_id, uint8_t speec) { LOGP(DRR, LOGL_DEBUG, "MSC assign complete (do nothing).\n"); } static void msc_assign_fail(struct gsm_subscriber_connection *conn, uint8_t cause, uint8_t *rr_cause) { LOGP(DRR, LOGL_DEBUG, "MSC assign failure (do nothing).\n"); } static void msc_classmark_chg(struct gsm_subscriber_connection *conn, const uint8_t *cm2, uint8_t cm2_len, const uint8_t *cm3, uint8_t cm3_len) { struct gsm_subscriber *subscr = conn->subscr; if (subscr) { subscr->equipment.classmark2_len = cm2_len; memcpy(subscr->equipment.classmark2, cm2, cm2_len); if (cm3) { subscr->equipment.classmark3_len = cm3_len; memcpy(subscr->equipment.classmark3, cm3, cm3_len); } db_sync_equipment(&subscr->equipment); } } static void msc_ciph_m_compl(struct gsm_subscriber_connection *conn, struct msgb *msg, uint8_t alg_id) { gsm_cbfn *cb; DEBUGP(DRR, "CIPHERING MODE COMPLETE\n"); /* Safety check */ if (!conn->sec_operation) { DEBUGP(DRR, "No authentication/cipher operation in progress !!!\n"); return; } /* FIXME: check for MI (if any) */ /* Call back whatever was in progress (if anything) ... */ cb = conn->sec_operation->cb; if (cb) { cb(GSM_HOOK_RR_SECURITY, GSM_SECURITY_SUCCEEDED, NULL, conn, conn->sec_operation->cb_data); } /* Complete the operation */ release_security_operation(conn); } static struct bsc_api msc_handler = { .sapi_n_reject = msc_sapi_n_reject, .compl_l3 = msc_compl_l3, .dtap = msc_dtap, .clear_request = msc_clear_request, .assign_compl = msc_assign_compl, .assign_fail = msc_assign_fail, .classmark_chg = msc_classmark_chg, .cipher_mode_compl = msc_ciph_m_compl, }; struct bsc_api *msc_bsc_api() { return &msc_handler; } /* lchan release handling */ void msc_release_connection(struct gsm_subscriber_connection *conn) { /* skip when we are in release, e.g. due an error */ if (conn->in_release) return; /* skip releasing of silent calls as they have no transaction */ if (conn->silent_call) return; /* check if there is a pending operation */ if (conn->loc_operation || conn->sec_operation || conn->anch_operation) return; if (trans_has_conn(conn)) return; /* no more connections, asking to release the channel */ /* * We had stopped the LU expire timer T3212. Now we are about * to send the MS back to the idle state and this should lead * to restarting the timer. Set the new expiration time. */ if (conn->expire_timer_stopped) subscr_update_expire_lu(conn->subscr, conn->bts); conn->in_release = 1; gsm0808_clear(conn); subscr_con_free(conn); } openbsc-0.15.0/openbsc/src/libmsc/rrlp.c000066400000000000000000000054111265565154000200540ustar00rootroot00000000000000/* Radio Resource LCS (Location) Protocol, GMS TS 04.31 */ /* (C) 2009 by Harald Welte * * 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 . * */ #include #include #include #include /* RRLP msPositionReq, nsBased, * Accuracy=60, Method=gps, ResponseTime=2, oneSet */ static const uint8_t ms_based_pos_req[] = { 0x40, 0x01, 0x78, 0xa8 }; /* RRLP msPositionReq, msBasedPref, Accuracy=60, Method=gpsOrEOTD, ResponseTime=5, multipleSets */ static const uint8_t ms_pref_pos_req[] = { 0x40, 0x02, 0x79, 0x50 }; /* RRLP msPositionReq, msAssistedPref, Accuracy=60, Method=gpsOrEOTD, ResponseTime=5, multipleSets */ static const uint8_t ass_pref_pos_req[] = { 0x40, 0x03, 0x79, 0x50 }; static int send_rrlp_req(struct gsm_subscriber_connection *conn) { struct gsm_network *net = conn->bts->network; const uint8_t *req; switch (net->rrlp.mode) { case RRLP_MODE_MS_BASED: req = ms_based_pos_req; break; case RRLP_MODE_MS_PREF: req = ms_pref_pos_req; break; case RRLP_MODE_ASS_PREF: req = ass_pref_pos_req; break; case RRLP_MODE_NONE: default: return 0; } return gsm48_send_rr_app_info(conn, 0x00, sizeof(ms_based_pos_req), req); } static int subscr_sig_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct gsm_subscriber *subscr; struct gsm_subscriber_connection *conn; switch (signal) { case S_SUBSCR_ATTACHED: /* A subscriber has attached. */ subscr = signal_data; conn = connection_for_subscr(subscr); if (!conn) break; send_rrlp_req(conn); break; } return 0; } static int paging_sig_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct paging_signal_data *psig_data = signal_data; switch (signal) { case S_PAGING_SUCCEEDED: /* A subscriber has attached. */ send_rrlp_req(psig_data->conn); break; case S_PAGING_EXPIRED: break; } return 0; } void on_dso_load_rrlp(void) { osmo_signal_register_handler(SS_SUBSCR, subscr_sig_cb, NULL); osmo_signal_register_handler(SS_PAGING, paging_sig_cb, NULL); } openbsc-0.15.0/openbsc/src/libmsc/silent_call.c000066400000000000000000000077771265565154000214070ustar00rootroot00000000000000/* GSM silent call feature */ /* * (C) 2009 by Harald Welte * * 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include /* paging of the requested subscriber has completed */ static int paging_cb_silent(unsigned int hooknum, unsigned int event, struct msgb *msg, void *_conn, void *_data) { struct gsm_subscriber_connection *conn = _conn; struct scall_signal_data sigdata; int rc = 0; if (hooknum != GSM_HOOK_RR_PAGING) return -EINVAL; DEBUGP(DLSMS, "paging_cb_silent: "); sigdata.conn = conn; sigdata.data = _data; switch (event) { case GSM_PAGING_SUCCEEDED: DEBUGPC(DLSMS, "success, using Timeslot %u on ARFCN %u\n", conn->lchan->ts->nr, conn->lchan->ts->trx->arfcn); conn->silent_call = 1; /* increment lchan reference count */ osmo_signal_dispatch(SS_SCALL, S_SCALL_SUCCESS, &sigdata); break; case GSM_PAGING_EXPIRED: case GSM_PAGING_BUSY: case GSM_PAGING_OOM: DEBUGP(DLSMS, "expired\n"); osmo_signal_dispatch(SS_SCALL, S_SCALL_EXPIRED, &sigdata); break; default: rc = -EINVAL; break; } return rc; } /* receive a layer 3 message from a silent call */ int silent_call_rx(struct gsm_subscriber_connection *conn, struct msgb *msg) { /* FIXME: do something like sending it through a UDP port */ LOGP(DLSMS, LOGL_NOTICE, "Discarding L3 message from a silent call.\n"); return 0; } struct msg_match { uint8_t pdisc; uint8_t msg_type; }; /* list of messages that are handled inside OpenBSC, even in a silent call */ static const struct msg_match silent_call_accept[] = { { GSM48_PDISC_MM, GSM48_MT_MM_LOC_UPD_REQUEST }, { GSM48_PDISC_MM, GSM48_MT_MM_CM_SERV_REQ }, }; /* decide if we need to reroute a message as part of a silent call */ int silent_call_reroute(struct gsm_subscriber_connection *conn, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); uint8_t pdisc = gh->proto_discr & 0x0f; int i; /* if we're not part of a silent call, never reroute */ if (!conn->silent_call) return 0; /* check if we are a special message that is handled in openbsc */ for (i = 0; i < ARRAY_SIZE(silent_call_accept); i++) { if (silent_call_accept[i].pdisc == pdisc && silent_call_accept[i].msg_type == gh->msg_type) return 0; } /* otherwise, reroute */ LOGP(DLSMS, LOGL_INFO, "Rerouting L3 message from a silent call.\n"); return 1; } /* initiate a silent call with a given subscriber */ int gsm_silent_call_start(struct gsm_subscriber *subscr, void *data, int type) { struct subscr_request *req; req = subscr_request_channel(subscr, type, paging_cb_silent, data); return req != NULL; } /* end a silent call with a given subscriber */ int gsm_silent_call_stop(struct gsm_subscriber *subscr) { struct gsm_subscriber_connection *conn; conn = connection_for_subscr(subscr); if (!conn) return -EINVAL; /* did we actually establish a silent call for this guy? */ if (!conn->silent_call) return -EINVAL; DEBUGPC(DLSMS, "Stopping silent call using Timeslot %u on ARFCN %u\n", conn->lchan->ts->nr, conn->lchan->ts->trx->arfcn); conn->silent_call = 0; msc_release_connection(conn); return 0; } openbsc-0.15.0/openbsc/src/libmsc/smpp_openbsc.c000066400000000000000000000405401265565154000215670ustar00rootroot00000000000000/* OpenBSC SMPP 3.4 interface, SMSC-side implementation */ /* (C) 2012-2013 by Harald Welte * * 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 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "smpp_smsc.h" /*! \brief find gsm_subscriber for a given SMPP NPI/TON/Address */ static struct gsm_subscriber *subscr_by_dst(struct gsm_network *net, uint8_t npi, uint8_t ton, const char *addr) { struct gsm_subscriber *subscr = NULL; switch (npi) { case NPI_Land_Mobile_E212: subscr = subscr_get_by_imsi(net->subscr_group, addr); break; case NPI_ISDN_E163_E164: case NPI_Private: subscr = subscr_get_by_extension(net->subscr_group, addr); break; default: LOGP(DSMPP, LOGL_NOTICE, "Unsupported NPI: %u\n", npi); break; } /* tag the context in case we know it */ log_set_context(BSC_CTX_SUBSCR, subscr); return subscr; } /*! \brief find a TLV with given tag in list of libsmpp34 TLVs */ static struct tlv_t *find_tlv(struct tlv_t *head, uint16_t tag) { struct tlv_t *t; for (t = head; t != NULL; t = t->next) { if (t->tag == tag) return t; } return NULL; } /*! \brief convert from submit_sm_t to gsm_sms */ static int submit_to_sms(struct gsm_sms **psms, struct gsm_network *net, const struct submit_sm_t *submit) { struct gsm_subscriber *dest; struct gsm_sms *sms; struct tlv_t *t; const uint8_t *sms_msg; unsigned int sms_msg_len; int mode; dest = subscr_by_dst(net, submit->dest_addr_npi, submit->dest_addr_ton, (const char *)submit->destination_addr); if (!dest) { LOGP(DLSMS, LOGL_NOTICE, "SMPP SUBMIT-SM for unknown subscriber: " "%s (NPI=%u)\n", submit->destination_addr, submit->dest_addr_npi); return ESME_RINVDSTADR; } t = find_tlv(submit->tlv, TLVID_message_payload); if (t) { if (submit->sm_length) { /* ERROR: we cannot have both! */ LOGP(DLSMS, LOGL_ERROR, "SMPP Cannot have payload in " "TLV _and_ in the header\n"); subscr_put(dest); return ESME_ROPTPARNOTALLWD; } sms_msg = t->value.octet; sms_msg_len = t->length; } else if (submit->sm_length > 0 && submit->sm_length < 255) { sms_msg = submit->short_message; sms_msg_len = submit->sm_length; } else { LOGP(DLSMS, LOGL_ERROR, "SMPP neither message payload nor valid sm_length.\n"); subscr_put(dest); return ESME_RINVPARLEN; } sms = sms_alloc(); sms->source = SMS_SOURCE_SMPP; sms->smpp.sequence_nr = submit->sequence_number; /* fill in the destination address */ sms->receiver = dest; sms->dst.ton = submit->dest_addr_ton; sms->dst.npi = submit->dest_addr_npi; strncpy(sms->dst.addr, dest->extension, sizeof(sms->dst.addr)-1); /* fill in the source address */ sms->src.ton = submit->source_addr_ton; sms->src.npi = submit->source_addr_npi; strncpy(sms->src.addr, (char *)submit->source_addr, sizeof(sms->src.addr)-1); if (submit->esm_class & 0x40) sms->ud_hdr_ind = 1; if (submit->esm_class & 0x80) { sms->reply_path_req = 1; #warning Implement reply path } if (submit->data_coding == 0x00 || /* SMSC default */ submit->data_coding == 0x01) { /* GSM default alphabet */ sms->data_coding_scheme = GSM338_DCS_1111_7BIT; mode = MODE_7BIT; } else if ((submit->data_coding & 0xFC) == 0xF0) { /* 03.38 DCS default */ /* pass DCS 1:1 through from SMPP to GSM */ sms->data_coding_scheme = submit->data_coding; mode = MODE_7BIT; } else if (submit->data_coding == 0x02 || submit->data_coding == 0x04) { /* 8-bit binary */ sms->data_coding_scheme = GSM338_DCS_1111_8BIT_DATA; mode = MODE_8BIT; } else if ((submit->data_coding & 0xFC) == 0xF4) { /* 03.38 DCS 8bit */ /* pass DCS 1:1 through from SMPP to GSM */ sms->data_coding_scheme = submit->data_coding; mode = MODE_8BIT; } else if (submit->data_coding == 0x08) { /* UCS-2 */ sms->data_coding_scheme = (2 << 2); mode = MODE_8BIT; } else { sms_free(sms); LOGP(DLSMS, LOGL_ERROR, "SMPP Unknown Data Coding 0x%02x\n", submit->data_coding); return ESME_RUNKNOWNERR; } if (mode == MODE_7BIT) { uint8_t ud_len = 0, padbits = 0; sms->data_coding_scheme = GSM338_DCS_1111_7BIT; if (sms->ud_hdr_ind) { ud_len = *sms_msg + 1; printf("copying %u bytes user data...\n", ud_len); memcpy(sms->user_data, sms_msg, OSMO_MIN(ud_len, sizeof(sms->user_data))); sms_msg += ud_len; sms_msg_len -= ud_len; padbits = 7 - (ud_len % 7); } gsm_septets2octets(sms->user_data+ud_len, sms_msg, sms_msg_len, padbits); sms->user_data_len = (ud_len*8 + padbits)/7 + sms_msg_len;/* SEPTETS */ /* FIXME: sms->text */ } else { memcpy(sms->user_data, sms_msg, sms_msg_len); sms->user_data_len = sms_msg_len; } *psms = sms; return ESME_ROK; } /*! \brief handle incoming libsmpp34 ssubmit_sm_t from remote ESME */ int handle_smpp_submit(struct osmo_esme *esme, struct submit_sm_t *submit, struct submit_sm_resp_t *submit_r) { struct gsm_sms *sms; struct gsm_network *net = esme->smsc->priv; struct sms_signal_data sig; int rc = -1; rc = submit_to_sms(&sms, net, submit); if (rc != ESME_ROK) { submit_r->command_status = rc; return 0; } smpp_esme_get(esme); sms->smpp.esme = esme; sms->protocol_id = submit->protocol_id; switch (submit->esm_class & 3) { case 0: /* default */ case 1: /* datagram */ case 3: /* store-and-forward */ rc = db_sms_store(sms); sms_free(sms); sms = NULL; if (rc < 0) { LOGP(DLSMS, LOGL_ERROR, "SMPP SUBMIT-SM: Unable to " "store SMS in database\n"); submit_r->command_status = ESME_RSYSERR; return 0; } strcpy((char *)submit_r->message_id, "msg_id_not_implemented"); LOGP(DLSMS, LOGL_INFO, "SMPP SUBMIT-SM: Stored in DB\n"); memset(&sig, 0, sizeof(sig)); osmo_signal_dispatch(SS_SMS, S_SMS_SUBMITTED, &sig); rc = 0; break; case 2: /* forward (i.e. transaction) mode */ LOGP(DLSMS, LOGL_DEBUG, "SMPP SUBMIT-SM: Forwarding in " "real time (Transaction/Forward mode)\n"); sms->smpp.transaction_mode = 1; gsm411_send_sms_subscr(sms->receiver, sms); rc = 1; /* don't send any response yet */ break; } return rc; } static void alert_all_esme(struct smsc *smsc, struct gsm_subscriber *subscr, uint8_t smpp_avail_status) { struct osmo_esme *esme; llist_for_each_entry(esme, &smsc->esme_list, list) { /* we currently send an alert notification to each ESME that is * connected, and do not require a (non-existant) delivery * pending flag to be set before, FIXME: make this VTY * configurable */ if (esme->acl && esme->acl->deliver_src_imsi) { smpp_tx_alert(esme, TON_Subscriber_Number, NPI_Land_Mobile_E212, subscr->imsi, 0); } else { smpp_tx_alert(esme, TON_Network_Specific, NPI_ISDN_E163_E164, subscr->extension, 0); } } } /*! \brief signal handler for status of attempted SMS deliveries */ static int smpp_sms_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct sms_signal_data *sig_sms = signal_data; struct gsm_sms *sms = sig_sms->sms; struct smsc *smsc = handler_data; int rc = 0; if (!sms) return 0; if (sms->source != SMS_SOURCE_SMPP) return 0; switch (signal) { case S_SMS_MEM_EXCEEDED: /* fall-through: There is no ESME_Rxxx result code to * indicate a MEMORY EXCEEDED in transaction mode back * to the ESME */ case S_SMS_UNKNOWN_ERROR: if (sms->smpp.transaction_mode) { /* Send back the SUBMIT-SM response with apropriate error */ LOGP(DLSMS, LOGL_INFO, "SMPP SUBMIT-SM: Error\n"); rc = smpp_tx_submit_r(sms->smpp.esme, sms->smpp.sequence_nr, ESME_RDELIVERYFAILURE, sms->smpp.msg_id); } break; case S_SMS_DELIVERED: /* SMS layer tells us the delivery has been completed */ if (sms->smpp.transaction_mode) { /* Send back the SUBMIT-SM response */ LOGP(DLSMS, LOGL_INFO, "SMPP SUBMIT-SM: Success\n"); rc = smpp_tx_submit_r(sms->smpp.esme, sms->smpp.sequence_nr, ESME_ROK, sms->smpp.msg_id); } break; case S_SMS_SMMA: if (!sig_sms->trans || !sig_sms->trans->subscr) { /* SMMA without a subscriber? strange... */ LOGP(DLSMS, LOGL_NOTICE, "SMMA without subscriber?\n"); break; } /* There's no real 1:1 match for SMMA in SMPP. However, * an ALERT NOTIFICATION seems to be the most logical * choice */ alert_all_esme(smsc, sig_sms->trans->subscr, 0); break; } return rc; } /*! \brief signal handler for subscriber related signals */ static int smpp_subscr_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct gsm_subscriber *subscr = signal_data; struct smsc *smsc = handler_data; uint8_t smpp_avail_status; /* determine the smpp_avail_status depending on attach/detach */ switch (signal) { case S_SUBSCR_ATTACHED: smpp_avail_status = 0; break; case S_SUBSCR_DETACHED: smpp_avail_status = 2; break; default: return 0; } alert_all_esme(smsc, subscr, smpp_avail_status); return 0; } /* GSM 03.38 6.2.1 Character expanding (no decode!) */ static int gsm_7bit_expand(char *text, const uint8_t *user_data, uint8_t septet_l, uint8_t ud_hdr_ind) { int i = 0; int shift = 0; uint8_t c; /* skip the user data header */ if (ud_hdr_ind) { /* get user data header length + 1 (for the 'user data header length'-field) */ shift = ((user_data[0] + 1) * 8) / 7; if ((((user_data[0] + 1) * 8) % 7) != 0) shift++; septet_l = septet_l - shift; } for (i = 0; i < septet_l; i++) { c = ((user_data[((i + shift) * 7 + 7) >> 3] << (7 - (((i + shift) * 7 + 7) & 7))) | (user_data[((i + shift) * 7) >> 3] >> (((i + shift) * 7) & 7))) & 0x7f; *(text++) = c; } *text = '\0'; return i; } /* FIXME: libsmpp34 helpers, they should be part of libsmpp34! */ void append_tlv(tlv_t **req_tlv, uint16_t tag, const uint8_t *data, uint16_t len) { tlv_t tlv; memset(&tlv, 0, sizeof(tlv)); tlv.tag = tag; tlv.length = len; memcpy(tlv.value.octet, data, tlv.length); build_tlv(req_tlv, &tlv); } void append_tlv_u8(tlv_t **req_tlv, uint16_t tag, uint8_t val) { tlv_t tlv; memset(&tlv, 0, sizeof(tlv)); tlv.tag = tag; tlv.length = 1; tlv.value.val08 = val; build_tlv(req_tlv, &tlv); } void append_tlv_u16(tlv_t **req_tlv, uint16_t tag, uint16_t val) { tlv_t tlv; memset(&tlv, 0, sizeof(tlv)); tlv.tag = tag; tlv.length = 2; tlv.value.val16 = htons(val); build_tlv(req_tlv, &tlv); } /* Append the Osmocom vendor-specific additional TLVs to a SMPP msg */ static void append_osmo_tlvs(tlv_t **req_tlv, const struct gsm_lchan *lchan) { int idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep), lchan->meas_rep_idx, 1); const struct gsm_meas_rep *mr = &lchan->meas_rep[idx]; const struct gsm_meas_rep_unidir *ul_meas = &mr->ul; const struct gsm_meas_rep_unidir *dl_meas = &mr->dl; /* Osmocom vendor-specific SMPP34 extensions */ append_tlv_u16(req_tlv, TLVID_osmo_arfcn, lchan->ts->trx->arfcn); if (mr->flags & MEAS_REP_F_MS_L1) { uint8_t ms_dbm; append_tlv_u8(req_tlv, TLVID_osmo_ta, mr->ms_l1.ta); ms_dbm = ms_pwr_dbm(lchan->ts->trx->bts->band, mr->ms_l1.pwr); append_tlv_u8(req_tlv, TLVID_osmo_ms_l1_txpwr, ms_dbm); } else if (mr->flags & MEAS_REP_F_MS_TO) append_tlv_u8(req_tlv, TLVID_osmo_ta, mr->ms_timing_offset); append_tlv_u16(req_tlv, TLVID_osmo_rxlev_ul, rxlev2dbm(ul_meas->full.rx_lev)); append_tlv_u8(req_tlv, TLVID_osmo_rxqual_ul, ul_meas->full.rx_qual); if (mr->flags & MEAS_REP_F_DL_VALID) { append_tlv_u16(req_tlv, TLVID_osmo_rxlev_dl, rxlev2dbm(dl_meas->full.rx_lev)); append_tlv_u8(req_tlv, TLVID_osmo_rxqual_dl, dl_meas->full.rx_qual); } if (lchan->conn && lchan->conn->subscr) { struct gsm_subscriber *subscr = lchan->conn->subscr; size_t imei_len = strlen(subscr->equipment.imei); if (imei_len) append_tlv(req_tlv, TLVID_osmo_imei, (uint8_t *)subscr->equipment.imei, imei_len+1); } } static int deliver_to_esme(struct osmo_esme *esme, struct gsm_sms *sms, struct gsm_subscriber_connection *conn) { struct deliver_sm_t deliver; uint8_t dcs; int mode; memset(&deliver, 0, sizeof(deliver)); deliver.command_length = 0; deliver.command_id = DELIVER_SM; deliver.command_status = ESME_ROK; strcpy((char *)deliver.service_type, "CMT"); if (esme->acl && esme->acl->deliver_src_imsi) { deliver.source_addr_ton = TON_Subscriber_Number; deliver.source_addr_npi = NPI_Land_Mobile_E212; snprintf((char *)deliver.source_addr, sizeof(deliver.source_addr), "%s", conn->subscr->imsi); } else { deliver.source_addr_ton = TON_Network_Specific; deliver.source_addr_npi = NPI_ISDN_E163_E164; snprintf((char *)deliver.source_addr, sizeof(deliver.source_addr), "%s", conn->subscr->extension); } deliver.dest_addr_ton = sms->dst.ton; deliver.dest_addr_npi = sms->dst.npi; memcpy(deliver.destination_addr, sms->dst.addr, sizeof(deliver.destination_addr)); deliver.esm_class = 1; /* datagram mode */ if (sms->ud_hdr_ind) deliver.esm_class |= 0x40; if (sms->reply_path_req) deliver.esm_class |= 0x80; deliver.protocol_id = sms->protocol_id; deliver.priority_flag = 0; deliver.registered_delivery = 0; /* Figure out SMPP DCS from TP-DCS */ dcs = sms->data_coding_scheme; if (smpp_determine_scheme(dcs, &deliver.data_coding, &mode) == -1) return -1; /* Transparently pass on DCS via SMPP if requested */ if (esme->acl && esme->acl->dcs_transparent) deliver.data_coding = dcs; if (mode == MODE_7BIT) { uint8_t *dst = deliver.short_message; /* SMPP has this strange notion of putting 7bit SMS in * an octet-aligned mode */ if (sms->ud_hdr_ind) { /* length (bytes) of UDH inside UD */ uint8_t udh_len = sms->user_data[0] + 1; /* copy over the UDH */ memcpy(dst, sms->user_data, udh_len); dst += udh_len; deliver.sm_length = udh_len; } /* add decoded text */ deliver.sm_length += gsm_7bit_expand((char *)dst, sms->user_data, sms->user_data_len, sms->ud_hdr_ind); } else { deliver.sm_length = sms->user_data_len; memcpy(deliver.short_message, sms->user_data, deliver.sm_length); deliver.sm_length = sms->user_data_len; memcpy(deliver.short_message, sms->user_data, deliver.sm_length); } if (esme->acl && esme->acl->osmocom_ext && conn->lchan) append_osmo_tlvs(&deliver.tlv, conn->lchan); return smpp_tx_deliver(esme, &deliver); } static struct smsc *g_smsc; int smpp_route_smpp_first(struct gsm_sms *sms, struct gsm_subscriber_connection *conn) { return g_smsc->smpp_first; } int smpp_try_deliver(struct gsm_sms *sms, struct gsm_subscriber_connection *conn) { struct osmo_esme *esme; struct osmo_smpp_addr dst; memset(&dst, 0, sizeof(dst)); dst.ton = sms->dst.ton; dst.npi = sms->dst.npi; memcpy(dst.addr, sms->dst.addr, sizeof(dst.addr)); esme = smpp_route(g_smsc, &dst); if (!esme) return 1; /* unknown subscriber */ return deliver_to_esme(esme, sms, conn); } struct smsc *smsc_from_vty(struct vty *v) { /* FIXME: this is ugly */ return g_smsc; } /*! \brief Initialize the OpenBSC SMPP interface */ int smpp_openbsc_init(void *ctx, uint16_t port) { struct smsc *smsc = talloc_zero(ctx, struct smsc); int rc; rc = smpp_smsc_init(smsc, port); if (rc < 0) talloc_free(smsc); osmo_signal_register_handler(SS_SMS, smpp_sms_cb, smsc); osmo_signal_register_handler(SS_SUBSCR, smpp_subscr_cb, smsc); g_smsc = smsc; smpp_vty_init(); return rc; } void smpp_openbsc_set_net(struct gsm_network *net) { g_smsc->priv = net; } openbsc-0.15.0/openbsc/src/libmsc/smpp_smsc.c000066400000000000000000000627301265565154000211100ustar00rootroot00000000000000/* SMPP 3.4 interface, SMSC-side implementation */ /* (C) 2012 by Harald Welte * * 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 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "smpp_smsc.h" #include #include /*! \brief Ugly wrapper. libsmpp34 should do this itself! */ #define SMPP34_UNPACK(rc, type, str, data, len) \ memset(str, 0, sizeof(*str)); \ rc = smpp34_unpack(type, str, data, len) enum emse_bind { ESME_BIND_RX = 0x01, ESME_BIND_TX = 0x02, }; const struct value_string smpp_status_strs[] = { { ESME_ROK, "No Error" }, { ESME_RINVMSGLEN, "Message Length is invalid" }, { ESME_RINVCMDLEN, "Command Length is invalid" }, { ESME_RINVCMDID, "Invalid Command ID" }, { ESME_RINVBNDSTS, "Incorrect BIND Status for given command" }, { ESME_RALYBND, "ESME Already in Bound State" }, { ESME_RINVPRTFLG, "Invalid Priority Flag" }, { ESME_RINVREGDLVFLG, "Invalid Registered Delivery Flag" }, { ESME_RSYSERR, "System Error" }, { ESME_RINVSRCADR, "Invalid Source Address" }, { ESME_RINVDSTADR, "Invalid Destination Address" }, { ESME_RINVMSGID, "Message ID is invalid" }, { ESME_RBINDFAIL, "Bind failed" }, { ESME_RINVPASWD, "Invalid Password" }, { ESME_RINVSYSID, "Invalid System ID" }, { ESME_RCANCELFAIL, "Cancel SM Failed" }, { ESME_RREPLACEFAIL, "Replace SM Failed" }, { ESME_RMSGQFUL, "Message Queue Full" }, { ESME_RINVSERTYP, "Invalid Service Type" }, { ESME_RINVNUMDESTS, "Invalid number of destinations" }, { ESME_RINVDLNAME, "Invalid Distribution List name" }, { ESME_RINVDESTFLAG, "Destination flag is invalid" }, { ESME_RINVSUBREP, "Invalid submit with replace request" }, { ESME_RINVESMCLASS, "Invalid esm_class field data" }, { ESME_RCNTSUBDL, "Cannot Submit to Distribution List" }, { ESME_RSUBMITFAIL, "submit_sm or submit_multi failed" }, { ESME_RINVSRCTON, "Invalid Source address TON" }, { ESME_RINVSRCNPI, "Invalid Sourec address NPI" }, { ESME_RINVDSTTON, "Invalid Destination address TON" }, { ESME_RINVDSTNPI, "Invalid Desetination address NPI" }, { ESME_RINVSYSTYP, "Invalid system_type field" }, { ESME_RINVREPFLAG, "Invalid replace_if_present field" }, { ESME_RINVNUMMSGS, "Invalid number of messages" }, { ESME_RTHROTTLED, "Throttling error (ESME has exceeded message limits)" }, { ESME_RINVSCHED, "Invalid Scheduled Delivery Time" }, { ESME_RINVEXPIRY, "Invalid message validity period (Expiry time)" }, { ESME_RINVDFTMSGID, "Predefined Message Invalid or Not Found" }, { ESME_RX_T_APPN, "ESME Receiver Temporary App Error Code" }, { ESME_RX_P_APPN, "ESME Receiver Permanent App Error Code" }, { ESME_RX_R_APPN, "ESME Receiver Reject Message Error Code" }, { ESME_RQUERYFAIL, "query_sm request failed" }, { ESME_RINVOPTPARSTREAM,"Error in the optional part of the PDU Body" }, { ESME_ROPTPARNOTALLWD, "Optional Parameter not allowed" }, { ESME_RINVPARLEN, "Invalid Parameter Length" }, { ESME_RMISSINGOPTPARAM,"Expected Optional Parameter missing" }, { ESME_RINVOPTPARAMVAL, "Invalid Optional Parameter Value" }, { ESME_RDELIVERYFAILURE,"Delivery Failure (used for data_sm_resp)" }, { ESME_RUNKNOWNERR, "Unknown Error" }, { 0, NULL } }; /*! \brief compare if two SMPP addresses are equal */ int smpp_addr_eq(const struct osmo_smpp_addr *a, const struct osmo_smpp_addr *b) { if (a->ton == b->ton && a->npi == b->npi && !strcmp(a->addr, b->addr)) return 1; return 0; } struct osmo_smpp_acl *smpp_acl_by_system_id(struct smsc *smsc, const char *sys_id) { struct osmo_smpp_acl *acl; llist_for_each_entry(acl, &smsc->acl_list, list) { if (!strcmp(acl->system_id, sys_id)) return acl; } return NULL; } struct osmo_smpp_acl *smpp_acl_alloc(struct smsc *smsc, const char *sys_id) { struct osmo_smpp_acl *acl; if (strlen(sys_id) > SMPP_SYS_ID_LEN) return NULL; if (smpp_acl_by_system_id(smsc, sys_id)) return NULL; acl = talloc_zero(smsc, struct osmo_smpp_acl); if (!acl) return NULL; acl->smsc = smsc; strcpy(acl->system_id, sys_id); INIT_LLIST_HEAD(&acl->route_list); llist_add_tail(&acl->list, &smsc->acl_list); return acl; } void smpp_acl_delete(struct osmo_smpp_acl *acl) { struct osmo_smpp_route *r, *r2; llist_del(&acl->list); /* kill any active ESMEs */ if (acl->esme) { struct osmo_esme *esme = acl->esme; osmo_fd_unregister(&esme->wqueue.bfd); close(esme->wqueue.bfd.fd); esme->wqueue.bfd.fd = -1; esme->acl = NULL; smpp_esme_put(esme); } /* delete all routes for this ACL */ llist_for_each_entry_safe(r, r2, &acl->route_list, list) { llist_del(&r->list); llist_del(&r->global_list); talloc_free(r); } talloc_free(acl); } static struct osmo_smpp_route *route_alloc(struct osmo_smpp_acl *acl) { struct osmo_smpp_route *r; r = talloc_zero(acl, struct osmo_smpp_route); if (!r) return NULL; llist_add_tail(&r->list, &acl->route_list); llist_add_tail(&r->global_list, &acl->smsc->route_list); return r; } int smpp_route_pfx_add(struct osmo_smpp_acl *acl, const struct osmo_smpp_addr *pfx) { struct osmo_smpp_route *r; llist_for_each_entry(r, &acl->route_list, list) { if (r->type == SMPP_ROUTE_PREFIX && smpp_addr_eq(&r->u.prefix, pfx)) return -EEXIST; } r = route_alloc(acl); if (!r) return -ENOMEM; r->type = SMPP_ROUTE_PREFIX; r->acl = acl; memcpy(&r->u.prefix, pfx, sizeof(r->u.prefix)); return 0; } int smpp_route_pfx_del(struct osmo_smpp_acl *acl, const struct osmo_smpp_addr *pfx) { struct osmo_smpp_route *r, *r2; llist_for_each_entry_safe(r, r2, &acl->route_list, list) { if (r->type == SMPP_ROUTE_PREFIX && smpp_addr_eq(&r->u.prefix, pfx)) { llist_del(&r->list); talloc_free(r); return 0; } } return -ENODEV; } /*! \brief increaes the use/reference count */ void smpp_esme_get(struct osmo_esme *esme) { esme->use++; } static void esme_destroy(struct osmo_esme *esme) { osmo_wqueue_clear(&esme->wqueue); if (esme->wqueue.bfd.fd >= 0) { osmo_fd_unregister(&esme->wqueue.bfd); close(esme->wqueue.bfd.fd); } llist_del(&esme->list); talloc_free(esme); } static uint32_t esme_inc_seq_nr(struct osmo_esme *esme) { esme->own_seq_nr++; if (esme->own_seq_nr > 0x7fffffff) esme->own_seq_nr = 1; return esme->own_seq_nr; } /*! \brief decrease the use/reference count, free if it is 0 */ void smpp_esme_put(struct osmo_esme *esme) { esme->use--; if (esme->use <= 0) esme_destroy(esme); } /*! \brief try to find a SMPP route (ESME) for given destination */ struct osmo_esme * smpp_route(const struct smsc *smsc, const struct osmo_smpp_addr *dest) { struct osmo_smpp_route *r; struct osmo_smpp_acl *acl = NULL; DEBUGP(DSMPP, "Looking up route for (%u/%u/%s)\n", dest->ton, dest->npi, dest->addr); /* search for a specific route */ llist_for_each_entry(r, &smsc->route_list, global_list) { switch (r->type) { case SMPP_ROUTE_PREFIX: DEBUGP(DSMPP, "Checking prefix route (%u/%u/%s)->%s\n", r->u.prefix.ton, r->u.prefix.npi, r->u.prefix.addr, r->acl->system_id); if (r->u.prefix.ton == dest->ton && r->u.prefix.npi == dest->npi && !strncmp(r->u.prefix.addr, dest->addr, strlen(r->u.prefix.addr))) { DEBUGP(DSMPP, "Found prefix route ACL\n"); acl = r->acl; } break; default: break; } if (acl) break; } if (!acl) { /* check for default route */ if (smsc->def_route) { DEBUGP(DSMPP, "Using existing default route\n"); acl = smsc->def_route; } } if (acl && acl->esme) { struct osmo_esme *esme; DEBUGP(DSMPP, "ACL even has ESME, we can route to it!\n"); esme = acl->esme; if (esme->bind_flags & ESME_BIND_RX) return esme; else LOGP(DSMPP, LOGL_NOTICE, "[%s] is matching route, " "but not bound for Rx, discarding MO SMS\n", esme->system_id); } return NULL; } /*! \brief initialize the libsmpp34 data structure for a response */ #define INIT_RESP(type, resp, req) { \ memset((resp), 0, sizeof(*(resp))); \ (resp)->command_length = 0; \ (resp)->command_id = type; \ (resp)->command_status = ESME_ROK; \ (resp)->sequence_number = (req)->sequence_number; \ } /*! \brief pack a libsmpp34 data strcutrure and send it to the ESME */ #define PACK_AND_SEND(esme, ptr) pack_and_send(esme, (ptr)->command_id, ptr) static int pack_and_send(struct osmo_esme *esme, uint32_t type, void *ptr) { struct msgb *msg = msgb_alloc(4096, "SMPP_Tx"); int rc, rlen; if (!msg) return -ENOMEM; rc = smpp34_pack(type, msg->tail, msgb_tailroom(msg), &rlen, ptr); if (rc != 0) { LOGP(DSMPP, LOGL_ERROR, "[%s] Error during smpp34_pack(): %s\n", esme->system_id, smpp34_strerror); msgb_free(msg); return -EINVAL; } msgb_put(msg, rlen); if (osmo_wqueue_enqueue(&esme->wqueue, msg) != 0) { LOGP(DSMPP, LOGL_ERROR, "[%s] Write queue full. Dropping message\n", esme->system_id); msgb_free(msg); return -EAGAIN; } return 0; } /*! \brief transmit a generic NACK to a remote ESME */ static int smpp_tx_gen_nack(struct osmo_esme *esme, uint32_t seq, uint32_t status) { struct generic_nack_t nack; char buf[SMALL_BUFF]; nack.command_length = 0; nack.command_id = GENERIC_NACK; nack.sequence_number = seq; nack.command_status = status; LOGP(DSMPP, LOGL_ERROR, "[%s] Tx GENERIC NACK: %s\n", esme->system_id, str_command_status(status, buf)); return PACK_AND_SEND(esme, &nack); } /*! \brief retrieve SMPP command ID from a msgb */ static inline uint32_t smpp_msgb_cmdid(struct msgb *msg) { uint8_t *tmp = msgb_data(msg) + 4; return ntohl(*(uint32_t *)tmp); } /*! \brief retrieve SMPP sequence number from a msgb */ static inline uint32_t smpp_msgb_seq(struct msgb *msg) { uint8_t *tmp = msgb_data(msg); return ntohl(*(uint32_t *)tmp); } /*! \brief handle an incoming SMPP generic NACK */ static int smpp_handle_gen_nack(struct osmo_esme *esme, struct msgb *msg) { struct generic_nack_t nack; char buf[SMALL_BUFF]; int rc; SMPP34_UNPACK(rc, GENERIC_NACK, &nack, msgb_data(msg), msgb_length(msg)); if (rc < 0) { LOGP(DSMPP, LOGL_ERROR, "[%s] Error in smpp34_unpack():%s\n", esme->system_id, smpp34_strerror); return rc; } LOGP(DSMPP, LOGL_ERROR, "[%s] Rx GENERIC NACK: %s\n", esme->system_id, str_command_status(nack.command_status, buf)); return 0; } static int _process_bind(struct osmo_esme *esme, uint8_t if_version, uint32_t bind_flags, const char *sys_id, const char *passwd) { struct osmo_smpp_acl *acl; if (if_version != SMPP_VERSION) return ESME_RSYSERR; if (esme->bind_flags) return ESME_RALYBND; esme->smpp_version = if_version; snprintf(esme->system_id, sizeof(esme->system_id), "%s", sys_id); acl = smpp_acl_by_system_id(esme->smsc, esme->system_id); if (!esme->smsc->accept_all) { if (!acl) { /* This system is unknown */ return ESME_RINVSYSID; } else { if (strlen(acl->passwd) && strcmp(acl->passwd, passwd)) { return ESME_RINVPASWD; } } } if (acl) { esme->acl = acl; acl->esme = esme; } esme->bind_flags = bind_flags; return ESME_ROK; } /*! \brief handle an incoming SMPP BIND RECEIVER */ static int smpp_handle_bind_rx(struct osmo_esme *esme, struct msgb *msg) { struct bind_receiver_t bind; struct bind_receiver_resp_t bind_r; int rc; SMPP34_UNPACK(rc, BIND_RECEIVER, &bind, msgb_data(msg), msgb_length(msg)); if (rc < 0) { LOGP(DSMPP, LOGL_ERROR, "[%s] Error in smpp34_unpack():%s\n", esme->system_id, smpp34_strerror); return rc; } INIT_RESP(BIND_TRANSMITTER_RESP, &bind_r, &bind); LOGP(DSMPP, LOGL_INFO, "[%s] Rx BIND Rx from (Version %02x)\n", bind.system_id, bind.interface_version); rc = _process_bind(esme, bind.interface_version, ESME_BIND_RX, (const char *)bind.system_id, (const char *)bind.password); bind_r.command_status = rc; return PACK_AND_SEND(esme, &bind_r); } /*! \brief handle an incoming SMPP BIND TRANSMITTER */ static int smpp_handle_bind_tx(struct osmo_esme *esme, struct msgb *msg) { struct bind_transmitter_t bind; struct bind_transmitter_resp_t bind_r; struct tlv_t tlv; int rc; SMPP34_UNPACK(rc, BIND_TRANSMITTER, &bind, msgb_data(msg), msgb_length(msg)); if (rc < 0) { LOGP(DSMPP, LOGL_ERROR, "[%s] Error in smpp34_unpack():%s\n", esme->system_id, smpp34_strerror); return rc; } INIT_RESP(BIND_TRANSMITTER_RESP, &bind_r, &bind); LOGP(DSMPP, LOGL_INFO, "[%s] Rx BIND Tx (Version %02x)\n", bind.system_id, bind.interface_version); rc = _process_bind(esme, bind.interface_version, ESME_BIND_TX, (const char *)bind.system_id, (const char *)bind.password); bind_r.command_status = rc; /* build response */ snprintf((char *)bind_r.system_id, sizeof(bind_r.system_id), "%s", esme->smsc->system_id); /* add interface version TLV */ tlv.tag = TLVID_sc_interface_version; tlv.length = sizeof(uint8_t); tlv.value.val16 = esme->smpp_version; build_tlv(&bind_r.tlv, &tlv); return PACK_AND_SEND(esme, &bind_r); } /*! \brief handle an incoming SMPP BIND TRANSCEIVER */ static int smpp_handle_bind_trx(struct osmo_esme *esme, struct msgb *msg) { struct bind_transceiver_t bind; struct bind_transceiver_resp_t bind_r; int rc; SMPP34_UNPACK(rc, BIND_TRANSCEIVER, &bind, msgb_data(msg), msgb_length(msg)); if (rc < 0) { LOGP(DSMPP, LOGL_ERROR, "[%s] Error in smpp34_unpack():%s\n", esme->system_id, smpp34_strerror); return rc; } INIT_RESP(BIND_TRANSCEIVER_RESP, &bind_r, &bind); LOGP(DSMPP, LOGL_INFO, "[%s] Rx BIND Trx (Version %02x)\n", bind.system_id, bind.interface_version); rc = _process_bind(esme, bind.interface_version, ESME_BIND_RX|ESME_BIND_TX, (const char *)bind.system_id, (const char *)bind.password); bind_r.command_status = rc; return PACK_AND_SEND(esme, &bind_r); } /*! \brief handle an incoming SMPP UNBIND */ static int smpp_handle_unbind(struct osmo_esme *esme, struct msgb *msg) { struct unbind_t unbind; struct unbind_resp_t unbind_r; int rc; SMPP34_UNPACK(rc, UNBIND, &unbind, msgb_data(msg), msgb_length(msg)); if (rc < 0) { LOGP(DSMPP, LOGL_ERROR, "[%s] Error in smpp34_unpack():%s\n", esme->system_id, smpp34_strerror); return rc; } INIT_RESP(UNBIND_RESP, &unbind_r, &unbind); LOGP(DSMPP, LOGL_INFO, "[%s] Rx UNBIND\n", esme->system_id); if (esme->bind_flags == 0) { unbind_r.command_status = ESME_RINVBNDSTS; goto err; } esme->bind_flags = 0; err: return PACK_AND_SEND(esme, &unbind_r); } /*! \brief handle an incoming SMPP ENQUIRE LINK */ static int smpp_handle_enq_link(struct osmo_esme *esme, struct msgb *msg) { struct enquire_link_t enq; struct enquire_link_resp_t enq_r; int rc; SMPP34_UNPACK(rc, ENQUIRE_LINK, &enq, msgb_data(msg), msgb_length(msg)); if (rc < 0) { LOGP(DSMPP, LOGL_ERROR, "[%s] Error in smpp34_unpack():%s\n", esme->system_id, smpp34_strerror); return rc; } LOGP(DSMPP, LOGL_DEBUG, "[%s] Rx Enquire Link\n", esme->system_id); INIT_RESP(ENQUIRE_LINK_RESP, &enq_r, &enq); LOGP(DSMPP, LOGL_DEBUG, "[%s] Tx Enquire Link Response\n", esme->system_id); return PACK_AND_SEND(esme, &enq_r); } /*! \brief send a SUBMIT-SM RESPONSE to a remote ESME */ int smpp_tx_submit_r(struct osmo_esme *esme, uint32_t sequence_nr, uint32_t command_status, char *msg_id) { struct submit_sm_resp_t submit_r; memset(&submit_r, 0, sizeof(submit_r)); submit_r.command_length = 0; submit_r.command_id = SUBMIT_SM_RESP; submit_r.command_status = command_status; submit_r.sequence_number= sequence_nr; snprintf((char *) submit_r.message_id, sizeof(submit_r.message_id), "%s", msg_id); return PACK_AND_SEND(esme, &submit_r); } static const struct value_string smpp_avail_strs[] = { { 0, "Available" }, { 1, "Denied" }, { 2, "Unavailable" }, { 0, NULL } }; /*! \brief send an ALERT_NOTIFICATION to a remote ESME */ int smpp_tx_alert(struct osmo_esme *esme, uint8_t ton, uint8_t npi, const char *addr, uint8_t avail_status) { struct alert_notification_t alert; struct tlv_t tlv; memset(&alert, 0, sizeof(alert)); alert.command_length = 0; alert.command_id = ALERT_NOTIFICATION; alert.command_status = ESME_ROK; alert.sequence_number = esme_inc_seq_nr(esme); alert.source_addr_ton = ton; alert.source_addr_npi = npi; snprintf((char *)alert.source_addr, sizeof(alert.source_addr), "%s", addr); tlv.tag = TLVID_ms_availability_status; tlv.length = sizeof(uint8_t); tlv.value.val08 = avail_status; build_tlv(&alert.tlv, &tlv); LOGP(DSMPP, LOGL_DEBUG, "[%s] Tx ALERT_NOTIFICATION (%s/%u/%u): %s\n", esme->system_id, alert.source_addr, alert.source_addr_ton, alert.source_addr_npi, get_value_string(smpp_avail_strs, avail_status)); return PACK_AND_SEND(esme, &alert); } /* \brief send a DELIVER-SM message to given ESME */ int smpp_tx_deliver(struct osmo_esme *esme, struct deliver_sm_t *deliver) { deliver->sequence_number = esme_inc_seq_nr(esme); return PACK_AND_SEND(esme, deliver); } /*! \brief handle an incoming SMPP DELIVER-SM RESPONSE */ static int smpp_handle_deliver_resp(struct osmo_esme *esme, struct msgb *msg) { struct deliver_sm_resp_t deliver_r; int rc; memset(&deliver_r, 0, sizeof(deliver_r)); SMPP34_UNPACK(rc, DELIVER_SM_RESP, &deliver_r, msgb_data(msg), msgb_length(msg)); if (rc < 0) { LOGP(DSMPP, LOGL_ERROR, "[%s] Error in smpp34_unpack():%s\n", esme->system_id, smpp34_strerror); return rc; } LOGP(DSMPP, LOGL_INFO, "[%s] Rx DELIVER-SM RESP (%s)\n", esme->system_id, get_value_string(smpp_status_strs, deliver_r.command_status)); return 0; } /*! \brief handle an incoming SMPP SUBMIT-SM */ static int smpp_handle_submit(struct osmo_esme *esme, struct msgb *msg) { struct submit_sm_t submit; struct submit_sm_resp_t submit_r; int rc; memset(&submit, 0, sizeof(submit)); SMPP34_UNPACK(rc, SUBMIT_SM, &submit, msgb_data(msg), msgb_length(msg)); if (rc < 0) { LOGP(DSMPP, LOGL_ERROR, "[%s] Error in smpp34_unpack():%s\n", esme->system_id, smpp34_strerror); return rc; } INIT_RESP(SUBMIT_SM_RESP, &submit_r, &submit); if (!(esme->bind_flags & ESME_BIND_TX)) { submit_r.command_status = ESME_RINVBNDSTS; return PACK_AND_SEND(esme, &submit_r); } LOGP(DSMPP, LOGL_INFO, "[%s] Rx SUBMIT-SM (%s/%u/%u)\n", esme->system_id, submit.destination_addr, submit.dest_addr_ton, submit.dest_addr_npi); INIT_RESP(SUBMIT_SM_RESP, &submit_r, &submit); rc = handle_smpp_submit(esme, &submit, &submit_r); if (rc == 0) return PACK_AND_SEND(esme, &submit_r); return rc; } /*! \brief one complete SMPP PDU from the ESME has been received */ static int smpp_pdu_rx(struct osmo_esme *esme, struct msgb *msg __uses) { uint32_t cmd_id = smpp_msgb_cmdid(msg); int rc = 0; LOGP(DSMPP, LOGL_DEBUG, "[%s] smpp_pdu_rx(%s)\n", esme->system_id, osmo_hexdump(msgb_data(msg), msgb_length(msg))); switch (cmd_id) { case GENERIC_NACK: rc = smpp_handle_gen_nack(esme, msg); break; case BIND_RECEIVER: rc = smpp_handle_bind_rx(esme, msg); break; case BIND_TRANSMITTER: rc = smpp_handle_bind_tx(esme, msg); break; case BIND_TRANSCEIVER: rc = smpp_handle_bind_trx(esme, msg); break; case UNBIND: rc = smpp_handle_unbind(esme, msg); break; case ENQUIRE_LINK: rc = smpp_handle_enq_link(esme, msg); break; case SUBMIT_SM: rc = smpp_handle_submit(esme, msg); break; case DELIVER_SM_RESP: rc = smpp_handle_deliver_resp(esme, msg); break; case DELIVER_SM: break; case DATA_SM: break; case CANCEL_SM: case QUERY_SM: case REPLACE_SM: case SUBMIT_MULTI: LOGP(DSMPP, LOGL_NOTICE, "[%s] Unimplemented PDU Commmand " "0x%08x\n", esme->system_id, cmd_id); break; default: LOGP(DSMPP, LOGL_ERROR, "[%s] Unknown PDU Command 0x%08x\n", esme->system_id, cmd_id); rc = smpp_tx_gen_nack(esme, smpp_msgb_seq(msg), ESME_RINVCMDID); break; } return rc; } /* This macro should be called after a call to read() in the read_cb of an * osmo_fd to properly check for errors. * rc is the return value of read, err_label is the label to jump to in case of * an error. The code there should handle closing the connection. * FIXME: This code should go in libosmocore utils.h so it can be used by other * projects as well. * */ #define OSMO_FD_CHECK_READ(rc, err_label) \ if (rc < 0) { \ /* EINTR is a non-fatal error, just try again */ \ if (errno == EINTR) \ return 0; \ goto err_label; \ } else if (rc == 0) { \ goto err_label; \ } /* !\brief call-back when per-ESME TCP socket has some data to be read */ static int esme_link_read_cb(struct osmo_fd *ofd) { struct osmo_esme *esme = ofd->data; uint32_t len; uint8_t *lenptr = (uint8_t *) &len; uint8_t *cur; struct msgb *msg; ssize_t rdlen, rc; switch (esme->read_state) { case READ_ST_IN_LEN: rdlen = sizeof(uint32_t) - esme->read_idx; rc = read(ofd->fd, lenptr + esme->read_idx, rdlen); if (rc < 0) LOGP(DSMPP, LOGL_ERROR, "[%s] read returned %zd (%s)\n", esme->system_id, rc, strerror(errno)); OSMO_FD_CHECK_READ(rc, dead_socket); esme->read_idx += rc; if (esme->read_idx >= sizeof(uint32_t)) { esme->read_len = ntohl(len); if (esme->read_len < 8 || esme->read_len > UINT16_MAX) { LOGP(DSMPP, LOGL_ERROR, "[%s] length invalid %u\n", esme->system_id, esme->read_len); goto dead_socket; } msg = msgb_alloc(esme->read_len, "SMPP Rx"); if (!msg) return -ENOMEM; esme->read_msg = msg; cur = msgb_put(msg, sizeof(uint32_t)); memcpy(cur, lenptr, sizeof(uint32_t)); esme->read_state = READ_ST_IN_MSG; esme->read_idx = sizeof(uint32_t); } break; case READ_ST_IN_MSG: msg = esme->read_msg; rdlen = esme->read_len - esme->read_idx; rc = read(ofd->fd, msg->tail, OSMO_MIN(rdlen, msgb_tailroom(msg))); if (rc < 0) LOGP(DSMPP, LOGL_ERROR, "[%s] read returned %zd (%s)\n", esme->system_id, rc, strerror(errno)); OSMO_FD_CHECK_READ(rc, dead_socket); esme->read_idx += rc; msgb_put(msg, rc); if (esme->read_idx >= esme->read_len) { rc = smpp_pdu_rx(esme, esme->read_msg); msgb_free(esme->read_msg); esme->read_msg = NULL; esme->read_idx = 0; esme->read_len = 0; esme->read_state = READ_ST_IN_LEN; } break; } return 0; dead_socket: msgb_free(esme->read_msg); osmo_fd_unregister(&esme->wqueue.bfd); close(esme->wqueue.bfd.fd); esme->wqueue.bfd.fd = -1; smpp_esme_put(esme); return 0; } /* call-back of write queue once it wishes to write a message to the socket */ static int esme_link_write_cb(struct osmo_fd *ofd, struct msgb *msg) { struct osmo_esme *esme = ofd->data; int rc; rc = write(ofd->fd, msgb_data(msg), msgb_length(msg)); if (rc == 0) { osmo_fd_unregister(&esme->wqueue.bfd); close(esme->wqueue.bfd.fd); esme->wqueue.bfd.fd = -1; smpp_esme_put(esme); } else if (rc < msgb_length(msg)) { LOGP(DSMPP, LOGL_ERROR, "[%s] Short write\n", esme->system_id); return -1; } return 0; } /* callback for already-accepted new TCP socket */ static int link_accept_cb(struct smsc *smsc, int fd, struct sockaddr_storage *s, socklen_t s_len) { struct osmo_esme *esme = talloc_zero(smsc, struct osmo_esme); if (!esme) { close(fd); return -ENOMEM; } smpp_esme_get(esme); esme->own_seq_nr = rand(); esme_inc_seq_nr(esme); esme->smsc = smsc; osmo_wqueue_init(&esme->wqueue, 10); esme->wqueue.bfd.fd = fd; esme->wqueue.bfd.data = esme; esme->wqueue.bfd.when = BSC_FD_READ; if (osmo_fd_register(&esme->wqueue.bfd) != 0) { close(fd); talloc_free(esme); return -EIO; } esme->wqueue.read_cb = esme_link_read_cb; esme->wqueue.write_cb = esme_link_write_cb; esme->sa_len = OSMO_MIN(sizeof(esme->sa), s_len); memcpy(&esme->sa, s, esme->sa_len); llist_add_tail(&esme->list, &smsc->esme_list); return 0; } /* callback of listening TCP socket */ static int smsc_fd_cb(struct osmo_fd *ofd, unsigned int what) { int rc; struct sockaddr_storage sa; socklen_t sa_len = sizeof(sa); rc = accept(ofd->fd, (struct sockaddr *)&sa, &sa_len); if (rc < 0) { LOGP(DSMPP, LOGL_ERROR, "Accept returns %d (%s)\n", rc, strerror(errno)); return rc; } return link_accept_cb(ofd->data, rc, &sa, sa_len); } /*! \brief Initialize the SMSC-side SMPP implementation */ int smpp_smsc_init(struct smsc *smsc, uint16_t port) { int rc; /* default port for SMPP */ if (port == 0) port = 2775; /* This will not work if we were to actually ever use FD 0 * (stdin) for this ... */ if (smsc->listen_ofd.fd <= 0) { INIT_LLIST_HEAD(&smsc->esme_list); INIT_LLIST_HEAD(&smsc->acl_list); INIT_LLIST_HEAD(&smsc->route_list); smsc->listen_ofd.data = smsc; smsc->listen_ofd.cb = smsc_fd_cb; } else { close(smsc->listen_ofd.fd); osmo_fd_unregister(&smsc->listen_ofd); } rc = osmo_sock_init_ofd(&smsc->listen_ofd, AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP, NULL, port, OSMO_SOCK_F_BIND); /* if there is an error, try to re-bind to the old port */ if (rc < 0) { rc = osmo_sock_init_ofd(&smsc->listen_ofd, AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP, NULL, smsc->listen_port, OSMO_SOCK_F_BIND); } else smsc->listen_port = port; return rc; } openbsc-0.15.0/openbsc/src/libmsc/smpp_smsc.h000066400000000000000000000063441265565154000211140ustar00rootroot00000000000000#ifndef _SMPP_SMSC_H #define _SMPP_SMSC_H #include #include #include #include #include #include #include #include #define SMPP_SYS_ID_LEN 16 #define SMPP_PASSWD_LEN 16 #define MODE_7BIT 7 #define MODE_8BIT 8 enum esme_read_state { READ_ST_IN_LEN = 0, READ_ST_IN_MSG = 1, }; struct osmo_smpp_acl; struct osmo_smpp_addr { uint8_t ton; uint8_t npi; char addr[21+1]; }; struct osmo_esme { struct llist_head list; struct smsc *smsc; struct osmo_smpp_acl *acl; int use; uint32_t own_seq_nr; struct osmo_wqueue wqueue; struct sockaddr_storage sa; socklen_t sa_len; enum esme_read_state read_state; uint32_t read_len; uint32_t read_idx; struct msgb *read_msg; uint8_t smpp_version; char system_id[SMPP_SYS_ID_LEN+1]; uint8_t bind_flags; }; struct osmo_smpp_acl { struct llist_head list; struct smsc *smsc; struct osmo_esme *esme; char *description; char system_id[SMPP_SYS_ID_LEN+1]; char passwd[SMPP_PASSWD_LEN+1]; int default_route; int deliver_src_imsi; int osmocom_ext; int dcs_transparent; struct llist_head route_list; }; enum osmo_smpp_rtype { SMPP_ROUTE_NONE, SMPP_ROUTE_PREFIX, }; struct osmo_smpp_route { struct llist_head list; /*!< in acl.route_list */ struct llist_head global_list; /*!< in smsc->route_list */ struct osmo_smpp_acl *acl; enum osmo_smpp_rtype type; union { struct osmo_smpp_addr prefix; } u; }; struct smsc { struct osmo_fd listen_ofd; struct llist_head esme_list; struct llist_head acl_list; struct llist_head route_list; uint16_t listen_port; char system_id[SMPP_SYS_ID_LEN+1]; int accept_all; int smpp_first; struct osmo_smpp_acl *def_route; void *priv; }; int smpp_addr_eq(const struct osmo_smpp_addr *a, const struct osmo_smpp_addr *b); int smpp_smsc_init(struct smsc *smsc, uint16_t port); void smpp_esme_get(struct osmo_esme *esme); void smpp_esme_put(struct osmo_esme *esme); struct osmo_esme * smpp_route(const struct smsc *smsc, const struct osmo_smpp_addr *dest); struct osmo_smpp_acl *smpp_acl_alloc(struct smsc *smsc, const char *sys_id); struct osmo_smpp_acl *smpp_acl_by_system_id(struct smsc *smsc, const char *sys_id); void smpp_acl_delete(struct osmo_smpp_acl *acl); int smpp_tx_submit_r(struct osmo_esme *esme, uint32_t sequence_nr, uint32_t command_status, char *msg_id); int smpp_tx_alert(struct osmo_esme *esme, uint8_t ton, uint8_t npi, const char *addr, uint8_t avail_status); int smpp_tx_deliver(struct osmo_esme *esme, struct deliver_sm_t *deliver); int handle_smpp_submit(struct osmo_esme *esme, struct submit_sm_t *submit, struct submit_sm_resp_t *submit_r); int smpp_route_pfx_add(struct osmo_smpp_acl *acl, const struct osmo_smpp_addr *pfx); int smpp_route_pfx_del(struct osmo_smpp_acl *acl, const struct osmo_smpp_addr *pfx); int smpp_vty_init(void); int smpp_determine_scheme(uint8_t dcs, uint8_t *data_coding, int *mode); struct gsm_sms; struct gsm_subscriber_connection; int smpp_route_smpp_first(struct gsm_sms *sms, struct gsm_subscriber_connection *conn); int smpp_try_deliver(struct gsm_sms *sms, struct gsm_subscriber_connection *conn); #endif openbsc-0.15.0/openbsc/src/libmsc/smpp_utils.c000066400000000000000000000030041265565154000212700ustar00rootroot00000000000000 /* (C) 2012-2013 by Harald Welte * * 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 . */ #include "smpp_smsc.h" #include int smpp_determine_scheme(uint8_t dcs, uint8_t *data_coding, int *mode) { if ((dcs & 0xF0) == 0xF0) { if (dcs & 0x04) { /* bit 2 == 1: 8bit data */ *data_coding = 0x02; *mode = MODE_8BIT; } else { /* bit 2 == 0: default alphabet */ *data_coding = 0x01; *mode = MODE_7BIT; } } else if ((dcs & 0xE0) == 0) { switch (dcs & 0xC) { case 0: *data_coding = 0x01; *mode = MODE_7BIT; break; case 4: *data_coding = 0x02; *mode = MODE_8BIT; break; case 8: *data_coding = 0x08; /* UCS-2 */ *mode = MODE_8BIT; break; default: goto unknown_mo; } } else { unknown_mo: LOGP(DLSMS, LOGL_ERROR, "SMPP MO Unknown Data Coding 0x%02x\n", dcs); return -1; } return 0; } openbsc-0.15.0/openbsc/src/libmsc/smpp_vty.c000066400000000000000000000343651265565154000207700ustar00rootroot00000000000000/* SMPP vty interface */ /* (C) 2012 by Harald Welte * 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include "smpp_smsc.h" struct smsc *smsc_from_vty(struct vty *v); static struct cmd_node smpp_node = { SMPP_NODE, "%s(config-smpp)# ", 1, }; static struct cmd_node esme_node = { SMPP_ESME_NODE, "%s(config-smpp-esme)# ", 1, }; DEFUN(cfg_smpp, cfg_smpp_cmd, "smpp", "Configure SMPP SMS Interface") { vty->node = SMPP_NODE; return CMD_SUCCESS; } DEFUN(cfg_smpp_first, cfg_smpp_first_cmd, "smpp-first", "Try SMPP routes before the subscriber DB\n") { struct smsc *smsc = smsc_from_vty(vty); smsc->smpp_first = 1; return CMD_SUCCESS; } DEFUN(cfg_no_smpp_first, cfg_no_smpp_first_cmd, "no smpp-first", NO_STR "Try SMPP before routes before the subscriber DB\n") { struct smsc *smsc = smsc_from_vty(vty); smsc->smpp_first = 0; return CMD_SUCCESS; } DEFUN(cfg_smpp_port, cfg_smpp_port_cmd, "local-tcp-port <1-65535>", "Set the local TCP port on which we listen for SMPP\n" "TCP port number") { struct smsc *smsc = smsc_from_vty(vty); uint16_t port = atoi(argv[0]); int rc; rc = smpp_smsc_init(smsc, port); if (rc < 0) { vty_out(vty, "%% Cannot bind to new port %u nor to " "old port %u%s", port, smsc->listen_port, VTY_NEWLINE); return CMD_WARNING; } if (port != smsc->listen_port) { vty_out(vty, "%% Cannot bind to new port %u, staying on old" "port %u%s", port, smsc->listen_port, VTY_NEWLINE); return CMD_WARNING; } return CMD_SUCCESS; } DEFUN(cfg_smpp_sys_id, cfg_smpp_sys_id_cmd, "system-id ID", "Set the System ID of this SMSC\n" "Alphanumeric SMSC System ID\n") { struct smsc *smsc = smsc_from_vty(vty); if (strlen(argv[0])+1 > sizeof(smsc->system_id)) return CMD_WARNING; strcpy(smsc->system_id, argv[0]); return CMD_SUCCESS; } DEFUN(cfg_smpp_policy, cfg_smpp_policy_cmd, "policy (accept-all|closed)", "Set the authentication policy of this SMSC\n" "Accept all SMPP connections independeint of system ID / passwd\n" "Accept only SMPP connections from ESMEs explicitly configured") { struct smsc *smsc = smsc_from_vty(vty); if (!strcmp(argv[0], "accept-all")) smsc->accept_all = 1; else smsc->accept_all = 0; return CMD_SUCCESS; } static int config_write_smpp(struct vty *vty) { struct smsc *smsc = smsc_from_vty(vty); vty_out(vty, "smpp%s", VTY_NEWLINE); vty_out(vty, " local-tcp-port %u%s", smsc->listen_port, VTY_NEWLINE); if (strlen(smsc->system_id) > 0) vty_out(vty, " system-id %s%s", smsc->system_id, VTY_NEWLINE); vty_out(vty, " policy %s%s", smsc->accept_all ? "accept-all" : "closed", VTY_NEWLINE); vty_out(vty, " %ssmpp-first%s", smsc->smpp_first ? "" : "no ", VTY_NEWLINE); return CMD_SUCCESS; } DEFUN(cfg_esme, cfg_esme_cmd, "esme NAME", "Configure a particular ESME\n" "Alphanumeric System ID of the ESME to be configured\n") { struct smsc *smsc = smsc_from_vty(vty); struct osmo_smpp_acl *acl; const char *id = argv[0]; if (strlen(id) > 16) { vty_out(vty, "%% System ID cannot be more than 16 " "characters long%s", VTY_NEWLINE); return CMD_WARNING; } acl = smpp_acl_by_system_id(smsc, id); if (!acl) { acl = smpp_acl_alloc(smsc, id); if (!acl) return CMD_WARNING; } vty->index = acl; vty->index_sub = &acl->description; vty->node = SMPP_ESME_NODE; return CMD_SUCCESS; } DEFUN(cfg_no_esme, cfg_no_esme_cmd, "no esme NAME", NO_STR "Remove ESME configuration\n" "Alphanumeric System ID of the ESME to be removed\n") { struct smsc *smsc = smsc_from_vty(vty); struct osmo_smpp_acl *acl; const char *id = argv[0]; acl = smpp_acl_by_system_id(smsc, id); if (!acl) { vty_out(vty, "%% ESME with system id '%s' unknown%s", id, VTY_NEWLINE); return CMD_WARNING; } /* FIXME: close the connection, free data structure, etc. */ smpp_acl_delete(acl); return CMD_SUCCESS; } DEFUN(cfg_esme_passwd, cfg_esme_passwd_cmd, "password PASSWORD", "Set the password for this ESME\n" "Alphanumeric password string\n") { struct osmo_smpp_acl *acl = vty->index; if (strlen(argv[0])+1 > sizeof(acl->passwd)) return CMD_WARNING; strcpy(acl->passwd, argv[0]); return CMD_SUCCESS; } DEFUN(cfg_esme_no_passwd, cfg_esme_no_passwd_cmd, "no password", NO_STR "Remove the password for this ESME\n") { struct osmo_smpp_acl *acl = vty->index; memset(acl->passwd, 0, sizeof(acl->passwd)); return CMD_SUCCESS; } static int osmo_is_digits(const char *str) { int i; for (i = 0; i < strlen(str); i++) { if (!isdigit(str[i])) return 0; } return 1; } static const struct value_string route_errstr[] = { { -EEXIST, "Route already exists" }, { -ENODEV, "Route does not exist" }, { -ENOMEM, "No memory" }, { -EINVAL, "Invalid" }, { 0, NULL } }; static const struct value_string smpp_ton_str_short[] = { { TON_Unknown, "unknown" }, { TON_International, "international" }, { TON_National, "national" }, { TON_Network_Specific, "network" }, { TON_Subscriber_Number,"subscriber" }, { TON_Alphanumeric, "alpha" }, { TON_Abbreviated, "abbrev" }, { 0, NULL } }; static const struct value_string smpp_npi_str_short[] = { { NPI_Unknown, "unknown" }, { NPI_ISDN_E163_E164, "isdn" }, { NPI_Data_X121, "x121" }, { NPI_Telex_F69, "f69" }, { NPI_Land_Mobile_E212, "e212" }, { NPI_National, "national" }, { NPI_Private, "private" }, { NPI_ERMES, "ermes" }, { NPI_Internet_IP, "ip" }, { NPI_WAP_Client_Id, "wap" }, { 0, NULL } }; #define SMPP_ROUTE_STR "Configure a route for MO-SMS to be sent to this ESME\n" #define SMPP_ROUTE_P_STR SMPP_ROUTE_STR "Prefix-match route\n" #define SMPP_PREFIX_STR "Destination number prefix\n" #define TON_CMD "(unknown|international|national|network|subscriber|alpha|abbrev)" #define NPI_CMD "(unknown|isdn|x121|f69|e212|national|private|ermes|ip|wap)" #define TON_STR "Unknown type-of-number\n" \ "International type-of-number\n" \ "National type-of-number\n" \ "Network specific type-of-number\n" \ "Subscriber type-of-number\n" \ "Alphanumeric type-of-number\n" \ "Abbreviated type-of-number\n" #define NPI_STR "Unknown numbering plan\n" \ "ISDN (E.164) numbering plan\n" \ "X.121 numbering plan\n" \ "F.69 numbering plan\n" \ "E.212 numbering plan\n" \ "National numbering plan\n" \ "Private numbering plan\n" \ "ERMES numbering plan\n" \ "IP numbering plan\n" \ "WAP numbeing plan\n" DEFUN(cfg_esme_route_pfx, cfg_esme_route_pfx_cmd, "route prefix " TON_CMD " " NPI_CMD " PREFIX", SMPP_ROUTE_P_STR TON_STR NPI_STR SMPP_PREFIX_STR) { struct osmo_smpp_acl *acl = vty->index; struct osmo_smpp_addr pfx; int rc; /* check if DESTINATION is all-digits */ if (!osmo_is_digits(argv[2])) { vty_out(vty, "%% PREFIX has to be numeric%s", VTY_NEWLINE); return CMD_WARNING; } pfx.ton = get_string_value(smpp_ton_str_short, argv[0]); pfx.npi = get_string_value(smpp_npi_str_short, argv[1]); snprintf(pfx.addr, sizeof(pfx.addr), "%s", argv[2]); rc = smpp_route_pfx_add(acl, &pfx); if (rc < 0) { vty_out(vty, "%% error adding prefix route: %s%s", get_value_string(route_errstr, rc), VTY_NEWLINE); return CMD_WARNING; } return CMD_SUCCESS; } DEFUN(cfg_esme_no_route_pfx, cfg_esme_no_route_pfx_cmd, "no route prefix " TON_CMD " " NPI_CMD " PREFIX", NO_STR SMPP_ROUTE_P_STR TON_STR NPI_STR SMPP_PREFIX_STR) { struct osmo_smpp_acl *acl = vty->index; struct osmo_smpp_addr pfx; int rc; /* check if DESTINATION is all-digits */ if (!osmo_is_digits(argv[2])) { vty_out(vty, "%% PREFIX has to be numeric%s", VTY_NEWLINE); return CMD_WARNING; } pfx.ton = get_string_value(smpp_ton_str_short, argv[0]); pfx.npi = get_string_value(smpp_npi_str_short, argv[1]); snprintf(pfx.addr, sizeof(pfx.addr), "%s", argv[2]); rc = smpp_route_pfx_del(acl, &pfx); if (rc < 0) { vty_out(vty, "%% error removing prefix route: %s%s", get_value_string(route_errstr, rc), VTY_NEWLINE); return CMD_WARNING; } return CMD_SUCCESS; } DEFUN(cfg_esme_defaultroute, cfg_esme_defaultroute_cmd, "default-route", "Set this ESME as default-route for all SMS to unknown destinations") { struct osmo_smpp_acl *acl = vty->index; acl->default_route = 1; if (!acl->smsc->def_route) acl->smsc->def_route = acl; return CMD_SUCCESS; } DEFUN(cfg_no_esme_defaultroute, cfg_esme_no_defaultroute_cmd, "no default-route", NO_STR "Remove this ESME as default-route for all SMS to unknown destinations") { struct osmo_smpp_acl *acl = vty->index; acl->default_route = 0; /* remove currently active default route, if it was created by * this ACL */ if (acl->smsc->def_route && acl->smsc->def_route == acl) acl->smsc->def_route = NULL; return CMD_SUCCESS; } DEFUN(cfg_esme_del_src_imsi, cfg_esme_del_src_imsi_cmd, "deliver-src-imsi", "Enable the use of IMSI as source addres in DELIVER") { struct osmo_smpp_acl *acl = vty->index; acl->deliver_src_imsi = 1; return CMD_SUCCESS; } DEFUN(cfg_esme_no_del_src_imsi, cfg_esme_no_del_src_imsi_cmd, "no deliver-src-imsi", NO_STR "Disable the use of IMSI as source addres in DELIVER") { struct osmo_smpp_acl *acl = vty->index; acl->deliver_src_imsi = 0; return CMD_SUCCESS; } DEFUN(cfg_esme_osmo_ext, cfg_esme_osmo_ext_cmd, "osmocom-extensions", "Enable the use of Osmocom SMPP Extensions for this ESME") { struct osmo_smpp_acl *acl = vty->index; acl->osmocom_ext = 1; return CMD_SUCCESS; } DEFUN(cfg_esme_no_osmo_ext, cfg_esme_no_osmo_ext_cmd, "no osmocom-extensions", NO_STR "Disable the use of Osmocom SMPP Extensions for this ESME") { struct osmo_smpp_acl *acl = vty->index; acl->osmocom_ext = 0; return CMD_SUCCESS; } DEFUN(cfg_esme_dcs_transp, cfg_esme_dcs_transp_cmd, "dcs-transparent", "Enable the transparent pass-through of TP-DCS to SMPP DataCoding") { struct osmo_smpp_acl *acl = vty->index; acl->dcs_transparent = 1; return CMD_SUCCESS; } DEFUN(cfg_esme_no_dcs_transp, cfg_esme_no_dcs_transp_cmd, "no dcs-transparent", NO_STR "Disable the transparent pass-through of TP-DCS to SMPP DataCoding") { struct osmo_smpp_acl *acl = vty->index; acl->dcs_transparent = 0; return CMD_SUCCESS; } static void dump_one_esme(struct vty *vty, struct osmo_esme *esme) { char host[128], serv[128]; host[0] = 0; serv[0] = 0; getnameinfo((const struct sockaddr *) &esme->sa, esme->sa_len, host, sizeof(host), serv, sizeof(serv), NI_NUMERICSERV); vty_out(vty, "ESME System ID: %s, Password: %s, SMPP Version %02x%s", esme->system_id, esme->acl ? esme->acl->passwd : "", esme->smpp_version, VTY_NEWLINE); vty_out(vty, " Connected from: %s:%s%s", host, serv, VTY_NEWLINE); if (esme->smsc->def_route == esme->acl) vty_out(vty, " Is current default route%s", VTY_NEWLINE); } DEFUN(show_esme, show_esme_cmd, "show smpp esme", SHOW_STR "SMPP Interface\n" "SMPP Extrenal SMS Entity\n") { struct smsc *smsc = smsc_from_vty(vty); struct osmo_esme *esme; llist_for_each_entry(esme, &smsc->esme_list, list) dump_one_esme(vty, esme); return CMD_SUCCESS; } static void write_esme_route_single(struct vty *vty, struct osmo_smpp_route *r) { switch (r->type) { case SMPP_ROUTE_PREFIX: vty_out(vty, " route prefix %s ", get_value_string(smpp_ton_str_short, r->u.prefix.ton)); vty_out(vty, "%s %s%s", get_value_string(smpp_npi_str_short, r->u.prefix.npi), r->u.prefix.addr, VTY_NEWLINE); break; case SMPP_ROUTE_NONE: break; } } static void config_write_esme_single(struct vty *vty, struct osmo_smpp_acl *acl) { struct osmo_smpp_route *r; vty_out(vty, " esme %s%s", acl->system_id, VTY_NEWLINE); if (strlen(acl->passwd)) vty_out(vty, " password %s%s", acl->passwd, VTY_NEWLINE); if (acl->default_route) vty_out(vty, " default-route%s", VTY_NEWLINE); if (acl->deliver_src_imsi) vty_out(vty, " deliver-src-imsi%s", VTY_NEWLINE); if (acl->osmocom_ext) vty_out(vty, " osmocom-extensions%s", VTY_NEWLINE); if (acl->dcs_transparent) vty_out(vty, " dcs-transparent%s", VTY_NEWLINE); llist_for_each_entry(r, &acl->route_list, list) write_esme_route_single(vty, r); } static int config_write_esme(struct vty *v) { struct smsc *smsc = smsc_from_vty(v); struct osmo_smpp_acl *acl; llist_for_each_entry(acl, &smsc->acl_list, list) config_write_esme_single(v, acl); return CMD_SUCCESS; } int smpp_vty_init(void) { install_node(&smpp_node, config_write_smpp); vty_install_default(SMPP_NODE); install_element(CONFIG_NODE, &cfg_smpp_cmd); install_element(SMPP_NODE, &cfg_smpp_first_cmd); install_element(SMPP_NODE, &cfg_no_smpp_first_cmd); install_element(SMPP_NODE, &cfg_smpp_port_cmd); install_element(SMPP_NODE, &cfg_smpp_sys_id_cmd); install_element(SMPP_NODE, &cfg_smpp_policy_cmd); install_element(SMPP_NODE, &cfg_esme_cmd); install_element(SMPP_NODE, &cfg_no_esme_cmd); install_node(&esme_node, config_write_esme); vty_install_default(SMPP_ESME_NODE); install_element(SMPP_ESME_NODE, &cfg_esme_passwd_cmd); install_element(SMPP_ESME_NODE, &cfg_esme_no_passwd_cmd); install_element(SMPP_ESME_NODE, &cfg_esme_route_pfx_cmd); install_element(SMPP_ESME_NODE, &cfg_esme_no_route_pfx_cmd); install_element(SMPP_ESME_NODE, &cfg_esme_defaultroute_cmd); install_element(SMPP_ESME_NODE, &cfg_esme_no_defaultroute_cmd); install_element(SMPP_ESME_NODE, &cfg_esme_del_src_imsi_cmd); install_element(SMPP_ESME_NODE, &cfg_esme_no_del_src_imsi_cmd); install_element(SMPP_ESME_NODE, &cfg_esme_osmo_ext_cmd); install_element(SMPP_ESME_NODE, &cfg_esme_no_osmo_ext_cmd); install_element(SMPP_ESME_NODE, &cfg_esme_dcs_transp_cmd); install_element(SMPP_ESME_NODE, &cfg_esme_no_dcs_transp_cmd); install_element_ve(&show_esme_cmd); return 0; } openbsc-0.15.0/openbsc/src/libmsc/sms_queue.c000066400000000000000000000337701265565154000211140ustar00rootroot00000000000000/* SMS queue to continously attempt to deliver SMS */ /* * (C) 2010 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 . * */ /** * The difficulty of such a queue is to send a lot of SMS without * overloading the paging subsystem and the database and other users * of the MSC. To make the best use we would need to know the number * of pending paging requests, then throttle the number of SMS we * want to send and such. * We will start with a very simple SMS Queue and then try to speed * things up by collecting data from other parts of the system. */ #include #include #include #include #include #include #include #include #include #include /* * One pending SMS that we wait for. */ struct gsm_sms_pending { struct llist_head entry; struct gsm_subscriber *subscr; unsigned long long sms_id; int failed_attempts; int resend; }; struct gsm_sms_queue { struct osmo_timer_list resend_pending; struct osmo_timer_list push_queue; struct gsm_network *network; int max_fail; int max_pending; int pending; struct llist_head pending_sms; unsigned long long last_subscr_id; }; static int sms_subscr_cb(unsigned int, unsigned int, void *, void *); static int sms_sms_cb(unsigned int, unsigned int, void *, void *); static struct gsm_sms_pending *sms_find_pending(struct gsm_sms_queue *smsq, struct gsm_sms *sms) { struct gsm_sms_pending *pending; llist_for_each_entry(pending, &smsq->pending_sms, entry) { if (pending->sms_id == sms->id) return pending; } return NULL; } static int sms_is_in_pending(struct gsm_sms_queue *smsq, struct gsm_sms *sms) { return sms_find_pending(smsq, sms) != NULL; } static struct gsm_sms_pending *sms_subscriber_find_pending( struct gsm_sms_queue *smsq, struct gsm_subscriber *subscr) { struct gsm_sms_pending *pending; llist_for_each_entry(pending, &smsq->pending_sms, entry) { if (pending->subscr == subscr) return pending; } return NULL; } static int sms_subscriber_is_pending(struct gsm_sms_queue *smsq, struct gsm_subscriber *subscr) { return sms_subscriber_find_pending(smsq, subscr) != NULL; } static struct gsm_sms_pending *sms_pending_from(struct gsm_sms_queue *smsq, struct gsm_sms *sms) { struct gsm_sms_pending *pending; pending = talloc_zero(smsq, struct gsm_sms_pending); if (!pending) return NULL; pending->subscr = subscr_get(sms->receiver); pending->sms_id = sms->id; return pending; } static void sms_pending_free(struct gsm_sms_pending *pending) { subscr_put(pending->subscr); llist_del(&pending->entry); talloc_free(pending); } static void sms_pending_resend(struct gsm_sms_pending *pending) { struct gsm_sms_queue *smsq; LOGP(DLSMS, LOGL_DEBUG, "Scheduling resend of SMS %llu.\n", pending->sms_id); pending->resend = 1; smsq = pending->subscr->group->net->sms_queue; if (osmo_timer_pending(&smsq->resend_pending)) return; osmo_timer_schedule(&smsq->resend_pending, 1, 0); } static void sms_pending_failed(struct gsm_sms_pending *pending, int paging_error) { struct gsm_sms_queue *smsq; LOGP(DLSMS, LOGL_NOTICE, "Sending SMS %llu failed %d times.\n", pending->sms_id, pending->failed_attempts); smsq = pending->subscr->group->net->sms_queue; if (++pending->failed_attempts < smsq->max_fail) return sms_pending_resend(pending); sms_pending_free(pending); smsq->pending -= 1; sms_queue_trigger(smsq); } /* * Resend all SMS that are scheduled for a resend. This is done to * avoid an immediate failure. */ static void sms_resend_pending(void *_data) { struct gsm_sms_pending *pending, *tmp; struct gsm_sms_queue *smsq = _data; llist_for_each_entry_safe(pending, tmp, &smsq->pending_sms, entry) { struct gsm_sms *sms; if (!pending->resend) continue; sms = db_sms_get(smsq->network, pending->sms_id); /* the sms is gone? Move to the next */ if (!sms) { sms_pending_free(pending); smsq->pending -= 1; sms_queue_trigger(smsq); } else { pending->resend = 0; gsm411_send_sms_subscr(sms->receiver, sms); } } } static struct gsm_sms *take_next_sms(struct gsm_sms_queue *smsq) { struct gsm_sms *sms; sms = db_sms_get_unsent_by_subscr(smsq->network, smsq->last_subscr_id, 10); if (sms) { smsq->last_subscr_id = sms->receiver->id + 1; return sms; } /* need to wrap around */ smsq->last_subscr_id = 0; sms = db_sms_get_unsent_by_subscr(smsq->network, smsq->last_subscr_id, 10); if (sms) smsq->last_subscr_id = sms->receiver->id + 1; return sms; } /** * I will submit up to max_pending - pending SMS to the * subsystem. */ static void sms_submit_pending(void *_data) { struct gsm_sms_queue *smsq = _data; int attempts = smsq->max_pending - smsq->pending; int initialized = 0; unsigned long long first_sub = 0; int attempted = 0, rounds = 0; LOGP(DLSMS, LOGL_DEBUG, "Attempting to send %d SMS\n", attempts); do { struct gsm_sms_pending *pending; struct gsm_sms *sms; sms = take_next_sms(smsq); if (!sms) break; rounds += 1; /* * This code needs to detect a loop. It assumes that no SMS * will vanish during the time this is executed. We will remember * the id of the first GSM subscriber we see and then will * compare this. The Database code should make sure that we will * see all other subscribers first before seeing this one again. * * It is always scary to have an infinite loop like this. */ if (!initialized) { first_sub = sms->receiver->id; initialized = 1; } else if (first_sub == sms->receiver->id) { sms_free(sms); break; } /* no need to send a pending sms */ if (sms_is_in_pending(smsq, sms)) { LOGP(DLSMS, LOGL_DEBUG, "SMSqueue with pending sms: %llu. Skipping\n", sms->id); sms_free(sms); continue; } /* no need to send a SMS with the same receiver */ if (sms_subscriber_is_pending(smsq, sms->receiver)) { LOGP(DLSMS, LOGL_DEBUG, "SMSqueue with pending sub: %llu. Skipping\n", sms->receiver->id); sms_free(sms); continue; } pending = sms_pending_from(smsq, sms); if (!pending) { LOGP(DLSMS, LOGL_ERROR, "Failed to create pending SMS entry.\n"); sms_free(sms); continue; } attempted += 1; smsq->pending += 1; llist_add_tail(&pending->entry, &smsq->pending_sms); gsm411_send_sms_subscr(sms->receiver, sms); } while (attempted < attempts && rounds < 1000); LOGP(DLSMS, LOGL_DEBUG, "SMSqueue added %d messages in %d rounds\n", attempted, rounds); } /** * Send the next SMS or trigger the queue */ static void sms_send_next(struct gsm_subscriber *subscr) { struct gsm_sms_queue *smsq = subscr->group->net->sms_queue; struct gsm_sms_pending *pending; struct gsm_sms *sms; /* the subscriber should not be in the queue */ OSMO_ASSERT(!sms_subscriber_is_pending(smsq, subscr)); /* check for more messages for this subscriber */ sms = db_sms_get_unsent_for_subscr(subscr); if (!sms) goto no_pending_sms; /* No sms should be scheduled right now */ OSMO_ASSERT(!sms_is_in_pending(smsq, sms)); /* Remember that we deliver this SMS and send it */ pending = sms_pending_from(smsq, sms); if (!pending) { LOGP(DLSMS, LOGL_ERROR, "Failed to create pending SMS entry.\n"); sms_free(sms); goto no_pending_sms; } smsq->pending += 1; llist_add_tail(&pending->entry, &smsq->pending_sms); gsm411_send_sms_subscr(sms->receiver, sms); return; no_pending_sms: /* Try to send the SMS to avoid the queue being stuck */ sms_submit_pending(subscr->group->net->sms_queue); } /* * Kick off the queue again. */ int sms_queue_trigger(struct gsm_sms_queue *smsq) { if (osmo_timer_pending(&smsq->push_queue)) return 0; osmo_timer_schedule(&smsq->push_queue, 1, 0); return 0; } int sms_queue_start(struct gsm_network *network, int max_pending) { struct gsm_sms_queue *sms = talloc_zero(network, struct gsm_sms_queue); if (!sms) { LOGP(DMSC, LOGL_ERROR, "Failed to create the SMS queue.\n"); return -1; } osmo_signal_register_handler(SS_SUBSCR, sms_subscr_cb, network); osmo_signal_register_handler(SS_SMS, sms_sms_cb, network); network->sms_queue = sms; INIT_LLIST_HEAD(&sms->pending_sms); sms->max_fail = 1; sms->network = network; sms->max_pending = max_pending; sms->push_queue.data = sms; sms->push_queue.cb = sms_submit_pending; sms->resend_pending.data = sms; sms->resend_pending.cb = sms_resend_pending; sms_submit_pending(sms); return 0; } static int sub_ready_for_sm(struct gsm_network *net, struct gsm_subscriber *subscr) { struct gsm_sms *sms; struct gsm_sms_pending *pending; struct gsm_subscriber_connection *conn; /* * The code used to be very clever and tried to submit * a SMS during the Location Updating Request. This has * two issues: * 1.) The Phone might not be ready yet, e.g. the C155 * will not respond to the Submit when it is booting. * 2.) The queue is already trying to submit SMS to the * user and by not responding to the paging request * we will set the LAC back to 0. We would have to * stop the paging and move things over. * * We need to be careful in what we try here. */ /* check if we have pending requests */ pending = sms_subscriber_find_pending(net->sms_queue, subscr); if (pending) { LOGP(DMSC, LOGL_NOTICE, "Pending paging while subscriber %llu attached.\n", subscr->id); return 0; } conn = connection_for_subscr(subscr); if (!conn) return -1; /* Now try to deliver any pending SMS to this sub */ sms = db_sms_get_unsent_for_subscr(subscr); if (!sms) return -1; gsm411_send_sms(conn, sms); return 0; } static int sms_subscr_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct gsm_subscriber *subscr = signal_data; if (signal != S_SUBSCR_ATTACHED) return 0; /* this is readyForSM */ return sub_ready_for_sm(handler_data, subscr); } static int sms_sms_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct gsm_network *network = handler_data; struct sms_signal_data *sig_sms = signal_data; struct gsm_sms_pending *pending; struct gsm_subscriber *subscr; /* We got a new SMS and maybe should launch the queue again. */ if (signal == S_SMS_SUBMITTED || signal == S_SMS_SMMA) { /* TODO: For SMMA we might want to re-use the radio connection. */ sms_queue_trigger(network->sms_queue); return 0; } if (!sig_sms->sms) return -1; /* * Find the entry of our queue. The SMS subsystem will submit * sms that are not in our control as we just have a channel * open anyway. */ pending = sms_find_pending(network->sms_queue, sig_sms->sms); if (!pending) return 0; switch (signal) { case S_SMS_DELIVERED: /* Remember the subscriber and clear the pending entry */ network->sms_queue->pending -= 1; subscr = subscr_get(pending->subscr); sms_pending_free(pending); /* Attempt to send another SMS to this subscriber */ sms_send_next(subscr); subscr_put(subscr); break; case S_SMS_MEM_EXCEEDED: network->sms_queue->pending -= 1; sms_pending_free(pending); sms_queue_trigger(network->sms_queue); break; case S_SMS_UNKNOWN_ERROR: /* * There can be many reasons for this failure. E.g. the paging * timed out, the subscriber was not paged at all, or there was * a protocol error. The current strategy is to try sending the * next SMS for busy/oom and to retransmit when we have paged. * * When the paging expires three times we will disable the * subscriber. If we have some kind of other transmit error we * should flag the SMS as bad. */ switch (sig_sms->paging_result) { case 0: /* BAD SMS? */ db_sms_inc_deliver_attempts(sig_sms->sms); sms_pending_failed(pending, 0); break; case GSM_PAGING_EXPIRED: sms_pending_failed(pending, 1); break; case GSM_PAGING_OOM: case GSM_PAGING_BUSY: network->sms_queue->pending -= 1; sms_pending_free(pending); sms_queue_trigger(network->sms_queue); break; default: LOGP(DLSMS, LOGL_ERROR, "Unhandled result: %d\n", sig_sms->paging_result); } break; default: LOGP(DLSMS, LOGL_ERROR, "Unhandled result: %d\n", sig_sms->paging_result); } return 0; } /* VTY helper functions */ int sms_queue_stats(struct gsm_sms_queue *smsq, struct vty *vty) { struct gsm_sms_pending *pending; vty_out(vty, "SMSqueue with max_pending: %d pending: %d%s", smsq->max_pending, smsq->pending, VTY_NEWLINE); llist_for_each_entry(pending, &smsq->pending_sms, entry) vty_out(vty, " SMS Pending for Subscriber: %llu SMS: %llu Failed: %d.%s", pending->subscr->id, pending->sms_id, pending->failed_attempts, VTY_NEWLINE); return 0; } int sms_queue_set_max_pending(struct gsm_sms_queue *smsq, int max_pending) { LOGP(DLSMS, LOGL_NOTICE, "SMSqueue old max: %d new: %d\n", smsq->max_pending, max_pending); smsq->max_pending = max_pending; return 0; } int sms_queue_set_max_failure(struct gsm_sms_queue *smsq, int max_fail) { LOGP(DLSMS, LOGL_NOTICE, "SMSqueue max failure old: %d new: %d\n", smsq->max_fail, max_fail); smsq->max_fail = max_fail; return 0; } int sms_queue_clear(struct gsm_sms_queue *smsq) { struct gsm_sms_pending *pending, *tmp; llist_for_each_entry_safe(pending, tmp, &smsq->pending_sms, entry) { LOGP(DLSMS, LOGL_NOTICE, "SMSqueue clearing for sub %llu\n", pending->subscr->id); sms_pending_free(pending); } smsq->pending = 0; return 0; } openbsc-0.15.0/openbsc/src/libmsc/token_auth.c000066400000000000000000000101061265565154000212330ustar00rootroot00000000000000/* SMS based token authentication for ad-hoc GSM networks */ /* (C) 2009 by Harald Welte * * 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 . * */ #include #include #include #include #include #include #include #include #include #define TOKEN_SMS_TEXT "HAR 2009 GSM. Register at http://har2009.gnumonks.org/ " \ "Your IMSI is %s, auth token is %08X, phone no is %s." static char *build_sms_string(struct gsm_subscriber *subscr, uint32_t token) { char *sms_str; unsigned int len; len = strlen(subscr->imsi) + 8 + strlen(TOKEN_SMS_TEXT); sms_str = talloc_size(tall_bsc_ctx, len); if (!sms_str) return NULL; snprintf(sms_str, len, TOKEN_SMS_TEXT, subscr->imsi, token, subscr->extension); sms_str[len-1] = '\0'; return sms_str; } static int token_subscr_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct gsm_subscriber *subscr = signal_data; struct gsm_sms *sms; int rc = 0; if (signal != S_SUBSCR_ATTACHED) return 0; if (subscr->group->net->auth_policy != GSM_AUTH_POLICY_TOKEN) return 0; if (subscr->flags & GSM_SUBSCRIBER_FIRST_CONTACT) { struct gsm_subscriber *sender; uint32_t token; char *sms_str; /* we've seen this subscriber for the first time. */ rc = db_subscriber_alloc_token(subscr, &token); if (rc != 0) { rc = -EIO; goto unauth; } sms_str = build_sms_string(subscr, token); if (!sms_str) { rc = -ENOMEM; goto unauth; } /* FIXME: don't use ID 1 static */ sender = subscr_get_by_id(subscr->group, 1); sms = sms_from_text(subscr, sender, 0, sms_str); subscr_put(sender); talloc_free(sms_str); if (!sms) { rc = -ENOMEM; goto unauth; } rc = gsm411_send_sms_subscr(subscr, sms); /* FIXME: else, delete the subscirber from database */ unauth: /* make sure we don't allow him in again unless he clicks the web UI */ subscr->authorized = 0; db_sync_subscriber(subscr); if (rc) { struct gsm_subscriber_connection *conn = connection_for_subscr(subscr); if (conn) { uint8_t auth_rand[16]; /* kick the subscriber off the network */ gsm48_tx_mm_auth_req(conn, auth_rand, 0); gsm48_tx_mm_auth_rej(conn); /* FIXME: close the channel early ?*/ //gsm48_send_rr_Release(lchan); } } } return rc; } static int token_sms_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct sms_signal_data *sig = signal_data; struct gsm_sms *sms = sig->sms;; struct gsm_subscriber_connection *conn; uint8_t auth_rand[16]; if (signal != S_SMS_DELIVERED) return 0; /* these are not the droids we've been looking for */ if (!sms->receiver || !(sms->receiver->flags & GSM_SUBSCRIBER_FIRST_CONTACT)) return 0; if (sms->receiver->group->net->auth_policy != GSM_AUTH_POLICY_TOKEN) return 0; conn = connection_for_subscr(sms->receiver); if (conn) { /* kick the subscriber off the network */ gsm48_tx_mm_auth_req(conn, auth_rand, 0); gsm48_tx_mm_auth_rej(conn); /* FIXME: close the channel early ?*/ //gsm48_send_rr_Release(lchan); } return 0; } //static __attribute__((constructor)) void on_dso_load_token(void) void on_dso_load_token(void) { osmo_signal_register_handler(SS_SUBSCR, token_subscr_cb, NULL); osmo_signal_register_handler(SS_SMS, token_sms_cb, NULL); } openbsc-0.15.0/openbsc/src/libmsc/transaction.c000066400000000000000000000077641265565154000214370ustar00rootroot00000000000000/* GSM 04.07 Transaction handling */ /* (C) 2009 by Harald Welte * 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 . * */ #include #include #include #include #include #include #include #include #include #include void *tall_trans_ctx; void _gsm48_cc_trans_free(struct gsm_trans *trans); struct gsm_trans *trans_find_by_id(struct gsm_subscriber_connection *conn, uint8_t proto, uint8_t trans_id) { struct gsm_trans *trans; struct gsm_network *net = conn->bts->network; struct gsm_subscriber *subscr = conn->subscr; llist_for_each_entry(trans, &net->trans_list, entry) { if (trans->subscr == subscr && trans->protocol == proto && trans->transaction_id == trans_id) return trans; } return NULL; } struct gsm_trans *trans_find_by_callref(struct gsm_network *net, uint32_t callref) { struct gsm_trans *trans; llist_for_each_entry(trans, &net->trans_list, entry) { if (trans->callref == callref) return trans; } return NULL; } struct gsm_trans *trans_alloc(struct gsm_network *net, struct gsm_subscriber *subscr, uint8_t protocol, uint8_t trans_id, uint32_t callref) { struct gsm_trans *trans; DEBUGP(DCC, "subscr=%p, net=%p\n", subscr, net); trans = talloc_zero(tall_trans_ctx, struct gsm_trans); if (!trans) return NULL; trans->subscr = subscr; subscr_get(trans->subscr); trans->protocol = protocol; trans->transaction_id = trans_id; trans->callref = callref; trans->net = net; llist_add_tail(&trans->entry, &net->trans_list); return trans; } void trans_free(struct gsm_trans *trans) { switch (trans->protocol) { case GSM48_PDISC_CC: _gsm48_cc_trans_free(trans); break; case GSM48_PDISC_SMS: _gsm411_sms_trans_free(trans); break; } if (trans->paging_request) { subscr_remove_request(trans->paging_request); trans->paging_request = NULL; } if (trans->subscr) { subscr_put(trans->subscr); trans->subscr = NULL; } llist_del(&trans->entry); if (trans->conn) msc_release_connection(trans->conn); trans->conn = NULL; talloc_free(trans); } /* allocate an unused transaction ID for the given subscriber * in the given protocol using the ti_flag specified */ int trans_assign_trans_id(struct gsm_network *net, struct gsm_subscriber *subscr, uint8_t protocol, uint8_t ti_flag) { struct gsm_trans *trans; unsigned int used_tid_bitmask = 0; int i, j, h; if (ti_flag) ti_flag = 0x8; /* generate bitmask of already-used TIDs for this (subscr,proto) */ llist_for_each_entry(trans, &net->trans_list, entry) { if (trans->subscr != subscr || trans->protocol != protocol || trans->transaction_id == 0xff) continue; used_tid_bitmask |= (1 << trans->transaction_id); } /* find a new one, trying to go in a 'circular' pattern */ for (h = 6; h > 0; h--) if (used_tid_bitmask & (1 << (h | ti_flag))) break; for (i = 0; i < 7; i++) { j = ((h + i) % 7) | ti_flag; if ((used_tid_bitmask & (1 << j)) == 0) return j; } return -1; } int trans_has_conn(const struct gsm_subscriber_connection *conn) { struct gsm_trans *trans; llist_for_each_entry(trans, &conn->bts->network->trans_list, entry) if (trans->conn == conn) return 1; return 0; } openbsc-0.15.0/openbsc/src/libmsc/ussd.c000066400000000000000000000054751265565154000200650ustar00rootroot00000000000000/* Network-specific handling of mobile-originated USSDs. */ /* (C) 2008-2009 by Harald Welte * (C) 2008, 2009 by Holger Hans Peter Freyther * (C) 2009 by Mike Haben * * 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 . * */ /* This module defines the network-specific handling of mobile-originated USSD messages. */ #include #include #include #include #include #include #include #include /* Declarations of USSD strings to be recognised */ const char USSD_TEXT_OWN_NUMBER[] = "*#100#"; /* Forward declarations of network-specific handler functions */ static int send_own_number(struct gsm_subscriber_connection *conn, const struct msgb *msg, const struct ussd_request *req); /* Entrypoint - handler function common to all mobile-originated USSDs */ int handle_rcv_ussd(struct gsm_subscriber_connection *conn, struct msgb *msg) { int rc; struct ussd_request req; struct gsm48_hdr *gh; memset(&req, 0, sizeof(req)); gh = msgb_l3(msg); rc = gsm0480_decode_ussd_request(gh, msgb_l3len(msg), &req); if (!rc) { DEBUGP(DMM, "Unhandled SS\n"); rc = gsm0480_send_ussd_reject(conn, msg, &req); msc_release_connection(conn); return rc; } /* Release-Complete */ if (req.text[0] == '\0') return 0; if (!strcmp(USSD_TEXT_OWN_NUMBER, (const char *)req.text)) { DEBUGP(DMM, "USSD: Own number requested\n"); rc = send_own_number(conn, msg, &req); } else { DEBUGP(DMM, "Unhandled USSD %s\n", req.text); rc = gsm0480_send_ussd_reject(conn, msg, &req); } /* check if we can release it */ msc_release_connection(conn); return rc; } /* A network-specific handler function */ static int send_own_number(struct gsm_subscriber_connection *conn, const struct msgb *msg, const struct ussd_request *req) { char *own_number = conn->subscr->extension; char response_string[GSM_EXTENSION_LENGTH + 20]; /* Need trailing CR as EOT character */ snprintf(response_string, sizeof(response_string), "Your extension is %s\r", own_number); return gsm0480_send_ussd_response(conn, msg, response_string, req); } openbsc-0.15.0/openbsc/src/libmsc/vty_interface_layer3.c000066400000000000000000000745341265565154000232320ustar00rootroot00000000000000/* OpenBSC interface to quagga VTY */ /* (C) 2009 by Harald Welte * (C) 2009-2011 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "meas_feed.h" extern struct gsm_network *gsmnet_from_vty(struct vty *v); static void subscr_dump_full_vty(struct vty *vty, struct gsm_subscriber *subscr) { int rc; int reqs; struct gsm_auth_info ainfo; struct gsm_auth_tuple atuple; struct llist_head *entry; char expire_time[200]; vty_out(vty, " ID: %llu, Authorized: %d%s", subscr->id, subscr->authorized, VTY_NEWLINE); if (strlen(subscr->name)) vty_out(vty, " Name: '%s'%s", subscr->name, VTY_NEWLINE); if (strlen(subscr->extension)) vty_out(vty, " Extension: %s%s", subscr->extension, VTY_NEWLINE); vty_out(vty, " LAC: %d/0x%x%s", subscr->lac, subscr->lac, VTY_NEWLINE); vty_out(vty, " IMSI: %s%s", subscr->imsi, VTY_NEWLINE); if (subscr->tmsi != GSM_RESERVED_TMSI) vty_out(vty, " TMSI: %08X%s", subscr->tmsi, VTY_NEWLINE); rc = db_get_authinfo_for_subscr(&ainfo, subscr); if (!rc) { vty_out(vty, " A3A8 algorithm id: %d%s", ainfo.auth_algo, VTY_NEWLINE); vty_out(vty, " A3A8 Ki: %s%s", osmo_hexdump(ainfo.a3a8_ki, ainfo.a3a8_ki_len), VTY_NEWLINE); } rc = db_get_lastauthtuple_for_subscr(&atuple, subscr); if (!rc) { vty_out(vty, " A3A8 last tuple (used %d times):%s", atuple.use_count, VTY_NEWLINE); vty_out(vty, " seq # : %d%s", atuple.key_seq, VTY_NEWLINE); vty_out(vty, " RAND : %s%s", osmo_hexdump(atuple.rand, sizeof(atuple.rand)), VTY_NEWLINE); vty_out(vty, " SRES : %s%s", osmo_hexdump(atuple.sres, sizeof(atuple.sres)), VTY_NEWLINE); vty_out(vty, " Kc : %s%s", osmo_hexdump(atuple.kc, sizeof(atuple.kc)), VTY_NEWLINE); } /* print the expiration time of a subscriber */ strftime(expire_time, sizeof(expire_time), "%a, %d %b %Y %T %z", localtime(&subscr->expire_lu)); expire_time[sizeof(expire_time) - 1] = '\0'; vty_out(vty, " Expiration Time: %s%s", expire_time, VTY_NEWLINE); reqs = 0; llist_for_each(entry, &subscr->requests) reqs += 1; vty_out(vty, " Paging: %s paging Requests: %d%s", subscr->is_paging ? "is" : "not", reqs, VTY_NEWLINE); vty_out(vty, " Use count: %u%s", subscr->use_count, VTY_NEWLINE); } /* Subscriber */ DEFUN(show_subscr_cache, show_subscr_cache_cmd, "show subscriber cache", SHOW_STR "Show information about subscribers\n" "Display contents of subscriber cache\n") { struct gsm_subscriber *subscr; llist_for_each_entry(subscr, &active_subscribers, entry) { vty_out(vty, " Subscriber:%s", VTY_NEWLINE); subscr_dump_full_vty(vty, subscr); } return CMD_SUCCESS; } DEFUN(sms_send_pend, sms_send_pend_cmd, "sms send pending", "SMS related comamnds\n" "SMS Sending related commands\n" "Send all pending SMS") { struct gsm_network *gsmnet = gsmnet_from_vty(vty); struct gsm_sms *sms; int id = 0; while (1) { sms = db_sms_get_unsent_by_subscr(gsmnet, id, UINT_MAX); if (!sms) break; gsm411_send_sms_subscr(sms->receiver, sms); id = sms->receiver->id + 1; } return CMD_SUCCESS; } static int _send_sms_str(struct gsm_subscriber *receiver, struct gsm_subscriber *sender, char *str, uint8_t tp_pid) { struct gsm_sms *sms; sms = sms_from_text(receiver, sender, 0, str); sms->protocol_id = tp_pid; /* store in database for the queue */ if (db_sms_store(sms) != 0) { LOGP(DLSMS, LOGL_ERROR, "Failed to store SMS in Database\n"); sms_free(sms); return CMD_WARNING; } sms_free(sms); sms_queue_trigger(receiver->group->net->sms_queue); return CMD_SUCCESS; } static struct gsm_subscriber *get_subscr_by_argv(struct gsm_network *gsmnet, const char *type, const char *id) { if (!strcmp(type, "extension")) return subscr_get_by_extension(gsmnet->subscr_group, id); else if (!strcmp(type, "imsi")) return subscr_get_by_imsi(gsmnet->subscr_group, id); else if (!strcmp(type, "tmsi")) return subscr_get_by_tmsi(gsmnet->subscr_group, atoi(id)); else if (!strcmp(type, "id")) return subscr_get_by_id(gsmnet->subscr_group, atoi(id)); return NULL; } #define SUBSCR_TYPES "(extension|imsi|tmsi|id)" #define SUBSCR_HELP "Operations on a Subscriber\n" \ "Identify subscriber by his extension (phone number)\n" \ "Identify subscriber by his IMSI\n" \ "Identify subscriber by his TMSI\n" \ "Identify subscriber by his database ID\n" \ "Identifier for the subscriber\n" DEFUN(show_subscr, show_subscr_cmd, "show subscriber " SUBSCR_TYPES " ID", SHOW_STR SUBSCR_HELP) { struct gsm_network *gsmnet = gsmnet_from_vty(vty); struct gsm_subscriber *subscr = get_subscr_by_argv(gsmnet, argv[0], argv[1]); if (!subscr) { vty_out(vty, "%% No subscriber found for %s %s%s", argv[0], argv[1], VTY_NEWLINE); return CMD_WARNING; } subscr_dump_full_vty(vty, subscr); subscr_put(subscr); return CMD_SUCCESS; } DEFUN(subscriber_create, subscriber_create_cmd, "subscriber create imsi ID", "Operations on a Subscriber\n" \ "Create new subscriber\n" \ "Identify the subscriber by his IMSI\n" \ "Identifier for the subscriber\n") { struct gsm_network *gsmnet = gsmnet_from_vty(vty); struct gsm_subscriber *subscr; subscr = subscr_create_subscriber(gsmnet->subscr_group, argv[0]); if (!subscr) { vty_out(vty, "%% No subscriber created for IMSI %s%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } /* Show info about the created subscriber. */ subscr_dump_full_vty(vty, subscr); subscr_put(subscr); return CMD_SUCCESS; } DEFUN(subscriber_send_pending_sms, subscriber_send_pending_sms_cmd, "subscriber " SUBSCR_TYPES " ID sms pending-send", SUBSCR_HELP "SMS Operations\n" "Send pending SMS\n") { struct gsm_network *gsmnet = gsmnet_from_vty(vty); struct gsm_subscriber *subscr; struct gsm_sms *sms; subscr = get_subscr_by_argv(gsmnet, argv[0], argv[1]); if (!subscr) { vty_out(vty, "%% No subscriber found for %s %s%s", argv[0], argv[1], VTY_NEWLINE); return CMD_WARNING; } sms = db_sms_get_unsent_by_subscr(gsmnet, subscr->id, UINT_MAX); if (sms) gsm411_send_sms_subscr(sms->receiver, sms); subscr_put(subscr); return CMD_SUCCESS; } DEFUN(subscriber_send_sms, subscriber_send_sms_cmd, "subscriber " SUBSCR_TYPES " ID sms sender " SUBSCR_TYPES " SENDER_ID send .LINE", SUBSCR_HELP "SMS Operations\n" SUBSCR_HELP "Send SMS\n" "Actual SMS Text\n") { struct gsm_network *gsmnet = gsmnet_from_vty(vty); struct gsm_subscriber *subscr = get_subscr_by_argv(gsmnet, argv[0], argv[1]); struct gsm_subscriber *sender = get_subscr_by_argv(gsmnet, argv[2], argv[3]); char *str; int rc; if (!subscr) { vty_out(vty, "%% No subscriber found for %s %s%s", argv[0], argv[1], VTY_NEWLINE); rc = CMD_WARNING; goto err; } if (!sender) { vty_out(vty, "%% No sender found for %s %s%s", argv[2], argv[3], VTY_NEWLINE); rc = CMD_WARNING; goto err; } str = argv_concat(argv, argc, 4); rc = _send_sms_str(subscr, sender, str, 0); talloc_free(str); err: if (sender) subscr_put(sender); if (subscr) subscr_put(subscr); return rc; } DEFUN(subscriber_silent_sms, subscriber_silent_sms_cmd, "subscriber " SUBSCR_TYPES " ID silent-sms sender " SUBSCR_TYPES " SENDER_ID send .LINE", SUBSCR_HELP "Silent SMS Operations\n" SUBSCR_HELP "Send SMS\n" "Actual SMS Text\n") { struct gsm_network *gsmnet = gsmnet_from_vty(vty); struct gsm_subscriber *subscr = get_subscr_by_argv(gsmnet, argv[0], argv[1]); struct gsm_subscriber *sender = get_subscr_by_argv(gsmnet, argv[2], argv[3]); char *str; int rc; if (!subscr) { vty_out(vty, "%% No subscriber found for %s %s%s", argv[0], argv[1], VTY_NEWLINE); rc = CMD_WARNING; goto err; } if (!sender) { vty_out(vty, "%% No sender found for %s %s%s", argv[2], argv[3], VTY_NEWLINE); rc = CMD_WARNING; goto err; } str = argv_concat(argv, argc, 4); rc = _send_sms_str(subscr, sender, str, 64); talloc_free(str); err: if (sender) subscr_put(sender); if (subscr) subscr_put(subscr); return rc; } #define CHAN_TYPES "(any|tch/f|tch/any|sdcch)" #define CHAN_TYPE_HELP \ "Any channel\n" \ "TCH/F channel\n" \ "Any TCH channel\n" \ "SDCCH channel\n" DEFUN(subscriber_silent_call_start, subscriber_silent_call_start_cmd, "subscriber " SUBSCR_TYPES " ID silent-call start (any|tch/f|tch/any|sdcch)", SUBSCR_HELP "Silent call operation\n" "Start silent call\n" CHAN_TYPE_HELP) { struct gsm_network *gsmnet = gsmnet_from_vty(vty); struct gsm_subscriber *subscr = get_subscr_by_argv(gsmnet, argv[0], argv[1]); int rc, type; if (!subscr) { vty_out(vty, "%% No subscriber found for %s %s%s", argv[0], argv[1], VTY_NEWLINE); return CMD_WARNING; } if (!strcmp(argv[2], "tch/f")) type = RSL_CHANNEED_TCH_F; else if (!strcmp(argv[2], "tch/any")) type = RSL_CHANNEED_TCH_ForH; else if (!strcmp(argv[2], "sdcch")) type = RSL_CHANNEED_SDCCH; else type = RSL_CHANNEED_ANY; /* Defaults to ANY */ rc = gsm_silent_call_start(subscr, vty, type); if (rc <= 0) { vty_out(vty, "%% Subscriber not attached%s", VTY_NEWLINE); subscr_put(subscr); return CMD_WARNING; } subscr_put(subscr); return CMD_SUCCESS; } DEFUN(subscriber_silent_call_stop, subscriber_silent_call_stop_cmd, "subscriber " SUBSCR_TYPES " ID silent-call stop", SUBSCR_HELP "Silent call operation\n" "Stop silent call\n" CHAN_TYPE_HELP) { struct gsm_network *gsmnet = gsmnet_from_vty(vty); struct gsm_subscriber *subscr = get_subscr_by_argv(gsmnet, argv[0], argv[1]); int rc; if (!subscr) { vty_out(vty, "%% No subscriber found for %s %s%s", argv[0], argv[1], VTY_NEWLINE); return CMD_WARNING; } rc = gsm_silent_call_stop(subscr); if (rc < 0) { subscr_put(subscr); return CMD_WARNING; } subscr_put(subscr); return CMD_SUCCESS; } DEFUN(subscriber_ussd_notify, subscriber_ussd_notify_cmd, "subscriber " SUBSCR_TYPES " ID ussd-notify (0|1|2) .TEXT", SUBSCR_HELP "Send a USSD notify to the subscriber\n" "Alerting Level 0\n" "Alerting Level 1\n" "Alerting Level 2\n" "Text of USSD message to send\n") { char *text; struct gsm_subscriber_connection *conn; struct gsm_network *gsmnet = gsmnet_from_vty(vty); struct gsm_subscriber *subscr = get_subscr_by_argv(gsmnet, argv[0], argv[1]); int level; if (!subscr) { vty_out(vty, "%% No subscriber found for %s %s%s", argv[0], argv[1], VTY_NEWLINE); return CMD_WARNING; } level = atoi(argv[2]); text = argv_concat(argv, argc, 3); if (!text) { subscr_put(subscr); return CMD_WARNING; } conn = connection_for_subscr(subscr); if (!conn) { vty_out(vty, "%% An active connection is required for %s %s%s", argv[0], argv[1], VTY_NEWLINE); subscr_put(subscr); talloc_free(text); return CMD_WARNING; } gsm0480_send_ussdNotify(conn, level, text); gsm0480_send_releaseComplete(conn); subscr_put(subscr); talloc_free(text); return CMD_SUCCESS; } DEFUN(ena_subscr_delete, ena_subscr_delete_cmd, "subscriber " SUBSCR_TYPES " ID delete", SUBSCR_HELP "Delete subscriber in HLR\n") { int rc; struct gsm_network *gsmnet = gsmnet_from_vty(vty); struct gsm_subscriber *subscr = get_subscr_by_argv(gsmnet, argv[0], argv[1]); if (!subscr) { vty_out(vty, "%% No subscriber found for %s %s%s", argv[0], argv[1], VTY_NEWLINE); return CMD_WARNING; } if (subscr->use_count != 1) { vty_out(vty, "Removing active subscriber%s", VTY_NEWLINE); } rc = db_subscriber_delete(subscr); subscr_put(subscr); if (rc != 0) { vty_out(vty, "Failed to remove subscriber%s", VTY_NEWLINE); return CMD_WARNING; } return CMD_SUCCESS; } DEFUN(ena_subscr_authorized, ena_subscr_authorized_cmd, "subscriber " SUBSCR_TYPES " ID authorized (0|1)", SUBSCR_HELP "(De-)Authorize subscriber in HLR\n" "Subscriber should NOT be authorized\n" "Subscriber should be authorized\n") { struct gsm_network *gsmnet = gsmnet_from_vty(vty); struct gsm_subscriber *subscr = get_subscr_by_argv(gsmnet, argv[0], argv[1]); if (!subscr) { vty_out(vty, "%% No subscriber found for %s %s%s", argv[0], argv[1], VTY_NEWLINE); return CMD_WARNING; } subscr->authorized = atoi(argv[2]); db_sync_subscriber(subscr); subscr_put(subscr); return CMD_SUCCESS; } DEFUN(ena_subscr_name, ena_subscr_name_cmd, "subscriber " SUBSCR_TYPES " ID name .NAME", SUBSCR_HELP "Set the name of the subscriber\n" "Name of the Subscriber\n") { struct gsm_network *gsmnet = gsmnet_from_vty(vty); struct gsm_subscriber *subscr = get_subscr_by_argv(gsmnet, argv[0], argv[1]); char *name; if (!subscr) { vty_out(vty, "%% No subscriber found for %s %s%s", argv[0], argv[1], VTY_NEWLINE); return CMD_WARNING; } name = argv_concat(argv, argc, 2); if (!name) { subscr_put(subscr); return CMD_WARNING; } if (strlen(name) > sizeof(subscr->name)-1) { vty_out(vty, "%% NAME is too long, max. %zu characters are allowed%s", sizeof(subscr->name)-1, VTY_NEWLINE); return CMD_WARNING; } strncpy(subscr->name, name, sizeof(subscr->name)); talloc_free(name); db_sync_subscriber(subscr); subscr_put(subscr); return CMD_SUCCESS; } DEFUN(ena_subscr_extension, ena_subscr_extension_cmd, "subscriber " SUBSCR_TYPES " ID extension EXTENSION", SUBSCR_HELP "Set the extension (phone number) of the subscriber\n" "Extension (phone number)\n") { struct gsm_network *gsmnet = gsmnet_from_vty(vty); struct gsm_subscriber *subscr = get_subscr_by_argv(gsmnet, argv[0], argv[1]); const char *ext = argv[2]; if (!subscr) { vty_out(vty, "%% No subscriber found for %s %s%s", argv[0], argv[1], VTY_NEWLINE); return CMD_WARNING; } if (strlen(ext) > sizeof(subscr->extension)-1) { vty_out(vty, "%% EXTENSION is too long, max. %zu characters are allowed%s", sizeof(subscr->extension)-1, VTY_NEWLINE); return CMD_WARNING; } strncpy(subscr->extension, ext, sizeof(subscr->extension)); db_sync_subscriber(subscr); subscr_put(subscr); return CMD_SUCCESS; } DEFUN(ena_subscr_handover, ena_subscr_handover_cmd, "subscriber " SUBSCR_TYPES " ID handover BTS_NR", SUBSCR_HELP "Handover the active connection\n" "Number of the BTS to handover to\n") { int ret; struct gsm_subscriber_connection *conn; struct gsm_bts *bts; struct gsm_network *gsmnet = gsmnet_from_vty(vty); struct gsm_subscriber *subscr = get_subscr_by_argv(gsmnet, argv[0], argv[1]); if (!subscr) { vty_out(vty, "%% No subscriber found for %s %s.%s", argv[0], argv[1], VTY_NEWLINE); return CMD_WARNING; } conn = connection_for_subscr(subscr); if (!conn) { vty_out(vty, "%% No active connection for subscriber %s %s.%s", argv[0], argv[1], VTY_NEWLINE); subscr_put(subscr); return CMD_WARNING; } bts = gsm_bts_num(gsmnet, atoi(argv[2])); if (!bts) { vty_out(vty, "%% BTS with number(%d) could not be found.%s", atoi(argv[2]), VTY_NEWLINE); subscr_put(subscr); return CMD_WARNING; } /* now start the handover */ ret = bsc_handover_start(conn->lchan, bts); if (ret != 0) { vty_out(vty, "%% Handover failed with errno %d.%s", ret, VTY_NEWLINE); } else { vty_out(vty, "%% Handover started from %s", gsm_lchan_name(conn->lchan)); vty_out(vty, " to %s.%s", gsm_lchan_name(conn->ho_lchan), VTY_NEWLINE); } subscr_put(subscr); return CMD_SUCCESS; } #define A3A8_ALG_TYPES "(none|xor|comp128v1)" #define A3A8_ALG_HELP \ "Use No A3A8 algorithm\n" \ "Use XOR algorithm\n" \ "Use COMP128v1 algorithm\n" DEFUN(ena_subscr_a3a8, ena_subscr_a3a8_cmd, "subscriber " SUBSCR_TYPES " ID a3a8 " A3A8_ALG_TYPES " [KI]", SUBSCR_HELP "Set a3a8 parameters for the subscriber\n" A3A8_ALG_HELP "Encryption Key Ki\n") { struct gsm_network *gsmnet = gsmnet_from_vty(vty); struct gsm_subscriber *subscr = get_subscr_by_argv(gsmnet, argv[0], argv[1]); const char *alg_str = argv[2]; const char *ki_str = argc == 4 ? argv[3] : NULL; struct gsm_auth_info ainfo; int rc, minlen, maxlen; if (!subscr) { vty_out(vty, "%% No subscriber found for %s %s%s", argv[0], argv[1], VTY_NEWLINE); return CMD_WARNING; } if (!strcasecmp(alg_str, "none")) { ainfo.auth_algo = AUTH_ALGO_NONE; minlen = maxlen = 0; } else if (!strcasecmp(alg_str, "xor")) { ainfo.auth_algo = AUTH_ALGO_XOR; minlen = A38_XOR_MIN_KEY_LEN; maxlen = A38_XOR_MAX_KEY_LEN; } else if (!strcasecmp(alg_str, "comp128v1")) { ainfo.auth_algo = AUTH_ALGO_COMP128v1; minlen = maxlen = A38_COMP128_KEY_LEN; } else { /* Unknown method */ subscr_put(subscr); vty_out(vty, "%% Unknown auth method %s%s", alg_str, VTY_NEWLINE); return CMD_WARNING; } if (ki_str) { rc = osmo_hexparse(ki_str, ainfo.a3a8_ki, sizeof(ainfo.a3a8_ki)); if ((rc > maxlen) || (rc < minlen)) { subscr_put(subscr); vty_out(vty, "%% Wrong Ki `%s'%s", ki_str, VTY_NEWLINE); return CMD_WARNING; } ainfo.a3a8_ki_len = rc; } else { ainfo.a3a8_ki_len = 0; if (minlen) { subscr_put(subscr); vty_out(vty, "%% Missing Ki argument%s", VTY_NEWLINE); return CMD_WARNING; } } rc = db_sync_authinfo_for_subscr( ainfo.auth_algo == AUTH_ALGO_NONE ? NULL : &ainfo, subscr); /* the last tuple probably invalid with the new auth settings */ db_sync_lastauthtuple_for_subscr(NULL, subscr); subscr_put(subscr); if (rc) { vty_out(vty, "%% Operation has failed%s", VTY_NEWLINE); return CMD_WARNING; } return CMD_SUCCESS; } DEFUN(subscriber_purge, subscriber_purge_cmd, "subscriber purge-inactive", "Operations on a Subscriber\n" "Purge subscribers with a zero use count.\n") { struct gsm_network *net = gsmnet_from_vty(vty); int purged; purged = subscr_purge_inactive(net->subscr_group); vty_out(vty, "%d subscriber(s) were purged.%s", purged, VTY_NEWLINE); return CMD_SUCCESS; } DEFUN(subscriber_update, subscriber_update_cmd, "subscriber " SUBSCR_TYPES " ID update", SUBSCR_HELP "Update the subscriber data from the dabase.\n") { struct gsm_network *gsmnet = gsmnet_from_vty(vty); struct gsm_subscriber *subscr = get_subscr_by_argv(gsmnet, argv[0], argv[1]); if (!subscr) { vty_out(vty, "%% No subscriber found for %s %s%s", argv[0], argv[1], VTY_NEWLINE); return CMD_WARNING; } subscr_update_from_db(subscr); subscr_put(subscr); return CMD_SUCCESS; } static int scall_cbfn(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct scall_signal_data *sigdata = signal_data; struct vty *vty = sigdata->data; switch (signal) { case S_SCALL_SUCCESS: vty_out(vty, "%% silent call on ARFCN %u timeslot %u%s", sigdata->conn->lchan->ts->trx->arfcn, sigdata->conn->lchan->ts->nr, VTY_NEWLINE); break; case S_SCALL_EXPIRED: vty_out(vty, "%% silent call expired paging%s", VTY_NEWLINE); break; } return 0; } DEFUN(show_stats, show_stats_cmd, "show statistics", SHOW_STR "Display network statistics\n") { struct gsm_network *net = gsmnet_from_vty(vty); openbsc_vty_print_statistics(vty, net); vty_out(vty, "Location Update : %lu attach, %lu normal, %lu periodic%s", osmo_counter_get(net->stats.loc_upd_type.attach), osmo_counter_get(net->stats.loc_upd_type.normal), osmo_counter_get(net->stats.loc_upd_type.periodic), VTY_NEWLINE); vty_out(vty, "IMSI Detach Indications : %lu%s", osmo_counter_get(net->stats.loc_upd_type.detach), VTY_NEWLINE); vty_out(vty, "Location Update Response: %lu accept, %lu reject%s", osmo_counter_get(net->stats.loc_upd_resp.accept), osmo_counter_get(net->stats.loc_upd_resp.reject), VTY_NEWLINE); vty_out(vty, "Handover : %lu attempted, %lu no_channel, %lu timeout, " "%lu completed, %lu failed%s", osmo_counter_get(net->stats.handover.attempted), osmo_counter_get(net->stats.handover.no_channel), osmo_counter_get(net->stats.handover.timeout), osmo_counter_get(net->stats.handover.completed), osmo_counter_get(net->stats.handover.failed), VTY_NEWLINE); vty_out(vty, "SMS MO : %lu submitted, %lu no receiver%s", osmo_counter_get(net->stats.sms.submitted), osmo_counter_get(net->stats.sms.no_receiver), VTY_NEWLINE); vty_out(vty, "SMS MT : %lu delivered, %lu no memory, %lu other error%s", osmo_counter_get(net->stats.sms.delivered), osmo_counter_get(net->stats.sms.rp_err_mem), osmo_counter_get(net->stats.sms.rp_err_other), VTY_NEWLINE); vty_out(vty, "MO Calls : %lu setup, %lu connect ack%s", osmo_counter_get(net->stats.call.mo_setup), osmo_counter_get(net->stats.call.mo_connect_ack), VTY_NEWLINE); vty_out(vty, "MT Calls : %lu setup, %lu connect%s", osmo_counter_get(net->stats.call.mt_setup), osmo_counter_get(net->stats.call.mt_connect), VTY_NEWLINE); return CMD_SUCCESS; } DEFUN(show_smsqueue, show_smsqueue_cmd, "show sms-queue", SHOW_STR "Display SMSqueue statistics\n") { struct gsm_network *net = gsmnet_from_vty(vty); sms_queue_stats(net->sms_queue, vty); return CMD_SUCCESS; } DEFUN(smsqueue_trigger, smsqueue_trigger_cmd, "sms-queue trigger", "SMS Queue\n" "Trigger sending messages\n") { struct gsm_network *net = gsmnet_from_vty(vty); sms_queue_trigger(net->sms_queue); return CMD_SUCCESS; } DEFUN(smsqueue_max, smsqueue_max_cmd, "sms-queue max-pending <1-500>", "SMS Queue\n" "SMS to deliver in parallel\n" "Amount\n") { struct gsm_network *net = gsmnet_from_vty(vty); sms_queue_set_max_pending(net->sms_queue, atoi(argv[0])); return CMD_SUCCESS; } DEFUN(smsqueue_clear, smsqueue_clear_cmd, "sms-queue clear", "SMS Queue\n" "Clear the queue of pending SMS\n") { struct gsm_network *net = gsmnet_from_vty(vty); sms_queue_clear(net->sms_queue); return CMD_SUCCESS; } DEFUN(smsqueue_fail, smsqueue_fail_cmd, "sms-queue max-failure <1-500>", "SMS Queue\n" "Maximum amount of delivery failures\n" "Amount\n") { struct gsm_network *net = gsmnet_from_vty(vty); sms_queue_set_max_failure(net->sms_queue, atoi(argv[0])); return CMD_SUCCESS; } DEFUN(cfg_mncc_int, cfg_mncc_int_cmd, "mncc-int", "Configure internal MNCC handler") { vty->node = MNCC_INT_NODE; return CMD_SUCCESS; } static struct cmd_node mncc_int_node = { MNCC_INT_NODE, "%s(config-mncc-int)# ", 1, }; static const struct value_string tchf_codec_names[] = { { GSM48_CMODE_SPEECH_V1, "fr" }, { GSM48_CMODE_SPEECH_EFR, "efr" }, { GSM48_CMODE_SPEECH_AMR, "amr" }, { 0, NULL } }; static const struct value_string tchh_codec_names[] = { { GSM48_CMODE_SPEECH_V1, "hr" }, { GSM48_CMODE_SPEECH_AMR, "amr" }, { 0, NULL } }; static int config_write_mncc_int(struct vty *vty) { uint16_t meas_port; char *meas_host; const char *meas_scenario; meas_feed_cfg_get(&meas_host, &meas_port); meas_scenario = meas_feed_scenario_get(); vty_out(vty, "mncc-int%s", VTY_NEWLINE); vty_out(vty, " default-codec tch-f %s%s", get_value_string(tchf_codec_names, mncc_int.def_codec[0]), VTY_NEWLINE); vty_out(vty, " default-codec tch-h %s%s", get_value_string(tchh_codec_names, mncc_int.def_codec[1]), VTY_NEWLINE); if (meas_port) vty_out(vty, " meas-feed destination %s %u%s", meas_host, meas_port, VTY_NEWLINE); if (strlen(meas_scenario) > 0) vty_out(vty, " meas-feed scenario %s%s", meas_scenario, VTY_NEWLINE); return CMD_SUCCESS; } DEFUN(mnccint_def_codec_f, mnccint_def_codec_f_cmd, "default-codec tch-f (fr|efr|amr)", "Set default codec\n" "Codec for TCH/F\n" "Full-Rate\n" "Enhanced Full-Rate\n" "Adaptive Multi-Rate\n") { mncc_int.def_codec[0] = get_string_value(tchf_codec_names, argv[0]); return CMD_SUCCESS; } DEFUN(mnccint_def_codec_h, mnccint_def_codec_h_cmd, "default-codec tch-h (hr|amr)", "Set default codec\n" "Codec for TCH/H\n" "Half-Rate\n" "Adaptive Multi-Rate\n") { mncc_int.def_codec[1] = get_string_value(tchh_codec_names, argv[0]); return CMD_SUCCESS; } #define OBSOLETE_MSG "Obsolete\n" /* this is just for backwards compatibility as the sms code moved into * libosmocore and old config files no longer parse... */ DEFUN_DEPRECATED(log_level_sms, log_level_sms_cmd, "logging level sms (everything|debug|info|notice|error|fatal)", ".HIDDEN\n" OBSOLETE_MSG OBSOLETE_MSG OBSOLETE_MSG OBSOLETE_MSG OBSOLETE_MSG OBSOLETE_MSG OBSOLETE_MSG OBSOLETE_MSG) { vty_out(vty, "%% 'logging level sms' is now called 'logging level " "lsms', please update your config %s", VTY_NEWLINE); return CMD_SUCCESS; } #define MEAS_STR "Measurement export related\n" DEFUN(mnccint_meas_feed, mnccint_meas_feed_cmd, "meas-feed destination ADDR <0-65535>", MEAS_STR "destination\n" "address or hostname\n" "port number\n") { int rc; rc = meas_feed_cfg_set(argv[0], atoi(argv[1])); if (rc < 0) return CMD_WARNING; return CMD_SUCCESS; } DEFUN(meas_feed_scenario, meas_feed_scenario_cmd, "meas-feed scenario NAME", MEAS_STR "scenario\n" "Name up to 31 characters included in report\n") { meas_feed_scenario_set(argv[0]); return CMD_SUCCESS; } DEFUN(logging_fltr_imsi, logging_fltr_imsi_cmd, "logging filter imsi IMSI", LOGGING_STR FILTER_STR "Filter log messages by IMSI\n" "IMSI to be used as filter\n") { struct gsm_subscriber *subscr; struct gsm_network *gsmnet = gsmnet_from_vty(vty); struct log_target *tgt = osmo_log_vty2tgt(vty); if (!tgt) return CMD_WARNING; subscr = subscr_get_by_imsi(gsmnet->subscr_group, argv[0]); if (!subscr) { vty_out(vty, "%%no subscriber with IMSI(%s)%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } log_set_imsi_filter(tgt, subscr); return CMD_SUCCESS; } static struct cmd_node nitb_node = { NITB_NODE, "%s(config-nitb)# ", 1, }; DEFUN(cfg_nitb, cfg_nitb_cmd, "nitb", "Configure NITB options") { vty->node = NITB_NODE; return CMD_SUCCESS; } DEFUN(cfg_nitb_subscr_create, cfg_nitb_subscr_create_cmd, "subscriber-create-on-demand", "Make a new record when a subscriber is first seen.\n") { struct gsm_network *gsmnet = gsmnet_from_vty(vty); gsmnet->create_subscriber = 1; return CMD_SUCCESS; } DEFUN(cfg_nitb_no_subscr_create, cfg_nitb_no_subscr_create_cmd, "no subscriber-create-on-demand", NO_STR "Make a new record when a subscriber is first seen.\n") { struct gsm_network *gsmnet = gsmnet_from_vty(vty); gsmnet->create_subscriber = 0; return CMD_SUCCESS; } DEFUN(cfg_nitb_assign_tmsi, cfg_nitb_assign_tmsi_cmd, "assign-tmsi", "Assign TMSI during Location Updating.\n") { struct gsm_network *gsmnet = gsmnet_from_vty(vty); gsmnet->avoid_tmsi = 0; return CMD_SUCCESS; } DEFUN(cfg_nitb_no_assign_tmsi, cfg_nitb_no_assign_tmsi_cmd, "no assign-tmsi", NO_STR "Assign TMSI during Location Updating.\n") { struct gsm_network *gsmnet = gsmnet_from_vty(vty); gsmnet->avoid_tmsi = 1; return CMD_SUCCESS; } static int config_write_nitb(struct vty *vty) { struct gsm_network *gsmnet = gsmnet_from_vty(vty); vty_out(vty, "nitb%s", VTY_NEWLINE); vty_out(vty, " %ssubscriber-create-on-demand%s", gsmnet->create_subscriber ? "" : "no ", VTY_NEWLINE); vty_out(vty, " %sassign-tmsi%s", gsmnet->avoid_tmsi ? "no " : "", VTY_NEWLINE); return CMD_SUCCESS; } int bsc_vty_init_extra(void) { osmo_signal_register_handler(SS_SCALL, scall_cbfn, NULL); install_element_ve(&show_subscr_cmd); install_element_ve(&show_subscr_cache_cmd); install_element_ve(&sms_send_pend_cmd); install_element_ve(&subscriber_create_cmd); install_element_ve(&subscriber_send_sms_cmd); install_element_ve(&subscriber_silent_sms_cmd); install_element_ve(&subscriber_silent_call_start_cmd); install_element_ve(&subscriber_silent_call_stop_cmd); install_element_ve(&subscriber_ussd_notify_cmd); install_element_ve(&subscriber_update_cmd); install_element_ve(&show_stats_cmd); install_element_ve(&show_smsqueue_cmd); install_element_ve(&logging_fltr_imsi_cmd); install_element(ENABLE_NODE, &ena_subscr_delete_cmd); install_element(ENABLE_NODE, &ena_subscr_name_cmd); install_element(ENABLE_NODE, &ena_subscr_extension_cmd); install_element(ENABLE_NODE, &ena_subscr_authorized_cmd); install_element(ENABLE_NODE, &ena_subscr_a3a8_cmd); install_element(ENABLE_NODE, &ena_subscr_handover_cmd); install_element(ENABLE_NODE, &subscriber_purge_cmd); install_element(ENABLE_NODE, &smsqueue_trigger_cmd); install_element(ENABLE_NODE, &smsqueue_max_cmd); install_element(ENABLE_NODE, &smsqueue_clear_cmd); install_element(ENABLE_NODE, &smsqueue_fail_cmd); install_element(ENABLE_NODE, &subscriber_send_pending_sms_cmd); install_element(ENABLE_NODE, &meas_feed_scenario_cmd); install_element(CONFIG_NODE, &cfg_mncc_int_cmd); install_node(&mncc_int_node, config_write_mncc_int); vty_install_default(MNCC_INT_NODE); install_element(MNCC_INT_NODE, &mnccint_def_codec_f_cmd); install_element(MNCC_INT_NODE, &mnccint_def_codec_h_cmd); install_element(MNCC_INT_NODE, &mnccint_meas_feed_cmd); install_element(MNCC_INT_NODE, &meas_feed_scenario_cmd); install_element(CFG_LOG_NODE, &log_level_sms_cmd); install_element(CFG_LOG_NODE, &logging_fltr_imsi_cmd); install_element(CONFIG_NODE, &cfg_nitb_cmd); install_node(&nitb_node, config_write_nitb); install_element(NITB_NODE, &cfg_nitb_subscr_create_cmd); install_element(NITB_NODE, &cfg_nitb_no_subscr_create_cmd); install_element(NITB_NODE, &cfg_nitb_assign_tmsi_cmd); install_element(NITB_NODE, &cfg_nitb_no_assign_tmsi_cmd); return 0; } openbsc-0.15.0/openbsc/src/libtrau/000077500000000000000000000000001265565154000171215ustar00rootroot00000000000000openbsc-0.15.0/openbsc/src/libtrau/Makefile.am000066400000000000000000000005701265565154000211570ustar00rootroot00000000000000AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(LIBOSMONETIF_CFLAGS) $(COVERAGE_CFLAGS) AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOABIS_LIBS) $(COVERAGE_LDFLAGS) noinst_LIBRARIES = libtrau.a libtrau_a_SOURCES = rtp_proxy.c trau_mux.c trau_upqueue.c openbsc-0.15.0/openbsc/src/libtrau/rtp_proxy.c000066400000000000000000000451501265565154000213400ustar00rootroot00000000000000/* RTP proxy handling for ip.access nanoBTS */ /* (C) 2009-2013 by Harald Welte * 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 . * */ #include #include #include #include #include #include /* gettimeofday() */ #include /* get..() */ #include /* clock() */ #include /* uname() */ #include #include #include #include #include #include #include #include #include /* attempt to determine byte order */ #include #include static LLIST_HEAD(rtp_sockets); /* should we mangle the CNAME inside SDES of RTCP packets? We disable * this by default, as it seems to be not needed */ static int mangle_rtcp_cname = 0; enum rtp_bfd_priv { RTP_PRIV_NONE, RTP_PRIV_RTP, RTP_PRIV_RTCP }; #define RTP_ALLOC_SIZE 1500 #define RTCP_TYPE_SDES 202 #define RTCP_IE_CNAME 1 #define RTP_VERSION 2 /* 33 for FR, all other codecs have smaller size */ #define MAX_RTP_PAYLOAD_LEN 33 /* decode an rtp frame and create a new buffer with payload */ static int rtp_decode(struct msgb *msg, uint32_t callref, struct msgb **data) { struct msgb *new_msg; struct gsm_data_frame *frame; struct rtp_hdr *rtph = (struct rtp_hdr *)msg->data; struct rtp_x_hdr *rtpxh; uint8_t *payload, *payload_out; int payload_len; int msg_type; int x_len; if (msg->len < 12) { DEBUGPC(DLMUX, "received RTP frame too short (len = %d)\n", msg->len); return -EINVAL; } if (rtph->version != RTP_VERSION) { DEBUGPC(DLMUX, "received RTP version %d not supported.\n", rtph->version); return -EINVAL; } payload = msg->data + sizeof(struct rtp_hdr) + (rtph->csrc_count << 2); payload_len = msg->len - sizeof(struct rtp_hdr) - (rtph->csrc_count << 2); if (payload_len < 0) { DEBUGPC(DLMUX, "received RTP frame too short (len = %d, " "csrc count = %d)\n", msg->len, rtph->csrc_count); return -EINVAL; } if (rtph->extension) { if (payload_len < sizeof(struct rtp_x_hdr)) { DEBUGPC(DLMUX, "received RTP frame too short for " "extension header\n"); return -EINVAL; } rtpxh = (struct rtp_x_hdr *)payload; x_len = ntohs(rtpxh->length) * 4 + sizeof(struct rtp_x_hdr); payload += x_len; payload_len -= x_len; if (payload_len < 0) { DEBUGPC(DLMUX, "received RTP frame too short, " "extension header exceeds frame length\n"); return -EINVAL; } } if (rtph->padding) { if (payload_len < 1) { DEBUGPC(DLMUX, "received RTP frame too short for " "padding length\n"); return -EINVAL; } payload_len -= payload[payload_len - 1]; if (payload_len < 0) { DEBUGPC(DLMUX, "received RTP frame with padding " "greater than payload\n"); return -EINVAL; } } switch (rtph->payload_type) { case RTP_PT_GSM_FULL: msg_type = GSM_TCHF_FRAME; if (payload_len != RTP_LEN_GSM_FULL) { DEBUGPC(DLMUX, "received RTP full rate frame with " "payload length != %d (len = %d)\n", RTP_LEN_GSM_FULL, payload_len); return -EINVAL; } break; case RTP_PT_GSM_EFR: msg_type = GSM_TCHF_FRAME_EFR; if (payload_len != RTP_LEN_GSM_EFR) { DEBUGPC(DLMUX, "received RTP extended full rate frame " "with payload length != %d (len = %d)\n", RTP_LEN_GSM_EFR, payload_len); return -EINVAL; } break; case RTP_PT_GSM_HALF: msg_type = GSM_TCHH_FRAME; if (payload_len != RTP_LEN_GSM_HALF) { DEBUGPC(DLMUX, "received RTP half rate frame with " "payload length != %d (len = %d)\n", RTP_LEN_GSM_HALF, payload_len); return -EINVAL; } break; case RTP_PT_AMR: msg_type = GSM_TCH_FRAME_AMR; break; default: DEBUGPC(DLMUX, "received RTP frame with unknown payload " "type %d\n", rtph->payload_type); return -EINVAL; } if (payload_len > MAX_RTP_PAYLOAD_LEN) { DEBUGPC(DLMUX, "RTP payload too large (%d octets)\n", payload_len); return -EINVAL; } /* always allocate for the maximum possible size to avoid * fragmentation */ new_msg = msgb_alloc(sizeof(struct gsm_data_frame) + MAX_RTP_PAYLOAD_LEN, "GSM-DATA (TCH)"); if (!new_msg) return -ENOMEM; frame = (struct gsm_data_frame *) msgb_put(new_msg, sizeof(struct gsm_data_frame)); frame->msg_type = msg_type; frame->callref = callref; if (rtph->payload_type == RTP_PT_AMR) { /* for FR/HR/EFR the length is implicit. In AMR, we * need to make it explicit by using the first byte of * the data[] buffer as length byte */ uint8_t *data0 = msgb_put(new_msg, 1); *data0 = payload_len; } payload_out = msgb_put(new_msg, payload_len); memcpy(payload_out, payload, payload_len); *data = new_msg; return 0; } /*! \brief encode and send a rtp frame * \param[in] rs RTP socket through which we shall send * \param[in] frame GSM RTP frame to be sent */ int rtp_send_frame(struct rtp_socket *rs, struct gsm_data_frame *frame) { struct rtp_sub_socket *rss = &rs->rtp; struct msgb *msg; struct rtp_hdr *rtph; uint8_t *payload; int payload_type; int payload_len; int duration; /* in samples */ int is_bfi = 0; if (rs->tx_action != RTP_SEND_DOWNSTREAM) { /* initialize sequences */ rs->tx_action = RTP_SEND_DOWNSTREAM; rs->transmit.ssrc = rand(); rs->transmit.sequence = random(); rs->transmit.timestamp = random(); } switch (frame->msg_type) { case GSM_TCHF_FRAME: payload_type = RTP_PT_GSM_FULL; payload_len = RTP_LEN_GSM_FULL; duration = RTP_GSM_DURATION; break; case GSM_TCHF_FRAME_EFR: payload_type = RTP_PT_GSM_EFR; payload_len = RTP_LEN_GSM_EFR; duration = RTP_GSM_DURATION; break; case GSM_TCHH_FRAME: payload_type = RTP_PT_GSM_HALF; payload_len = RTP_LEN_GSM_HALF; duration = RTP_GSM_DURATION; break; case GSM_TCH_FRAME_AMR: payload_type = RTP_PT_AMR; payload_len = frame->data[0]; duration = RTP_GSM_DURATION; break; case GSM_BAD_FRAME: payload_type = 0; payload_len = 0; duration = RTP_GSM_DURATION; is_bfi = 1; break; default: DEBUGPC(DLMUX, "unsupported message type %d\n", frame->msg_type); return -EINVAL; } if (payload_len > MAX_RTP_PAYLOAD_LEN) { DEBUGPC(DLMUX, "RTP payload too large (%d octets)\n", payload_len); return -EINVAL; } if (is_bfi) { /* In case of a bad frame, just count and drop packet. */ rs->transmit.timestamp += duration; rs->transmit.sequence++; return 0; } msg = msgb_alloc(sizeof(struct rtp_hdr) + payload_len, "RTP-GSM"); if (!msg) return -ENOMEM; rtph = (struct rtp_hdr *) msgb_put(msg, sizeof(struct rtp_hdr)); rtph->version = RTP_VERSION; rtph->padding = 0; rtph->extension = 0; rtph->csrc_count = 0; rtph->marker = 0; rtph->payload_type = payload_type; rtph->sequence = htons(rs->transmit.sequence++); rtph->timestamp = htonl(rs->transmit.timestamp); rs->transmit.timestamp += duration; rtph->ssrc = htonl(rs->transmit.ssrc); payload = msgb_put(msg, payload_len); if (frame->msg_type == GSM_TCH_FRAME_AMR) memcpy(payload, frame->data + 1, payload_len); else memcpy(payload, frame->data, payload_len); msgb_enqueue(&rss->tx_queue, msg); rss->bfd.when |= BSC_FD_WRITE; return 0; } /* iterate over all chunks in one RTCP message, look for CNAME IEs and * replace all of those with 'new_cname' */ static int rtcp_sdes_cname_mangle(struct msgb *msg, struct rtcp_hdr *rh, uint16_t *rtcp_len, const char *new_cname) { uint8_t *rtcp_end; uint8_t *cur = (uint8_t *) rh; uint8_t tag, len = 0; rtcp_end = cur + *rtcp_len; /* move cur to end of RTP header */ cur += sizeof(*rh); /* iterate over Chunks */ while (cur+4 < rtcp_end) { /* skip four bytes SSRC/CSRC */ cur += 4; /* iterate over IE's inside the chunk */ while (cur+1 < rtcp_end) { tag = *cur++; if (tag == 0) { /* end of chunk, skip additional zero */ while ((*cur++ == 0) && (cur < rtcp_end)) { } break; } len = *cur++; if (tag == RTCP_IE_CNAME) { /* we've found the CNAME, lets mangle it */ if (len < strlen(new_cname)) { /* we need to make more space */ int increase = strlen(new_cname) - len; msgb_push(msg, increase); memmove(cur+len+increase, cur+len, rtcp_end - (cur+len)); /* FIXME: we have to respect RTCP * padding/alignment rules! */ len += increase; *(cur-1) += increase; rtcp_end += increase; *rtcp_len += increase; } /* copy new CNAME into message */ memcpy(cur, new_cname, strlen(new_cname)); /* FIXME: zero the padding in case new CNAME * is smaller than old one !!! */ } cur += len; } } return 0; } static int rtcp_mangle(struct msgb *msg, struct rtp_socket *rs) { struct rtp_sub_socket *rss = &rs->rtcp; struct rtcp_hdr *rtph; uint16_t old_len; int rc; if (!mangle_rtcp_cname) return 0; printf("RTCP\n"); /* iterate over list of RTCP messages */ rtph = (struct rtcp_hdr *)msg->data; while ((void *)rtph + sizeof(*rtph) <= (void *)msg->data + msg->len) { old_len = (ntohs(rtph->length) + 1) * 4; if ((void *)rtph + old_len > (void *)msg->data + msg->len) { DEBUGPC(DLMUX, "received RTCP packet too short for " "length element\n"); return -EINVAL; } if (rtph->type == RTCP_TYPE_SDES) { char new_cname[255]; strncpy(new_cname, inet_ntoa(rss->sin_local.sin_addr), sizeof(new_cname)); new_cname[sizeof(new_cname)-1] = '\0'; rc = rtcp_sdes_cname_mangle(msg, rtph, &old_len, new_cname); if (rc < 0) return rc; } rtph = (void *)rtph + old_len; } return 0; } /* read from incoming RTP/RTCP socket */ static int rtp_socket_read(struct rtp_socket *rs, struct rtp_sub_socket *rss) { int rc; struct msgb *msg = msgb_alloc(RTP_ALLOC_SIZE, "RTP/RTCP"); struct msgb *new_msg; struct rtp_sub_socket *other_rss; if (!msg) return -ENOMEM; rc = read(rss->bfd.fd, msg->data, RTP_ALLOC_SIZE); if (rc == 0) { rss->bfd.when &= ~BSC_FD_READ; goto out_free; } else if (rc < 0) { /* Ignore "connection refused". this happens, If we open the * socket faster than the remote side. */ if (errno == ECONNREFUSED) goto out_free; DEBUGPC(DLMUX, "Read of RTP socket (%p) failed (errno %d, " "%s)\n", rs, errno, strerror(errno)); rss->bfd.when &= ~BSC_FD_READ; goto out_free; } msgb_put(msg, rc); switch (rs->rx_action) { case RTP_PROXY: if (!rs->proxy.other_sock) { rc = -EIO; goto out_free; } if (rss->bfd.priv_nr == RTP_PRIV_RTP) other_rss = &rs->proxy.other_sock->rtp; else if (rss->bfd.priv_nr == RTP_PRIV_RTCP) { other_rss = &rs->proxy.other_sock->rtcp; /* modify RTCP SDES CNAME */ rc = rtcp_mangle(msg, rs); if (rc < 0) goto out_free; } else { rc = -EINVAL; goto out_free; } msgb_enqueue(&other_rss->tx_queue, msg); other_rss->bfd.when |= BSC_FD_WRITE; break; case RTP_RECV_UPSTREAM: if (!rs->receive.callref || !rs->receive.net) { rc = -EIO; goto out_free; } if (rss->bfd.priv_nr == RTP_PRIV_RTCP) { if (!mangle_rtcp_cname) { msgb_free(msg); break; } /* modify RTCP SDES CNAME */ rc = rtcp_mangle(msg, rs); if (rc < 0) goto out_free; msgb_enqueue(&rss->tx_queue, msg); rss->bfd.when |= BSC_FD_WRITE; break; } if (rss->bfd.priv_nr != RTP_PRIV_RTP) { rc = -EINVAL; goto out_free; } rc = rtp_decode(msg, rs->receive.callref, &new_msg); if (rc < 0) goto out_free; msgb_free(msg); trau_tx_to_mncc(rs->receive.net, new_msg); break; case RTP_NONE: /* if socket exists, but disabled by app */ msgb_free(msg); break; } return 0; out_free: msgb_free(msg); return rc; } /* \brief write from tx_queue to RTP/RTCP socket */ static int rtp_socket_write(struct rtp_socket *rs, struct rtp_sub_socket *rss) { struct msgb *msg; int written; msg = msgb_dequeue(&rss->tx_queue); if (!msg) { rss->bfd.when &= ~BSC_FD_WRITE; return 0; } written = write(rss->bfd.fd, msg->data, msg->len); if (written < msg->len) { LOGP(DLMIB, LOGL_ERROR, "short write"); msgb_free(msg); return -EIO; } msgb_free(msg); return 0; } /*! \brief callback for the select.c:bfd_* layer */ static int rtp_bfd_cb(struct osmo_fd *bfd, unsigned int flags) { struct rtp_socket *rs = bfd->data; struct rtp_sub_socket *rss; switch (bfd->priv_nr) { case RTP_PRIV_RTP: rss = &rs->rtp; break; case RTP_PRIV_RTCP: rss = &rs->rtcp; break; default: return -EINVAL; } if (flags & BSC_FD_READ) rtp_socket_read(rs, rss); if (flags & BSC_FD_WRITE) rtp_socket_write(rs, rss); return 0; } /*! \brief initialize one rtp sub-socket */ static void init_rss(struct rtp_sub_socket *rss, struct rtp_socket *rs, int fd, int priv_nr) { /* initialize bfd */ rss->bfd.fd = fd; rss->bfd.data = rs; rss->bfd.priv_nr = priv_nr; rss->bfd.cb = rtp_bfd_cb; } /*! \brief create a new RTP/RTCP socket and bind it */ struct rtp_socket *rtp_socket_create(void) { int rc; struct rtp_socket *rs; DEBUGP(DLMUX, "rtp_socket_create(): "); rs = talloc_zero(tall_bsc_ctx, struct rtp_socket); if (!rs) return NULL; INIT_LLIST_HEAD(&rs->rtp.tx_queue); INIT_LLIST_HEAD(&rs->rtcp.tx_queue); rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (rc < 0) goto out_free; init_rss(&rs->rtp, rs, rc, RTP_PRIV_RTP); rc = osmo_fd_register(&rs->rtp.bfd); if (rc < 0) goto out_rtp_socket; rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (rc < 0) goto out_rtp_bfd; init_rss(&rs->rtcp, rs, rc, RTP_PRIV_RTCP); rc = osmo_fd_register(&rs->rtcp.bfd); if (rc < 0) goto out_rtcp_socket; DEBUGPC(DLMUX, "success\n"); rc = rtp_socket_bind(rs, INADDR_ANY); if (rc < 0) goto out_rtcp_bfd; return rs; out_rtcp_bfd: osmo_fd_unregister(&rs->rtcp.bfd); out_rtcp_socket: close(rs->rtcp.bfd.fd); out_rtp_bfd: osmo_fd_unregister(&rs->rtp.bfd); out_rtp_socket: close(rs->rtp.bfd.fd); out_free: talloc_free(rs); DEBUGPC(DLMUX, "failed\n"); return NULL; } static int rtp_sub_socket_bind(struct rtp_sub_socket *rss, uint32_t ip, uint16_t port) { int rc; socklen_t alen = sizeof(rss->sin_local); rss->sin_local.sin_family = AF_INET; rss->sin_local.sin_addr.s_addr = htonl(ip); rss->sin_local.sin_port = htons(port); rss->bfd.when |= BSC_FD_READ; rc = bind(rss->bfd.fd, (struct sockaddr *)&rss->sin_local, sizeof(rss->sin_local)); if (rc < 0) return rc; /* retrieve the address we actually bound to, in case we * passed INADDR_ANY as IP address */ return getsockname(rss->bfd.fd, (struct sockaddr *)&rss->sin_local, &alen); } #define RTP_PORT_BASE 30000 static unsigned int next_udp_port = RTP_PORT_BASE; /*! \brief bind a RTP socket to a specific local address * \param[in] rs RTP socket to be bound * \param[in] ip local IP address to which socket is to be bound */ int rtp_socket_bind(struct rtp_socket *rs, uint32_t ip) { int rc = -EIO; struct in_addr ia; ia.s_addr = htonl(ip); DEBUGP(DLMUX, "rtp_socket_bind(rs=%p, IP=%s): ", rs, inet_ntoa(ia)); /* try to bind to a consecutive pair of ports */ for (next_udp_port = next_udp_port % 0xffff; next_udp_port < 0xffff; next_udp_port += 2) { rc = rtp_sub_socket_bind(&rs->rtp, ip, next_udp_port); if (rc != 0) continue; rc = rtp_sub_socket_bind(&rs->rtcp, ip, next_udp_port+1); if (rc == 0) break; } if (rc < 0) { DEBUGPC(DLMUX, "failed\n"); return rc; } ia.s_addr = rs->rtp.sin_local.sin_addr.s_addr; DEBUGPC(DLMUX, "BOUND_IP=%s, BOUND_PORT=%u\n", inet_ntoa(ia), ntohs(rs->rtp.sin_local.sin_port)); return ntohs(rs->rtp.sin_local.sin_port); } static int rtp_sub_socket_connect(struct rtp_sub_socket *rss, uint32_t ip, uint16_t port) { int rc; socklen_t alen = sizeof(rss->sin_local); rss->sin_remote.sin_family = AF_INET; rss->sin_remote.sin_addr.s_addr = htonl(ip); rss->sin_remote.sin_port = htons(port); rc = connect(rss->bfd.fd, (struct sockaddr *) &rss->sin_remote, sizeof(rss->sin_remote)); if (rc < 0) return rc; return getsockname(rss->bfd.fd, (struct sockaddr *)&rss->sin_local, &alen); } /*! \brief 'connect' a RTP socket to a remote peer * \param[in] rs RTP socket to be connected * \param[in] ip remote IP address to which to connect * \param[in] port remote UDP port number to which to connect */ int rtp_socket_connect(struct rtp_socket *rs, uint32_t ip, uint16_t port) { int rc; struct in_addr ia; ia.s_addr = htonl(ip); DEBUGP(DLMUX, "rtp_socket_connect(rs=%p, ip=%s, port=%u)\n", rs, inet_ntoa(ia), port); rc = rtp_sub_socket_connect(&rs->rtp, ip, port); if (rc < 0) return rc; return rtp_sub_socket_connect(&rs->rtcp, ip, port+1); } /*! \brief bind two RTP/RTCP sockets together in the proxy * \param[in] this First RTP socket * \param[in] other Second RTP socket */ int rtp_socket_proxy(struct rtp_socket *this, struct rtp_socket *other) { DEBUGP(DLMUX, "rtp_socket_proxy(this=%p, other=%p)\n", this, other); this->rx_action = RTP_PROXY; this->proxy.other_sock = other; other->rx_action = RTP_PROXY; other->proxy.other_sock = this; return 0; } /*! \brief bind RTP/RTCP socket to application, disabling proxy * \param[in] this RTP socket * \param[in] net gsm_network argument to trau_tx_to_mncc() * \param[in] callref callref argument to trau_tx_to_mncc() */ int rtp_socket_upstream(struct rtp_socket *this, struct gsm_network *net, uint32_t callref) { DEBUGP(DLMUX, "rtp_socket_proxy(this=%p, callref=%u)\n", this, callref); if (callref) { this->rx_action = RTP_RECV_UPSTREAM; this->receive.net = net; this->receive.callref = callref; } else this->rx_action = RTP_NONE; return 0; } static void free_tx_queue(struct rtp_sub_socket *rss) { struct msgb *msg; while ((msg = msgb_dequeue(&rss->tx_queue))) msgb_free(msg); } /*! \brief Free/release a previously allocated RTP socket * \param[in[] rs RTP/RTCP socket to be released */ int rtp_socket_free(struct rtp_socket *rs) { DEBUGP(DLMUX, "rtp_socket_free(rs=%p)\n", rs); /* make sure we don't leave references dangling to us */ if (rs->rx_action == RTP_PROXY && rs->proxy.other_sock) rs->proxy.other_sock->proxy.other_sock = NULL; osmo_fd_unregister(&rs->rtp.bfd); close(rs->rtp.bfd.fd); free_tx_queue(&rs->rtp); osmo_fd_unregister(&rs->rtcp.bfd); close(rs->rtcp.bfd.fd); free_tx_queue(&rs->rtcp); talloc_free(rs); return 0; } openbsc-0.15.0/openbsc/src/libtrau/trau_mux.c000066400000000000000000000336011265565154000211340ustar00rootroot00000000000000/* Simple TRAU frame reflector to route voice calls */ /* (C) 2009 by Harald Welte * 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include /* this corresponds to the bit-lengths of the individual codec * parameters as indicated in Table 1.1 of TS 06.10 */ static const uint8_t gsm_fr_map[] = { 6, 6, 5, 5, 4, 4, 3, 3, 7, 2, 2, 6, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 2, 2, 6, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 2, 2, 6, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 2, 2, 6, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }; /* * EFR TRAU parity * * g(x) = x^3 + x^1 + 1 */ static const struct osmo_crc8gen_code gsm0860_efr_crc3 = { .bits = 3, .poly = 0x3, .init = 0x0, .remainder = 0x7, }; /* EFR parity bits */ static inline void efr_parity_bits_1(ubit_t *check_bits, const ubit_t *d_bits) { memcpy(check_bits + 0 , d_bits + 0, 22); memcpy(check_bits + 22 , d_bits + 24, 3); check_bits[25] = d_bits[28]; } static inline void efr_parity_bits_2(ubit_t *check_bits, const ubit_t *d_bits) { memcpy(check_bits + 0 , d_bits + 42, 10); memcpy(check_bits + 10 , d_bits + 90, 2); } static inline void efr_parity_bits_3(ubit_t *check_bits, const ubit_t *d_bits) { memcpy(check_bits + 0 , d_bits + 98, 5); check_bits[5] = d_bits[104]; memcpy(check_bits + 6 , d_bits + 143, 2); } static inline void efr_parity_bits_4(ubit_t *check_bits, const ubit_t *d_bits) { memcpy(check_bits + 0 , d_bits + 151, 10); memcpy(check_bits + 10 , d_bits + 199, 2); } static inline void efr_parity_bits_5(ubit_t *check_bits, const ubit_t *d_bits) { memcpy(check_bits + 0 , d_bits + 207, 5); check_bits[5] = d_bits[213]; memcpy(check_bits + 6 , d_bits + 252, 2); } struct map_entry { struct llist_head list; struct gsm_e1_subslot src, dst; }; struct upqueue_entry { struct llist_head list; struct gsm_network *net; struct gsm_e1_subslot src; uint32_t callref; }; static LLIST_HEAD(ss_map); static LLIST_HEAD(ss_upqueue); void *tall_map_ctx, *tall_upq_ctx; /* map one particular subslot to another subslot */ int trau_mux_map(const struct gsm_e1_subslot *src, const struct gsm_e1_subslot *dst) { struct map_entry *me; me = talloc(tall_map_ctx, struct map_entry); if (!me) { LOGP(DLMIB, LOGL_FATAL, "Out of memory\n"); return -ENOMEM; } DEBUGP(DCC, "Setting up TRAU mux map between (e1=%u,ts=%u,ss=%u) " "and (e1=%u,ts=%u,ss=%u)\n", src->e1_nr, src->e1_ts, src->e1_ts_ss, dst->e1_nr, dst->e1_ts, dst->e1_ts_ss); /* make sure to get rid of any stale old mappings */ trau_mux_unmap(src, 0); trau_mux_unmap(dst, 0); memcpy(&me->src, src, sizeof(me->src)); memcpy(&me->dst, dst, sizeof(me->dst)); llist_add(&me->list, &ss_map); return 0; } int trau_mux_map_lchan(const struct gsm_lchan *src, const struct gsm_lchan *dst) { struct gsm_e1_subslot *src_ss, *dst_ss; src_ss = &src->ts->e1_link; dst_ss = &dst->ts->e1_link; return trau_mux_map(src_ss, dst_ss); } /* unmap one particular subslot from another subslot */ int trau_mux_unmap(const struct gsm_e1_subslot *ss, uint32_t callref) { struct map_entry *me, *me2; struct upqueue_entry *ue, *ue2; if (ss) llist_for_each_entry_safe(me, me2, &ss_map, list) { if (!memcmp(&me->src, ss, sizeof(*ss)) || !memcmp(&me->dst, ss, sizeof(*ss))) { llist_del(&me->list); return 0; } } llist_for_each_entry_safe(ue, ue2, &ss_upqueue, list) { if (ue->callref == callref) { llist_del(&ue->list); return 0; } if (ss && !memcmp(&ue->src, ss, sizeof(*ss))) { llist_del(&ue->list); return 0; } } return -ENOENT; } /* look-up an enty in the TRAU mux map */ static struct gsm_e1_subslot * lookup_trau_mux_map(const struct gsm_e1_subslot *src) { struct map_entry *me; llist_for_each_entry(me, &ss_map, list) { if (!memcmp(&me->src, src, sizeof(*src))) return &me->dst; if (!memcmp(&me->dst, src, sizeof(*src))) return &me->src; } return NULL; } /* look-up an enty in the TRAU upqueue */ struct upqueue_entry * lookup_trau_upqueue(const struct gsm_e1_subslot *src) { struct upqueue_entry *ue; llist_for_each_entry(ue, &ss_upqueue, list) { if (!memcmp(&ue->src, src, sizeof(*src))) return ue; } return NULL; } static const uint8_t c_bits_check_fr[] = { 0, 0, 0, 1, 0 }; static const uint8_t c_bits_check_efr[] = { 1, 1, 0, 1, 0 }; struct msgb *trau_decode_fr(uint32_t callref, const struct decoded_trau_frame *tf) { struct msgb *msg; struct gsm_data_frame *frame; unsigned char *data; int i, j, k, l, o; msg = msgb_alloc(sizeof(struct gsm_data_frame) + 33, "GSM-DATA"); if (!msg) return NULL; frame = (struct gsm_data_frame *)msg->data; memset(frame, 0, sizeof(struct gsm_data_frame)); data = frame->data; data[0] = 0xd << 4; /* reassemble d-bits */ i = 0; /* counts bits */ j = 4; /* counts output bits */ k = gsm_fr_map[0]-1; /* current number bit in element */ l = 0; /* counts element bits */ o = 0; /* offset input bits */ while (i < 260) { data[j/8] |= (tf->d_bits[k+o] << (7-(j%8))); /* to avoid out-of-bounds access in gsm_fr_map[++l] */ if (i == 259) break; if (--k < 0) { o += gsm_fr_map[l]; k = gsm_fr_map[++l]-1; } i++; j++; } if (tf->c_bits[11]) /* BFI */ frame->msg_type = GSM_BAD_FRAME; else frame->msg_type = GSM_TCHF_FRAME; frame->callref = callref; msgb_put(msg, sizeof(struct gsm_data_frame) + 33); return msg; } struct msgb *trau_decode_efr(uint32_t callref, const struct decoded_trau_frame *tf) { struct msgb *msg; struct gsm_data_frame *frame; unsigned char *data; int i, j, rc; ubit_t check_bits[26]; msg = msgb_alloc(sizeof(struct gsm_data_frame) + 31, "GSM-DATA"); if (!msg) return NULL; frame = (struct gsm_data_frame *)msg->data; memset(frame, 0, sizeof(struct gsm_data_frame)); frame->msg_type = GSM_TCHF_FRAME_EFR; frame->callref = callref; msgb_put(msg, sizeof(struct gsm_data_frame) + 31); if (tf->c_bits[11]) /* BFI */ goto bad_frame; data = frame->data; data[0] = 0xc << 4; /* reassemble d-bits */ for (i = 1, j = 4; i < 39; i++, j++) data[j/8] |= (tf->d_bits[i] << (7-(j%8))); efr_parity_bits_1(check_bits, tf->d_bits); rc = osmo_crc8gen_check_bits(&gsm0860_efr_crc3, check_bits, 26, tf->d_bits + 39); if (rc) goto bad_frame; for (i = 42, j = 42; i < 95; i++, j++) data[j/8] |= (tf->d_bits[i] << (7-(j%8))); efr_parity_bits_2(check_bits, tf->d_bits); rc = osmo_crc8gen_check_bits(&gsm0860_efr_crc3, check_bits, 12, tf->d_bits + 95); if (rc) goto bad_frame; for (i = 98, j = 95; i < 148; i++, j++) data[j/8] |= (tf->d_bits[i] << (7-(j%8))); efr_parity_bits_3(check_bits, tf->d_bits); rc = osmo_crc8gen_check_bits(&gsm0860_efr_crc3, check_bits, 8, tf->d_bits + 148); if (rc) goto bad_frame; for (i = 151, j = 145; i < 204; i++, j++) data[j/8] |= (tf->d_bits[i] << (7-(j%8))); efr_parity_bits_4(check_bits, tf->d_bits); rc = osmo_crc8gen_check_bits(&gsm0860_efr_crc3, check_bits, 12, tf->d_bits + 204); if (rc) goto bad_frame; for (i = 207, j = 198; i < 257; i++, j++) data[j/8] |= (tf->d_bits[i] << (7-(j%8))); efr_parity_bits_5(check_bits, tf->d_bits); rc = osmo_crc8gen_check_bits(&gsm0860_efr_crc3, check_bits, 8, tf->d_bits + 257); if (rc) goto bad_frame; return msg; bad_frame: frame->msg_type = GSM_BAD_FRAME; return msg; } /* we get called by subchan_demux */ int trau_mux_input(struct gsm_e1_subslot *src_e1_ss, const uint8_t *trau_bits, int num_bits) { struct decoded_trau_frame tf; uint8_t trau_bits_out[TRAU_FRAME_BITS]; struct gsm_e1_subslot *dst_e1_ss = lookup_trau_mux_map(src_e1_ss); struct subch_mux *mx; struct upqueue_entry *ue; int rc; /* decode TRAU, change it to downlink, re-encode */ rc = decode_trau_frame(&tf, trau_bits); if (rc) return rc; if (!dst_e1_ss) { struct msgb *msg = NULL; /* frame shall be sent to upqueue */ if (!(ue = lookup_trau_upqueue(src_e1_ss))) return -EINVAL; if (!ue->callref) return -EINVAL; if (!memcmp(tf.c_bits, c_bits_check_fr, 5)) msg = trau_decode_fr(ue->callref, &tf); else if (!memcmp(tf.c_bits, c_bits_check_efr, 5)) msg = trau_decode_efr(ue->callref, &tf); else { DEBUGPC(DLMUX, "illegal trau (C1-C5) %s\n", osmo_hexdump(tf.c_bits, 5)); DEBUGPC(DLMUX, "test trau (C1-C5) %s\n", osmo_hexdump(c_bits_check_efr, 5)); return -EINVAL; } if (!msg) return -ENOMEM; trau_tx_to_mncc(ue->net, msg); return 0; } mx = e1inp_get_mux(dst_e1_ss->e1_nr, dst_e1_ss->e1_ts); if (!mx) return -EINVAL; trau_frame_up2down(&tf); encode_trau_frame(trau_bits_out, &tf); /* and send it to the muxer */ return subchan_mux_enqueue(mx, dst_e1_ss->e1_ts_ss, trau_bits_out, TRAU_FRAME_BITS); } /* callback when a TRAU frame was received */ int subch_cb(struct subch_demux *dmx, int ch, uint8_t *data, int len, void *_priv) { struct e1inp_ts *e1i_ts = _priv; struct gsm_e1_subslot src_ss; src_ss.e1_nr = e1i_ts->line->num; src_ss.e1_ts = e1i_ts->num; src_ss.e1_ts_ss = ch; return trau_mux_input(&src_ss, data, len); } /* add receiver instance for lchan and callref */ int trau_recv_lchan(struct gsm_lchan *lchan, uint32_t callref) { struct gsm_e1_subslot *src_ss; struct upqueue_entry *ue; ue = talloc(tall_upq_ctx, struct upqueue_entry); if (!ue) return -ENOMEM; src_ss = &lchan->ts->e1_link; DEBUGP(DCC, "Setting up TRAU receiver (e1=%u,ts=%u,ss=%u) " "and (callref 0x%x)\n", src_ss->e1_nr, src_ss->e1_ts, src_ss->e1_ts_ss, callref); /* make sure to get rid of any stale old mappings */ trau_mux_unmap(src_ss, callref); memcpy(&ue->src, src_ss, sizeof(ue->src)); ue->net = lchan->ts->trx->bts->network; ue->callref = callref; llist_add(&ue->list, &ss_upqueue); return 0; } void trau_encode_fr(struct decoded_trau_frame *tf, const unsigned char *data) { int i, j, k, l, o; /* set c-bits and t-bits */ tf->c_bits[0] = 1; tf->c_bits[1] = 1; tf->c_bits[2] = 1; tf->c_bits[3] = 0; tf->c_bits[4] = 0; memset(&tf->c_bits[5], 0, 6); memset(&tf->c_bits[11], 1, 10); memset(&tf->t_bits[0], 1, 4); /* reassemble d-bits */ i = 0; /* counts bits */ j = 4; /* counts input bits */ k = gsm_fr_map[0]-1; /* current number bit in element */ l = 0; /* counts element bits */ o = 0; /* offset output bits */ while (i < 260) { tf->d_bits[k+o] = (data[j/8] >> (7-(j%8))) & 1; /* to avoid out-of-bounds access in gsm_fr_map[++l] */ if (i == 259) break; if (--k < 0) { o += gsm_fr_map[l]; k = gsm_fr_map[++l]-1; } i++; j++; } } void trau_encode_efr(struct decoded_trau_frame *tf, const unsigned char *data) { int i, j; ubit_t check_bits[26]; /* set c-bits and t-bits */ tf->c_bits[0] = 1; tf->c_bits[1] = 1; tf->c_bits[2] = 0; tf->c_bits[3] = 1; tf->c_bits[4] = 0; memset(&tf->c_bits[5], 0, 6); memset(&tf->c_bits[11], 1, 10); memset(&tf->t_bits[0], 1, 4); /* reassemble d-bits */ tf->d_bits[0] = 1; for (i = 1, j = 4; i < 39; i++, j++) tf->d_bits[i] = (data[j/8] >> (7-(j%8))) & 1; efr_parity_bits_1(check_bits, tf->d_bits); osmo_crc8gen_set_bits(&gsm0860_efr_crc3, check_bits, 26, tf->d_bits + 39); for (i = 42, j = 42; i < 95; i++, j++) tf->d_bits[i] = (data[j/8] >> (7-(j%8))) & 1; efr_parity_bits_2(check_bits, tf->d_bits); osmo_crc8gen_set_bits(&gsm0860_efr_crc3, check_bits, 12, tf->d_bits + 95); for (i = 98, j = 95; i < 148; i++, j++) tf->d_bits[i] = (data[j/8] >> (7-(j%8))) & 1; efr_parity_bits_3(check_bits, tf->d_bits); osmo_crc8gen_set_bits(&gsm0860_efr_crc3, check_bits, 8, tf->d_bits + 148); for (i = 151, j = 145; i < 204; i++, j++) tf->d_bits[i] = (data[j/8] >> (7-(j%8))) & 1; efr_parity_bits_4(check_bits, tf->d_bits); osmo_crc8gen_set_bits(&gsm0860_efr_crc3, check_bits, 12, tf->d_bits + 204); for (i = 207, j = 198; i < 257; i++, j++) tf->d_bits[i] = (data[j/8] >> (7-(j%8))) & 1; efr_parity_bits_5(check_bits, tf->d_bits); osmo_crc8gen_set_bits(&gsm0860_efr_crc3, check_bits, 8, tf->d_bits + 257); } int trau_send_frame(struct gsm_lchan *lchan, struct gsm_data_frame *frame) { uint8_t trau_bits_out[TRAU_FRAME_BITS]; struct gsm_e1_subslot *dst_e1_ss = &lchan->ts->e1_link; struct subch_mux *mx; struct decoded_trau_frame tf; mx = e1inp_get_mux(dst_e1_ss->e1_nr, dst_e1_ss->e1_ts); if (!mx) return -EINVAL; switch (frame->msg_type) { case GSM_TCHF_FRAME: trau_encode_fr(&tf, frame->data); break; case GSM_TCHF_FRAME_EFR: trau_encode_efr(&tf, frame->data); break; default: DEBUGPC(DLMUX, "unsupported message type %d\n", frame->msg_type); return -EINVAL; } encode_trau_frame(trau_bits_out, &tf); /* and send it to the muxer */ return subchan_mux_enqueue(mx, dst_e1_ss->e1_ts_ss, trau_bits_out, TRAU_FRAME_BITS); } /* switch trau muxer to new lchan */ int switch_trau_mux(struct gsm_lchan *old_lchan, struct gsm_lchan *new_lchan) { struct gsm_network *net = old_lchan->ts->trx->bts->network; struct gsm_trans *trans; /* look up transaction with TCH frame receive enabled */ llist_for_each_entry(trans, &net->trans_list, entry) { if (trans->conn && trans->conn->lchan == old_lchan && trans->tch_recv) { /* switch */ trau_recv_lchan(new_lchan, trans->callref); } } return 0; } openbsc-0.15.0/openbsc/src/libtrau/trau_upqueue.c000066400000000000000000000016711265565154000220160ustar00rootroot00000000000000/* trau_upqueue.c - Pass msgb's up the chain */ /* (C) 2010 by Harald Welte * 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 . * */ #include #include void trau_tx_to_mncc(struct gsm_network *net, struct msgb *msg) { net->mncc_recv(net, msg); } openbsc-0.15.0/openbsc/src/osmo-bsc/000077500000000000000000000000001265565154000172015ustar00rootroot00000000000000openbsc-0.15.0/openbsc/src/osmo-bsc/Makefile.am000066400000000000000000000020121265565154000212300ustar00rootroot00000000000000AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMONETIF_CFLAGS) $(LIBOSMOSCCP_CFLAGS) $(COVERAGE_CFLAGS) $(LIBOSMOABIS_CFLAGS) AM_LDFLAGS = $(COVERAGE_LDFLAGS) bin_PROGRAMS = osmo-bsc osmo_bsc_SOURCES = osmo_bsc_main.c osmo_bsc_vty.c osmo_bsc_api.c \ osmo_bsc_grace.c osmo_bsc_msc.c osmo_bsc_sccp.c \ osmo_bsc_filter.c osmo_bsc_bssap.c osmo_bsc_audio.c osmo_bsc_ctrl.c # once again since TRAU uses CC symbol :( osmo_bsc_LDADD = \ $(top_builddir)/src/libfilter/libfilter.a \ $(top_builddir)/src/libbsc/libbsc.a \ $(top_builddir)/src/libmsc/libmsc.a \ $(top_builddir)/src/libbsc/libbsc.a \ $(top_builddir)/src/libtrau/libtrau.a \ $(top_builddir)/src/libcommon/libcommon.a \ $(top_builddir)/src/libcommon/libcommon.a \ $(LIBOSMOSCCP_LIBS) $(LIBOSMOCORE_LIBS) \ $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOCTRL_LIBS) \ $(COVERAGE_LDFLAGS) $(LIBOSMOABIS_LIBS) openbsc-0.15.0/openbsc/src/osmo-bsc/osmo_bsc_api.c000066400000000000000000000351731265565154000220130ustar00rootroot00000000000000/* (C) 2009-2015 by Holger Hans Peter Freyther * (C) 2009-2011 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 . * */ #include #include #include #include #include #include #include #define return_when_not_connected(conn) \ if (!conn->sccp_con) {\ LOGP(DMSC, LOGL_ERROR, "MSC Connection not present.\n"); \ return; \ } #define return_when_not_connected_val(conn, ret) \ if (!conn->sccp_con) {\ LOGP(DMSC, LOGL_ERROR, "MSC Connection not present.\n"); \ return ret; \ } #define queue_msg_or_return(resp) \ if (!resp) { \ LOGP(DMSC, LOGL_ERROR, "Failed to allocate response.\n"); \ return; \ } \ bsc_queue_for_msc(conn->sccp_con, resp); static int bsc_clear_request(struct gsm_subscriber_connection *conn, uint32_t cause); static int complete_layer3(struct gsm_subscriber_connection *conn, struct msgb *msg, struct osmo_msc_data *msc); static uint16_t get_network_code_for_msc(struct osmo_msc_data *msc) { if (msc->core_mnc != -1) return msc->core_mnc; return msc->network->network_code; } static uint16_t get_country_code_for_msc(struct osmo_msc_data *msc) { if (msc->core_mcc != -1) return msc->core_mcc; return msc->network->country_code; } static uint16_t get_lac_for_msc(struct osmo_msc_data *msc, struct gsm_bts *bts) { if (msc->core_lac != -1) return msc->core_lac; return bts->location_area_code; } static uint16_t get_ci_for_msc(struct osmo_msc_data *msc, struct gsm_bts *bts) { if (msc->core_ci != -1) return msc->core_ci; return bts->cell_identity; } static void bsc_maybe_lu_reject(struct gsm_subscriber_connection *conn, int con_type, int cause) { struct msgb *msg; /* ignore cm service request or such */ if (con_type != FLT_CON_TYPE_LU) return; msg = gsm48_create_loc_upd_rej(cause); if (!msg) { LOGP(DMM, LOGL_ERROR, "Failed to create msg for LOCATION UPDATING REJECT.\n"); return; } msg->lchan = conn->lchan; gsm0808_submit_dtap(conn, msg, 0, 0); } static int bsc_filter_initial(struct osmo_bsc_data *bsc, struct osmo_msc_data *msc, struct gsm_subscriber_connection *conn, struct msgb *msg, char **imsi, int *con_type, int *lu_cause) { struct bsc_filter_request req; struct bsc_filter_reject_cause cause; struct gsm48_hdr *gh = msgb_l3(msg); int rc; req.ctx = conn; req.black_list = NULL; req.access_lists = bsc_access_lists(); req.local_lst_name = msc->acc_lst_name; req.global_lst_name = conn->bts->network->bsc_data->acc_lst_name; req.bsc_nr = 0; rc = bsc_msg_filter_initial(gh, msgb_l3len(msg), &req, con_type, imsi, &cause); *lu_cause = cause.lu_reject_cause; return rc; } static int bsc_filter_data(struct gsm_subscriber_connection *conn, struct msgb *msg, int *lu_cause) { struct bsc_filter_request req; struct gsm48_hdr *gh = msgb_l3(msg); struct bsc_filter_reject_cause cause; int rc; req.ctx = conn; req.black_list = NULL; req.access_lists = bsc_access_lists(); req.local_lst_name = conn->sccp_con->msc->acc_lst_name; req.global_lst_name = conn->bts->network->bsc_data->acc_lst_name; req.bsc_nr = 0; rc = bsc_msg_filter_data(gh, msgb_l3len(msg), &req, &conn->sccp_con->filter_state, &cause); *lu_cause = cause.lu_reject_cause; return rc; } static void bsc_sapi_n_reject(struct gsm_subscriber_connection *conn, int dlci) { struct msgb *resp; return_when_not_connected(conn); LOGP(DMSC, LOGL_NOTICE, "Tx MSC SAPI N REJECT DLCI=0x%02x\n", dlci); resp = gsm0808_create_sapi_reject(dlci); queue_msg_or_return(resp); } static void bsc_cipher_mode_compl(struct gsm_subscriber_connection *conn, struct msgb *msg, uint8_t chosen_encr) { struct msgb *resp; return_when_not_connected(conn); LOGP(DMSC, LOGL_DEBUG, "CIPHER MODE COMPLETE from MS, forwarding to MSC\n"); resp = gsm0808_create_cipher_complete(msg, chosen_encr); queue_msg_or_return(resp); } static void bsc_send_ussd_no_srv(struct gsm_subscriber_connection *conn, struct msgb *msg, const char *text) { struct gsm48_hdr *gh; int8_t pdisc; uint8_t mtype; int drop_message = 1; if (!text) return; if (!msg || msgb_l3len(msg) < sizeof(*gh)) return; gh = msgb_l3(msg); pdisc = gh->proto_discr & 0x0f; mtype = gh->msg_type & 0xbf; /* Is CM service request? */ if (pdisc == GSM48_PDISC_MM && mtype == GSM48_MT_MM_CM_SERV_REQ) { struct gsm48_service_request *cm; cm = (struct gsm48_service_request *) &gh->data[0]; /* Is type SMS or call? */ if (cm->cm_service_type == GSM48_CMSERV_SMS) drop_message = 0; else if (cm->cm_service_type == GSM48_CMSERV_MO_CALL_PACKET) drop_message = 0; } if (drop_message) { LOGP(DMSC, LOGL_DEBUG, "Skipping (not sending) USSD message: '%s'\n", text); return; } LOGP(DMSC, LOGL_INFO, "Sending CM Service Accept\n"); gsm48_tx_mm_serv_ack(conn); LOGP(DMSC, LOGL_INFO, "Sending USSD message: '%s'\n", text); gsm0480_send_ussdNotify(conn, 1, text); gsm0480_send_releaseComplete(conn); } /* * Instruct to reserve data for a new connectiom, create the complete * layer three message, send it to open the connection. */ static int bsc_compl_l3(struct gsm_subscriber_connection *conn, struct msgb *msg, uint16_t chosen_channel) { struct osmo_msc_data *msc; LOGP(DMSC, LOGL_INFO, "Tx MSC COMPL L3\n"); /* find the MSC link we want to use */ msc = bsc_find_msc(conn, msg); if (!msc) { LOGP(DMSC, LOGL_ERROR, "Failed to find a MSC for a connection.\n"); bsc_send_ussd_no_srv(conn, msg, conn->bts->network->bsc_data->ussd_no_msc_txt); return -1; } return complete_layer3(conn, msg, msc); } static int complete_layer3(struct gsm_subscriber_connection *conn, struct msgb *msg, struct osmo_msc_data *msc) { int con_type, rc, lu_cause; char *imsi = NULL; struct timeval tv; struct msgb *resp; uint16_t network_code; uint16_t country_code; uint16_t lac; uint16_t ci; enum bsc_con ret; int send_ping = msc->advanced_ping; /* Advanced ping/pong handling */ if (osmo_timer_pending(&msc->pong_timer)) send_ping = 0; if (msc->ping_timeout <= 0) send_ping = 0; if (send_ping && osmo_timer_remaining(&msc->ping_timer, NULL, &tv) == -1) send_ping = 0; /* Check the filter */ rc = bsc_filter_initial(msc->network->bsc_data, msc, conn, msg, &imsi, &con_type, &lu_cause); if (rc < 0) { bsc_maybe_lu_reject(conn, con_type, lu_cause); return BSC_API_CONN_POL_REJECT; } /* allocate resource for a new connection */ ret = bsc_create_new_connection(conn, msc, send_ping); if (ret != BSC_CON_SUCCESS) { /* allocation has failed */ if (ret == BSC_CON_REJECT_NO_LINK) bsc_send_ussd_no_srv(conn, msg, msc->ussd_msc_lost_txt); else if (ret == BSC_CON_REJECT_RF_GRACE) bsc_send_ussd_no_srv(conn, msg, msc->ussd_grace_txt); return BSC_API_CONN_POL_REJECT; } if (imsi) conn->sccp_con->filter_state.imsi = talloc_steal(conn, imsi); conn->sccp_con->filter_state.con_type = con_type; /* check return value, if failed check msg for and send USSD */ network_code = get_network_code_for_msc(conn->sccp_con->msc); country_code = get_country_code_for_msc(conn->sccp_con->msc); lac = get_lac_for_msc(conn->sccp_con->msc, conn->bts); ci = get_ci_for_msc(conn->sccp_con->msc, conn->bts); bsc_scan_bts_msg(conn, msg); resp = gsm0808_create_layer3(msg, network_code, country_code, lac, ci); if (!resp) { LOGP(DMSC, LOGL_DEBUG, "Failed to create layer3 message.\n"); sccp_connection_free(conn->sccp_con->sccp); bsc_delete_connection(conn->sccp_con); return BSC_API_CONN_POL_REJECT; } if (bsc_open_connection(conn->sccp_con, resp) != 0) { sccp_connection_free(conn->sccp_con->sccp); bsc_delete_connection(conn->sccp_con); msgb_free(resp); return BSC_API_CONN_POL_REJECT; } return BSC_API_CONN_POL_ACCEPT; } /* * Plastic surgery... we want to give up the current connection */ static int move_to_msc(struct gsm_subscriber_connection *_conn, struct msgb *msg, struct osmo_msc_data *msc) { struct osmo_bsc_sccp_con *old_con = _conn->sccp_con; /* * 1. Give up the old connection. * This happens by sending a clear request to the MSC, * it should end with the MSC releasing the connection. */ old_con->conn = NULL; bsc_clear_request(_conn, 0); /* * 2. Attempt to create a new connection to the local * MSC. If it fails the caller will need to handle this * properly. */ _conn->sccp_con = NULL; if (complete_layer3(_conn, msg, msc) != BSC_API_CONN_POL_ACCEPT) { gsm0808_clear(_conn); subscr_con_free(_conn); return 1; } return 2; } static int handle_cc_setup(struct gsm_subscriber_connection *conn, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); uint8_t pdisc = gh->proto_discr & 0x0f; uint8_t mtype = gh->msg_type & 0xbf; struct osmo_msc_data *msc; struct gsm_mncc_number called; struct tlv_parsed tp; unsigned payload_len; char _dest_nr[35]; /* * Do we have a setup message here? if not return fast. */ if (pdisc != GSM48_PDISC_CC || mtype != GSM48_MT_CC_SETUP) return 0; payload_len = msgb_l3len(msg) - sizeof(*gh); tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); if (!TLVP_PRESENT(&tp, GSM48_IE_CALLED_BCD)) { LOGP(DMSC, LOGL_ERROR, "Called BCD not present in setup.\n"); return -1; } memset(&called, 0, sizeof(called)); gsm48_decode_called(&called, TLVP_VAL(&tp, GSM48_IE_CALLED_BCD) - 1); if (called.plan != 1 && called.plan != 0) return 0; if (called.plan == 1 && called.type == 1) { _dest_nr[0] = _dest_nr[1] = '0'; memcpy(_dest_nr + 2, called.number, sizeof(called.number)); } else memcpy(_dest_nr, called.number, sizeof(called.number)); /* * Check if the connection should be moved... */ llist_for_each_entry(msc, &conn->bts->network->bsc_data->mscs, entry) { if (msc->type != MSC_CON_TYPE_LOCAL) continue; if (!msc->local_pref) continue; if (regexec(&msc->local_pref_reg, _dest_nr, 0, NULL, 0) != 0) continue; return move_to_msc(conn, msg, msc); } return 0; } static void bsc_dtap(struct gsm_subscriber_connection *conn, uint8_t link_id, struct msgb *msg) { int lu_cause; struct msgb *resp; return_when_not_connected(conn); LOGP(DMSC, LOGL_INFO, "Tx MSC DTAP LINK_ID=0x%02x\n", link_id); /* * We might want to move this connection to a new MSC. Ask someone * to handle it. If it was handled we will return. */ if (handle_cc_setup(conn, msg) >= 1) return; /* Check the filter */ if (bsc_filter_data(conn, msg, &lu_cause) < 0) { bsc_maybe_lu_reject(conn, conn->sccp_con->filter_state.con_type, lu_cause); bsc_clear_request(conn, 0); return; } bsc_scan_bts_msg(conn, msg); resp = gsm0808_create_dtap(msg, link_id); queue_msg_or_return(resp); } static void bsc_assign_compl(struct gsm_subscriber_connection *conn, uint8_t rr_cause, uint8_t chosen_channel, uint8_t encr_alg_id, uint8_t speech_model) { struct msgb *resp; return_when_not_connected(conn); LOGP(DMSC, LOGL_INFO, "Tx MSC ASSIGN COMPL\n"); resp = gsm0808_create_assignment_completed(rr_cause, chosen_channel, encr_alg_id, speech_model); queue_msg_or_return(resp); } static void bsc_assign_fail(struct gsm_subscriber_connection *conn, uint8_t cause, uint8_t *rr_cause) { struct msgb *resp; return_when_not_connected(conn); LOGP(DMSC, LOGL_INFO, "Tx MSC ASSIGN FAIL\n"); resp = gsm0808_create_assignment_failure(cause, rr_cause); queue_msg_or_return(resp); } static int bsc_clear_request(struct gsm_subscriber_connection *conn, uint32_t cause) { struct osmo_bsc_sccp_con *sccp; struct msgb *resp; return_when_not_connected_val(conn, 1); LOGP(DMSC, LOGL_INFO, "Tx MSC CLEAR REQUEST\n"); /* * Remove the connection from BSC<->SCCP part, the SCCP part * will either be cleared by channel release or MSC disconnect */ sccp = conn->sccp_con; sccp->conn = NULL; conn->sccp_con = NULL; resp = gsm0808_create_clear_rqst(GSM0808_CAUSE_RADIO_INTERFACE_FAILURE); if (!resp) { LOGP(DMSC, LOGL_ERROR, "Failed to allocate response.\n"); return 1; } bsc_queue_for_msc(sccp, resp); return 1; } static void bsc_cm_update(struct gsm_subscriber_connection *conn, const uint8_t *cm2, uint8_t cm2_len, const uint8_t *cm3, uint8_t cm3_len) { struct msgb *resp; return_when_not_connected(conn); resp = gsm0808_create_classmark_update(cm2, cm2_len, cm3, cm3_len); queue_msg_or_return(resp); } static void bsc_mr_config(struct gsm_subscriber_connection *conn, struct gsm_lchan *lchan, int full_rate) { struct osmo_msc_data *msc; struct gsm48_multi_rate_conf *ms_conf, *bts_conf; if (!conn->sccp_con) { LOGP(DMSC, LOGL_ERROR, "No msc data available on conn %p. Audio will be broken.\n", conn); return; } msc = conn->sccp_con->msc; /* initialize the data structure */ lchan->mr_ms_lv[0] = sizeof(*ms_conf); lchan->mr_bts_lv[0] = sizeof(*bts_conf); ms_conf = (struct gsm48_multi_rate_conf *) &lchan->mr_ms_lv[1]; bts_conf = (struct gsm48_multi_rate_conf *) &lchan->mr_bts_lv[1]; memset(ms_conf, 0, sizeof(*ms_conf)); memset(bts_conf, 0, sizeof(*bts_conf)); bts_conf->ver = ms_conf->ver = 1; bts_conf->icmi = ms_conf->icmi = 1; /* maybe gcc see's it is copy of _one_ byte */ bts_conf->m4_75 = ms_conf->m4_75 = msc->amr_conf.m4_75; bts_conf->m5_15 = ms_conf->m5_15 = msc->amr_conf.m5_15; bts_conf->m5_90 = ms_conf->m5_90 = msc->amr_conf.m5_90; bts_conf->m6_70 = ms_conf->m6_70 = msc->amr_conf.m6_70; bts_conf->m7_40 = ms_conf->m7_40 = msc->amr_conf.m7_40; bts_conf->m7_95 = ms_conf->m7_95 = msc->amr_conf.m7_95; if (full_rate) { bts_conf->m10_2 = ms_conf->m10_2 = msc->amr_conf.m10_2; bts_conf->m12_2 = ms_conf->m12_2 = msc->amr_conf.m12_2; } /* now copy this into the bts structure */ memcpy(lchan->mr_bts_lv, lchan->mr_ms_lv, sizeof(lchan->mr_ms_lv)); } static struct bsc_api bsc_handler = { .sapi_n_reject = bsc_sapi_n_reject, .cipher_mode_compl = bsc_cipher_mode_compl, .compl_l3 = bsc_compl_l3, .dtap = bsc_dtap, .assign_compl = bsc_assign_compl, .assign_fail = bsc_assign_fail, .clear_request = bsc_clear_request, .classmark_chg = bsc_cm_update, .mr_config = bsc_mr_config, }; struct bsc_api *osmo_bsc_api() { return &bsc_handler; } openbsc-0.15.0/openbsc/src/osmo-bsc/osmo_bsc_audio.c000066400000000000000000000037471265565154000223450ustar00rootroot00000000000000/* * ipaccess audio handling * * (C) 2009-2010 by Holger Hans Peter Freyther * (C) 2009-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 . * */ #include #include #include #include #include #include #include static int handle_abisip_signal(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct gsm_subscriber_connection *con; struct gsm_lchan *lchan = signal_data; int rc; if (subsys != SS_ABISIP) return 0; con = lchan->conn; if (!con || !con->sccp_con) return 0; switch (signal) { case S_ABISIP_CRCX_ACK: /* * TODO: handle handover here... then the audio should go to * the old mgcp port.. */ /* we can ask it to connect now */ LOGP(DMSC, LOGL_DEBUG, "Connecting BTS to port: %d conn: %d\n", con->sccp_con->rtp_port, lchan->abis_ip.conn_id); rc = rsl_ipacc_mdcx(lchan, ntohl(INADDR_ANY), con->sccp_con->rtp_port, lchan->abis_ip.rtp_payload2); if (rc < 0) { LOGP(DMSC, LOGL_ERROR, "Failed to send MDCX: %d\n", rc); return rc; } break; } return 0; } int osmo_bsc_audio_init(struct gsm_network *net) { osmo_signal_register_handler(SS_ABISIP, handle_abisip_signal, net); return 0; } openbsc-0.15.0/openbsc/src/osmo-bsc/osmo_bsc_bssap.c000066400000000000000000000353361265565154000223530ustar00rootroot00000000000000/* GSM 08.08 BSSMAP handling */ /* (C) 2009-2012 by Holger Hans Peter Freyther * (C) 2009-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 . * */ #include #include #include #include #include #include #include #include #include /* * helpers for the assignment command */ enum gsm0808_permitted_speech audio_support_to_gsm88(struct gsm_audio_support *audio) { if (audio->hr) { switch (audio->ver) { case 1: return GSM0808_PERM_HR1; break; case 2: return GSM0808_PERM_HR2; break; case 3: return GSM0808_PERM_HR3; break; default: LOGP(DMSC, LOGL_ERROR, "Wrong speech mode: %d\n", audio->ver); return GSM0808_PERM_FR1; } } else { switch (audio->ver) { case 1: return GSM0808_PERM_FR1; break; case 2: return GSM0808_PERM_FR2; break; case 3: return GSM0808_PERM_FR3; break; default: LOGP(DMSC, LOGL_ERROR, "Wrong speech mode: %d\n", audio->ver); return GSM0808_PERM_HR1; } } } enum gsm48_chan_mode gsm88_to_chan_mode(enum gsm0808_permitted_speech speech) { switch (speech) { case GSM0808_PERM_HR1: case GSM0808_PERM_FR1: return GSM48_CMODE_SPEECH_V1; break; case GSM0808_PERM_HR2: case GSM0808_PERM_FR2: return GSM48_CMODE_SPEECH_EFR; break; case GSM0808_PERM_HR3: case GSM0808_PERM_FR3: return GSM48_CMODE_SPEECH_AMR; break; } LOGP(DMSC, LOGL_FATAL, "Should not be reached.\n"); return GSM48_CMODE_SPEECH_AMR; } static int bssmap_handle_reset_ack(struct osmo_msc_data *msc, struct msgb *msg, unsigned int length) { LOGP(DMSC, LOGL_NOTICE, "Reset ACK from MSC\n"); return 0; } /* GSM 08.08 § 3.2.1.19 */ static int bssmap_handle_paging(struct osmo_msc_data *msc, struct msgb *msg, unsigned int payload_length) { struct gsm_subscriber *subscr; struct tlv_parsed tp; char mi_string[GSM48_MI_SIZE]; uint32_t tmsi = GSM_RESERVED_TMSI; unsigned int lac = GSM_LAC_RESERVED_ALL_BTS; uint8_t data_length; const uint8_t *data; uint8_t chan_needed = RSL_CHANNEED_ANY; tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l4h + 1, payload_length - 1, 0, 0); if (!TLVP_PRESENT(&tp, GSM0808_IE_IMSI)) { LOGP(DMSC, LOGL_ERROR, "Mandatory IMSI not present.\n"); return -1; } else if ((TLVP_VAL(&tp, GSM0808_IE_IMSI)[0] & GSM_MI_TYPE_MASK) != GSM_MI_TYPE_IMSI) { LOGP(DMSC, LOGL_ERROR, "Wrong content in the IMSI\n"); return -1; } if (!TLVP_PRESENT(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST)) { LOGP(DMSC, LOGL_ERROR, "Mandatory CELL IDENTIFIER LIST not present.\n"); return -1; } if (TLVP_PRESENT(&tp, GSM0808_IE_TMSI) && TLVP_LEN(&tp, GSM0808_IE_TMSI) == 4) { tmsi = ntohl(tlvp_val32_unal(&tp, GSM0808_IE_TMSI)); } /* * parse the IMSI */ gsm48_mi_to_string(mi_string, sizeof(mi_string), TLVP_VAL(&tp, GSM0808_IE_IMSI), TLVP_LEN(&tp, GSM0808_IE_IMSI)); /* * parse the cell identifier list */ data_length = TLVP_LEN(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST); data = TLVP_VAL(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST); /* * Support paging to all network or one BTS at one LAC */ if (data_length == 3 && data[0] == CELL_IDENT_LAC) { lac = osmo_load16be(&data[1]); } else if (data_length > 1 || (data[0] & 0x0f) != CELL_IDENT_BSS) { LOGP(DMSC, LOGL_ERROR, "Unsupported Cell Identifier List: %s\n", osmo_hexdump(data, data_length)); return -1; } if (TLVP_PRESENT(&tp, GSM0808_IE_CHANNEL_NEEDED) && TLVP_LEN(&tp, GSM0808_IE_CHANNEL_NEEDED) == 1) chan_needed = TLVP_VAL(&tp, GSM0808_IE_CHANNEL_NEEDED)[0] & 0x03; if (TLVP_PRESENT(&tp, GSM0808_IE_EMLPP_PRIORITY)) { LOGP(DMSC, LOGL_ERROR, "eMLPP is not handled\n"); } subscr = subscr_get_or_create(msc->network->subscr_group, mi_string); if (!subscr) { LOGP(DMSC, LOGL_ERROR, "Failed to allocate a subscriber for %s\n", mi_string); return -1; } subscr->lac = lac; subscr->tmsi = tmsi; LOGP(DMSC, LOGL_INFO, "Paging request from MSC IMSI: '%s' TMSI: '0x%x/%u' LAC: 0x%x\n", mi_string, tmsi, tmsi, lac); bsc_grace_paging_request(subscr, chan_needed, msc); return 0; } /* * GSM 08.08 § 3.1.9.1 and 3.2.1.21... * release our gsm_subscriber_connection and send message */ static int bssmap_handle_clear_command(struct osmo_bsc_sccp_con *conn, struct msgb *msg, unsigned int payload_length) { struct msgb *resp; /* TODO: handle the cause of this package */ if (conn->conn) { LOGP(DMSC, LOGL_INFO, "Releasing all transactions on %p\n", conn); gsm0808_clear(conn->conn); subscr_con_free(conn->conn); conn->conn = NULL; } /* send the clear complete message */ resp = gsm0808_create_clear_complete(); if (!resp) { LOGP(DMSC, LOGL_ERROR, "Sending clear complete failed.\n"); return -1; } bsc_queue_for_msc(conn, resp); return 0; } /* * GSM 08.08 § 3.4.7 cipher mode handling. We will have to pick * the cipher to be used for this. In case we are already using * a cipher we will have to send cipher mode reject to the MSC, * otherwise we will have to pick something that we and the MS * is supporting. Currently we are doing it in a rather static * way by picking one ecnryption or no encrytpion. */ static int bssmap_handle_cipher_mode(struct osmo_bsc_sccp_con *conn, struct msgb *msg, unsigned int payload_length) { uint16_t len; struct gsm_network *network = NULL; const uint8_t *data; struct tlv_parsed tp; struct msgb *resp; int reject_cause = -1; int include_imeisv = 1; if (!conn->conn) { LOGP(DMSC, LOGL_ERROR, "No lchan/msc_data in cipher mode command.\n"); goto reject; } if (conn->ciphering_handled) { LOGP(DMSC, LOGL_ERROR, "Already seen ciphering command. Protocol Error.\n"); goto reject; } conn->ciphering_handled = 1; tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l4h + 1, payload_length - 1, 0, 0); if (!TLVP_PRESENT(&tp, GSM0808_IE_ENCRYPTION_INFORMATION)) { LOGP(DMSC, LOGL_ERROR, "IE Encryption Information missing.\n"); goto reject; } /* * check if our global setting is allowed * - Currently we check for A5/0 and A5/1 * - Copy the key if that is necessary * - Otherwise reject */ len = TLVP_LEN(&tp, GSM0808_IE_ENCRYPTION_INFORMATION); if (len < 1) { LOGP(DMSC, LOGL_ERROR, "IE Encryption Information is too short.\n"); goto reject; } network = conn->conn->bts->network; data = TLVP_VAL(&tp, GSM0808_IE_ENCRYPTION_INFORMATION); if (TLVP_PRESENT(&tp, GSM0808_IE_CIPHER_RESPONSE_MODE)) include_imeisv = TLVP_VAL(&tp, GSM0808_IE_CIPHER_RESPONSE_MODE)[0] & 0x1; if (network->a5_encryption == 0 && (data[0] & 0x1) == 0x1) { gsm0808_cipher_mode(conn->conn, 0, NULL, 0, include_imeisv); } else if (network->a5_encryption != 0 && (data[0] & 0x2) == 0x2) { gsm0808_cipher_mode(conn->conn, 1, &data[1], len - 1, include_imeisv); } else { LOGP(DMSC, LOGL_ERROR, "Can not select encryption...\n"); goto reject; } return 0; reject: resp = gsm0808_create_cipher_reject(reject_cause); if (!resp) { LOGP(DMSC, LOGL_ERROR, "Sending the cipher reject failed.\n"); return -1; } bsc_queue_for_msc(conn, resp); return -1; } /* * Handle the assignment request message. * * See §3.2.1.1 for the message type */ static int bssmap_handle_assignm_req(struct osmo_bsc_sccp_con *conn, struct msgb *msg, unsigned int length) { struct msgb *resp; struct osmo_msc_data *msc; struct tlv_parsed tp; uint8_t *data; uint8_t timeslot; uint8_t multiplex; enum gsm48_chan_mode chan_mode = GSM48_CMODE_SIGN; int i, supported, port, full_rate = -1; if (!conn->conn) { LOGP(DMSC, LOGL_ERROR, "No lchan/msc_data in cipher mode command.\n"); return -1; } tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l4h + 1, length - 1, 0, 0); if (!TLVP_PRESENT(&tp, GSM0808_IE_CHANNEL_TYPE)) { LOGP(DMSC, LOGL_ERROR, "Mandatory channel type not present.\n"); goto reject; } if (!TLVP_PRESENT(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE)) { LOGP(DMSC, LOGL_ERROR, "Identity code missing. Audio routing will not work.\n"); goto reject; } conn->cic = osmo_load16be(TLVP_VAL(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE)); timeslot = conn->cic & 0x1f; multiplex = (conn->cic & ~0x1f) >> 5; /* * Currently we only support a limited subset of all * possible channel types. The limitation ends by not using * multi-slot, limiting the channel coding, speech... */ if (TLVP_LEN(&tp, GSM0808_IE_CHANNEL_TYPE) < 3) { LOGP(DMSC, LOGL_ERROR, "ChannelType len !=3 not supported: %d\n", TLVP_LEN(&tp, GSM0808_IE_CHANNEL_TYPE)); goto reject; } /* * Try to figure out if we support the proposed speech codecs. For * now we will always pick the full rate codecs. */ data = (uint8_t *) TLVP_VAL(&tp, GSM0808_IE_CHANNEL_TYPE); if ((data[0] & 0xf) != 0x1) { LOGP(DMSC, LOGL_ERROR, "ChannelType != speech: %d\n", data[0]); goto reject; } /* * go through the list of preferred codecs of our gsm network * and try to find it among the permitted codecs. If we found * it we will send chan_mode to the right mode and break the * inner loop. The outer loop will exit due chan_mode having * the correct value. */ full_rate = 0; msc = conn->msc; for (supported = 0; chan_mode == GSM48_CMODE_SIGN && supported < msc->audio_length; ++supported) { int perm_val = audio_support_to_gsm88(msc->audio_support[supported]); for (i = 2; i < TLVP_LEN(&tp, GSM0808_IE_CHANNEL_TYPE); ++i) { if ((data[i] & 0x7f) == perm_val) { chan_mode = gsm88_to_chan_mode(perm_val); full_rate = (data[i] & 0x4) == 0; break; } else if ((data[i] & 0x80) == 0x00) { break; } } } if (chan_mode == GSM48_CMODE_SIGN) { LOGP(DMSC, LOGL_ERROR, "No supported audio type found.\n"); goto reject; } /* map it to a MGCP Endpoint and a RTP port */ port = mgcp_timeslot_to_endpoint(multiplex, timeslot); conn->rtp_port = rtp_calculate_port(port, msc->rtp_base); return gsm0808_assign_req(conn->conn, chan_mode, full_rate); reject: resp = gsm0808_create_assignment_failure(GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE, NULL); if (!resp) { LOGP(DMSC, LOGL_ERROR, "Channel allocation failure.\n"); return -1; } bsc_queue_for_msc(conn, resp); return -1; } static int bssmap_rcvmsg_udt(struct osmo_msc_data *msc, struct msgb *msg, unsigned int length) { int ret = 0; if (length < 1) { LOGP(DMSC, LOGL_ERROR, "Not enough room: %d\n", length); return -1; } LOGP(DMSC, LOGL_INFO, "Rx MSC UDT BSSMAP %s\n", gsm0808_bssmap_name(msg->l4h[0])); switch (msg->l4h[0]) { case BSS_MAP_MSG_RESET_ACKNOWLEDGE: ret = bssmap_handle_reset_ack(msc, msg, length); break; case BSS_MAP_MSG_PAGING: ret = bssmap_handle_paging(msc, msg, length); break; } return ret; } static int bssmap_rcvmsg_dt1(struct osmo_bsc_sccp_con *conn, struct msgb *msg, unsigned int length) { int ret = 0; if (length < 1) { LOGP(DMSC, LOGL_ERROR, "Not enough room: %d\n", length); return -1; } LOGP(DMSC, LOGL_INFO, "Rx MSC DT1 BSSMAP %s\n", gsm0808_bssmap_name(msg->l4h[0])); switch (msg->l4h[0]) { case BSS_MAP_MSG_CLEAR_CMD: ret = bssmap_handle_clear_command(conn, msg, length); break; case BSS_MAP_MSG_CIPHER_MODE_CMD: ret = bssmap_handle_cipher_mode(conn, msg, length); break; case BSS_MAP_MSG_ASSIGMENT_RQST: ret = bssmap_handle_assignm_req(conn, msg, length); break; default: LOGP(DMSC, LOGL_NOTICE, "Unimplemented msg type: %s\n", gsm0808_bssmap_name(msg->l4h[0])); break; } return ret; } static int dtap_rcvmsg(struct osmo_bsc_sccp_con *conn, struct msgb *msg, unsigned int length) { struct dtap_header *header; struct msgb *gsm48; uint8_t *data; int rc, dtap_rc; LOGP(DMSC, LOGL_DEBUG, "Rx MSC DTAP: %s\n", osmo_hexdump(msg->l3h, length)); if (!conn->conn) { LOGP(DMSC, LOGL_ERROR, "No subscriber connection available\n"); return -1; } header = (struct dtap_header *) msg->l3h; if (sizeof(*header) >= length) { LOGP(DMSC, LOGL_ERROR, "The DTAP header does not fit. Wanted: %zu got: %u\n", sizeof(*header), length); LOGP(DMSC, LOGL_ERROR, "hex: %s\n", osmo_hexdump(msg->l3h, length)); return -1; } if (header->length > length - sizeof(*header)) { LOGP(DMSC, LOGL_ERROR, "The DTAP l4 information does not fit: header: %u length: %u\n", header->length, length); LOGP(DMSC, LOGL_ERROR, "hex: %s\n", osmo_hexdump(msg->l3h, length)); return -1; } LOGP(DMSC, LOGL_INFO, "Rx MSC DTAP, SAPI: %u CHAN: %u\n", header->link_id & 0x07, header->link_id & 0xC0); /* forward the data */ gsm48 = gsm48_msgb_alloc(); if (!gsm48) { LOGP(DMSC, LOGL_ERROR, "Allocation of the message failed.\n"); return -1; } gsm48->l3h = gsm48->data; data = msgb_put(gsm48, length - sizeof(*header)); memcpy(data, msg->l3h + sizeof(*header), length - sizeof(*header)); /* pass it to the filter for extra actions */ rc = bsc_scan_msc_msg(conn->conn, gsm48); dtap_rc = gsm0808_submit_dtap(conn->conn, gsm48, header->link_id, 1); if (rc == BSS_SEND_USSD) bsc_send_welcome_ussd(conn->conn); return dtap_rc; } int bsc_handle_udt(struct osmo_msc_data *msc, struct msgb *msgb, unsigned int length) { struct bssmap_header *bs; LOGP(DMSC, LOGL_DEBUG, "Rx MSC UDT: %s\n", osmo_hexdump(msgb->l3h, length)); if (length < sizeof(*bs)) { LOGP(DMSC, LOGL_ERROR, "The header is too short.\n"); return -1; } bs = (struct bssmap_header *) msgb->l3h; if (bs->length < length - sizeof(*bs)) return -1; switch (bs->type) { case BSSAP_MSG_BSS_MANAGEMENT: msgb->l4h = &msgb->l3h[sizeof(*bs)]; bssmap_rcvmsg_udt(msc, msgb, length - sizeof(*bs)); break; default: LOGP(DMSC, LOGL_NOTICE, "Unimplemented msg type: %s\n", gsm0808_bssmap_name(bs->type)); } return 0; } int bsc_handle_dt1(struct osmo_bsc_sccp_con *conn, struct msgb *msg, unsigned int len) { if (len < sizeof(struct bssmap_header)) { LOGP(DMSC, LOGL_ERROR, "The header is too short.\n"); } switch (msg->l3h[0]) { case BSSAP_MSG_BSS_MANAGEMENT: msg->l4h = &msg->l3h[sizeof(struct bssmap_header)]; bssmap_rcvmsg_dt1(conn, msg, len - sizeof(struct bssmap_header)); break; case BSSAP_MSG_DTAP: dtap_rcvmsg(conn, msg, len); break; default: LOGP(DMSC, LOGL_NOTICE, "Unimplemented BSSAP msg type: %s\n", gsm0808_bssap_name(msg->l3h[0])); } return -1; } openbsc-0.15.0/openbsc/src/osmo-bsc/osmo_bsc_ctrl.c000066400000000000000000000413601265565154000222010ustar00rootroot00000000000000/* (C) 2011 by Daniel Willmann * (C) 2011 by Holger Hans Peter Freyther * (C) 2011 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void osmo_bsc_send_trap(struct ctrl_cmd *cmd, struct bsc_msc_connection *msc_con) { struct ctrl_cmd *trap; struct ctrl_handle *ctrl; struct osmo_msc_data *msc_data; msc_data = (struct osmo_msc_data *) msc_con->write_queue.bfd.data; ctrl = msc_data->network->ctrl; trap = ctrl_cmd_trap(cmd); if (!trap) { LOGP(DCTRL, LOGL_ERROR, "Failed to create trap.\n"); return; } ctrl_cmd_send_to_all(ctrl, trap); ctrl_cmd_send(&msc_con->write_queue, trap); talloc_free(trap); } CTRL_CMD_DEFINE_RO(msc_connection_status, "msc_connection_status"); static int msc_connection_status = 0; static int get_msc_connection_status(struct ctrl_cmd *cmd, void *data) { if (msc_connection_status) cmd->reply = "connected"; else cmd->reply = "disconnected"; return CTRL_CMD_REPLY; } static int msc_connection_status_trap_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct ctrl_cmd *cmd; struct gsm_network *gsmnet = (struct gsm_network *)handler_data; if (signal == S_MSC_LOST && msc_connection_status == 1) { LOGP(DCTRL, LOGL_DEBUG, "MSC connection lost, sending TRAP.\n"); msc_connection_status = 0; } else if (signal == S_MSC_CONNECTED && msc_connection_status == 0) { LOGP(DCTRL, LOGL_DEBUG, "MSC connection (re)established, sending TRAP.\n"); msc_connection_status = 1; } else { return 0; } cmd = ctrl_cmd_create(tall_bsc_ctx, CTRL_TYPE_TRAP); if (!cmd) { LOGP(DCTRL, LOGL_ERROR, "Trap creation failed.\n"); return 0; } cmd->id = "0"; cmd->variable = "msc_connection_status"; get_msc_connection_status(cmd, NULL); ctrl_cmd_send_to_all(gsmnet->ctrl, cmd); talloc_free(cmd); return 0; } CTRL_CMD_DEFINE_RO(bts_connection_status, "bts_connection_status"); static int bts_connection_status = 0; static int get_bts_connection_status(struct ctrl_cmd *cmd, void *data) { if (bts_connection_status) cmd->reply = "connected"; else cmd->reply = "disconnected"; return CTRL_CMD_REPLY; } static int bts_connection_status_trap_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct ctrl_cmd *cmd; struct gsm_network *gsmnet = (struct gsm_network *)handler_data; struct gsm_bts *bts; int bts_current_status; if (signal != S_L_INP_TEI_DN && signal != S_L_INP_TEI_UP) { return 0; } bts_current_status = 0; /* Check if OML on at least one BTS is up */ llist_for_each_entry(bts, &gsmnet->bts_list, list) { if (bts->oml_link) { bts_current_status = 1; break; } } if (bts_connection_status == 0 && bts_current_status == 1) { LOGP(DCTRL, LOGL_DEBUG, "BTS connection (re)established, sending TRAP.\n"); } else if (bts_connection_status == 1 && bts_current_status == 0) { LOGP(DCTRL, LOGL_DEBUG, "No more BTS connected, sending TRAP.\n"); } else { return 0; } cmd = ctrl_cmd_create(tall_bsc_ctx, CTRL_TYPE_TRAP); if (!cmd) { LOGP(DCTRL, LOGL_ERROR, "Trap creation failed.\n"); return 0; } bts_connection_status = bts_current_status; cmd->id = "0"; cmd->variable = "bts_connection_status"; get_bts_connection_status(cmd, NULL); ctrl_cmd_send_to_all(gsmnet->ctrl, cmd); talloc_free(cmd); return 0; } static int get_bts_loc(struct ctrl_cmd *cmd, void *data); static void generate_location_state_trap(struct gsm_bts *bts, struct bsc_msc_connection *msc_con) { struct ctrl_cmd *cmd; const char *oper, *admin, *policy; cmd = ctrl_cmd_create(msc_con, CTRL_TYPE_TRAP); if (!cmd) { LOGP(DCTRL, LOGL_ERROR, "Failed to create TRAP command.\n"); return; } cmd->id = "0"; cmd->variable = talloc_asprintf(cmd, "bts.%i.location-state", bts->nr); /* Prepare the location reply */ cmd->node = bts; get_bts_loc(cmd, NULL); oper = osmo_bsc_rf_get_opstate_name(osmo_bsc_rf_get_opstate_by_bts(bts)); admin = osmo_bsc_rf_get_adminstate_name(osmo_bsc_rf_get_adminstate_by_bts(bts)); policy = osmo_bsc_rf_get_policy_name(osmo_bsc_rf_get_policy_by_bts(bts)); cmd->reply = talloc_asprintf_append(cmd->reply, ",%s,%s,%s,%d,%d", oper, admin, policy, bts->network->country_code, bts->network->network_code); osmo_bsc_send_trap(cmd, msc_con); talloc_free(cmd); } void bsc_gen_location_state_trap(struct gsm_bts *bts) { struct osmo_msc_data *msc; llist_for_each_entry(msc, &bts->network->bsc_data->mscs, entry) generate_location_state_trap(bts, msc->msc_con); } static int location_equal(struct bts_location *a, struct bts_location *b) { return ((a->tstamp == b->tstamp) && (a->valid == b->valid) && (a->lat == b->lat) && (a->lon == b->lon) && (a->height == b->height)); } static void cleanup_locations(struct llist_head *locations) { struct bts_location *myloc, *tmp; int invalpos = 0, i = 0; LOGP(DCTRL, LOGL_DEBUG, "Checking position list.\n"); llist_for_each_entry_safe(myloc, tmp, locations, list) { i++; if (i > 3) { LOGP(DCTRL, LOGL_DEBUG, "Deleting old position.\n"); llist_del(&myloc->list); talloc_free(myloc); } else if (myloc->valid == BTS_LOC_FIX_INVALID) { /* Only capture the newest of subsequent invalid positions */ invalpos++; if (invalpos > 1) { LOGP(DCTRL, LOGL_DEBUG, "Deleting subsequent invalid position.\n"); invalpos--; i--; llist_del(&myloc->list); talloc_free(myloc); } } else { invalpos = 0; } } LOGP(DCTRL, LOGL_DEBUG, "Found %i positions.\n", i); } CTRL_CMD_DEFINE(bts_loc, "location"); static int get_bts_loc(struct ctrl_cmd *cmd, void *data) { struct bts_location *curloc; struct gsm_bts *bts = (struct gsm_bts *) cmd->node; if (!bts) { cmd->reply = "bts not found."; return CTRL_CMD_ERROR; } if (llist_empty(&bts->loc_list)) { cmd->reply = talloc_asprintf(cmd, "0,invalid,0,0,0"); return CTRL_CMD_REPLY; } else { curloc = llist_entry(bts->loc_list.next, struct bts_location, list); } cmd->reply = talloc_asprintf(cmd, "%lu,%s,%f,%f,%f", curloc->tstamp, get_value_string(bts_loc_fix_names, curloc->valid), curloc->lat, curloc->lon, curloc->height); if (!cmd->reply) { cmd->reply = "OOM"; return CTRL_CMD_ERROR; } return CTRL_CMD_REPLY; } static int set_bts_loc(struct ctrl_cmd *cmd, void *data) { char *saveptr, *lat, *lon, *height, *tstamp, *valid, *tmp; struct bts_location *curloc, *lastloc; int ret; struct gsm_bts *bts = (struct gsm_bts *) cmd->node; if (!bts) { cmd->reply = "bts not found."; return CTRL_CMD_ERROR; } tmp = talloc_strdup(cmd, cmd->value); if (!tmp) goto oom; curloc = talloc_zero(tall_bsc_ctx, struct bts_location); if (!curloc) { talloc_free(tmp); goto oom; } INIT_LLIST_HEAD(&curloc->list); tstamp = strtok_r(tmp, ",", &saveptr); valid = strtok_r(NULL, ",", &saveptr); lat = strtok_r(NULL, ",", &saveptr); lon = strtok_r(NULL, ",", &saveptr); height = strtok_r(NULL, "\0", &saveptr); curloc->tstamp = atol(tstamp); curloc->valid = get_string_value(bts_loc_fix_names, valid); curloc->lat = atof(lat); curloc->lon = atof(lon); curloc->height = atof(height); talloc_free(tmp); lastloc = llist_entry(bts->loc_list.next, struct bts_location, list); /* Add location to the end of the list */ llist_add(&curloc->list, &bts->loc_list); ret = get_bts_loc(cmd, data); if (!location_equal(curloc, lastloc)) bsc_gen_location_state_trap(bts); cleanup_locations(&bts->loc_list); return ret; oom: cmd->reply = "OOM"; return CTRL_CMD_ERROR; } static int verify_bts_loc(struct ctrl_cmd *cmd, const char *value, void *data) { char *saveptr, *latstr, *lonstr, *heightstr, *tstampstr, *validstr, *tmp; time_t tstamp; int valid; double lat, lon, height __attribute__((unused)); tmp = talloc_strdup(cmd, value); if (!tmp) return 1; tstampstr = strtok_r(tmp, ",", &saveptr); validstr = strtok_r(NULL, ",", &saveptr); latstr = strtok_r(NULL, ",", &saveptr); lonstr = strtok_r(NULL, ",", &saveptr); heightstr = strtok_r(NULL, "\0", &saveptr); if ((tstampstr == NULL) || (validstr == NULL) || (latstr == NULL) || (lonstr == NULL) || (heightstr == NULL)) goto err; tstamp = atol(tstampstr); valid = get_string_value(bts_loc_fix_names, validstr); lat = atof(latstr); lon = atof(lonstr); height = atof(heightstr); talloc_free(tmp); tmp = NULL; if (((tstamp == 0) && (valid != BTS_LOC_FIX_INVALID)) || (lat < -90) || (lat > 90) || (lon < -180) || (lon > 180) || (valid < 0)) { goto err; } return 0; err: talloc_free(tmp); cmd->reply = talloc_strdup(cmd, "The format is ,(invalid|fix2d|fix3d),,,"); return 1; } CTRL_CMD_DEFINE(bts_timezone, "timezone"); static int get_bts_timezone(struct ctrl_cmd *cmd, void *data) { struct gsm_bts *bts = (struct gsm_bts *) cmd->node; if (!bts) { cmd->reply = "bts not found."; return CTRL_CMD_ERROR; } if (bts->tz.override) cmd->reply = talloc_asprintf(cmd, "%d,%d,%d", bts->tz.hr, bts->tz.mn, bts->tz.dst); else cmd->reply = talloc_asprintf(cmd, "off"); if (!cmd->reply) { cmd->reply = "OOM"; return CTRL_CMD_ERROR; } return CTRL_CMD_REPLY; } static int set_bts_timezone(struct ctrl_cmd *cmd, void *data) { char *saveptr, *hourstr, *minstr, *dststr, *tmp = 0; int override; struct gsm_bts *bts = (struct gsm_bts *) cmd->node; if (!bts) { cmd->reply = "bts not found."; return CTRL_CMD_ERROR; } tmp = talloc_strdup(cmd, cmd->value); if (!tmp) goto oom; hourstr = strtok_r(tmp, ",", &saveptr); minstr = strtok_r(NULL, ",", &saveptr); dststr = strtok_r(NULL, ",", &saveptr); override = 0; if (hourstr != NULL) override = strcasecmp(hourstr, "off") != 0; bts->tz.override = override; if (override) { bts->tz.hr = hourstr ? atol(hourstr) : 0; bts->tz.mn = minstr ? atol(minstr) : 0; bts->tz.dst = dststr ? atol(dststr) : 0; } talloc_free(tmp); tmp = NULL; return get_bts_timezone(cmd, data); oom: cmd->reply = "OOM"; return CTRL_CMD_ERROR; } static int verify_bts_timezone(struct ctrl_cmd *cmd, const char *value, void *data) { char *saveptr, *hourstr, *minstr, *dststr, *tmp; int override, tz_hours, tz_mins, tz_dst; tmp = talloc_strdup(cmd, value); if (!tmp) return 1; hourstr = strtok_r(tmp, ",", &saveptr); minstr = strtok_r(NULL, ",", &saveptr); dststr = strtok_r(NULL, ",", &saveptr); if (hourstr == NULL) goto err; override = strcasecmp(hourstr, "off") != 0; if (!override) { talloc_free(tmp); return 0; } if (minstr == NULL || dststr == NULL) goto err; tz_hours = atol(hourstr); tz_mins = atol(minstr); tz_dst = atol(dststr); talloc_free(tmp); tmp = NULL; if ((tz_hours < -19) || (tz_hours > 19) || (tz_mins < 0) || (tz_mins >= 60) || (tz_mins % 15 != 0) || (tz_dst < 0) || (tz_dst > 2)) goto err; return 0; err: talloc_free(tmp); cmd->reply = talloc_strdup(cmd, "The format is ,, or 'off' where -19 <= hours <= 19, mins in {0, 15, 30, 45}, and 0 <= dst <= 2"); return 1; } CTRL_CMD_DEFINE(net_notification, "notification"); static int get_net_notification(struct ctrl_cmd *cmd, void *data) { cmd->reply = "There is nothing to read"; return CTRL_CMD_ERROR; } static int set_net_notification(struct ctrl_cmd *cmd, void *data) { struct ctrl_cmd *trap; struct gsm_network *net; net = cmd->node; trap = ctrl_cmd_create(tall_bsc_ctx, CTRL_TYPE_TRAP); if (!trap) { LOGP(DCTRL, LOGL_ERROR, "Trap creation failed\n"); goto handled; } trap->id = "0"; trap->variable = "notification"; trap->reply = talloc_strdup(trap, cmd->value); /* * This should only be sent to local systems. In the future * we might even ask for systems to register to receive * the notifications. */ ctrl_cmd_send_to_all(net->ctrl, trap); talloc_free(trap); handled: return CTRL_CMD_HANDLED; } static int verify_net_notification(struct ctrl_cmd *cmd, const char *value, void *data) { return 0; } CTRL_CMD_DEFINE(net_inform_msc, "inform-msc-v1"); static int get_net_inform_msc(struct ctrl_cmd *cmd, void *data) { cmd->reply = "There is nothing to read"; return CTRL_CMD_ERROR; } static int set_net_inform_msc(struct ctrl_cmd *cmd, void *data) { struct gsm_network *net; struct osmo_msc_data *msc; net = cmd->node; llist_for_each_entry(msc, &net->bsc_data->mscs, entry) { struct ctrl_cmd *trap; trap = ctrl_cmd_create(tall_bsc_ctx, CTRL_TYPE_TRAP); if (!trap) { LOGP(DCTRL, LOGL_ERROR, "Trap creation failed\n"); continue; } trap->id = "0"; trap->variable = "inform-msc-v1"; trap->reply = talloc_strdup(trap, cmd->value); ctrl_cmd_send(&msc->msc_con->write_queue, trap); talloc_free(trap); } return CTRL_CMD_HANDLED; } static int verify_net_inform_msc(struct ctrl_cmd *cmd, const char *value, void *data) { return 0; } CTRL_CMD_DEFINE(net_ussd_notify, "ussd-notify-v1"); static int get_net_ussd_notify(struct ctrl_cmd *cmd, void *data) { cmd->reply = "There is nothing to read"; return CTRL_CMD_ERROR; } static int set_net_ussd_notify(struct ctrl_cmd *cmd, void *data) { struct gsm_subscriber_connection *conn; struct gsm_network *net; char *saveptr = NULL; char *cic_str, *alert_str, *text_str; int cic, alert; /* Verify has done the test for us */ cic_str = strtok_r(cmd->value, ",", &saveptr); alert_str = strtok_r(NULL, ",", &saveptr); text_str = strtok_r(NULL, ",", &saveptr); if (!cic_str || !alert_str || !text_str) { cmd->reply = "Programming issue. How did this pass verify?"; return CTRL_CMD_ERROR; } cmd->reply = "No connection found"; cic = atoi(cic_str); alert = atoi(alert_str); net = cmd->node; llist_for_each_entry(conn, bsc_api_sub_connections(net), entry) { if (!conn->sccp_con) continue; if (conn->sccp_con->cic != cic) continue; /* * This is a hack. My E71 does not like to immediately * receive a release complete on a TCH. So schedule a * release complete to clear any previous attempt. The * right thing would be to track invokeId and only send * the release complete when we get a returnResultLast * for this invoke id. */ gsm0480_send_releaseComplete(conn); gsm0480_send_ussdNotify(conn, alert, text_str); cmd->reply = "Found a connection"; break; } return CTRL_CMD_REPLY; } static int verify_net_ussd_notify(struct ctrl_cmd *cmd, const char *value, void *data) { char *saveptr = NULL; char *inp, *cic, *alert, *text; inp = talloc_strdup(cmd, value); cic = strtok_r(inp, ",", &saveptr); alert = strtok_r(NULL, ",", &saveptr); text = strtok_r(NULL, ",", &saveptr); talloc_free(inp); if (!cic || !alert || !text) return 1; return 0; } static int msc_signal_handler(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct msc_signal_data *msc; struct gsm_network *net; struct gsm_bts *bts; if (subsys != SS_MSC) return 0; if (signal != S_MSC_AUTHENTICATED) return 0; msc = signal_data; net = msc->data->network; llist_for_each_entry(bts, &net->bts_list, list) generate_location_state_trap(bts, msc->data->msc_con); return 0; } int bsc_ctrl_cmds_install(struct gsm_network *net) { int rc; rc = bsc_base_ctrl_cmds_install(); if (rc) goto end; rc = ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_loc); if (rc) goto end; rc = ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_timezone); if (rc) goto end; rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_msc_connection_status); if (rc) goto end; rc = osmo_signal_register_handler(SS_MSC, &msc_connection_status_trap_cb, net); if (rc) goto end; rc = osmo_signal_register_handler(SS_MSC, msc_signal_handler, NULL); if (rc) goto end; rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_bts_connection_status); if (rc) goto end; rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_notification); if (rc) goto end; rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_inform_msc); if (rc) goto end; rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_ussd_notify); if (rc) goto end; rc = osmo_signal_register_handler(SS_L_INPUT, &bts_connection_status_trap_cb, net); end: return rc; } openbsc-0.15.0/openbsc/src/osmo-bsc/osmo_bsc_filter.c000066400000000000000000000224231265565154000225210ustar00rootroot00000000000000/* (C) 2009-2011 by Holger Hans Peter Freyther * (C) 2009-2011 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 . * */ #include #include #include #include #include #include #include static void handle_lu_request(struct gsm_subscriber_connection *conn, struct msgb *msg) { struct gsm48_hdr *gh; struct gsm48_loc_upd_req *lu; struct gsm48_loc_area_id lai; struct gsm_network *net; if (msgb_l3len(msg) < sizeof(*gh) + sizeof(*lu)) { LOGP(DMSC, LOGL_ERROR, "LU too small to look at: %u\n", msgb_l3len(msg)); return; } net = conn->bts->network; gh = msgb_l3(msg); lu = (struct gsm48_loc_upd_req *) gh->data; gsm48_generate_lai(&lai, net->country_code, net->network_code, conn->bts->location_area_code); if (memcmp(&lai, &lu->lai, sizeof(lai)) != 0) { LOGP(DMSC, LOGL_DEBUG, "Marking con for welcome USSD.\n"); conn->sccp_con->new_subscriber = 1; } } /* extract a subscriber from the paging response */ static struct gsm_subscriber *extract_sub(struct gsm_subscriber_connection *conn, struct msgb *msg) { uint8_t mi_type; char mi_string[GSM48_MI_SIZE]; struct gsm48_hdr *gh; struct gsm48_pag_resp *resp; struct gsm_subscriber *subscr; if (msgb_l3len(msg) < sizeof(*gh) + sizeof(*resp)) { LOGP(DMSC, LOGL_ERROR, "PagingResponse too small: %u\n", msgb_l3len(msg)); return NULL; } gh = msgb_l3(msg); resp = (struct gsm48_pag_resp *) &gh->data[0]; gsm48_paging_extract_mi(resp, msgb_l3len(msg) - sizeof(*gh), mi_string, &mi_type); DEBUGP(DRR, "PAGING RESPONSE: MI(%s)=%s\n", gsm48_mi_type_name(mi_type), mi_string); switch (mi_type) { case GSM_MI_TYPE_TMSI: subscr = subscr_active_by_tmsi(conn->bts->network->subscr_group, tmsi_from_string(mi_string)); break; case GSM_MI_TYPE_IMSI: subscr = subscr_active_by_imsi(conn->bts->network->subscr_group, mi_string); break; default: subscr = NULL; break; } return subscr; } /* we will need to stop the paging request */ static int handle_page_resp(struct gsm_subscriber_connection *conn, struct msgb *msg) { struct gsm_subscriber *subscr = extract_sub(conn, msg); if (!subscr) { LOGP(DMSC, LOGL_ERROR, "Non active subscriber got paged.\n"); return -1; } paging_request_stop(conn->bts, subscr, conn, msg); subscr_put(subscr); return 0; } static int is_cm_service_for_emerg(struct msgb *msg) { struct gsm48_service_request *cm; struct gsm48_hdr *gh = msgb_l3(msg); if (msgb_l3len(msg) < sizeof(*gh) + sizeof(*cm)) { LOGP(DMSC, LOGL_ERROR, "CM ServiceRequest does not fit.\n"); return 0; } cm = (struct gsm48_service_request *) &gh->data[0]; return cm->cm_service_type == GSM48_CMSERV_EMERGENCY; } struct osmo_msc_data *bsc_find_msc(struct gsm_subscriber_connection *conn, struct msgb *msg) { struct gsm48_hdr *gh; int8_t pdisc; uint8_t mtype; struct osmo_bsc_data *bsc; struct osmo_msc_data *msc, *pag_msc; struct gsm_subscriber *subscr; int is_emerg = 0; bsc = conn->bts->network->bsc_data; if (msgb_l3len(msg) < sizeof(*gh)) { LOGP(DMSC, LOGL_ERROR, "There is no GSM48 header here.\n"); return NULL; } gh = msgb_l3(msg); pdisc = gh->proto_discr & 0x0f; mtype = gh->msg_type & 0xbf; /* * We are asked to select a MSC here but they are not equal. We * want to respond to a paging request on the MSC where we got the * request from. This is where we need to decide where this connection * will go. */ if (pdisc == GSM48_PDISC_RR && mtype == GSM48_MT_RR_PAG_RESP) goto paging; else if (pdisc == GSM48_PDISC_MM && mtype == GSM48_MT_MM_CM_SERV_REQ) { is_emerg = is_cm_service_for_emerg(msg); goto round_robin; } else goto round_robin; round_robin: llist_for_each_entry(msc, &bsc->mscs, entry) { if (!msc->msc_con->is_authenticated) continue; if (!is_emerg && msc->type != MSC_CON_TYPE_NORMAL) continue; if (is_emerg && !msc->allow_emerg) continue; /* force round robin by moving it to the end */ llist_move_tail(&msc->entry, &bsc->mscs); return msc; } return NULL; paging: subscr = extract_sub(conn, msg); if (!subscr) { LOGP(DMSC, LOGL_ERROR, "Got paged but no subscriber found.\n"); return NULL; } pag_msc = paging_get_data(conn->bts, subscr); subscr_put(subscr); llist_for_each_entry(msc, &bsc->mscs, entry) { if (msc != pag_msc) continue; /* * We don't check if the MSC is connected. In case it * is not the connection will be dropped. */ /* force round robin by moving it to the end */ llist_move_tail(&msc->entry, &bsc->mscs); return msc; } LOGP(DMSC, LOGL_ERROR, "Got paged but no request found.\n"); return NULL; } /** * This is used to scan a message for extra functionality of the BSC. This * includes scanning for location updating requests/acceptd and then send * a welcome USSD message to the subscriber. */ int bsc_scan_bts_msg(struct gsm_subscriber_connection *conn, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); uint8_t pdisc = gh->proto_discr & 0x0f; uint8_t mtype = gh->msg_type & 0xbf; if (pdisc == GSM48_PDISC_MM) { if (mtype == GSM48_MT_MM_LOC_UPD_REQUEST) handle_lu_request(conn, msg); } else if (pdisc == GSM48_PDISC_RR) { if (mtype == GSM48_MT_RR_PAG_RESP) handle_page_resp(conn, msg); } return 0; } static int send_welcome_ussd(struct gsm_subscriber_connection *conn) { struct osmo_bsc_sccp_con *bsc_con; bsc_con = conn->sccp_con; if (!bsc_con) { LOGP(DMSC, LOGL_DEBUG, "No SCCP connection associated.\n"); return 0; } if (!bsc_con->msc->ussd_welcome_txt) { LOGP(DMSC, LOGL_DEBUG, "No USSD Welcome text defined.\n"); return 0; } return BSS_SEND_USSD; } int bsc_send_welcome_ussd(struct gsm_subscriber_connection *conn) { gsm0480_send_ussdNotify(conn, 1, conn->sccp_con->msc->ussd_welcome_txt); gsm0480_send_releaseComplete(conn); return 0; } static int bsc_patch_mm_info(struct gsm_subscriber_connection *conn, uint8_t *data, unsigned int length) { struct tlv_parsed tp; int parse_res; struct gsm_bts *bts = conn->bts; int tzunits; uint8_t tzbsd = 0; uint8_t dst = 0; parse_res = tlv_parse(&tp, &gsm48_mm_att_tlvdef, data, length, 0, 0); if (parse_res <= 0 && parse_res != -3) return 0; /* Is TZ patching enabled? */ if (!bts->tz.override) return 0; /* Convert tz.hr and tz.mn to units */ if (bts->tz.hr < 0) { tzunits = -bts->tz.hr*4; tzbsd |= 0x08; } else tzunits = bts->tz.hr*4; tzunits = tzunits + (bts->tz.mn/15); tzbsd |= (tzunits % 10)*0x10 + (tzunits / 10); /* Convert DST value */ if (bts->tz.dst >= 0 && bts->tz.dst <= 2) dst = bts->tz.dst; if (TLVP_PRESENT(&tp, GSM48_IE_UTC)) { LOGP(DMSC, LOGL_DEBUG, "Changing 'Local time zone' from 0x%02x to 0x%02x.\n", TLVP_VAL(&tp, GSM48_IE_UTC)[6], tzbsd); ((uint8_t *)(TLVP_VAL(&tp, GSM48_IE_UTC)))[0] = tzbsd; } if (TLVP_PRESENT(&tp, GSM48_IE_NET_TIME_TZ)) { LOGP(DMSC, LOGL_DEBUG, "Changing 'Universal time and local time zone' TZ from " "0x%02x to 0x%02x.\n", TLVP_VAL(&tp, GSM48_IE_NET_TIME_TZ)[6], tzbsd); ((uint8_t *)(TLVP_VAL(&tp, GSM48_IE_NET_TIME_TZ)))[6] = tzbsd; } #ifdef GSM48_IE_NET_DST if (TLVP_PRESENT(&tp, GSM48_IE_NET_DST)) { LOGP(DMSC, LOGL_DEBUG, "Changing 'Network daylight saving time' from " "0x%02x to 0x%02x.\n", TLVP_VAL(&tp, GSM48_IE_NET_DST)[0], dst); ((uint8_t *)(TLVP_VAL(&tp, GSM48_IE_NET_DST)))[0] = dst; } #endif return 0; } static int has_core_identity(struct osmo_msc_data *msc) { if (msc->core_mnc != -1) return 1; if (msc->core_mcc != -1) return 1; if (msc->core_lac != -1) return 1; if (msc->core_ci != -1) return 1; return 0; } /** * Messages coming back from the MSC. */ int bsc_scan_msc_msg(struct gsm_subscriber_connection *conn, struct msgb *msg) { struct osmo_msc_data *msc; struct gsm_network *net; struct gsm48_loc_area_id *lai; struct gsm48_hdr *gh; uint8_t mtype; int length = msgb_l3len(msg); if (length < sizeof(*gh)) { LOGP(DMSC, LOGL_ERROR, "GSM48 header does not fit.\n"); return -1; } gh = (struct gsm48_hdr *) msgb_l3(msg); length -= (const char *)&gh->data[0] - (const char *)gh; mtype = gh->msg_type & 0xbf; net = conn->bts->network; msc = conn->sccp_con->msc; if (mtype == GSM48_MT_MM_LOC_UPD_ACCEPT) { if (has_core_identity(msc)) { if (msgb_l3len(msg) >= sizeof(*gh) + sizeof(*lai)) { lai = (struct gsm48_loc_area_id *) &gh->data[0]; gsm48_generate_lai(lai, net->country_code, net->network_code, conn->bts->location_area_code); } } if (conn->sccp_con->new_subscriber) return send_welcome_ussd(conn); return 0; } else if (mtype == GSM48_MT_MM_INFO) { bsc_patch_mm_info(conn, &gh->data[0], length); } return 0; } openbsc-0.15.0/openbsc/src/osmo-bsc/osmo_bsc_grace.c000066400000000000000000000104541265565154000223160ustar00rootroot00000000000000/* * (C) 2010-2013 by Holger Hans Peter Freyther * (C) 2010-2013 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 . * */ #include #include #include #include #include #include #include int bsc_grace_allow_new_connection(struct gsm_network *network, struct gsm_bts *bts) { if (bts->excl_from_rf_lock) return 1; return network->bsc_data->rf_ctrl->policy == S_RF_ON; } static int normal_paging(struct gsm_subscriber *subscr, int chan_needed, struct osmo_msc_data *msc) { /* we can't page by lac.. we need to page everything */ if (msc->core_lac != -1) { struct gsm_bts *bts; llist_for_each_entry(bts, &msc->network->bts_list, list) paging_request_bts(bts, subscr, chan_needed, NULL, msc); return 0; } return paging_request(subscr->group->net, subscr, chan_needed, NULL, msc); } static int locked_paging(struct gsm_subscriber *subscr, int chan_needed, struct osmo_msc_data *msc) { struct gsm_bts *bts = NULL; /* * Check if there is any BTS that is on for the given lac. Start * with NULL and iterate through all bts. */ llist_for_each_entry(bts, &msc->network->bts_list, list) { /* * continue if the BTS is not excluded from the lock */ if (!bts->excl_from_rf_lock) continue; /* in case of no lac patching is in place, check the BTS */ if (msc->core_lac == -1 && subscr->lac != bts->location_area_code) continue; /* * now page on this bts */ paging_request_bts(bts, subscr, chan_needed, NULL, msc); }; /* All bts are either off or in the grace period */ return 0; } /** * Try to not page if everything the cell is not on. */ int bsc_grace_paging_request(struct gsm_subscriber *subscr, int chan_needed, struct osmo_msc_data *msc) { if (subscr->group->net->bsc_data->rf_ctrl->policy == S_RF_ON) return normal_paging(subscr, chan_needed, msc); return locked_paging(subscr, chan_needed, msc); } static int handle_sub(struct gsm_lchan *lchan, const char *text) { struct gsm_subscriber_connection *conn; /* only send it to TCH */ if (lchan->type != GSM_LCHAN_TCH_H && lchan->type != GSM_LCHAN_TCH_F) return -1; /* only send on the primary channel */ conn = lchan->conn; if (!conn) return -1; if (conn->lchan != lchan) return -1; /* only when active */ if (lchan->state != LCHAN_S_ACTIVE) return -1; gsm0480_send_ussdNotify(conn, 0, text); gsm0480_send_releaseComplete(conn); return 0; } /* * The place to handle the grace mode. Right now we will send * USSD messages to the subscriber, in the future we might start * a timer to have different modes for the grace period. */ static int handle_grace(struct gsm_network *network) { int ts_nr, lchan_nr; struct gsm_bts *bts; struct gsm_bts_trx *trx; if (!network->bsc_data->mid_call_txt) return 0; llist_for_each_entry(bts, &network->bts_list, list) { llist_for_each_entry(trx, &bts->trx_list, list) { for (ts_nr = 0; ts_nr < TRX_NR_TS; ++ts_nr) { struct gsm_bts_trx_ts *ts = &trx->ts[ts_nr]; for (lchan_nr = 0; lchan_nr < TS_MAX_LCHAN; ++lchan_nr) { handle_sub(&ts->lchan[lchan_nr], network->bsc_data->mid_call_txt); } } } } return 0; } static int handle_rf_signal(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct rf_signal_data *sig; if (subsys != SS_RF) return -1; sig = signal_data; if (signal == S_RF_GRACE) handle_grace(sig->net); return 0; } static __attribute__((constructor)) void on_dso_load_grace(void) { osmo_signal_register_handler(SS_RF, handle_rf_signal, NULL); } openbsc-0.15.0/openbsc/src/osmo-bsc/osmo_bsc_main.c000066400000000000000000000154421265565154000221630ustar00rootroot00000000000000/* (C) 2008-2009 by Harald Welte * (C) 2009-2011 by Holger Hans Peter Freyther * (C) 2009-2011 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define _GNU_SOURCE #include #include #include #include #include #include #include "../../bscconfig.h" struct gsm_network *bsc_gsmnet = 0; static const char *config_file = "openbsc.cfg"; static const char *rf_ctrl = NULL; extern const char *openbsc_copyright; static int daemonize = 0; static struct llist_head access_lists; struct llist_head *bsc_access_lists(void) { return &access_lists; } static void print_usage() { printf("Usage: osmo-bsc\n"); } static void print_help() { printf(" Some useful help...\n"); printf(" -h --help this text\n"); printf(" -D --daemonize Fork the process into a background daemon\n"); printf(" -d option --debug=DRLL:DCC:DMM:DRR:DRSL:DNM enable debugging\n"); printf(" -s --disable-color\n"); printf(" -T --timestamp. Print a timestamp in the debug output.\n"); printf(" -c --config-file filename The config file to use.\n"); printf(" -l --local=IP. The local address of the MGCP.\n"); printf(" -e --log-level number. Set a global loglevel.\n"); printf(" -r --rf-ctl NAME. A unix domain socket to listen for cmds.\n"); printf(" -t --testmode. A special mode to provoke failures at the MSC.\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'}, {"local", 1, 0, 'l'}, {"log-level", 1, 0, 'e'}, {"rf-ctl", 1, 0, 'r'}, {"testmode", 0, 0, 't'}, {0, 0, 0, 0} }; c = getopt_long(argc, argv, "hd:DsTc:e:r:t", 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 'r': rf_ctrl = optarg; break; default: /* ignore */ break; } } } extern int bsc_vty_go_parent(struct vty *vty); static struct vty_app_info vty_info = { .name = "OsmoBSC", .version = PACKAGE_VERSION, .go_parent_cb = bsc_vty_go_parent, .is_config_node = bsc_vty_is_config_node, }; extern int bsc_shutdown_net(struct gsm_network *net); static void signal_handler(int signal) { struct osmo_msc_data *msc; fprintf(stdout, "signal %u received\n", signal); switch (signal) { case SIGINT: bsc_shutdown_net(bsc_gsmnet); osmo_signal_dispatch(SS_L_GLOBAL, S_L_GLOBAL_SHUTDOWN, NULL); sleep(3); 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: if (!bsc_gsmnet->bsc_data) return; llist_for_each_entry(msc, &bsc_gsmnet->bsc_data->mscs, entry) bsc_msc_lost(msc->msc_con); break; default: break; } } int main(int argc, char **argv) { struct osmo_msc_data *msc; struct osmo_bsc_data *data; int rc; tall_bsc_ctx = talloc_named_const(NULL, 1, "openbsc"); osmo_init_logging(&log_info); bts_init(); libosmo_abis_init(tall_bsc_ctx); /* enable filters */ /* This needs to precede handle_options() */ vty_info.copyright = openbsc_copyright; vty_init(&vty_info); bsc_vty_init(&log_info); bsc_msg_lst_vty_init(tall_bsc_ctx, &access_lists, BSC_NODE); INIT_LLIST_HEAD(&access_lists); /* parse options */ handle_options(argc, argv); /* seed the PRNG */ srand(time(NULL)); /* initialize SCCP */ sccp_set_log_area(DSCCP); rc = bsc_bootstrap_network(NULL, config_file); if (rc < 0) { fprintf(stderr, "Bootstrapping the network failed. exiting.\n"); exit(1); } bsc_api_init(bsc_gsmnet, osmo_bsc_api()); bsc_gsmnet->ctrl = bsc_controlif_setup(bsc_gsmnet, OSMO_CTRL_PORT_NITB_BSC); if (!bsc_gsmnet) { fprintf(stderr, "Failed to init the control interface. Exiting.\n"); exit(1); } rc = bsc_ctrl_cmds_install(bsc_gsmnet); if (rc < 0) { fprintf(stderr, "Failed to install control commands. Exiting.\n"); exit(1); } data = bsc_gsmnet->bsc_data; if (rf_ctrl) bsc_replace_string(data, &data->rf_ctrl_name, rf_ctrl); data->rf_ctrl = osmo_bsc_rf_create(data->rf_ctrl_name, bsc_gsmnet); if (!data->rf_ctrl) { fprintf(stderr, "Failed to create the RF service.\n"); exit(1); } llist_for_each_entry(msc, &bsc_gsmnet->bsc_data->mscs, entry) { if (osmo_bsc_msc_init(msc) != 0) { LOGP(DNAT, LOGL_ERROR, "Failed to start up. Exiting.\n"); exit(1); } } if (osmo_bsc_sccp_init(bsc_gsmnet) != 0) { LOGP(DNM, LOGL_ERROR, "Failed to register SCCP.\n"); exit(1); } if (osmo_bsc_audio_init(bsc_gsmnet) != 0) { LOGP(DMSC, LOGL_ERROR, "Failed to register audio support.\n"); exit(1); } signal(SIGINT, &signal_handler); signal(SIGABRT, &signal_handler); signal(SIGUSR1, &signal_handler); signal(SIGUSR2, &signal_handler); osmo_init_ignore_signals(); if (daemonize) { rc = osmo_daemonize(); if (rc < 0) { perror("Error during daemonize"); exit(1); } } while (1) { osmo_select_main(0); } return 0; } openbsc-0.15.0/openbsc/src/osmo-bsc/osmo_bsc_msc.c000066400000000000000000000361521265565154000220220ustar00rootroot00000000000000/* * Handle the connection to the MSC. This include ping/timeout/reconnect * (C) 2008-2009 by Harald Welte * (C) 2009-2015 by Holger Hans Peter Freyther * (C) 2009-2015 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void initialize_if_needed(struct bsc_msc_connection *conn); static void send_lacs(struct gsm_network *net, struct bsc_msc_connection *conn); static void send_id_get_response(struct osmo_msc_data *data, int fd, struct msgb *inp); static void send_ping(struct osmo_msc_data *data); static void schedule_ping_pong(struct osmo_msc_data *data); /* * MGCP forwarding code */ static int mgcp_do_read(struct osmo_fd *fd) { struct osmo_msc_data *data = (struct osmo_msc_data *) fd->data; struct msgb *mgcp; int ret; mgcp = msgb_alloc_headroom(4096, 128, "mgcp_from_gw"); if (!mgcp) { LOGP(DMGCP, LOGL_ERROR, "Failed to allocate MGCP message.\n"); return -1; } ret = read(fd->fd, mgcp->data, 4096 - 128); if (ret <= 0) { LOGP(DMGCP, LOGL_ERROR, "Failed to read: %d/%s\n", errno, strerror(errno)); msgb_free(mgcp); return -1; } else if (ret > 4096 - 128) { LOGP(DMGCP, LOGL_ERROR, "Too much data: %d\n", ret); msgb_free(mgcp); return -1; } mgcp->l2h = msgb_put(mgcp, ret); msc_queue_write(data->msc_con, mgcp, IPAC_PROTO_MGCP_OLD); return 0; } static int mgcp_do_write(struct osmo_fd *fd, struct msgb *msg) { int ret; LOGP(DMGCP, LOGL_DEBUG, "Sending msg to MGCP GW size: %u\n", msg->len); ret = write(fd->fd, msg->data, msg->len); if (ret != msg->len) LOGP(DMGCP, LOGL_ERROR, "Failed to forward message to MGCP GW (%s).\n", strerror(errno)); return ret; } static void mgcp_forward(struct osmo_msc_data *data, struct msgb *msg) { struct msgb *mgcp; if (msgb_l2len(msg) > 4096) { LOGP(DMGCP, LOGL_ERROR, "Can not forward too big message.\n"); return; } mgcp = msgb_alloc(4096, "mgcp_to_gw"); if (!mgcp) { LOGP(DMGCP, LOGL_ERROR, "Failed to send message.\n"); return; } msgb_put(mgcp, msgb_l2len(msg)); memcpy(mgcp->data, msg->l2h, mgcp->len); if (osmo_wqueue_enqueue(&data->mgcp_agent, mgcp) != 0) { LOGP(DMGCP, LOGL_FATAL, "Could not queue message to MGCP GW.\n"); msgb_free(mgcp); } } static int mgcp_create_port(struct osmo_msc_data *data) { int on; struct sockaddr_in addr; data->mgcp_agent.bfd.fd = socket(AF_INET, SOCK_DGRAM, 0); if (data->mgcp_agent.bfd.fd < 0) { LOGP(DMGCP, LOGL_FATAL, "Failed to create UDP socket errno: %d\n", errno); return -1; } on = 1; setsockopt(data->mgcp_agent.bfd.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); /* try to bind the socket */ memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); addr.sin_port = 0; if (bind(data->mgcp_agent.bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { LOGP(DMGCP, LOGL_FATAL, "Failed to bind to any port.\n"); close(data->mgcp_agent.bfd.fd); data->mgcp_agent.bfd.fd = -1; return -1; } /* connect to the remote */ addr.sin_port = htons(2427); if (connect(data->mgcp_agent.bfd.fd, (struct sockaddr *) & addr, sizeof(addr)) < 0) { LOGP(DMGCP, LOGL_FATAL, "Failed to connect to local MGCP GW. %s\n", strerror(errno)); close(data->mgcp_agent.bfd.fd); data->mgcp_agent.bfd.fd = -1; return -1; } osmo_wqueue_init(&data->mgcp_agent, 10); data->mgcp_agent.bfd.when = BSC_FD_READ; data->mgcp_agent.bfd.data = data; data->mgcp_agent.read_cb = mgcp_do_read; data->mgcp_agent.write_cb = mgcp_do_write; if (osmo_fd_register(&data->mgcp_agent.bfd) != 0) { LOGP(DMGCP, LOGL_FATAL, "Failed to register BFD\n"); close(data->mgcp_agent.bfd.fd); data->mgcp_agent.bfd.fd = -1; return -1; } return 0; } /* * Send data to the network */ int msc_queue_write(struct bsc_msc_connection *conn, struct msgb *msg, int proto) { ipa_prepend_header(msg, proto); if (osmo_wqueue_enqueue(&conn->write_queue, msg) != 0) { LOGP(DMSC, LOGL_FATAL, "Failed to queue IPA/%d\n", proto); msgb_free(msg); return -1; } return 0; } int msc_queue_write_with_ping(struct bsc_msc_connection *conn, struct msgb *msg, int proto) { struct osmo_msc_data *data; uint8_t val; /* prepend the header */ ipa_prepend_header(msg, proto); if (osmo_wqueue_enqueue(&conn->write_queue, msg) != 0) { LOGP(DMSC, LOGL_FATAL, "Failed to queue IPA/%d\n", proto); msgb_free(msg); return -1; } /* add the ping as the other message */ val = IPAC_MSGT_PING; msgb_l16tv_put(msg, 1, IPAC_PROTO_IPACCESS, &val); data = (struct osmo_msc_data *) conn->write_queue.bfd.data; schedule_ping_pong(data); return 0; } static int msc_alink_do_write(struct osmo_fd *fd, struct msgb *msg) { int ret; LOGP(DMSC, LOGL_DEBUG, "Sending SCCP to MSC: %u\n", msgb_l2len(msg)); LOGP(DLMI, LOGL_DEBUG, "MSC TX %s\n", osmo_hexdump(msg->data, msg->len)); ret = write(fd->fd, msg->data, msg->len); if (ret < msg->len) perror("MSC: Failed to send SCCP"); return ret; } static void handle_ctrl(struct osmo_msc_data *msc, struct msgb *msg) { int ret; struct ctrl_cmd *cmd; cmd = ctrl_cmd_parse(msc->msc_con, msg); if (!cmd) { LOGP(DMSC, LOGL_ERROR, "Failed to parse control message.\n"); cmd = talloc_zero(msc->msc_con, struct ctrl_cmd); if (!cmd) { LOGP(DMSC, LOGL_ERROR, "OOM!\n"); return; } cmd->type = CTRL_TYPE_ERROR; cmd->id = "err"; cmd->reply = "Failed to parse control message."; ctrl_cmd_send(&msc->msc_con->write_queue, cmd); talloc_free(cmd); return; } ret = ctrl_cmd_handle(msc->network->ctrl, cmd, msc->network); if (ret != CTRL_CMD_HANDLED) ctrl_cmd_send(&msc->msc_con->write_queue, cmd); talloc_free(cmd); } static void osmo_ext_handle(struct osmo_msc_data *msc, struct msgb *msg) { struct ipaccess_head *hh; struct ipaccess_head_ext *hh_ext; hh = (struct ipaccess_head *) msg->data; hh_ext = (struct ipaccess_head_ext *) hh->data; if (msg->len < sizeof(*hh) + sizeof(*hh_ext)) { LOGP(DMSC, LOGL_ERROR, "Packet too short for extended header.\n"); return; } msg->l2h = hh_ext->data; if (hh_ext->proto == IPAC_PROTO_EXT_MGCP) mgcp_forward(msc, msg); else if (hh_ext->proto == IPAC_PROTO_EXT_LAC) send_lacs(msc->network, msc->msc_con); else if (hh_ext->proto == IPAC_PROTO_EXT_CTRL) handle_ctrl(msc, msg); } static int ipaccess_a_fd_cb(struct osmo_fd *bfd) { struct msgb *msg = NULL; struct ipaccess_head *hh; struct osmo_msc_data *data = (struct osmo_msc_data *) bfd->data; int ret; ret = ipa_msg_recv_buffered(bfd->fd, &msg, &data->msc_con->pending_msg); if (ret <= 0) { if (ret == -EAGAIN) return 0; if (ret == 0) { LOGP(DMSC, LOGL_ERROR, "The connection to the MSC was lost.\n"); bsc_msc_lost(data->msc_con); return -1; } LOGP(DMSC, LOGL_ERROR, "Failed to parse ip access message: %d\n", ret); return -1; } LOGP(DLMI, LOGL_DEBUG, "From MSC: %s proto: %d\n", osmo_hexdump(msg->data, msg->len), msg->l2h[0]); /* handle base message handling */ hh = (struct ipaccess_head *) msg->data; /* initialize the networking. This includes sending a GSM08.08 message */ msg->cb[0] = (unsigned long) data; if (hh->proto == IPAC_PROTO_IPACCESS) { ipa_ccm_rcvmsg_base(msg, bfd); if (msg->l2h[0] == IPAC_MSGT_ID_ACK) initialize_if_needed(data->msc_con); else if (msg->l2h[0] == IPAC_MSGT_ID_GET) { send_id_get_response(data, bfd->fd, msg); } else if (msg->l2h[0] == IPAC_MSGT_PONG) { osmo_timer_del(&data->pong_timer); } } else if (hh->proto == IPAC_PROTO_SCCP) { sccp_system_incoming_ctx(msg, data->msc_con); } else if (hh->proto == IPAC_PROTO_MGCP_OLD) { mgcp_forward(data, msg); } else if (hh->proto == IPAC_PROTO_OSMO) { osmo_ext_handle(data, msg); } msgb_free(msg); return 0; } static void send_ping(struct osmo_msc_data *data) { struct msgb *msg; msg = msgb_alloc_headroom(4096, 128, "ping"); if (!msg) { LOGP(DMSC, LOGL_ERROR, "Failed to create PING.\n"); return; } msg->l2h = msgb_put(msg, 1); msg->l2h[0] = IPAC_MSGT_PING; msc_queue_write(data->msc_con, msg, IPAC_PROTO_IPACCESS); } static void schedule_ping_pong(struct osmo_msc_data *data) { /* send another ping in 20 seconds */ osmo_timer_schedule(&data->ping_timer, data->ping_timeout, 0); /* also start a pong timer */ osmo_timer_schedule(&data->pong_timer, data->pong_timeout, 0); } static void msc_ping_timeout_cb(void *_data) { struct osmo_msc_data *data = (struct osmo_msc_data *) _data; if (data->ping_timeout <= 0) return; send_ping(data); schedule_ping_pong(data); } static void msc_pong_timeout_cb(void *_data) { struct osmo_msc_data *data = (struct osmo_msc_data *) _data; LOGP(DMSC, LOGL_ERROR, "MSC didn't answer PING. Closing connection.\n"); bsc_msc_lost(data->msc_con); } static void msc_connection_connected(struct bsc_msc_connection *con) { struct msc_signal_data sig; struct osmo_msc_data *data; int ret, on; on = 1; ret = setsockopt(con->write_queue.bfd.fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)); if (ret != 0) LOGP(DMSC, LOGL_ERROR, "Failed to set TCP_NODELAY: %s\n", strerror(errno)); data = (struct osmo_msc_data *) con->write_queue.bfd.data; msc_ping_timeout_cb(data); sig.data = data; osmo_signal_dispatch(SS_MSC, S_MSC_CONNECTED, &sig); } /* * The connection to the MSC was lost and we will need to free all * resources and then attempt to reconnect. */ static void msc_connection_was_lost(struct bsc_msc_connection *msc) { struct msc_signal_data sig; struct osmo_msc_data *data; LOGP(DMSC, LOGL_ERROR, "Lost MSC connection. Freing stuff.\n"); data = (struct osmo_msc_data *) msc->write_queue.bfd.data; osmo_timer_del(&data->ping_timer); osmo_timer_del(&data->pong_timer); sig.data = data; osmo_signal_dispatch(SS_MSC, S_MSC_LOST, &sig); msc->is_authenticated = 0; bsc_msc_schedule_connect(msc); } static void send_lacs(struct gsm_network *net, struct bsc_msc_connection *conn) { struct ipac_ext_lac_cmd *lac; struct gsm_bts *bts; struct msgb *msg; int lacs = 0; if (llist_empty(&net->bts_list)) { LOGP(DMSC, LOGL_ERROR, "No BTSs configured. Not sending LACs.\n"); return; } msg = msgb_alloc_headroom(4096, 128, "LAC Command"); if (!msg) { LOGP(DMSC, LOGL_ERROR, "Failed to create the LAC command.\n"); return; } lac = (struct ipac_ext_lac_cmd *) msgb_put(msg, sizeof(*lac)); lac->add_remove = 1; llist_for_each_entry(bts, &net->bts_list, list) { if (lacs++ == 0) lac->lac = htons(bts->location_area_code); else msgb_put_u16(msg, htons(bts->location_area_code)); } lac->nr_extra_lacs = lacs - 1; ipa_prepend_header_ext(msg, IPAC_PROTO_EXT_LAC); msc_queue_write(conn, msg, IPAC_PROTO_OSMO); } static void initialize_if_needed(struct bsc_msc_connection *conn) { struct msgb *msg; if (!conn->is_authenticated) { /* send a gsm 08.08 reset message from here */ msg = gsm0808_create_reset(); if (!msg) { LOGP(DMSC, LOGL_ERROR, "Failed to create the reset message.\n"); return; } sccp_write(msg, &sccp_ssn_bssap, &sccp_ssn_bssap, 0, conn); msgb_free(msg); conn->is_authenticated = 1; } } static int answer_challenge(struct osmo_msc_data *data, struct msgb *inp, struct osmo_auth_vector *vec) { int ret; struct tlv_parsed tvp; const uint8_t *mrand; uint8_t mrand_len; struct osmo_sub_auth_data auth = { .type = OSMO_AUTH_TYPE_GSM, .algo = OSMO_AUTH_ALG_MILENAGE, }; ret = ipa_ccm_idtag_parse_off(&tvp, inp->l2h + 1, msgb_l2len(inp) - 1, 1); if (ret < 0) { LOGP(DMSC, LOGL_ERROR, "ignoring IPA response " "message with malformed TLVs: %s\n", osmo_hexdump(inp->l2h + 1, msgb_l2len(inp) - 1)); return 0; } mrand = TLVP_VAL(&tvp, 0x23); mrand_len = TLVP_LEN(&tvp, 0x23); if (mrand_len != 16) { LOGP(DMSC, LOGL_ERROR, "RAND is not 16 bytes. Was %d\n", mrand_len); return 0; } /* copy the key */ memcpy(auth.u.umts.opc, data->bsc_key, 16); memcpy(auth.u.umts.k, data->bsc_key, 16); memset(auth.u.umts.amf, 0, 2); auth.u.umts.sqn = 0; /* generate the result */ memset(vec, 0, sizeof(*vec)); osmo_auth_gen_vec(vec, &auth, mrand); return 1; } static void send_id_get_response(struct osmo_msc_data *data, int fd, struct msgb *inp) { struct msc_signal_data sig; struct msgb *msg; struct osmo_auth_vector vec; int valid = 0; if (data->bsc_key_present) valid = answer_challenge(data, inp, &vec); msg = bsc_msc_id_get_resp(valid, data->bsc_token, vec.res, valid ? vec.res_len : 0); if (!msg) return; msc_queue_write(data->msc_con, msg, IPAC_PROTO_IPACCESS); sig.data = data; osmo_signal_dispatch(SS_MSC, S_MSC_AUTHENTICATED, &sig); } int osmo_bsc_msc_init(struct osmo_msc_data *data) { if (mgcp_create_port(data) != 0) return -1; data->msc_con = bsc_msc_create(data, &data->dests); if (!data->msc_con) { LOGP(DMSC, LOGL_ERROR, "Creating the MSC network connection failed.\n"); return -1; } data->ping_timer.cb = msc_ping_timeout_cb; data->ping_timer.data = data; data->pong_timer.cb = msc_pong_timeout_cb; data->pong_timer.data = data; data->msc_con->write_queue.bfd.data = data; data->msc_con->connection_loss = msc_connection_was_lost; data->msc_con->connected = msc_connection_connected; data->msc_con->write_queue.read_cb = ipaccess_a_fd_cb; data->msc_con->write_queue.write_cb = msc_alink_do_write; bsc_msc_connect(data->msc_con); return 0; } struct osmo_msc_data *osmo_msc_data_find(struct gsm_network *net, int nr) { struct osmo_msc_data *msc_data; llist_for_each_entry(msc_data, &net->bsc_data->mscs, entry) if (msc_data->nr == nr) return msc_data; return NULL; } struct osmo_msc_data *osmo_msc_data_alloc(struct gsm_network *net, int nr) { struct osmo_msc_data *msc_data; /* check if there is already one */ msc_data = osmo_msc_data_find(net, nr); if (msc_data) return msc_data; msc_data = talloc_zero(net, struct osmo_msc_data); if (!msc_data) return NULL; llist_add_tail(&msc_data->entry, &net->bsc_data->mscs); /* Init back pointer */ msc_data->network = net; INIT_LLIST_HEAD(&msc_data->dests); msc_data->ping_timeout = 20; msc_data->pong_timeout = 5; msc_data->core_mnc = -1; msc_data->core_mcc = -1; msc_data->core_ci = -1; msc_data->core_lac = -1; msc_data->rtp_base = 4000; msc_data->nr = nr; msc_data->allow_emerg = 1; /* Defaults for the audio setup */ msc_data->amr_conf.m5_90 = 1; return msc_data; } openbsc-0.15.0/openbsc/src/osmo-bsc/osmo_bsc_sccp.c000066400000000000000000000212331265565154000221620ustar00rootroot00000000000000/* Interaction with the SCCP subsystem */ /* * (C) 2009-2014 by Holger Hans Peter Freyther * (C) 2009-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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include /* SCCP helper */ #define SCCP_IT_TIMER 60 static LLIST_HEAD(active_connections); static void free_queued(struct osmo_bsc_sccp_con *conn) { struct msgb *msg; while (!llist_empty(&conn->sccp_queue)) { /* this is not allowed to fail */ msg = msgb_dequeue(&conn->sccp_queue); msgb_free(msg); } conn->sccp_queue_size = 0; } static void send_queued(struct osmo_bsc_sccp_con *conn) { struct msgb *msg; while (!llist_empty(&conn->sccp_queue)) { /* this is not allowed to fail */ msg = msgb_dequeue(&conn->sccp_queue); sccp_connection_write(conn->sccp, msg); msgb_free(msg); conn->sccp_queue_size -= 1; } } static void msc_outgoing_sccp_data(struct sccp_connection *conn, struct msgb *msg, unsigned int len) { struct osmo_bsc_sccp_con *bsc_con = (struct osmo_bsc_sccp_con *) conn->data_ctx; bsc_handle_dt1(bsc_con, msg, len); } static void msc_outgoing_sccp_state(struct sccp_connection *conn, int old_state) { struct osmo_bsc_sccp_con *con_data; if (conn->connection_state >= SCCP_CONNECTION_STATE_RELEASE_COMPLETE) { con_data = (struct osmo_bsc_sccp_con *) conn->data_ctx; if(con_data->conn) { LOGP(DMSC, LOGL_ERROR, "ERROR: The lchan is still associated.\n"); gsm0808_clear(con_data->conn); subscr_con_free(con_data->conn); con_data->conn = NULL; } con_data->sccp = NULL; free_queued(con_data); sccp_connection_free(conn); bsc_delete_connection(con_data); } else if (conn->connection_state == SCCP_CONNECTION_STATE_ESTABLISHED) { LOGP(DMSC, LOGL_DEBUG, "Connection established: %p\n", conn); con_data = (struct osmo_bsc_sccp_con *) conn->data_ctx; osmo_timer_del(&con_data->sccp_cc_timeout); osmo_timer_schedule(&con_data->sccp_it_timeout, SCCP_IT_TIMER, 0); send_queued(con_data); } } static void bsc_sccp_force_free(struct osmo_bsc_sccp_con *data) { if (data->conn) { gsm0808_clear(data->conn); subscr_con_free(data->conn); data->conn = NULL; } free_queued(data); sccp_connection_force_free(data->sccp); data->sccp = NULL; bsc_delete_connection(data); } static void sccp_it_timeout(void *_data) { struct osmo_bsc_sccp_con *data = (struct osmo_bsc_sccp_con *) _data; sccp_connection_send_it(data->sccp); osmo_timer_schedule(&data->sccp_it_timeout, SCCP_IT_TIMER, 0); } static void sccp_cc_timeout(void *_data) { struct osmo_bsc_sccp_con *data = (struct osmo_bsc_sccp_con *) _data; if (data->sccp->connection_state >= SCCP_CONNECTION_STATE_ESTABLISHED) return; LOGP(DMSC, LOGL_ERROR, "The connection was never established.\n"); bsc_sccp_force_free(data); } static void msc_sccp_write_ipa(struct sccp_connection *conn, struct msgb *msg, void *global_ctx, void *ctx) { struct bsc_msc_connection *msc_con; if (conn) { struct osmo_bsc_sccp_con *bsc_con = conn->data_ctx; msc_con = bsc_con->msc->msc_con; if (bsc_con->send_ping) { bsc_con->send_ping = 0; msc_queue_write_with_ping(msc_con, msg, IPAC_PROTO_SCCP); return; } } else { msc_con = ctx; } msc_queue_write(msc_con, msg, IPAC_PROTO_SCCP); } static int msc_sccp_accept(struct sccp_connection *connection, void *data) { LOGP(DMSC, LOGL_DEBUG, "Rejecting incoming SCCP connection.\n"); return -1; } static int msc_sccp_read(struct msgb *msgb, unsigned int length, void *data) { struct osmo_msc_data *msc = (struct osmo_msc_data *) msgb->cb[0]; return bsc_handle_udt(msc, msgb, length); } int bsc_queue_for_msc(struct osmo_bsc_sccp_con *conn, struct msgb *msg) { struct sccp_connection *sccp = conn->sccp; if (sccp->connection_state > SCCP_CONNECTION_STATE_ESTABLISHED) { LOGP(DMSC, LOGL_ERROR, "Connection closing, dropping packet on: %p\n", sccp); msgb_free(msg); } else if (sccp->connection_state == SCCP_CONNECTION_STATE_ESTABLISHED && conn->sccp_queue_size == 0) { sccp_connection_write(sccp, msg); msgb_free(msg); } else if (conn->sccp_queue_size > 10) { LOGP(DMSC, LOGL_ERROR, "Connection closing, dropping packet on: %p\n", sccp); msgb_free(msg); } else { LOGP(DMSC, LOGL_DEBUG, "Queueing packet on %p. Queue size: %d\n", sccp, conn->sccp_queue_size); conn->sccp_queue_size += 1; msgb_enqueue(&conn->sccp_queue, msg); } return 0; } enum bsc_con bsc_create_new_connection(struct gsm_subscriber_connection *conn, struct osmo_msc_data *msc, int send_ping) { struct osmo_bsc_sccp_con *bsc_con; struct sccp_connection *sccp; /* This should not trigger */ if (!msc || !msc->msc_con->is_authenticated) { LOGP(DMSC, LOGL_ERROR, "How did this happen? MSC is not connected. Dropping.\n"); return BSC_CON_REJECT_NO_LINK; } if (!bsc_grace_allow_new_connection(conn->bts->network, conn->bts)) { LOGP(DMSC, LOGL_NOTICE, "BSC in grace period. No new connections.\n"); return BSC_CON_REJECT_RF_GRACE; } sccp = sccp_connection_socket(); if (!sccp) { LOGP(DMSC, LOGL_ERROR, "Failed to allocate memory.\n"); return BSC_CON_NO_MEM; } bsc_con = talloc_zero(conn->bts, struct osmo_bsc_sccp_con); if (!bsc_con) { LOGP(DMSC, LOGL_ERROR, "Failed to allocate.\n"); sccp_connection_free(sccp); return BSC_CON_NO_MEM; } /* callbacks */ sccp->state_cb = msc_outgoing_sccp_state; sccp->data_cb = msc_outgoing_sccp_data; sccp->data_ctx = bsc_con; bsc_con->send_ping = send_ping; /* prepare the timers */ bsc_con->sccp_it_timeout.cb = sccp_it_timeout; bsc_con->sccp_it_timeout.data = bsc_con; bsc_con->sccp_cc_timeout.cb = sccp_cc_timeout; bsc_con->sccp_cc_timeout.data = bsc_con; INIT_LLIST_HEAD(&bsc_con->sccp_queue); bsc_con->sccp = sccp; bsc_con->msc = msc; bsc_con->conn = conn; llist_add_tail(&bsc_con->entry, &active_connections); conn->sccp_con = bsc_con; return BSC_CON_SUCCESS; } int bsc_open_connection(struct osmo_bsc_sccp_con *conn, struct msgb *msg) { osmo_timer_schedule(&conn->sccp_cc_timeout, 10, 0); sccp_connection_connect(conn->sccp, &sccp_ssn_bssap, msg); msgb_free(msg); return 0; } int bsc_delete_connection(struct osmo_bsc_sccp_con *sccp) { if (!sccp) return 0; if (sccp->conn) LOGP(DMSC, LOGL_ERROR, "Should have been cleared.\n"); llist_del(&sccp->entry); osmo_timer_del(&sccp->sccp_it_timeout); osmo_timer_del(&sccp->sccp_cc_timeout); talloc_free(sccp); return 0; } static void bsc_notify_msc_lost(struct osmo_bsc_sccp_con *con) { struct gsm_subscriber_connection *conn = con->conn; /* send USSD notification if string configured and con->data is set */ if (!conn) return; /* check for config string */ if (!con->msc->ussd_msc_lost_txt) return; if (con->msc->ussd_msc_lost_txt[0] == '\0') return; /* send USSD notification */ gsm0480_send_ussdNotify(conn, 1, conn->sccp_con->msc->ussd_msc_lost_txt); gsm0480_send_releaseComplete(conn); } static void bsc_notify_and_close_conns(struct bsc_msc_connection *msc_con) { struct osmo_bsc_sccp_con *con, *tmp; llist_for_each_entry_safe(con, tmp, &active_connections, entry) { if (con->msc->msc_con != msc_con) continue; bsc_notify_msc_lost(con); bsc_sccp_force_free(con); } } static int handle_msc_signal(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct msc_signal_data *msc; if (subsys != SS_MSC) return 0; msc = signal_data; if (signal == S_MSC_LOST) bsc_notify_and_close_conns(msc->data->msc_con); return 0; } int osmo_bsc_sccp_init(struct gsm_network *gsmnet) { sccp_set_log_area(DSCCP); sccp_system_init(msc_sccp_write_ipa, gsmnet); sccp_connection_set_incoming(&sccp_ssn_bssap, msc_sccp_accept, NULL); sccp_set_read(&sccp_ssn_bssap, msc_sccp_read, gsmnet); osmo_signal_register_handler(SS_MSC, handle_msc_signal, gsmnet); return 0; } openbsc-0.15.0/openbsc/src/osmo-bsc/osmo_bsc_vty.c000066400000000000000000000631621265565154000220630ustar00rootroot00000000000000/* Osmo BSC VTY Configuration */ /* (C) 2009-2015 by Holger Hans Peter Freyther * (C) 2009-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 . * */ #include #include #include #include #include #include #include #include #include #include #define IPA_STR "IP.ACCESS specific\n" extern struct gsm_network *bsc_gsmnet; static struct osmo_bsc_data *osmo_bsc_data(struct vty *vty) { return bsc_gsmnet->bsc_data; } static struct osmo_msc_data *osmo_msc_data(struct vty *vty) { return osmo_msc_data_find(bsc_gsmnet, (int) vty->index); } static struct cmd_node bsc_node = { BSC_NODE, "%s(config-bsc)# ", 1, }; static struct cmd_node msc_node = { MSC_NODE, "%s(config-msc)# ", 1, }; DEFUN(cfg_net_msc, cfg_net_msc_cmd, "msc [<0-1000>]", "Configure MSC details\n" "MSC connection to configure\n") { int index = argc == 1 ? atoi(argv[0]) : 0; struct osmo_msc_data *msc; msc = osmo_msc_data_alloc(bsc_gsmnet, index); if (!msc) { vty_out(vty, "%%Failed to allocate MSC data.%s", VTY_NEWLINE); return CMD_WARNING; } vty->index = (void *) index; vty->node = MSC_NODE; return CMD_SUCCESS; } DEFUN(cfg_net_bsc, cfg_net_bsc_cmd, "bsc", "Configure BSC\n") { vty->node = BSC_NODE; return CMD_SUCCESS; } static void write_msc_amr_options(struct vty *vty, struct osmo_msc_data *msc) { #define WRITE_AMR(vty, msc, name, var) \ vty_out(vty, " amr-config %s %s%s", \ name, msc->amr_conf.var ? "allowed" : "forbidden", \ VTY_NEWLINE); WRITE_AMR(vty, msc, "12_2k", m12_2); WRITE_AMR(vty, msc, "10_2k", m10_2); WRITE_AMR(vty, msc, "7_95k", m7_95); WRITE_AMR(vty, msc, "7_40k", m7_40); WRITE_AMR(vty, msc, "6_70k", m6_70); WRITE_AMR(vty, msc, "5_90k", m5_90); WRITE_AMR(vty, msc, "5_15k", m5_15); WRITE_AMR(vty, msc, "4_75k", m4_75); #undef WRITE_AMR } static void write_msc(struct vty *vty, struct osmo_msc_data *msc) { struct bsc_msc_dest *dest; vty_out(vty, "msc %d%s", msc->nr, VTY_NEWLINE); if (msc->bsc_token) vty_out(vty, " token %s%s", msc->bsc_token, VTY_NEWLINE); if (msc->bsc_key_present) vty_out(vty, " auth-key %s%s", osmo_hexdump(msc->bsc_key, sizeof(msc->bsc_key)), VTY_NEWLINE); if (msc->core_mnc != -1) vty_out(vty, " core-mobile-network-code %d%s", msc->core_mnc, VTY_NEWLINE); if (msc->core_mcc != -1) vty_out(vty, " core-mobile-country-code %d%s", msc->core_mcc, VTY_NEWLINE); if (msc->core_lac != -1) vty_out(vty, " core-location-area-code %d%s", msc->core_lac, VTY_NEWLINE); if (msc->core_ci != -1) vty_out(vty, " core-cell-identity %d%s", msc->core_ci, VTY_NEWLINE); vty_out(vty, " ip.access rtp-base %d%s", msc->rtp_base, VTY_NEWLINE); if (msc->ping_timeout == -1) vty_out(vty, " no timeout-ping%s", VTY_NEWLINE); else { vty_out(vty, " timeout-ping %d%s", msc->ping_timeout, VTY_NEWLINE); vty_out(vty, " timeout-pong %d%s", msc->pong_timeout, VTY_NEWLINE); if (msc->advanced_ping) vty_out(vty, " timeout-ping advanced%s", VTY_NEWLINE); else vty_out(vty, " no timeout-ping advanced%s", VTY_NEWLINE); } if (msc->ussd_welcome_txt) vty_out(vty, " bsc-welcome-text %s%s", msc->ussd_welcome_txt, VTY_NEWLINE); else vty_out(vty, " no bsc-welcome-text%s", VTY_NEWLINE); if (msc->ussd_msc_lost_txt && msc->ussd_msc_lost_txt[0]) vty_out(vty, " bsc-msc-lost-text %s%s", msc->ussd_msc_lost_txt, VTY_NEWLINE); else vty_out(vty, " no bsc-msc-lost-text%s", VTY_NEWLINE); if (msc->ussd_grace_txt && msc->ussd_grace_txt[0]) vty_out(vty, " bsc-grace-text %s%s", msc->ussd_grace_txt, VTY_NEWLINE); else vty_out(vty, " no bsc-grace-text%s", VTY_NEWLINE); if (msc->audio_length != 0) { int i; vty_out(vty, " codec-list "); for (i = 0; i < msc->audio_length; ++i) { if (i != 0) vty_out(vty, " "); if (msc->audio_support[i]->hr) vty_out(vty, "hr%.1u", msc->audio_support[i]->ver); else vty_out(vty, "fr%.1u", msc->audio_support[i]->ver); } vty_out(vty, "%s", VTY_NEWLINE); } llist_for_each_entry(dest, &msc->dests, list) vty_out(vty, " dest %s %d %d%s", dest->ip, dest->port, dest->dscp, VTY_NEWLINE); vty_out(vty, " type %s%s", msc->type == MSC_CON_TYPE_NORMAL ? "normal" : "local", VTY_NEWLINE); vty_out(vty, " allow-emergency %s%s", msc->allow_emerg ? "allow" : "deny", VTY_NEWLINE); if (msc->local_pref) vty_out(vty, " local-prefix %s%s", msc->local_pref, VTY_NEWLINE); if (msc->acc_lst_name) vty_out(vty, " access-list-name %s%s", msc->acc_lst_name, VTY_NEWLINE); /* write amr options */ write_msc_amr_options(vty, msc); } static int config_write_msc(struct vty *vty) { struct osmo_msc_data *msc; struct osmo_bsc_data *bsc = osmo_bsc_data(vty); llist_for_each_entry(msc, &bsc->mscs, entry) write_msc(vty, msc); return CMD_SUCCESS; } static int config_write_bsc(struct vty *vty) { struct osmo_bsc_data *bsc = osmo_bsc_data(vty); vty_out(vty, "bsc%s", VTY_NEWLINE); if (bsc->mid_call_txt) vty_out(vty, " mid-call-text %s%s", bsc->mid_call_txt, VTY_NEWLINE); vty_out(vty, " mid-call-timeout %d%s", bsc->mid_call_timeout, VTY_NEWLINE); if (bsc->rf_ctrl_name) vty_out(vty, " bsc-rf-socket %s%s", bsc->rf_ctrl_name, VTY_NEWLINE); if (bsc->auto_off_timeout != -1) vty_out(vty, " bsc-auto-rf-off %d%s", bsc->auto_off_timeout, VTY_NEWLINE); if (bsc->ussd_no_msc_txt && bsc->ussd_no_msc_txt[0]) vty_out(vty, " missing-msc-text %s%s", bsc->ussd_no_msc_txt, VTY_NEWLINE); else vty_out(vty, " no missing-msc-text%s", VTY_NEWLINE); if (bsc->acc_lst_name) vty_out(vty, " access-list-name %s%s", bsc->acc_lst_name, VTY_NEWLINE); return CMD_SUCCESS; } DEFUN(cfg_net_bsc_token, cfg_net_bsc_token_cmd, "token TOKEN", "A token for the BSC to be sent to the MSC\n" "A token\n") { struct osmo_msc_data *data = osmo_msc_data(vty); bsc_replace_string(osmo_bsc_data(vty), &data->bsc_token, argv[0]); return CMD_SUCCESS; } DEFUN(cfg_net_bsc_key, cfg_net_bsc_key_cmd, "auth-key KEY", "Authentication (secret) key configuration\n" "Security key\n") { struct osmo_msc_data *data = osmo_msc_data(vty); osmo_hexparse(argv[0], data->bsc_key, sizeof(data->bsc_key)); data->bsc_key_present = 1; return CMD_SUCCESS; } DEFUN(cfg_net_no_bsc_key, cfg_net_bsc_no_key_cmd, "no auth-key", NO_STR "Authentication (secret) key configuration\n") { struct osmo_msc_data *data = osmo_msc_data(vty); memset(data->bsc_key, 0, sizeof(data->bsc_key)); data->bsc_key_present = 0; return CMD_SUCCESS; } DEFUN(cfg_net_bsc_ncc, cfg_net_bsc_ncc_cmd, "core-mobile-network-code <1-999>", "Use this network code for the core network\n" "MNC value\n") { struct osmo_msc_data *data = osmo_msc_data(vty); data->core_mnc = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_net_bsc_mcc, cfg_net_bsc_mcc_cmd, "core-mobile-country-code <1-999>", "Use this country code for the core network\n" "MCC value\n") { struct osmo_msc_data *data = osmo_msc_data(vty); data->core_mcc = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_net_bsc_lac, cfg_net_bsc_lac_cmd, "core-location-area-code <0-65535>", "Use this location area code for the core network\n" "LAC value\n") { struct osmo_msc_data *data = osmo_msc_data(vty); data->core_lac = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_net_bsc_ci, cfg_net_bsc_ci_cmd, "core-cell-identity <0-65535>", "Use this cell identity for the core network\n" "CI value\n") { struct osmo_msc_data *data = osmo_msc_data(vty); data->core_ci = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_net_bsc_rtp_base, cfg_net_bsc_rtp_base_cmd, "ip.access rtp-base <1-65000>", IPA_STR "Set the rtp-base port for the RTP stream\n" "Port number\n") { struct osmo_msc_data *data = osmo_msc_data(vty); data->rtp_base = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_net_bsc_codec_list, cfg_net_bsc_codec_list_cmd, "codec-list .LIST", "Set the allowed audio codecs\n" "List of audio codecs\n") { struct osmo_msc_data *data = osmo_msc_data(vty); int saw_fr, saw_hr; int i; saw_fr = saw_hr = 0; /* free the old list... if it exists */ if (data->audio_support) { talloc_free(data->audio_support); data->audio_support = NULL; data->audio_length = 0; } /* create a new array */ data->audio_support = talloc_zero_array(osmo_bsc_data(vty), struct gsm_audio_support *, argc); data->audio_length = argc; for (i = 0; i < argc; ++i) { /* check for hrX or frX */ if (strlen(argv[i]) != 3 || argv[i][1] != 'r' || (argv[i][0] != 'h' && argv[i][0] != 'f') || argv[i][2] < 0x30 || argv[i][2] > 0x39) goto error; data->audio_support[i] = talloc_zero(data->audio_support, struct gsm_audio_support); data->audio_support[i]->ver = atoi(argv[i] + 2); if (strncmp("hr", argv[i], 2) == 0) { data->audio_support[i]->hr = 1; saw_hr = 1; } else if (strncmp("fr", argv[i], 2) == 0) { data->audio_support[i]->hr = 0; saw_fr = 1; } if (saw_hr && saw_fr) { vty_out(vty, "Can not have full-rate and half-rate codec.%s", VTY_NEWLINE); return CMD_ERR_INCOMPLETE; } } return CMD_SUCCESS; error: vty_out(vty, "Codec name must be hrX or frX. Was '%s'%s", argv[i], VTY_NEWLINE); return CMD_ERR_INCOMPLETE; } DEFUN(cfg_net_msc_dest, cfg_net_msc_dest_cmd, "dest A.B.C.D <1-65000> <0-255>", "Add a destination to a MUX/MSC\n" "IP Address\n" "Port\n" "DSCP\n") { struct bsc_msc_dest *dest; struct osmo_msc_data *data = osmo_msc_data(vty); dest = talloc_zero(osmo_bsc_data(vty), struct bsc_msc_dest); if (!dest) { vty_out(vty, "%%Failed to create structure.%s", VTY_NEWLINE); return CMD_WARNING; } dest->ip = talloc_strdup(dest, argv[0]); if (!dest->ip) { vty_out(vty, "%%Failed to copy dest ip.%s", VTY_NEWLINE); talloc_free(dest); return CMD_WARNING; } dest->port = atoi(argv[1]); dest->dscp = atoi(argv[2]); llist_add_tail(&dest->list, &data->dests); return CMD_SUCCESS; } DEFUN(cfg_net_msc_no_dest, cfg_net_msc_no_dest_cmd, "no dest A.B.C.D <1-65000> <0-255>", NO_STR "Remove a destination to a MUX/MSC\n" "IP Address\n" "Port\n" "DSCP\n") { struct bsc_msc_dest *dest, *tmp; struct osmo_msc_data *data = osmo_msc_data(vty); int port = atoi(argv[1]); int dscp = atoi(argv[2]); llist_for_each_entry_safe(dest, tmp, &data->dests, list) { if (port != dest->port || dscp != dest->dscp || strcmp(dest->ip, argv[0]) != 0) continue; llist_del(&dest->list); talloc_free(dest); } return CMD_SUCCESS; } DEFUN(cfg_net_msc_no_ping_time, cfg_net_msc_no_ping_time_cmd, "no timeout-ping", NO_STR "Disable the ping/pong handling on A-link\n") { struct osmo_msc_data *data = osmo_msc_data(vty); data->ping_timeout = -1; return CMD_SUCCESS; } DEFUN(cfg_net_msc_ping_time, cfg_net_msc_ping_time_cmd, "timeout-ping <1-2147483647>", "Set the PING interval, negative for not sending PING\n" "Timeout in seconds\n") { struct osmo_msc_data *data = osmo_msc_data(vty); data->ping_timeout = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_net_msc_pong_time, cfg_net_msc_pong_time_cmd, "timeout-pong <1-2147483647>", "Set the time to wait for a PONG\n" "Timeout in seconds\n") { struct osmo_msc_data *data = osmo_msc_data(vty); data->pong_timeout = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_net_msc_advanced_ping, cfg_net_msc_advanced_ping_cmd, "timeout-ping advanced", "Ping timeout handling\nEnable advanced mode during SCCP\n") { struct osmo_msc_data *data = osmo_msc_data(vty); if (data->ping_timeout == -1) { vty_out(vty, "%%ping handling is disabled. Enable it first.%s", VTY_NEWLINE); return CMD_WARNING; } data->advanced_ping = 1; return CMD_SUCCESS; } DEFUN(cfg_no_net_msc_advanced_ping, cfg_no_net_msc_advanced_ping_cmd, "no timeout-ping advanced", NO_STR "Ping timeout handling\nEnable advanced mode during SCCP\n") { struct osmo_msc_data *data = osmo_msc_data(vty); data->advanced_ping = 0; return CMD_SUCCESS; } DEFUN(cfg_net_msc_welcome_ussd, cfg_net_msc_welcome_ussd_cmd, "bsc-welcome-text .TEXT", "Set the USSD notification to be sent\n" "Text to be sent\n") { struct osmo_msc_data *data = osmo_msc_data(vty); char *str = argv_concat(argv, argc, 0); if (!str) return CMD_WARNING; bsc_replace_string(osmo_bsc_data(vty), &data->ussd_welcome_txt, str); talloc_free(str); return CMD_SUCCESS; } DEFUN(cfg_net_msc_no_welcome_ussd, cfg_net_msc_no_welcome_ussd_cmd, "no bsc-welcome-text", NO_STR "Clear the USSD notification to be sent\n") { struct osmo_msc_data *data = osmo_msc_data(vty); talloc_free(data->ussd_welcome_txt); data->ussd_welcome_txt = NULL; return CMD_SUCCESS; } DEFUN(cfg_net_msc_lost_ussd, cfg_net_msc_lost_ussd_cmd, "bsc-msc-lost-text .TEXT", "Set the USSD notification to be sent on MSC connection loss\n" "Text to be sent\n") { struct osmo_msc_data *data = osmo_msc_data(vty); char *str = argv_concat(argv, argc, 0); if (!str) return CMD_WARNING; bsc_replace_string(osmo_bsc_data(vty), &data->ussd_msc_lost_txt, str); talloc_free(str); return CMD_SUCCESS; } DEFUN(cfg_net_msc_no_lost_ussd, cfg_net_msc_no_lost_ussd_cmd, "no bsc-msc-lost-text", NO_STR "Clear the USSD notification to be sent on MSC connection loss\n") { struct osmo_msc_data *data = osmo_msc_data(vty); talloc_free(data->ussd_msc_lost_txt); data->ussd_msc_lost_txt = 0; return CMD_SUCCESS; } DEFUN(cfg_net_msc_grace_ussd, cfg_net_msc_grace_ussd_cmd, "bsc-grace-text .TEXT", "Set the USSD notification to be sent when the MSC has entered the grace period\n" "Text to be sent\n") { struct osmo_msc_data *data = osmo_msc_data(vty); char *str = argv_concat(argv, argc, 0); if (!str) return CMD_WARNING; bsc_replace_string(osmo_bsc_data(vty), &data->ussd_grace_txt, str); talloc_free(str); return CMD_SUCCESS; } DEFUN(cfg_net_msc_no_grace_ussd, cfg_net_msc_no_grace_ussd_cmd, "no bsc-grace-text", NO_STR "Clear the USSD notification to be sent when the MSC has entered the grace period\n") { struct osmo_msc_data *data = osmo_msc_data(vty); talloc_free(data->ussd_grace_txt); data->ussd_grace_txt = NULL; return CMD_SUCCESS; } DEFUN(cfg_net_bsc_missing_msc_ussd, cfg_net_bsc_missing_msc_ussd_cmd, "missing-msc-text .TEXT", "Set the USSD notification to be send when a MSC has not been found.\n" "Text to be sent\n") { struct osmo_bsc_data *data = osmo_bsc_data(vty); char *txt = argv_concat(argv, argc, 0); if (!txt) return CMD_WARNING; bsc_replace_string(data, &data->ussd_no_msc_txt, txt); talloc_free(txt); return CMD_SUCCESS; } DEFUN(cfg_net_bsc_no_missing_msc_text, cfg_net_bsc_no_missing_msc_text_cmd, "no missing-msc-text", NO_STR "Clear the USSD notification to be send when a MSC has not been found.\n") { struct osmo_bsc_data *data = osmo_bsc_data(vty); talloc_free(data->ussd_no_msc_txt); data->ussd_no_msc_txt = 0; return CMD_SUCCESS; } DEFUN(cfg_net_msc_type, cfg_net_msc_type_cmd, "type (normal|local)", "Select the MSC type\n" "Plain GSM MSC\n" "Special MSC for local call routing\n") { struct osmo_msc_data *data = osmo_msc_data(vty); if (strcmp(argv[0], "normal") == 0) data->type = MSC_CON_TYPE_NORMAL; else if (strcmp(argv[0], "local") == 0) data->type = MSC_CON_TYPE_LOCAL; return CMD_SUCCESS; } DEFUN(cfg_net_msc_emerg, cfg_net_msc_emerg_cmd, "allow-emergency (allow|deny)", "Allow CM ServiceRequests with type emergency\n" "Allow\n" "Deny\n") { struct osmo_msc_data *data = osmo_msc_data(vty); data->allow_emerg = strcmp("allow", argv[0]) == 0; return CMD_SUCCESS; } DEFUN(cfg_net_msc_local_prefix, cfg_net_msc_local_prefix_cmd, "local-prefix REGEXP", "Prefix for local numbers\n" "REGEXP used\n") { struct osmo_msc_data *msc = osmo_msc_data(vty); if (gsm_parse_reg(msc, &msc->local_pref_reg, &msc->local_pref, argc, argv) != 0) { vty_out(vty, "%%Failed to parse the regexp: '%s'%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } return CMD_SUCCESS; } #define AMR_CONF_STR "AMR Multirate Configuration\n" #define AMR_COMMAND(name) \ DEFUN(cfg_net_msc_amr_##name, \ cfg_net_msc_amr_##name##_cmd, \ "amr-config " #name "k (allowed|forbidden)", \ AMR_CONF_STR "Bitrate\n" "Allowed\n" "Forbidden\n") \ { \ struct osmo_msc_data *msc = osmo_msc_data(vty); \ \ msc->amr_conf.m##name = strcmp(argv[0], "allowed") == 0; \ return CMD_SUCCESS; \ } AMR_COMMAND(12_2) AMR_COMMAND(10_2) AMR_COMMAND(7_95) AMR_COMMAND(7_40) AMR_COMMAND(6_70) AMR_COMMAND(5_90) AMR_COMMAND(5_15) AMR_COMMAND(4_75) DEFUN(cfg_msc_acc_lst_name, cfg_msc_acc_lst_name_cmd, "access-list-name NAME", "Set the name of the access list to use.\n" "The name of the to be used access list.") { struct osmo_msc_data *msc = osmo_msc_data(vty); bsc_replace_string(msc, &msc->acc_lst_name, argv[0]); return CMD_SUCCESS; } DEFUN(cfg_msc_no_acc_lst_name, cfg_msc_no_acc_lst_name_cmd, "no access-list-name", NO_STR "Remove the access list from the NAT.\n") { struct osmo_msc_data *msc = osmo_msc_data(vty); if (msc->acc_lst_name) { talloc_free(msc->acc_lst_name); msc->acc_lst_name = NULL; } return CMD_SUCCESS; } DEFUN(cfg_net_bsc_mid_call_text, cfg_net_bsc_mid_call_text_cmd, "mid-call-text .TEXT", "Set the USSD notifcation to be send.\n" "Text to be sent\n") { struct osmo_bsc_data *data = osmo_bsc_data(vty); char *txt = argv_concat(argv, argc, 0); if (!txt) return CMD_WARNING; bsc_replace_string(data, &data->mid_call_txt, txt); talloc_free(txt); return CMD_SUCCESS; } DEFUN(cfg_net_bsc_mid_call_timeout, cfg_net_bsc_mid_call_timeout_cmd, "mid-call-timeout NR", "Switch from Grace to Off in NR seconds.\n" "Timeout in seconds\n") { struct osmo_bsc_data *data = osmo_bsc_data(vty); data->mid_call_timeout = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_net_rf_socket, cfg_net_rf_socket_cmd, "bsc-rf-socket PATH", "Set the filename for the RF control interface.\n" "RF Control path\n") { struct osmo_bsc_data *data = osmo_bsc_data(vty); bsc_replace_string(data, &data->rf_ctrl_name, argv[0]); return CMD_SUCCESS; } DEFUN(cfg_net_rf_off_time, cfg_net_rf_off_time_cmd, "bsc-auto-rf-off <1-65000>", "Disable RF on MSC Connection\n" "Timeout\n") { struct osmo_bsc_data *data = osmo_bsc_data(vty); data->auto_off_timeout = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_net_no_rf_off_time, cfg_net_no_rf_off_time_cmd, "no bsc-auto-rf-off", NO_STR "Disable RF on MSC Connection\n") { struct osmo_bsc_data *data = osmo_bsc_data(vty); data->auto_off_timeout = -1; return CMD_SUCCESS; } DEFUN(cfg_bsc_acc_lst_name, cfg_bsc_acc_lst_name_cmd, "access-list-name NAME", "Set the name of the access list to use.\n" "The name of the to be used access list.") { struct osmo_bsc_data *bsc = osmo_bsc_data(vty); bsc_replace_string(bsc, &bsc->acc_lst_name, argv[0]); return CMD_SUCCESS; } DEFUN(cfg_bsc_no_acc_lst_name, cfg_bsc_no_acc_lst_name_cmd, "no access-list-name", NO_STR "Remove the access list from the BSC\n") { struct osmo_bsc_data *bsc = osmo_bsc_data(vty); if (bsc->acc_lst_name) { talloc_free(bsc->acc_lst_name); bsc->acc_lst_name = NULL; } return CMD_SUCCESS; } DEFUN(show_statistics, show_statistics_cmd, "show statistics", SHOW_STR "Statistics about the BSC\n") { openbsc_vty_print_statistics(vty, bsc_gsmnet); return CMD_SUCCESS; } DEFUN(show_mscs, show_mscs_cmd, "show mscs", SHOW_STR "MSC Connections and State\n") { struct osmo_msc_data *msc; llist_for_each_entry(msc, &bsc_gsmnet->bsc_data->mscs, entry) { vty_out(vty, "MSC Nr: %d is connected: %d auth: %d.%s", msc->nr, msc->msc_con ? msc->msc_con->is_connected : -1, msc->msc_con ? msc->msc_con->is_authenticated : -1, VTY_NEWLINE); } return CMD_SUCCESS; } DEFUN(show_pos, show_pos_cmd, "show position", SHOW_STR "Position information of the BTS\n") { struct gsm_bts *bts; struct bts_location *curloc; struct tm time; char timestr[50]; llist_for_each_entry(bts, &bsc_gsmnet->bts_list, list) { if (llist_empty(&bts->loc_list)) { vty_out(vty, "BTS Nr: %d position invalid%s", bts->nr, VTY_NEWLINE); continue; } curloc = llist_entry(bts->loc_list.next, struct bts_location, list); if (gmtime_r(&curloc->tstamp, &time) == NULL) { vty_out(vty, "Time conversion failed for BTS %d%s", bts->nr, VTY_NEWLINE); continue; } if (asctime_r(&time, timestr) == NULL) { vty_out(vty, "Time conversion failed for BTS %d%s", bts->nr, VTY_NEWLINE); continue; } /* Last character in asctime is \n */ timestr[strlen(timestr)-1] = 0; vty_out(vty, "BTS Nr: %d position: %s time: %s%s", bts->nr, get_value_string(bts_loc_fix_names, curloc->valid), timestr, VTY_NEWLINE); vty_out(vty, " lat: %f lon: %f height: %f%s", curloc->lat, curloc->lon, curloc->height, VTY_NEWLINE); } return CMD_SUCCESS; } DEFUN(gen_position_trap, gen_position_trap_cmd, "generate-location-state-trap <0-255>", "Generate location state report\n" "BTS to report\n") { int bts_nr; struct gsm_bts *bts; struct gsm_network *net = bsc_gsmnet; bts_nr = atoi(argv[0]); if (bts_nr >= net->num_bts) { vty_out(vty, "%% can't find BTS '%s'%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } bts = gsm_bts_num(net, bts_nr); bsc_gen_location_state_trap(bts); return CMD_SUCCESS; } DEFUN(logging_fltr_imsi, logging_fltr_imsi_cmd, "logging filter imsi IMSI", LOGGING_STR FILTER_STR "Filter log messages by IMSI\n" "IMSI to be used as filter\n") { struct gsm_subscriber *subscr; struct log_target *tgt = osmo_log_vty2tgt(vty); if (!tgt) return CMD_WARNING; subscr = subscr_get_or_create(bsc_gsmnet->subscr_group, argv[0]); if (!subscr) { vty_out(vty, "%%no subscriber with IMSI(%s)%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } log_set_imsi_filter(tgt, subscr); return CMD_SUCCESS; } int bsc_vty_init_extra(void) { install_element(CONFIG_NODE, &cfg_net_msc_cmd); install_element(CONFIG_NODE, &cfg_net_bsc_cmd); install_node(&bsc_node, config_write_bsc); vty_install_default(BSC_NODE); install_element(BSC_NODE, &cfg_net_bsc_mid_call_text_cmd); install_element(BSC_NODE, &cfg_net_bsc_mid_call_timeout_cmd); install_element(BSC_NODE, &cfg_net_rf_socket_cmd); install_element(BSC_NODE, &cfg_net_rf_off_time_cmd); install_element(BSC_NODE, &cfg_net_no_rf_off_time_cmd); install_element(BSC_NODE, &cfg_net_bsc_missing_msc_ussd_cmd); install_element(BSC_NODE, &cfg_net_bsc_no_missing_msc_text_cmd); install_element(BSC_NODE, &cfg_bsc_acc_lst_name_cmd); install_element(BSC_NODE, &cfg_bsc_no_acc_lst_name_cmd); install_node(&msc_node, config_write_msc); vty_install_default(MSC_NODE); install_element(MSC_NODE, &cfg_net_bsc_token_cmd); install_element(MSC_NODE, &cfg_net_bsc_key_cmd); install_element(MSC_NODE, &cfg_net_bsc_no_key_cmd); install_element(MSC_NODE, &cfg_net_bsc_ncc_cmd); install_element(MSC_NODE, &cfg_net_bsc_mcc_cmd); install_element(MSC_NODE, &cfg_net_bsc_lac_cmd); install_element(MSC_NODE, &cfg_net_bsc_ci_cmd); install_element(MSC_NODE, &cfg_net_bsc_rtp_base_cmd); install_element(MSC_NODE, &cfg_net_bsc_codec_list_cmd); install_element(MSC_NODE, &cfg_net_msc_dest_cmd); install_element(MSC_NODE, &cfg_net_msc_no_dest_cmd); install_element(MSC_NODE, &cfg_net_msc_no_ping_time_cmd); install_element(MSC_NODE, &cfg_net_msc_ping_time_cmd); install_element(MSC_NODE, &cfg_net_msc_pong_time_cmd); install_element(MSC_NODE, &cfg_net_msc_advanced_ping_cmd); install_element(MSC_NODE, &cfg_no_net_msc_advanced_ping_cmd); install_element(MSC_NODE, &cfg_net_msc_welcome_ussd_cmd); install_element(MSC_NODE, &cfg_net_msc_no_welcome_ussd_cmd); install_element(MSC_NODE, &cfg_net_msc_lost_ussd_cmd); install_element(MSC_NODE, &cfg_net_msc_no_lost_ussd_cmd); install_element(MSC_NODE, &cfg_net_msc_grace_ussd_cmd); install_element(MSC_NODE, &cfg_net_msc_no_grace_ussd_cmd); install_element(MSC_NODE, &cfg_net_msc_type_cmd); install_element(MSC_NODE, &cfg_net_msc_emerg_cmd); install_element(MSC_NODE, &cfg_net_msc_local_prefix_cmd); install_element(MSC_NODE, &cfg_net_msc_amr_12_2_cmd); install_element(MSC_NODE, &cfg_net_msc_amr_10_2_cmd); install_element(MSC_NODE, &cfg_net_msc_amr_7_95_cmd); install_element(MSC_NODE, &cfg_net_msc_amr_7_40_cmd); install_element(MSC_NODE, &cfg_net_msc_amr_6_70_cmd); install_element(MSC_NODE, &cfg_net_msc_amr_5_90_cmd); install_element(MSC_NODE, &cfg_net_msc_amr_5_15_cmd); install_element(MSC_NODE, &cfg_net_msc_amr_4_75_cmd); install_element(MSC_NODE, &cfg_msc_acc_lst_name_cmd); install_element(MSC_NODE, &cfg_msc_no_acc_lst_name_cmd); install_element_ve(&show_statistics_cmd); install_element_ve(&show_mscs_cmd); install_element_ve(&show_pos_cmd); install_element_ve(&logging_fltr_imsi_cmd); install_element(ENABLE_NODE, &gen_position_trap_cmd); install_element(CFG_LOG_NODE, &logging_fltr_imsi_cmd); return 0; } openbsc-0.15.0/openbsc/src/osmo-bsc_mgcp/000077500000000000000000000000001265565154000202075ustar00rootroot00000000000000openbsc-0.15.0/openbsc/src/osmo-bsc_mgcp/Makefile.am000066400000000000000000000007761265565154000222550ustar00rootroot00000000000000AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) \ $(LIBOSMOVTY_CFLAGS) $(LIBOSMOABIS_CFLAGS) \ $(LIBOSMONETIF_CFLAGS) $(COVERAGE_CFLAGS) bin_PROGRAMS = osmo-bsc_mgcp osmo_bsc_mgcp_SOURCES = mgcp_main.c osmo_bsc_mgcp_LDADD = $(top_builddir)/src/libcommon/libcommon.a \ $(top_builddir)/src/libmgcp/libmgcp.a -lrt \ $(LIBOSMOVTY_LIBS) $(LIBOSMOCORE_LIBS) \ $(LIBOSMONETIF_LIBS) $(LIBBCG729_LIBS) \ $(LIBRARY_GSM) openbsc-0.15.0/openbsc/src/osmo-bsc_mgcp/mgcp_main.c000066400000000000000000000165651265565154000223220ustar00rootroot00000000000000/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */ /* The main method to drive it as a standalone process */ /* * (C) 2009-2011 by Holger Hans Peter Freyther * (C) 2009-2011 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../../bscconfig.h" #ifdef BUILD_MGCP_TRANSCODING #include "openbsc/mgcp_transcode.h" #endif #define _GNU_SOURCE #include #warning "Make use of the rtp proxy code" static struct mgcp_config *cfg; static struct mgcp_trunk_config *reset_trunk; static int reset_endpoints = 0; static int daemonize = 0; const char *openbsc_copyright = "Copyright (C) 2009-2010 Holger Freyther and On-Waves\r\n" "Contributions by Daniel Willmann, Jan Lübbe, Stefan Schmidt\r\n" "Dieter Spaar, Andreas Eversberg, Harald Welte\r\n\r\n" "License AGPLv3+: GNU AGPL version 3 or later \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 = "mgcp.cfg"; /* used by msgb and mgcp */ void *tall_bsc_ctx = NULL; static void print_help() { printf("Some useful help...\n"); printf(" -h --help is printing this text.\n"); printf(" -c --config-file filename The config file to use.\n"); printf(" -s --disable-color\n"); printf(" -D --daemonize Fork the process into a background daemon\n"); printf(" -V --version Print the version number\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'}, {"config-file", 1, 0, 'c'}, {"daemonize", 0, 0, 'D'}, {"version", 0, 0, 'V'}, {"disable-color", 0, 0, 's'}, {0, 0, 0, 0}, }; c = getopt_long(argc, argv, "hc:VD", long_options, &option_index); if (c == -1) break; switch(c) { case 'h': print_help(); exit(0); break; case 'c': config_file = talloc_strdup(tall_bsc_ctx, optarg); break; case 's': log_set_use_color(osmo_stderr_target, 0); break; case 'V': print_version(1); exit(0); break; case 'D': daemonize = 1; break; default: /* ignore */ break; }; } } /* simply remember this */ static int mgcp_rsip_cb(struct mgcp_trunk_config *tcfg) { reset_endpoints = 1; reset_trunk = tcfg; return 0; } static int read_call_agent(struct osmo_fd *fd, unsigned int what) { struct sockaddr_in addr; socklen_t slen = sizeof(addr); struct msgb *msg; struct msgb *resp; int i; msg = (struct msgb *) fd->data; /* read one less so we can use it as a \0 */ int rc = recvfrom(cfg->gw_fd.bfd.fd, msg->data, msg->data_len - 1, 0, (struct sockaddr *) &addr, &slen); if (rc < 0) { perror("Gateway failed to read"); return -1; } else if (slen > sizeof(addr)) { fprintf(stderr, "Gateway received message from outerspace: %zu %zu\n", (size_t) slen, sizeof(addr)); return -1; } /* handle message now */ msg->l2h = msgb_put(msg, rc); resp = mgcp_handle_message(cfg, msg); msgb_reset(msg); if (resp) { sendto(cfg->gw_fd.bfd.fd, resp->l2h, msgb_l2len(resp), 0, (struct sockaddr *) &addr, sizeof(addr)); msgb_free(resp); } if (reset_endpoints) { LOGP(DMGCP, LOGL_NOTICE, "Asked to reset endpoints: %d/%d\n", reset_trunk->trunk_nr, reset_trunk->trunk_type); reset_endpoints = 0; /* is checking in_addr.s_addr == INADDR_LOOPBACK making it more secure? */ for (i = 1; i < reset_trunk->number_endpoints; ++i) mgcp_release_endp(&reset_trunk->endpoints[i]); } return 0; } extern int bsc_vty_go_parent(struct vty *vty); static struct vty_app_info vty_info = { .name = "OpenBSC MGCP", .version = PACKAGE_VERSION, .go_parent_cb = bsc_vty_go_parent, .is_config_node = bsc_vty_is_config_node, }; int main(int argc, char **argv) { struct gsm_network dummy_network; struct sockaddr_in addr; int on = 1, rc; tall_bsc_ctx = talloc_named_const(NULL, 1, "mgcp-callagent"); osmo_init_ignore_signals(); osmo_init_logging(&log_info); cfg = mgcp_config_alloc(); if (!cfg) return -1; #ifdef BUILD_MGCP_TRANSCODING cfg->setup_rtp_processing_cb = &mgcp_transcoding_setup; cfg->rtp_processing_cb = &mgcp_transcoding_process_rtp; cfg->get_net_downlink_format_cb = &mgcp_transcoding_net_downlink_format; #endif vty_info.copyright = openbsc_copyright; vty_init(&vty_info); logging_vty_add_cmds(&log_info); mgcp_vty_init(); handle_options(argc, argv); rc = mgcp_parse_config(config_file, cfg, MGCP_BSC); if (rc < 0) return rc; rc = telnet_init(tall_bsc_ctx, &dummy_network, OSMO_VTY_PORT_BSC_MGCP); if (rc < 0) return rc; /* set some callbacks */ cfg->reset_cb = mgcp_rsip_cb; /* we need to bind a socket */ if (rc == 0) { cfg->gw_fd.bfd.when = BSC_FD_READ; cfg->gw_fd.bfd.cb = read_call_agent; cfg->gw_fd.bfd.fd = socket(AF_INET, SOCK_DGRAM, 0); if (cfg->gw_fd.bfd.fd < 0) { perror("Gateway failed to listen"); return -1; } setsockopt(cfg->gw_fd.bfd.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(cfg->source_port); inet_aton(cfg->source_addr, &addr.sin_addr); if (bind(cfg->gw_fd.bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { perror("Gateway failed to bind"); return -1; } cfg->gw_fd.bfd.data = msgb_alloc(4096, "mgcp-msg"); if (!cfg->gw_fd.bfd.data) { fprintf(stderr, "Gateway memory error.\n"); return -1; } if (cfg->call_agent_addr) { addr.sin_port = htons(2727); inet_aton(cfg->call_agent_addr, &addr.sin_addr); if (connect(cfg->gw_fd.bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { LOGP(DMGCP, LOGL_ERROR, "Failed to connect to: '%s'. errno: %d\n", cfg->call_agent_addr, errno); close(cfg->gw_fd.bfd.fd); cfg->gw_fd.bfd.fd = -1; return -1; } } if (osmo_fd_register(&cfg->gw_fd.bfd) != 0) { LOGP(DMGCP, LOGL_FATAL, "Failed to register the fd\n"); return -1; } LOGP(DMGCP, LOGL_NOTICE, "Configured for MGCP.\n"); } /* initialisation */ srand(time(NULL)); if (daemonize) { rc = osmo_daemonize(); if (rc < 0) { perror("Error during daemonize"); exit(1); } } /* main loop */ while (1) { osmo_select_main(0); } return 0; } openbsc-0.15.0/openbsc/src/osmo-bsc_nat/000077500000000000000000000000001265565154000200435ustar00rootroot00000000000000openbsc-0.15.0/openbsc/src/osmo-bsc_nat/Makefile.am000066400000000000000000000016731265565154000221060ustar00rootroot00000000000000AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOSCCP_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(LIBOSMONETIF_CFLAGS) $(LIBCRYPTO_CFLAGS) $(COVERAGE_CFLAGS) AM_LDFLAGS = $(COVERAGE_LDFLAGS) bin_PROGRAMS = osmo-bsc_nat osmo_bsc_nat_SOURCES = bsc_filter.c bsc_mgcp_utils.c bsc_nat.c bsc_nat_utils.c \ bsc_nat_vty.c bsc_sccp.c bsc_ussd.c bsc_nat_ctrl.c \ bsc_nat_rewrite.c bsc_nat_rewrite_trie.c bsc_nat_filter.c osmo_bsc_nat_LDADD = \ $(top_builddir)/src/libmgcp/libmgcp.a \ $(top_builddir)/src/libbsc/libbsc.a \ $(top_builddir)/src/libtrau/libtrau.a \ $(top_builddir)/src/libcommon/libcommon.a \ $(top_builddir)/src/libfilter/libfilter.a \ -lrt $(LIBOSMOSCCP_LIBS) $(LIBOSMOCORE_LIBS) \ $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOCTRL_LIBS) \ $(LIBOSMOABIS_LIBS) $(LIBOSMONETIF_LIBS) $(LIBCRYPTO_LIBS) openbsc-0.15.0/openbsc/src/osmo-bsc_nat/bsc_filter.c000066400000000000000000000133251265565154000223270ustar00rootroot00000000000000/* BSC Multiplexer/NAT */ /* * (C) 2010 by Holger Hans Peter Freyther * (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 . * */ #include #include #include #include #include #include #include /* * The idea is to have a simple struct describing a IPA packet with * SCCP SSN and the GSM 08.08 payload and decide. We will both have * a white and a blacklist of packets we want to handle. * * TODO: Implement a "NOT" in the filter language. */ #define ALLOW_ANY -1 #define FILTER_TO_BSC 1 #define FILTER_TO_MSC 2 #define FILTER_TO_BOTH 3 struct bsc_pkt_filter { int ipa_proto; int dest_ssn; int bssap; int gsm; int filter_dir; }; static struct bsc_pkt_filter black_list[] = { /* filter reset messages to the MSC */ { IPAC_PROTO_SCCP, SCCP_SSN_BSSAP, 0, BSS_MAP_MSG_RESET, FILTER_TO_MSC }, /* filter reset ack messages to the BSC */ { IPAC_PROTO_SCCP, SCCP_SSN_BSSAP, 0, BSS_MAP_MSG_RESET_ACKNOWLEDGE, FILTER_TO_BSC }, /* filter ip access */ { IPAC_PROTO_IPACCESS, ALLOW_ANY, ALLOW_ANY, ALLOW_ANY, FILTER_TO_MSC }, }; static struct bsc_pkt_filter white_list[] = { /* allow IPAC_PROTO_SCCP messages to both sides */ { IPAC_PROTO_SCCP, ALLOW_ANY, ALLOW_ANY, ALLOW_ANY, FILTER_TO_BOTH }, /* allow MGCP messages to both sides */ { IPAC_PROTO_MGCP_OLD, ALLOW_ANY, ALLOW_ANY, ALLOW_ANY, FILTER_TO_BOTH }, }; struct bsc_nat_parsed *bsc_nat_parse(struct msgb *msg) { struct sccp_parse_result result; struct bsc_nat_parsed *parsed; struct ipaccess_head *hh; /* quick fail */ if (msg->len < 4) return NULL; parsed = talloc_zero(msg, struct bsc_nat_parsed); if (!parsed) return NULL; /* more init */ parsed->ipa_proto = parsed->called_ssn = parsed->calling_ssn = -1; parsed->sccp_type = parsed->bssap = parsed->gsm_type = -1; /* start parsing */ hh = (struct ipaccess_head *) msg->data; parsed->ipa_proto = hh->proto; msg->l2h = &hh->data[0]; /* do a size check on the input */ if (ntohs(hh->len) != msgb_l2len(msg)) { LOGP(DLINP, LOGL_ERROR, "Wrong input length?\n"); talloc_free(parsed); return NULL; } /* analyze sccp down here */ if (parsed->ipa_proto == IPAC_PROTO_SCCP) { memset(&result, 0, sizeof(result)); if (sccp_parse_header(msg, &result) != 0) { talloc_free(parsed); return 0; } if (msg->l3h && msgb_l3len(msg) < 3) { LOGP(DNAT, LOGL_ERROR, "Not enough space or GSM payload\n"); talloc_free(parsed); return 0; } parsed->sccp_type = sccp_determine_msg_type(msg); parsed->src_local_ref = result.source_local_reference; parsed->dest_local_ref = result.destination_local_reference; if (parsed->dest_local_ref) parsed->original_dest_ref = *parsed->dest_local_ref; parsed->called_ssn = result.called.ssn; parsed->calling_ssn = result.calling.ssn; /* in case of connection confirm we have no payload */ if (msg->l3h) { parsed->bssap = msg->l3h[0]; parsed->gsm_type = msg->l3h[2]; } } return parsed; } int bsc_nat_filter_ipa(int dir, struct msgb *msg, struct bsc_nat_parsed *parsed) { int i; /* go through the blacklist now */ for (i = 0; i < ARRAY_SIZE(black_list); ++i) { /* ignore the rule? */ if (black_list[i].filter_dir != FILTER_TO_BOTH && black_list[i].filter_dir != dir) continue; /* the proto is not blacklisted */ if (black_list[i].ipa_proto != ALLOW_ANY && black_list[i].ipa_proto != parsed->ipa_proto) continue; if (parsed->ipa_proto == IPAC_PROTO_SCCP) { /* the SSN is not blacklisted */ if (black_list[i].dest_ssn != ALLOW_ANY && black_list[i].dest_ssn != parsed->called_ssn) continue; /* bssap */ if (black_list[i].bssap != ALLOW_ANY && black_list[i].bssap != parsed->bssap) continue; /* gsm */ if (black_list[i].gsm != ALLOW_ANY && black_list[i].gsm != parsed->gsm_type) continue; /* blacklisted */ LOGP(DNAT, LOGL_INFO, "Blacklisted with rule %d\n", i); return 1; } else { /* blacklisted, we have no content sniffing yet */ LOGP(DNAT, LOGL_INFO, "Blacklisted with rule %d\n", i); return 1; } } /* go through the whitelust now */ for (i = 0; i < ARRAY_SIZE(white_list); ++i) { /* ignore the rule? */ if (white_list[i].filter_dir != FILTER_TO_BOTH && white_list[i].filter_dir != dir) continue; /* the proto is not whitelisted */ if (white_list[i].ipa_proto != ALLOW_ANY && white_list[i].ipa_proto != parsed->ipa_proto) continue; if (parsed->ipa_proto == IPAC_PROTO_SCCP) { /* the SSN is not whitelisted */ if (white_list[i].dest_ssn != ALLOW_ANY && white_list[i].dest_ssn != parsed->called_ssn) continue; /* bssap */ if (white_list[i].bssap != ALLOW_ANY && white_list[i].bssap != parsed->bssap) continue; /* gsm */ if (white_list[i].gsm != ALLOW_ANY && white_list[i].gsm != parsed->gsm_type) continue; /* whitelisted */ LOGP(DNAT, LOGL_INFO, "Whitelisted with rule %d\n", i); return 0; } else { /* whitelisted */ return 0; } } return 1; } openbsc-0.15.0/openbsc/src/osmo-bsc_nat/bsc_mgcp_utils.c000066400000000000000000000753001265565154000232110ustar00rootroot00000000000000/** * This file contains helper routines for MGCP Gateway handling. * * The first thing to remember is that each BSC has its own namespace/range * of endpoints. Whenever a BSSMAP ASSIGNMENT REQUEST is received this code * will be called to select an endpoint on the BSC. The mapping from original * multiplex/timeslot to BSC multiplex'/timeslot' will be stored. * * The second part is to take messages on the public MGCP GW interface * and forward them to the right BSC. This requires the MSC to first * assign the timeslot. This assumption has been true so far. We are using * the policy_cb of the MGCP protocol code to decide if the request should * be immediately answered or delayed. An extension "Z: noanswer" is used * to request the BSC to not respond. This is saving some bytes of bandwidth * and as we are using TCP to forward the message we know it will arrive. * The mgcp_do_read method reads these messages and hands them to the protocol * parsing code which will call the mentioned policy_cb. The bsc_mgcp_forward * method is used on the way back from the BSC to the network. * * The third part is to patch messages forwarded to the BSC. This includes * the endpoint number, the ports to be used inside the SDP file and maybe * some other bits. * */ /* * (C) 2010-2012 by Holger Hans Peter Freyther * (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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void send_direct(struct bsc_nat *nat, struct msgb *output) { if (osmo_wqueue_enqueue(&nat->mgcp_cfg->gw_fd, output) != 0) { LOGP(DMGCP, LOGL_ERROR, "Failed to queue MGCP msg.\n"); msgb_free(output); } } static void mgcp_queue_for_call_agent(struct bsc_nat *nat, struct msgb *output) { if (nat->mgcp_ipa) bsc_nat_send_mgcp_to_msc(nat, output); else send_direct(nat, output); } int bsc_mgcp_nr_multiplexes(int max_endpoints) { int div = max_endpoints / 32; if ((max_endpoints % 32) != 0) div += 1; return div; } static int bsc_init_endps_if_needed(struct bsc_connection *con) { int multiplexes; /* we have done that */ if (con->_endpoint_status) return 0; /* we have no config... */ if (!con->cfg) return -1; multiplexes = bsc_mgcp_nr_multiplexes(con->cfg->max_endpoints); con->number_multiplexes = multiplexes; con->max_endpoints = con->cfg->max_endpoints; con->_endpoint_status = talloc_zero_array(con, char, 32 * multiplexes + 1); return con->_endpoint_status == NULL; } static int bsc_assign_endpoint(struct bsc_connection *bsc, struct nat_sccp_connection *con) { int multiplex; int timeslot; const int number_endpoints = bsc->max_endpoints; int i; mgcp_endpoint_to_timeslot(bsc->last_endpoint, &multiplex, ×lot); timeslot += 1; for (i = 0; i < number_endpoints; ++i) { int endpoint; /* Wrap around timeslots */ if (timeslot == 0) timeslot = 1; if (timeslot == 0x1f) { timeslot = 1; multiplex += 1; } /* Wrap around the multiplex */ if (multiplex >= bsc->number_multiplexes) multiplex = 0; endpoint = mgcp_timeslot_to_endpoint(multiplex, timeslot); /* Now check if we are allowed to assign this one */ if (endpoint >= bsc->max_endpoints) { multiplex = 0; timeslot = 1; endpoint = mgcp_timeslot_to_endpoint(multiplex, timeslot); } if (bsc->_endpoint_status[endpoint] == 0) { bsc->_endpoint_status[endpoint] = 1; con->bsc_endp = endpoint; bsc->last_endpoint = endpoint; return 0; } timeslot += 1; } return -1; } static uint16_t create_cic(int endpoint) { int timeslot, multiplex; mgcp_endpoint_to_timeslot(endpoint, &multiplex, ×lot); return (multiplex << 5) | (timeslot & 0x1f); } int bsc_mgcp_assign_patch(struct nat_sccp_connection *con, struct msgb *msg) { struct nat_sccp_connection *mcon; struct tlv_parsed tp; uint16_t cic; uint8_t timeslot; uint8_t multiplex; unsigned int endp; if (!msg->l3h) { LOGP(DNAT, LOGL_ERROR, "Assignment message should have l3h pointer.\n"); return -1; } if (msgb_l3len(msg) < 3) { LOGP(DNAT, LOGL_ERROR, "Assignment message has not enough space for GSM0808.\n"); return -1; } tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 3, msgb_l3len(msg) - 3, 0, 0); if (!TLVP_PRESENT(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE)) { LOGP(DNAT, LOGL_ERROR, "Circuit identity code not found in assignment message.\n"); return -1; } cic = ntohs(tlvp_val16_unal(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE)); timeslot = cic & 0x1f; multiplex = (cic & ~0x1f) >> 5; endp = mgcp_timeslot_to_endpoint(multiplex, timeslot); if (endp >= con->bsc->nat->mgcp_cfg->trunk.number_endpoints) { LOGP(DNAT, LOGL_ERROR, "MSC attempted to assign bad endpoint 0x%x\n", endp); return -1; } /* find stale connections using that endpoint */ llist_for_each_entry(mcon, &con->bsc->nat->sccp_connections, list_entry) { if (mcon->msc_endp == endp) { LOGP(DNAT, LOGL_ERROR, "Endpoint %d was assigned to 0x%x and now 0x%x\n", endp, sccp_src_ref_to_int(&mcon->patched_ref), sccp_src_ref_to_int(&con->patched_ref)); bsc_mgcp_dlcx(mcon); } } con->msc_endp = endp; if (bsc_init_endps_if_needed(con->bsc) != 0) return -1; if (bsc_assign_endpoint(con->bsc, con) != 0) return -1; /* * now patch the message for the new CIC... * still assumed to be one multiplex only */ cic = htons(create_cic(con->bsc_endp)); memcpy((uint8_t *) TLVP_VAL(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE), &cic, sizeof(cic)); return 0; } static void bsc_mgcp_free_endpoint(struct bsc_nat *nat, int i) { if (nat->bsc_endpoints[i].transaction_id) { talloc_free(nat->bsc_endpoints[i].transaction_id); nat->bsc_endpoints[i].transaction_id = NULL; } nat->bsc_endpoints[i].transaction_state = 0; nat->bsc_endpoints[i].bsc = NULL; } void bsc_mgcp_free_endpoints(struct bsc_nat *nat) { int i; for (i = 1; i < nat->mgcp_cfg->trunk.number_endpoints; ++i){ bsc_mgcp_free_endpoint(nat, i); mgcp_release_endp(&nat->mgcp_cfg->trunk.endpoints[i]); } } /* send a MDCX where we do not want a response */ static void bsc_mgcp_send_mdcx(struct bsc_connection *bsc, int port, struct mgcp_endpoint *endp) { char buf[2096]; int len; len = snprintf(buf, sizeof(buf), "MDCX 23 %x@mgw MGCP 1.0\r\n" "Z: noanswer\r\n" "\r\n" "c=IN IP4 %s\r\n" "m=audio %d RTP/AVP 255\r\n", port, mgcp_bts_src_addr(endp), endp->bts_end.local_port); if (len < 0) { LOGP(DMGCP, LOGL_ERROR, "snprintf for MDCX failed.\n"); return; } bsc_write_mgcp(bsc, (uint8_t *) buf, len); } static void bsc_mgcp_send_dlcx(struct bsc_connection *bsc, int endpoint, int trans) { char buf[2096]; int len; /* * The following is a bit of a spec violation. According to the * MGCP grammar the transaction id is are upto 9 digits but we * prefix it with an alpha numeric value so we can easily recognize * it as a response. */ len = snprintf(buf, sizeof(buf), "DLCX nat-%u %x@mgw MGCP 1.0\r\n", trans, endpoint); if (len < 0) { LOGP(DMGCP, LOGL_ERROR, "snprintf for DLCX failed.\n"); return; } bsc_write_mgcp(bsc, (uint8_t *) buf, len); } void bsc_mgcp_init(struct nat_sccp_connection *con) { con->msc_endp = -1; con->bsc_endp = -1; } /** * This code will remember the network side of the audio statistics and * once the internal DLCX response arrives this can be combined with the * the BSC side and forwarded as a trap. */ static void remember_pending_dlcx(struct nat_sccp_connection *con, uint32_t transaction) { struct bsc_nat_call_stats *stats; struct bsc_connection *bsc = con->bsc; struct mgcp_endpoint *endp; stats = talloc_zero(bsc, struct bsc_nat_call_stats); if (!stats) { LOGP(DNAT, LOGL_NOTICE, "Failed to allocate statistics for endpoint 0x%x\n", con->msc_endp); return; } /* take the endpoint here */ endp = &bsc->nat->mgcp_cfg->trunk.endpoints[con->msc_endp]; stats->remote_ref = con->remote_ref; stats->src_ref = con->patched_ref; stats->ci = endp->ci; stats->bts_rtp_port = endp->bts_end.rtp_port; stats->bts_addr = endp->bts_end.addr; stats->net_rtp_port = endp->net_end.rtp_port; stats->net_addr = endp->net_end.addr; stats->net_ps = endp->net_end.packets; stats->net_os = endp->net_end.octets; stats->bts_pr = endp->bts_end.packets; stats->bts_or = endp->bts_end.octets; mgcp_state_calc_loss(&endp->bts_state, &endp->bts_end, &stats->bts_expected, &stats->bts_loss); stats->bts_jitter = mgcp_state_calc_jitter(&endp->bts_state); stats->trans_id = transaction; stats->msc_endpoint = con->msc_endp; /* * Too many pending requests.. let's remove the first two items. */ if (!llist_empty(&bsc->pending_dlcx) && bsc->pending_dlcx_count >= bsc->cfg->max_endpoints * 3) { struct bsc_nat_call_stats *tmp; LOGP(DNAT, LOGL_ERROR, "Too many(%d) pending DLCX responses on BSC: %d\n", bsc->pending_dlcx_count, bsc->cfg->nr); bsc->pending_dlcx_count -= 1; tmp = (struct bsc_nat_call_stats *) bsc->pending_dlcx.next; llist_del(&tmp->entry); talloc_free(tmp); } bsc->pending_dlcx_count += 1; llist_add_tail(&stats->entry, &bsc->pending_dlcx); } void bsc_mgcp_dlcx(struct nat_sccp_connection *con) { /* send a DLCX down the stream */ if (con->bsc_endp != -1 && con->bsc->_endpoint_status) { LOGP(DNAT, LOGL_NOTICE, "Endpoint 0x%x was allocated for bsc: %d. Freeing it.\n", con->bsc_endp, con->bsc->cfg->nr); if (con->bsc->_endpoint_status[con->bsc_endp] != 1) LOGP(DNAT, LOGL_ERROR, "Endpoint 0x%x was not in use\n", con->bsc_endp); remember_pending_dlcx(con, con->bsc->next_transaction); con->bsc->_endpoint_status[con->bsc_endp] = 0; bsc_mgcp_send_dlcx(con->bsc, con->bsc_endp, con->bsc->next_transaction++); bsc_mgcp_free_endpoint(con->bsc->nat, con->msc_endp); } bsc_mgcp_init(con); } /* * Search for the pending request */ static void handle_dlcx_response(struct bsc_connection *bsc, struct msgb *msg, int code, const char *transaction) { uint32_t trans_id = UINT32_MAX; uint32_t b_ps, b_os, n_pr, n_or, jitter; int loss; struct bsc_nat_call_stats *tmp, *stat = NULL; struct ctrl_cmd *cmd; /* parse the transaction identifier */ int rc = sscanf(transaction, "nat-%u", &trans_id); if (rc != 1) { LOGP(DNAT, LOGL_ERROR, "Can not parse transaction id: '%s'\n", transaction); return; } /* find the answer for the request we made */ llist_for_each_entry(tmp, &bsc->pending_dlcx, entry) { if (trans_id != tmp->trans_id) continue; stat = tmp; break; } if (!stat) { LOGP(DNAT, LOGL_ERROR, "Can not find transaction for: %u\n", trans_id); return; } /* attempt to parse the data now */ rc = mgcp_parse_stats(msg, &b_ps, &b_os, &n_pr, &n_or, &loss, &jitter); if (rc != 0) LOGP(DNAT, LOGL_ERROR, "Can not parse connection statistics: %d\n", rc); /* send a trap now */ cmd = ctrl_cmd_create(bsc, CTRL_TYPE_TRAP); if (!cmd) { LOGP(DNAT, LOGL_ERROR, "Creating a ctrl cmd failed.\n"); goto free_stat; } cmd->id = "0"; cmd->variable = talloc_asprintf(cmd, "net.0.bsc.%d.call_stats.v2", bsc->cfg->nr); cmd->reply = talloc_asprintf(cmd, "mg_ip_addr=%s,mg_port=%d,", inet_ntoa(stat->net_addr), stat->net_rtp_port); cmd->reply = talloc_asprintf_append(cmd->reply, "endpoint_ip_addr=%s,endpoint_port=%d,", inet_ntoa(stat->bts_addr), stat->bts_rtp_port); cmd->reply = talloc_asprintf_append(cmd->reply, "nat_pkt_in=%u,nat_pkt_out=%u," "nat_bytes_in=%u,nat_bytes_out=%u," "nat_jitter=%u,nat_pkt_lost=%d,", stat->bts_pr, stat->net_ps, stat->bts_or, stat->net_os, stat->bts_jitter, stat->bts_loss); cmd->reply = talloc_asprintf_append(cmd->reply, "bsc_pkt_in=%u,bsc_pkt_out=%u," "bsc_bytes_in=%u,bsc_bytes_out=%u," "bsc_jitter=%u,bsc_pkt_lost=%d,", n_pr, b_ps, n_or, b_os, jitter, loss); cmd->reply = talloc_asprintf_append(cmd->reply, "sccp_src_ref=%u,sccp_dst_ref=%u", sccp_src_ref_to_int(&stat->src_ref), sccp_src_ref_to_int(&stat->remote_ref)); /* send it and be done */ ctrl_cmd_send_to_all(bsc->nat->ctrl, cmd); talloc_free(cmd); free_stat: bsc->pending_dlcx_count -= 1; llist_del(&stat->entry); talloc_free(stat); } struct nat_sccp_connection *bsc_mgcp_find_con(struct bsc_nat *nat, int endpoint) { struct nat_sccp_connection *con = NULL; struct nat_sccp_connection *sccp; llist_for_each_entry(sccp, &nat->sccp_connections, list_entry) { if (sccp->msc_endp == -1) continue; if (sccp->msc_endp != endpoint) continue; con = sccp; } if (con) return con; LOGP(DMGCP, LOGL_ERROR, "Failed to find the connection for endpoint: 0x%x\n", endpoint); return NULL; } static int nat_osmux_only(struct mgcp_config *mgcp_cfg, struct bsc_config *bsc_cfg) { if (mgcp_cfg->osmux == OSMUX_USAGE_ONLY) return 1; if (bsc_cfg->osmux == OSMUX_USAGE_ONLY) return 1; return 0; } static int bsc_mgcp_policy_cb(struct mgcp_trunk_config *tcfg, int endpoint, int state, const char *transaction_id) { struct bsc_nat *nat; struct bsc_endpoint *bsc_endp; struct nat_sccp_connection *sccp; struct mgcp_endpoint *mgcp_endp; struct msgb *bsc_msg; nat = tcfg->cfg->data; bsc_endp = &nat->bsc_endpoints[endpoint]; mgcp_endp = &nat->mgcp_cfg->trunk.endpoints[endpoint]; if (bsc_endp->transaction_id) { LOGP(DMGCP, LOGL_ERROR, "Endpoint 0x%x had pending transaction: '%s'\n", endpoint, bsc_endp->transaction_id); talloc_free(bsc_endp->transaction_id); bsc_endp->transaction_id = NULL; bsc_endp->transaction_state = 0; } bsc_endp->bsc = NULL; sccp = bsc_mgcp_find_con(nat, endpoint); if (!sccp) { LOGP(DMGCP, LOGL_ERROR, "Did not find BSC for change on endpoint: 0x%x state: %d\n", endpoint, state); switch (state) { case MGCP_ENDP_CRCX: return MGCP_POLICY_REJECT; break; case MGCP_ENDP_DLCX: return MGCP_POLICY_CONT; break; case MGCP_ENDP_MDCX: return MGCP_POLICY_CONT; break; default: LOGP(DMGCP, LOGL_FATAL, "Unhandled state: %d\n", state); return MGCP_POLICY_CONT; break; } } /* Allocate a Osmux circuit ID */ if (state == MGCP_ENDP_CRCX) { if (nat->mgcp_cfg->osmux && sccp->bsc->cfg->osmux) { osmux_allocate_cid(mgcp_endp); if (mgcp_endp->osmux.allocated_cid < 0 && nat_osmux_only(nat->mgcp_cfg, sccp->bsc->cfg)) { LOGP(DMGCP, LOGL_ERROR, "Rejecting usage of endpoint\n"); return MGCP_POLICY_REJECT; } } } /* we need to generate a new and patched message */ bsc_msg = bsc_mgcp_rewrite((char *) nat->mgcp_msg, nat->mgcp_length, sccp->bsc_endp, mgcp_bts_src_addr(mgcp_endp), mgcp_endp->bts_end.local_port, mgcp_endp->osmux.allocated_cid, &mgcp_endp->net_end.codec.payload_type, nat->sdp_ensure_amr_mode_set); if (!bsc_msg) { LOGP(DMGCP, LOGL_ERROR, "Failed to patch the msg.\n"); return MGCP_POLICY_CONT; } bsc_endp->transaction_id = talloc_strdup(nat, transaction_id); bsc_endp->transaction_state = state; bsc_endp->bsc = sccp->bsc; /* we need to update some bits */ if (state == MGCP_ENDP_CRCX) { struct sockaddr_in sock; /* Annotate the allocated Osmux CID until the bsc confirms that * it agrees to use Osmux for this voice flow. */ if (mgcp_endp->osmux.allocated_cid >= 0 && mgcp_endp->osmux.state != OSMUX_STATE_ENABLED) { mgcp_endp->osmux.state = OSMUX_STATE_ACTIVATING; mgcp_endp->osmux.cid = mgcp_endp->osmux.allocated_cid; } socklen_t len = sizeof(sock); if (getpeername(sccp->bsc->write_queue.bfd.fd, (struct sockaddr *) &sock, &len) != 0) { LOGP(DMGCP, LOGL_ERROR, "Can not get the peername...%d/%s\n", errno, strerror(errno)); } else { mgcp_endp->bts_end.addr = sock.sin_addr; } /* send the message and a fake MDCX to force sending of a dummy packet */ bsc_write(sccp->bsc, bsc_msg, IPAC_PROTO_MGCP_OLD); bsc_mgcp_send_mdcx(sccp->bsc, sccp->bsc_endp, mgcp_endp); return MGCP_POLICY_DEFER; } else if (state == MGCP_ENDP_DLCX) { /* we will free the endpoint now and send a DLCX to the BSC */ msgb_free(bsc_msg); bsc_mgcp_dlcx(sccp); /* libmgcp clears the MGCP endpoint for us */ if (mgcp_endp->osmux.state == OSMUX_STATE_ENABLED) osmux_release_cid(mgcp_endp); return MGCP_POLICY_CONT; } else { bsc_write(sccp->bsc, bsc_msg, IPAC_PROTO_MGCP_OLD); return MGCP_POLICY_DEFER; } } /* * We do have a failure, free data downstream.. */ static void free_chan_downstream(struct mgcp_endpoint *endp, struct bsc_endpoint *bsc_endp, struct bsc_connection *bsc) { LOGP(DMGCP, LOGL_ERROR, "No CI, freeing endpoint 0x%x in state %d\n", ENDPOINT_NUMBER(endp), bsc_endp->transaction_state); /* if a CRCX failed... send a DLCX down the stream */ if (bsc_endp->transaction_state == MGCP_ENDP_CRCX) { struct nat_sccp_connection *con; con = bsc_mgcp_find_con(bsc->nat, ENDPOINT_NUMBER(endp)); if (!con) { LOGP(DMGCP, LOGL_ERROR, "No SCCP connection for endp 0x%x\n", ENDPOINT_NUMBER(endp)); } else { if (con->bsc == bsc) { bsc_mgcp_send_dlcx(bsc, con->bsc_endp, con->bsc->next_transaction++); } else { LOGP(DMGCP, LOGL_ERROR, "Endpoint belongs to a different BSC\n"); } } } bsc_mgcp_free_endpoint(bsc->nat, ENDPOINT_NUMBER(endp)); mgcp_release_endp(endp); } static void bsc_mgcp_osmux_confirm(struct mgcp_endpoint *endp, const char *str) { unsigned int osmux_cid; char *res; res = strstr(str, "X-Osmux: "); if (!res) { LOGP(DMGCP, LOGL_INFO, "BSC doesn't want to use Osmux, failing back to RTP\n"); goto err; } if (sscanf(res, "X-Osmux: %u", &osmux_cid) != 1) { LOGP(DMGCP, LOGL_ERROR, "Failed to parse Osmux CID '%s'\n", str); goto err; } if (endp->osmux.cid != osmux_cid) { LOGP(DMGCP, LOGL_ERROR, "BSC sent us wrong CID %u, we expected %u", osmux_cid, endp->osmux.cid); goto err; } LOGP(DMGCP, LOGL_NOTICE, "bsc accepted to use Osmux (cid=%u)\n", osmux_cid); return; err: osmux_release_cid(endp); endp->osmux.state = OSMUX_STATE_DISABLED; } /* * We have received a msg from the BSC. We will see if we know * this transaction and if it belongs to the BSC. Then we will * need to patch the content to point to the local network and we * need to update the I: that was assigned by the BSS. * * Only responses to CRCX and DLCX should arrive here. The DLCX * needs to be handled specially to combine the two statistics. */ void bsc_mgcp_forward(struct bsc_connection *bsc, struct msgb *msg) { struct msgb *output; struct bsc_endpoint *bsc_endp = NULL; struct mgcp_endpoint *endp = NULL; int i, code; char transaction_id[60]; /* Some assumption that our buffer is big enough.. and null terminate */ if (msgb_l2len(msg) > 2000) { LOGP(DMGCP, LOGL_ERROR, "MGCP message too long.\n"); return; } msg->l2h[msgb_l2len(msg)] = '\0'; if (bsc_mgcp_parse_response((const char *) msg->l2h, &code, transaction_id) != 0) { LOGP(DMGCP, LOGL_ERROR, "Failed to parse response code.\n"); return; } for (i = 1; i < bsc->nat->mgcp_cfg->trunk.number_endpoints; ++i) { if (bsc->nat->bsc_endpoints[i].bsc != bsc) continue; /* no one listening? a bug? */ if (!bsc->nat->bsc_endpoints[i].transaction_id) continue; if (strcmp(transaction_id, bsc->nat->bsc_endpoints[i].transaction_id) != 0) continue; endp = &bsc->nat->mgcp_cfg->trunk.endpoints[i]; bsc_endp = &bsc->nat->bsc_endpoints[i]; break; } if (!bsc_endp && strncmp("nat-", transaction_id, 4) == 0) { handle_dlcx_response(bsc, msg, code, transaction_id); return; } if (!bsc_endp) { LOGP(DMGCP, LOGL_ERROR, "Could not find active endpoint: %s for msg: '%s'\n", transaction_id, (const char *) msg->l2h); return; } endp->ci = bsc_mgcp_extract_ci((const char *) msg->l2h); if (endp->ci == CI_UNUSED) { free_chan_downstream(endp, bsc_endp, bsc); return; } if (endp->osmux.state == OSMUX_STATE_ACTIVATING) bsc_mgcp_osmux_confirm(endp, (const char *) msg->l2h); /* If we require osmux and it is disabled.. fail */ if (nat_osmux_only(bsc->nat->mgcp_cfg, bsc->cfg) && endp->osmux.state == OSMUX_STATE_DISABLED) { LOGP(DMGCP, LOGL_ERROR, "Failed to activate osmux endpoint 0x%x\n", ENDPOINT_NUMBER(endp)); free_chan_downstream(endp, bsc_endp, bsc); return; } /* free some stuff */ talloc_free(bsc_endp->transaction_id); bsc_endp->transaction_id = NULL; bsc_endp->transaction_state = 0; /* * rewrite the information. In case the endpoint was deleted * there should be nothing for us to rewrite so putting endp->rtp_port * with the value of 0 should be no problem. */ output = bsc_mgcp_rewrite((char * ) msg->l2h, msgb_l2len(msg), -1, mgcp_net_src_addr(endp), endp->net_end.local_port, -1, &endp->bts_end.codec.payload_type, bsc->nat->sdp_ensure_amr_mode_set); if (!output) { LOGP(DMGCP, LOGL_ERROR, "Failed to rewrite MGCP msg.\n"); return; } mgcp_queue_for_call_agent(bsc->nat, output); } int bsc_mgcp_parse_response(const char *str, int *code, char transaction[60]) { int rc; /* we want to parse two strings */ rc = sscanf(str, "%3d %59s\n", code, transaction) != 2; transaction[59] = '\0'; return rc; } uint32_t bsc_mgcp_extract_ci(const char *str) { unsigned int ci; char *res = strstr(str, "I: "); if (!res) { LOGP(DMGCP, LOGL_ERROR, "No CI in msg '%s'\n", str); return CI_UNUSED; } if (sscanf(res, "I: %u", &ci) != 1) { LOGP(DMGCP, LOGL_ERROR, "Failed to parse CI in msg '%s'\n", str); return CI_UNUSED; } return ci; } /** * Create a new MGCPCommand based on the input and endpoint from a message */ static void patch_mgcp(struct msgb *output, const char *op, const char *tok, int endp, int len, int cr, int osmux_cid) { int slen; int ret; char buf[40]; char osmux_extension[strlen("\nX-Osmux: 255") + 1]; buf[0] = buf[39] = '\0'; ret = sscanf(tok, "%*s %s", buf); if (ret != 1) { LOGP(DMGCP, LOGL_ERROR, "Failed to find Endpoint in: %s\n", tok); return; } if (osmux_cid >= 0) sprintf(osmux_extension, "\nX-Osmux: %u", osmux_cid & 0xff); else osmux_extension[0] = '\0'; slen = sprintf((char *) output->l3h, "%s %s %x@mgw MGCP 1.0%s%s", op, buf, endp, osmux_extension, cr ? "\r\n" : "\n"); output->l3h = msgb_put(output, slen); } /* we need to replace some strings... */ struct msgb *bsc_mgcp_rewrite(char *input, int length, int endpoint, const char *ip, int port, int osmux_cid, int *first_payload_type, int ensure_mode_set) { static const char crcx_str[] = "CRCX "; static const char dlcx_str[] = "DLCX "; static const char mdcx_str[] = "MDCX "; static const char ip_str[] = "c=IN IP4 "; static const char aud_str[] = "m=audio "; static const char fmt_str[] = "a=fmtp:"; char buf[128]; char *running, *token; struct msgb *output; /* keep state to add the a=fmtp line */ int found_fmtp = 0; int payload = -1; int cr = 1; if (length > 4096 - 256) { LOGP(DMGCP, LOGL_ERROR, "Input is too long.\n"); return NULL; } output = msgb_alloc_headroom(4096, 128, "MGCP rewritten"); if (!output) { LOGP(DMGCP, LOGL_ERROR, "Failed to allocate new MGCP msg.\n"); return NULL; } running = input; output->l2h = output->data; output->l3h = output->l2h; for (token = strsep(&running, "\n"); running; token = strsep(&running, "\n")) { int len = strlen(token); cr = len > 0 && token[len - 1] == '\r'; if (strncmp(crcx_str, token, (sizeof crcx_str) - 1) == 0) { patch_mgcp(output, "CRCX", token, endpoint, len, cr, osmux_cid); } else if (strncmp(dlcx_str, token, (sizeof dlcx_str) - 1) == 0) { patch_mgcp(output, "DLCX", token, endpoint, len, cr, -1); } else if (strncmp(mdcx_str, token, (sizeof mdcx_str) - 1) == 0) { patch_mgcp(output, "MDCX", token, endpoint, len, cr, -1); } else if (strncmp(ip_str, token, (sizeof ip_str) - 1) == 0) { output->l3h = msgb_put(output, strlen(ip_str)); memcpy(output->l3h, ip_str, strlen(ip_str)); output->l3h = msgb_put(output, strlen(ip)); memcpy(output->l3h, ip, strlen(ip)); if (cr) { output->l3h = msgb_put(output, 2); output->l3h[0] = '\r'; output->l3h[1] = '\n'; } else { output->l3h = msgb_put(output, 1); output->l3h[0] = '\n'; } } else if (strncmp(aud_str, token, (sizeof aud_str) - 1) == 0) { int offset; if (sscanf(token, "m=audio %*d RTP/AVP %n%d", &offset, &payload) != 1) { LOGP(DMGCP, LOGL_ERROR, "Could not parsed audio line.\n"); msgb_free(output); return NULL; } snprintf(buf, sizeof(buf)-1, "m=audio %d RTP/AVP %s\n", port, &token[offset]); buf[sizeof(buf)-1] = '\0'; output->l3h = msgb_put(output, strlen(buf)); memcpy(output->l3h, buf, strlen(buf)); } else if (strncmp(fmt_str, token, (sizeof fmt_str) - 1) == 0) { found_fmtp = 1; goto copy; } else { copy: output->l3h = msgb_put(output, len + 1); memcpy(output->l3h, token, len); output->l3h[len] = '\n'; } } /* * the above code made sure that we have 128 bytes lefts. So we can * safely append another line. */ if (ensure_mode_set && !found_fmtp && payload != -1) { snprintf(buf, sizeof(buf) - 1, "a=fmtp:%d mode-set=2%s", payload, cr ? "\r\n" : "\n"); buf[sizeof(buf) - 1] = '\0'; output->l3h = msgb_put(output, strlen(buf)); memcpy(output->l3h, buf, strlen(buf)); } if (payload != -1 && first_payload_type) *first_payload_type = payload; return output; } /* * This comes from the MSC and we will now parse it. The caller needs * to free the msgb. */ void bsc_nat_handle_mgcp(struct bsc_nat *nat, struct msgb *msg) { struct msgb *resp; if (!nat->mgcp_ipa) { LOGP(DMGCP, LOGL_ERROR, "MGCP message not allowed on IPA.\n"); return; } if (msgb_l2len(msg) > sizeof(nat->mgcp_msg) - 1) { LOGP(DMGCP, LOGL_ERROR, "MGCP msg too big for handling.\n"); return; } memcpy(nat->mgcp_msg, msg->l2h, msgb_l2len(msg)); nat->mgcp_length = msgb_l2len(msg); nat->mgcp_msg[nat->mgcp_length] = '\0'; /* now handle the message */ resp = mgcp_handle_message(nat->mgcp_cfg, msg); /* we do have a direct answer... e.g. AUEP */ if (resp) mgcp_queue_for_call_agent(nat, resp); return; } static int mgcp_do_read(struct osmo_fd *fd) { struct bsc_nat *nat; struct msgb *msg, *resp; int rc; nat = fd->data; rc = read(fd->fd, nat->mgcp_msg, sizeof(nat->mgcp_msg) - 1); if (rc <= 0) { LOGP(DMGCP, LOGL_ERROR, "Failed to read errno: %d\n", errno); return -1; } nat->mgcp_msg[rc] = '\0'; nat->mgcp_length = rc; msg = msgb_alloc(sizeof(nat->mgcp_msg), "MGCP GW Read"); if (!msg) { LOGP(DMGCP, LOGL_ERROR, "Failed to create buffer.\n"); return -1; } msg->l2h = msgb_put(msg, rc); memcpy(msg->l2h, nat->mgcp_msg, msgb_l2len(msg)); resp = mgcp_handle_message(nat->mgcp_cfg, msg); msgb_free(msg); /* we do have a direct answer... e.g. AUEP */ if (resp) mgcp_queue_for_call_agent(nat, resp); return 0; } static int mgcp_do_write(struct osmo_fd *bfd, struct msgb *msg) { int rc; rc = write(bfd->fd, msg->data, msg->len); if (rc != msg->len) { LOGP(DMGCP, LOGL_ERROR, "Failed to write msg to MGCP CallAgent.\n"); return -1; } return rc; } static int init_mgcp_socket(struct bsc_nat *nat, struct mgcp_config *cfg) { struct sockaddr_in addr; int on; cfg->gw_fd.bfd.fd = socket(AF_INET, SOCK_DGRAM, 0); if (cfg->gw_fd.bfd.fd < 0) { LOGP(DMGCP, LOGL_ERROR, "Failed to create MGCP socket. errno: %d\n", errno); return -1; } on = 1; setsockopt(cfg->gw_fd.bfd.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(cfg->source_port); inet_aton(cfg->source_addr, &addr.sin_addr); if (bind(cfg->gw_fd.bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { LOGP(DMGCP, LOGL_ERROR, "Failed to bind on %s:%d errno: %d\n", cfg->source_addr, cfg->source_port, errno); close(cfg->gw_fd.bfd.fd); cfg->gw_fd.bfd.fd = -1; return -1; } addr.sin_port = htons(2727); inet_aton(cfg->call_agent_addr, &addr.sin_addr); if (connect(cfg->gw_fd.bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { LOGP(DMGCP, LOGL_ERROR, "Failed to connect to: '%s'. errno: %d\n", cfg->call_agent_addr, errno); close(cfg->gw_fd.bfd.fd); cfg->gw_fd.bfd.fd = -1; return -1; } osmo_wqueue_init(&cfg->gw_fd, 10); cfg->gw_fd.bfd.when = BSC_FD_READ; cfg->gw_fd.bfd.data = nat; cfg->gw_fd.read_cb = mgcp_do_read; cfg->gw_fd.write_cb = mgcp_do_write; if (osmo_fd_register(&cfg->gw_fd.bfd) != 0) { LOGP(DMGCP, LOGL_ERROR, "Failed to register MGCP fd.\n"); close(cfg->gw_fd.bfd.fd); cfg->gw_fd.bfd.fd = -1; return -1; } return 0; } int bsc_mgcp_nat_init(struct bsc_nat *nat) { struct mgcp_config *cfg = nat->mgcp_cfg; if (!cfg->call_agent_addr) { LOGP(DMGCP, LOGL_ERROR, "The BSC nat requires the call agent ip to be set.\n"); return -1; } if (cfg->bts_ip) { LOGP(DMGCP, LOGL_ERROR, "Do not set the BTS ip for the nat.\n"); return -1; } /* initialize the MGCP socket */ if (!nat->mgcp_ipa) { int rc = init_mgcp_socket(nat, cfg); if (rc != 0) return rc; } /* some more MGCP config handling */ cfg->data = nat; cfg->policy_cb = bsc_mgcp_policy_cb; cfg->trunk.force_realloc = 1; if (cfg->bts_ip) talloc_free(cfg->bts_ip); cfg->bts_ip = ""; nat->bsc_endpoints = talloc_zero_array(nat, struct bsc_endpoint, cfg->trunk.number_endpoints + 1); if (!nat->bsc_endpoints) { LOGP(DMGCP, LOGL_ERROR, "Failed to allocate nat endpoints\n"); close(cfg->gw_fd.bfd.fd); cfg->gw_fd.bfd.fd = -1; return -1; } if (mgcp_reset_transcoder(cfg) < 0) { LOGP(DMGCP, LOGL_ERROR, "Failed to send packet to the transcoder.\n"); talloc_free(nat->bsc_endpoints); nat->bsc_endpoints = NULL; close(cfg->gw_fd.bfd.fd); cfg->gw_fd.bfd.fd = -1; return -1; } return 0; } void bsc_mgcp_clear_endpoints_for(struct bsc_connection *bsc) { struct rate_ctr *ctr = NULL; int i; if (bsc->cfg) ctr = &bsc->cfg->stats.ctrg->ctr[BCFG_CTR_DROPPED_CALLS]; for (i = 1; i < bsc->nat->mgcp_cfg->trunk.number_endpoints; ++i) { struct bsc_endpoint *bsc_endp = &bsc->nat->bsc_endpoints[i]; if (bsc_endp->bsc != bsc) continue; if (ctr) rate_ctr_inc(ctr); bsc_mgcp_free_endpoint(bsc->nat, i); mgcp_release_endp(&bsc->nat->mgcp_cfg->trunk.endpoints[i]); } } openbsc-0.15.0/openbsc/src/osmo-bsc_nat/bsc_nat.c000066400000000000000000001262631265565154000216320ustar00rootroot00000000000000/* BSC Multiplexer/NAT */ /* * (C) 2010-2013 by Holger Hans Peter Freyther * (C) 2010-2013 by On-Waves * (C) 2009 by Harald Welte * 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../../bscconfig.h" #define SCCP_CLOSE_TIME 20 #define SCCP_CLOSE_TIME_TIMEOUT 19 static const char *config_file = "bsc-nat.cfg"; static struct in_addr local_addr; static struct osmo_fd bsc_listen; static const char *msc_ip = NULL; static struct osmo_timer_list sccp_close; static int daemonize = 0; const char *openbsc_copyright = "Copyright (C) 2010 Holger Hans Peter Freyther and On-Waves\r\n" "License AGPLv3+: GNU AGPL version 3 or later \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 bsc_nat *nat; static void bsc_send_data(struct bsc_connection *bsc, const uint8_t *data, unsigned int length, int); static void msc_send_reset(struct bsc_msc_connection *con); static void bsc_stat_reject(int filter, struct bsc_connection *bsc, int normal); struct bsc_config *bsc_config_num(struct bsc_nat *nat, int num) { struct bsc_config *conf; llist_for_each_entry(conf, &nat->bsc_configs, entry) if (conf->nr == num) return conf; return NULL; } static void queue_for_msc(struct bsc_msc_connection *con, struct msgb *msg) { if (!con) { LOGP(DLINP, LOGL_ERROR, "No MSC Connection assigned. Check your code.\n"); msgb_free(msg); return; } if (osmo_wqueue_enqueue(&con->write_queue, msg) != 0) { LOGP(DLINP, LOGL_ERROR, "Failed to enqueue the write.\n"); msgb_free(msg); } } static void send_reset_ack(struct bsc_connection *bsc) { static const uint8_t gsm_reset_ack[] = { 0x09, 0x00, 0x03, 0x07, 0x0b, 0x04, 0x43, 0x01, 0x00, 0xfe, 0x04, 0x43, 0x5c, 0x00, 0xfe, 0x03, 0x00, 0x01, 0x31, }; bsc_send_data(bsc, gsm_reset_ack, sizeof(gsm_reset_ack), IPAC_PROTO_SCCP); } static void send_ping(struct bsc_connection *bsc) { static const uint8_t id_ping[] = { IPAC_MSGT_PING, }; bsc_send_data(bsc, id_ping, sizeof(id_ping), IPAC_PROTO_IPACCESS); } static void send_pong(struct bsc_connection *bsc) { static const uint8_t id_pong[] = { IPAC_MSGT_PONG, }; bsc_send_data(bsc, id_pong, sizeof(id_pong), IPAC_PROTO_IPACCESS); } static void bsc_pong_timeout(void *_bsc) { struct bsc_connection *bsc = _bsc; LOGP(DNAT, LOGL_ERROR, "BSC Nr: %d PONG timeout.\n", bsc->cfg->nr); bsc_close_connection(bsc); } static void bsc_ping_timeout(void *_bsc) { struct bsc_connection *bsc = _bsc; if (bsc->nat->ping_timeout < 0) return; send_ping(bsc); /* send another ping in 20 seconds */ osmo_timer_schedule(&bsc->ping_timeout, bsc->nat->ping_timeout, 0); /* also start a pong timer */ osmo_timer_schedule(&bsc->pong_timeout, bsc->nat->pong_timeout, 0); } static void start_ping_pong(struct bsc_connection *bsc) { bsc->pong_timeout.data = bsc; bsc->pong_timeout.cb = bsc_pong_timeout; bsc->ping_timeout.data = bsc; bsc->ping_timeout.cb = bsc_ping_timeout; bsc_ping_timeout(bsc); } static void send_id_ack(struct bsc_connection *bsc) { static const uint8_t id_ack[] = { IPAC_MSGT_ID_ACK }; bsc_send_data(bsc, id_ack, sizeof(id_ack), IPAC_PROTO_IPACCESS); } static void send_id_req(struct bsc_nat *nat, struct bsc_connection *bsc) { static const uint8_t s_id_req[] = { IPAC_MSGT_ID_GET, 0x01, IPAC_IDTAG_UNIT, 0x01, IPAC_IDTAG_MACADDR, 0x01, IPAC_IDTAG_LOCATION1, 0x01, IPAC_IDTAG_LOCATION2, 0x01, IPAC_IDTAG_EQUIPVERS, 0x01, IPAC_IDTAG_SWVERSION, 0x01, IPAC_IDTAG_UNITNAME, 0x01, IPAC_IDTAG_SERNR, }; uint8_t *mrand; uint8_t id_req[sizeof(s_id_req) + (2+16)]; uint8_t *buf = &id_req[sizeof(s_id_req)]; /* copy the static data */ memcpy(id_req, s_id_req, sizeof(s_id_req)); /* put the RAND with length, tag, value */ buf = v_put(buf, 0x11); buf = v_put(buf, 0x23); mrand = bsc->last_rand; if (RAND_bytes(mrand, 16) != 1) goto failed_random; memcpy(buf, mrand, 16); buf += 16; bsc_send_data(bsc, id_req, sizeof(id_req), IPAC_PROTO_IPACCESS); return; failed_random: /* the timeout will trigger and close this connection */ LOGP(DNAT, LOGL_ERROR, "Failed to read from urandom.\n"); return; } static struct msgb *nat_create_rlsd(struct nat_sccp_connection *conn) { struct sccp_connection_released *rel; struct msgb *msg; msg = msgb_alloc_headroom(4096, 128, "rlsd"); if (!msg) { LOGP(DNAT, LOGL_ERROR, "Failed to allocate released.\n"); return NULL; } msg->l2h = msgb_put(msg, sizeof(*rel)); rel = (struct sccp_connection_released *) msg->l2h; rel->type = SCCP_MSG_TYPE_RLSD; rel->release_cause = SCCP_RELEASE_CAUSE_SCCP_FAILURE; rel->destination_local_reference = conn->remote_ref; rel->source_local_reference = conn->patched_ref; return msg; } static void nat_send_rlsd_ussd(struct bsc_nat *nat, struct nat_sccp_connection *conn) { struct msgb *msg; if (!nat->ussd_con) return; msg = nat_create_rlsd(conn); if (!msg) return; bsc_do_write(&nat->ussd_con->queue, msg, IPAC_PROTO_SCCP); } static void nat_send_rlsd_msc(struct nat_sccp_connection *conn) { struct msgb *msg; msg = nat_create_rlsd(conn); if (!msg) return; ipa_prepend_header(msg, IPAC_PROTO_SCCP); queue_for_msc(conn->msc_con, msg); } static void nat_send_rlsd_bsc(struct nat_sccp_connection *conn) { struct msgb *msg; struct sccp_connection_released *rel; msg = msgb_alloc_headroom(4096, 128, "rlsd"); if (!msg) { LOGP(DNAT, LOGL_ERROR, "Failed to allocate clear command.\n"); return; } msg->l2h = msgb_put(msg, sizeof(*rel)); rel = (struct sccp_connection_released *) msg->l2h; rel->type = SCCP_MSG_TYPE_RLSD; rel->release_cause = SCCP_RELEASE_CAUSE_SCCP_FAILURE; rel->destination_local_reference = conn->real_ref; rel->source_local_reference = conn->remote_ref; bsc_write(conn->bsc, msg, IPAC_PROTO_SCCP); } static struct msgb *nat_creat_clrc(struct nat_sccp_connection *conn, uint8_t cause) { struct msgb *msg; struct msgb *sccp; msg = gsm0808_create_clear_command(cause); if (!msg) { LOGP(DNAT, LOGL_ERROR, "Failed to allocate clear command.\n"); return NULL; } sccp = sccp_create_dt1(&conn->real_ref, msg->data, msg->len); if (!sccp) { LOGP(DNAT, LOGL_ERROR, "Failed to allocate SCCP msg.\n"); msgb_free(msg); return NULL; } msgb_free(msg); return sccp; } static int nat_send_clrc_bsc(struct nat_sccp_connection *conn) { struct msgb *sccp; sccp = nat_creat_clrc(conn, 0x20); if (!sccp) return -1; return bsc_write(conn->bsc, sccp, IPAC_PROTO_SCCP); } static void nat_send_rlc(struct bsc_msc_connection *msc_con, struct sccp_source_reference *src, struct sccp_source_reference *dst) { struct sccp_connection_release_complete *rlc; struct msgb *msg; msg = msgb_alloc_headroom(4096, 128, "rlc"); if (!msg) { LOGP(DNAT, LOGL_ERROR, "Failed to sccp rlc.\n"); return; } msg->l2h = msgb_put(msg, sizeof(*rlc)); rlc = (struct sccp_connection_release_complete *) msg->l2h; rlc->type = SCCP_MSG_TYPE_RLC; rlc->destination_local_reference = *dst; rlc->source_local_reference = *src; ipa_prepend_header(msg, IPAC_PROTO_SCCP); queue_for_msc(msc_con, msg); } static void send_mgcp_reset(struct bsc_connection *bsc) { static const uint8_t mgcp_reset[] = { "RSIP 1 13@mgw MGCP 1.0\r\n" }; bsc_write_mgcp(bsc, mgcp_reset, sizeof mgcp_reset - 1); } void bsc_nat_send_mgcp_to_msc(struct bsc_nat *nat, struct msgb *msg) { ipa_prepend_header(msg, IPAC_PROTO_MGCP_OLD); queue_for_msc(nat->msc_con, msg); } /* * Below is the handling of messages coming * from the MSC and need to be forwarded to * a real BSC. */ static void initialize_msc_if_needed(struct bsc_msc_connection *msc_con) { if (msc_con->first_contact) return; msc_con->first_contact = 1; msc_send_reset(msc_con); } static void send_id_get_response(struct bsc_msc_connection *msc_con) { struct msgb *msg = bsc_msc_id_get_resp(0, nat->token, NULL, 0); if (!msg) return; ipa_prepend_header(msg, IPAC_PROTO_IPACCESS); queue_for_msc(msc_con, msg); } /* * Currently we are lacking refcounting so we need to copy each message. */ static void bsc_send_data(struct bsc_connection *bsc, const uint8_t *data, unsigned int length, int proto) { struct msgb *msg; if (length > 4096 - 128) { LOGP(DLINP, LOGL_ERROR, "Can not send message of that size.\n"); return; } msg = msgb_alloc_headroom(4096, 128, "to-bsc"); if (!msg) { LOGP(DLINP, LOGL_ERROR, "Failed to allocate memory for BSC msg.\n"); return; } msg->l2h = msgb_put(msg, length); memcpy(msg->data, data, length); bsc_write(bsc, msg, proto); } /* * Update the release statistics */ static void bsc_stat_reject(int filter, struct bsc_connection *bsc, int normal) { if (!bsc->cfg) { LOGP(DNAT, LOGL_ERROR, "BSC is not authenticated."); return; } if (filter >= 0) { LOGP(DNAT, LOGL_ERROR, "Connection was not rejected"); return; } if (filter == -1) rate_ctr_inc(&bsc->cfg->stats.ctrg->ctr[BCFG_CTR_ILL_PACKET]); else if (normal) rate_ctr_inc(&bsc->cfg->stats.ctrg->ctr[BCFG_CTR_REJECTED_MSG]); else rate_ctr_inc(&bsc->cfg->stats.ctrg->ctr[BCFG_CTR_REJECTED_CR]); } /* * Release an established connection. We will have to release it to the BSC * and to the network and we do it the following way. * 1.) Give up on the MSC side * 1.1) Send a RLSD message, it is a bit non standard but should work, we * ignore the RLC... we might complain about it. Other options would * be to send a Release Request, handle the Release Complete.. * 1.2) Mark the data structure to be con_local and wait for 2nd * * 2.) Give up on the BSC side * 2.1) Depending on the con type reject the service, or just close it */ static void bsc_send_con_release(struct bsc_connection *bsc, struct nat_sccp_connection *con, struct bsc_filter_reject_cause *cause) { struct msgb *rlsd; /* 1. release the network */ rlsd = sccp_create_rlsd(&con->patched_ref, &con->remote_ref, SCCP_RELEASE_CAUSE_END_USER_ORIGINATED); if (!rlsd) LOGP(DNAT, LOGL_ERROR, "Failed to create RLSD message.\n"); else { ipa_prepend_header(rlsd, IPAC_PROTO_SCCP); queue_for_msc(con->msc_con, rlsd); } con->con_local = NAT_CON_END_LOCAL; con->msc_con = NULL; /* 2. release the BSC side */ if (con->filter_state.con_type == FLT_CON_TYPE_LU) { struct msgb *payload, *udt; payload = gsm48_create_loc_upd_rej(cause->lu_reject_cause); if (payload) { gsm0808_prepend_dtap_header(payload, 0); udt = sccp_create_dt1(&con->real_ref, payload->data, payload->len); if (udt) bsc_write(bsc, udt, IPAC_PROTO_SCCP); else LOGP(DNAT, LOGL_ERROR, "Failed to create DT1\n"); msgb_free(payload); } else { LOGP(DNAT, LOGL_ERROR, "Failed to allocate LU Reject.\n"); } } nat_send_clrc_bsc(con); rlsd = sccp_create_rlsd(&con->remote_ref, &con->real_ref, SCCP_RELEASE_CAUSE_END_USER_ORIGINATED); if (!rlsd) { LOGP(DNAT, LOGL_ERROR, "Failed to allocate RLSD for the BSC.\n"); sccp_connection_destroy(con); return; } con->filter_state.con_type = FLT_CON_TYPE_LOCAL_REJECT; bsc_write(bsc, rlsd, IPAC_PROTO_SCCP); } static void bsc_send_con_refuse(struct bsc_connection *bsc, struct bsc_nat_parsed *parsed, int con_type, struct bsc_filter_reject_cause *cause) { struct msgb *payload; struct msgb *refuse; if (con_type == FLT_CON_TYPE_LU) payload = gsm48_create_loc_upd_rej(cause->lu_reject_cause); else if (con_type == FLT_CON_TYPE_CM_SERV_REQ || con_type == FLT_CON_TYPE_SSA) payload = gsm48_create_mm_serv_rej(cause->cm_reject_cause); else { LOGP(DNAT, LOGL_ERROR, "Unknown connection type: %d\n", con_type); payload = NULL; } /* * Some BSCs do not handle the payload inside a SCCP CREF msg * so we will need to: * 1.) Allocate a local connection and mark it as local.. * 2.) queue data for downstream.. and the RLC should delete everything */ if (payload) { struct msgb *cc, *udt, *clear, *rlsd; struct nat_sccp_connection *con; con = create_sccp_src_ref(bsc, parsed); if (!con) goto send_refuse; /* declare it local and assign a unique remote_ref */ con->filter_state.con_type = FLT_CON_TYPE_LOCAL_REJECT; con->con_local = NAT_CON_END_LOCAL; con->has_remote_ref = 1; con->remote_ref = con->patched_ref; /* 1. create a confirmation */ cc = sccp_create_cc(&con->remote_ref, &con->real_ref); if (!cc) goto send_refuse; /* 2. create the DT1 */ gsm0808_prepend_dtap_header(payload, 0); udt = sccp_create_dt1(&con->real_ref, payload->data, payload->len); if (!udt) { msgb_free(cc); goto send_refuse; } /* 3. send a Clear Command */ clear = nat_creat_clrc(con, 0x20); if (!clear) { msgb_free(cc); msgb_free(udt); goto send_refuse; } /* 4. send a RLSD */ rlsd = sccp_create_rlsd(&con->remote_ref, &con->real_ref, SCCP_RELEASE_CAUSE_END_USER_ORIGINATED); if (!rlsd) { msgb_free(cc); msgb_free(udt); msgb_free(clear); goto send_refuse; } bsc_write(bsc, cc, IPAC_PROTO_SCCP); bsc_write(bsc, udt, IPAC_PROTO_SCCP); bsc_write(bsc, clear, IPAC_PROTO_SCCP); bsc_write(bsc, rlsd, IPAC_PROTO_SCCP); msgb_free(payload); return; } send_refuse: if (payload) msgb_free(payload); refuse = sccp_create_refuse(parsed->src_local_ref, SCCP_REFUSAL_SCCP_FAILURE, NULL, 0); if (!refuse) { LOGP(DNAT, LOGL_ERROR, "Creating refuse msg failed for SCCP 0x%x on BSC Nr: %d.\n", sccp_src_ref_to_int(parsed->src_local_ref), bsc->cfg->nr); return; } bsc_write(bsc, refuse, IPAC_PROTO_SCCP); } static void bsc_nat_send_paging(struct bsc_connection *bsc, struct msgb *msg) { if (bsc->cfg->forbid_paging) { LOGP(DNAT, LOGL_DEBUG, "Paging forbidden for BTS: %d\n", bsc->cfg->nr); return; } bsc_send_data(bsc, msg->l2h, msgb_l2len(msg), IPAC_PROTO_SCCP); } static void bsc_nat_handle_paging(struct bsc_nat *nat, struct msgb *msg) { struct bsc_connection *bsc; const uint8_t *paging_start; int paging_length, i, ret; ret = bsc_nat_find_paging(msg, &paging_start, &paging_length); if (ret != 0) { LOGP(DNAT, LOGL_ERROR, "Could not parse paging message: %d\n", ret); return; } /* This is quite expensive now */ for (i = 0; i < paging_length; i += 2) { unsigned int _lac = ntohs(*(unsigned int *) &paging_start[i]); unsigned int paged = 0; llist_for_each_entry(bsc, &nat->bsc_connections, list_entry) { if (!bsc->cfg) continue; if (!bsc->authenticated) continue; if (!bsc_config_handles_lac(bsc->cfg, _lac)) continue; bsc_nat_send_paging(bsc, msg); paged += 1; } /* highlight a possible config issue */ if (paged == 0) LOGP(DNAT, LOGL_ERROR, "No BSC for LAC %d/0x%d\n", _lac, _lac); } } /* * Update the auth status. This can be either a CIPHER MODE COMAMND or * a CM Serivce Accept. Maybe also LU Accept or such in the future. */ static void update_con_authorize(struct nat_sccp_connection *con, struct bsc_nat_parsed *parsed, struct msgb *msg) { if (!con) return; if (con->authorized) return; if (parsed->bssap == BSSAP_MSG_BSS_MANAGEMENT && parsed->gsm_type == BSS_MAP_MSG_CIPHER_MODE_CMD) { con->authorized = 1; } else if (parsed->bssap == BSSAP_MSG_DTAP) { uint8_t msg_type, proto; uint32_t len; struct gsm48_hdr *hdr48; hdr48 = bsc_unpack_dtap(parsed, msg, &len); if (!hdr48) return; proto = hdr48->proto_discr & 0x0f; msg_type = hdr48->msg_type & 0xbf; if (proto == GSM48_PDISC_MM && msg_type == GSM48_MT_MM_CM_SERV_ACC) con->authorized = 1; } } static int forward_sccp_to_bts(struct bsc_msc_connection *msc_con, struct msgb *msg) { struct nat_sccp_connection *con = NULL; struct bsc_connection *bsc; struct bsc_nat_parsed *parsed; int proto; /* filter, drop, patch the message? */ parsed = bsc_nat_parse(msg); if (!parsed) { LOGP(DNAT, LOGL_ERROR, "Can not parse msg from BSC.\n"); return -1; } if (bsc_nat_filter_ipa(DIR_BSC, msg, parsed)) goto exit; proto = parsed->ipa_proto; /* Route and modify the SCCP packet */ if (proto == IPAC_PROTO_SCCP) { switch (parsed->sccp_type) { case SCCP_MSG_TYPE_UDT: /* forward UDT messages to every BSC */ goto send_to_all; break; case SCCP_MSG_TYPE_RLSD: case SCCP_MSG_TYPE_CREF: case SCCP_MSG_TYPE_DT1: case SCCP_MSG_TYPE_IT: con = patch_sccp_src_ref_to_bsc(msg, parsed, nat); if (parsed->gsm_type == BSS_MAP_MSG_ASSIGMENT_RQST) { osmo_counter_inc(nat->stats.sccp.calls); if (con) { struct rate_ctr_group *ctrg; ctrg = con->bsc->cfg->stats.ctrg; rate_ctr_inc(&ctrg->ctr[BCFG_CTR_SCCP_CALLS]); if (bsc_mgcp_assign_patch(con, msg) != 0) LOGP(DNAT, LOGL_ERROR, "Failed to assign...\n"); } else LOGP(DNAT, LOGL_ERROR, "Assignment command but no BSC.\n"); } else if (con && con->con_local == NAT_CON_END_USSD && parsed->gsm_type == BSS_MAP_MSG_CLEAR_CMD) { LOGP(DNAT, LOGL_NOTICE, "Clear Command for USSD Connection. Ignoring.\n"); con = NULL; } break; case SCCP_MSG_TYPE_CC: con = patch_sccp_src_ref_to_bsc(msg, parsed, nat); if (!con || update_sccp_src_ref(con, parsed) != 0) goto exit; break; case SCCP_MSG_TYPE_RLC: LOGP(DNAT, LOGL_ERROR, "Unexpected release complete from MSC.\n"); goto exit; break; case SCCP_MSG_TYPE_CR: /* MSC never opens a SCCP connection, fall through */ default: goto exit; } if (!con && parsed->sccp_type == SCCP_MSG_TYPE_RLSD) { LOGP(DNAT, LOGL_NOTICE, "Sending fake RLC on RLSD message to network.\n"); /* Exchange src/dest for the reply */ nat_send_rlc(msc_con, &parsed->original_dest_ref, parsed->src_local_ref); } else if (!con) LOGP(DNAT, LOGL_ERROR, "Unknown connection for msg type: 0x%x from the MSC.\n", parsed->sccp_type); } if (!con) { talloc_free(parsed); return -1; } if (!con->bsc->authenticated) { talloc_free(parsed); LOGP(DNAT, LOGL_ERROR, "Selected BSC not authenticated.\n"); return -1; } update_con_authorize(con, parsed, msg); talloc_free(parsed); bsc_send_data(con->bsc, msg->l2h, msgb_l2len(msg), proto); return 0; send_to_all: /* * Filter Paging from the network. We do not want to send a PAGING * Command to every BSC in our network. We will analys the PAGING * message and then send it to the authenticated messages... */ if (parsed->ipa_proto == IPAC_PROTO_SCCP && parsed->gsm_type == BSS_MAP_MSG_PAGING) { bsc_nat_handle_paging(nat, msg); goto exit; } /* currently send this to every BSC connected */ llist_for_each_entry(bsc, &nat->bsc_connections, list_entry) { if (!bsc->authenticated) continue; bsc_send_data(bsc, msg->l2h, msgb_l2len(msg), parsed->ipa_proto); } exit: talloc_free(parsed); return 0; } static void msc_connection_was_lost(struct bsc_msc_connection *con) { struct bsc_connection *bsc, *tmp; LOGP(DMSC, LOGL_ERROR, "Closing all connections downstream.\n"); llist_for_each_entry_safe(bsc, tmp, &nat->bsc_connections, list_entry) bsc_close_connection(bsc); bsc_mgcp_free_endpoints(nat); bsc_msc_schedule_connect(con); } static void msc_connection_connected(struct bsc_msc_connection *con) { osmo_counter_inc(nat->stats.msc.reconn); } static void msc_send_reset(struct bsc_msc_connection *msc_con) { static const uint8_t reset[] = { 0x00, 0x12, 0xfd, 0x09, 0x00, 0x03, 0x05, 0x07, 0x02, 0x42, 0xfe, 0x02, 0x42, 0xfe, 0x06, 0x00, 0x04, 0x30, 0x04, 0x01, 0x20 }; struct msgb *msg; msg = msgb_alloc_headroom(4096, 128, "08.08 reset"); if (!msg) { LOGP(DMSC, LOGL_ERROR, "Failed to allocate reset msg.\n"); return; } msg->l2h = msgb_put(msg, sizeof(reset)); memcpy(msg->l2h, reset, msgb_l2len(msg)); queue_for_msc(msc_con, msg); LOGP(DMSC, LOGL_NOTICE, "Scheduled GSM0808 reset msg for the MSC.\n"); } static int ipaccess_msc_read_cb(struct osmo_fd *bfd) { struct bsc_msc_connection *msc_con; struct msgb *msg = NULL; struct ipaccess_head *hh; int ret; msc_con = (struct bsc_msc_connection *) bfd->data; ret = ipa_msg_recv_buffered(bfd->fd, &msg, &msc_con->pending_msg); if (ret <= 0) { if (ret == -EAGAIN) return 0; if (ret == 0) LOGP(DNAT, LOGL_FATAL, "The connection the MSC(%s) was lost, exiting\n", msc_con->name); else LOGP(DNAT, LOGL_ERROR, "Failed to parse ip access message on %s: %d\n", msc_con->name, ret); bsc_msc_lost(msc_con); return -1; } LOGP(DNAT, LOGL_DEBUG, "MSG from MSC(%s): %s proto: %d\n", msc_con->name, osmo_hexdump(msg->data, msg->len), msg->l2h[0]); /* handle base message handling */ hh = (struct ipaccess_head *) msg->data; /* initialize the networking. This includes sending a GSM08.08 message */ if (hh->proto == IPAC_PROTO_IPACCESS) { ipa_ccm_rcvmsg_base(msg, bfd); if (msg->l2h[0] == IPAC_MSGT_ID_ACK) initialize_msc_if_needed(msc_con); else if (msg->l2h[0] == IPAC_MSGT_ID_GET) send_id_get_response(msc_con); } else if (hh->proto == IPAC_PROTO_SCCP) { forward_sccp_to_bts(msc_con, msg); } else if (hh->proto == IPAC_PROTO_MGCP_OLD) { bsc_nat_handle_mgcp(nat, msg); } msgb_free(msg); return 0; } static int ipaccess_msc_write_cb(struct osmo_fd *bfd, struct msgb *msg) { int rc; rc = write(bfd->fd, msg->data, msg->len); if (rc != msg->len) { LOGP(DNAT, LOGL_ERROR, "Failed to write MSG to MSC.\n"); return -1; } return rc; } /* * Below is the handling of messages coming * from the BSC and need to be forwarded to * a real BSC. */ /* * Remove the connection from the connections list, * remove it from the patching of SCCP header lists * as well. Maybe in the future even close connection.. */ void bsc_close_connection(struct bsc_connection *connection) { struct nat_sccp_connection *sccp_patch, *tmp; struct bsc_cmd_list *cmd_entry, *cmd_tmp; struct rate_ctr *ctr = NULL; /* stop the timeout timer */ osmo_timer_del(&connection->id_timeout); osmo_timer_del(&connection->ping_timeout); osmo_timer_del(&connection->pong_timeout); if (connection->cfg) ctr = &connection->cfg->stats.ctrg->ctr[BCFG_CTR_DROPPED_SCCP]; /* remove all SCCP connections */ llist_for_each_entry_safe(sccp_patch, tmp, &nat->sccp_connections, list_entry) { if (sccp_patch->bsc != connection) continue; if (ctr) rate_ctr_inc(ctr); if (sccp_patch->has_remote_ref) { if (sccp_patch->con_local == NAT_CON_END_MSC) nat_send_rlsd_msc(sccp_patch); else if (sccp_patch->con_local == NAT_CON_END_USSD) nat_send_rlsd_ussd(nat, sccp_patch); } sccp_connection_destroy(sccp_patch); } /* Reply to all outstanding commands */ llist_for_each_entry_safe(cmd_entry, cmd_tmp, &connection->cmd_pending, list_entry) { cmd_entry->cmd->type = CTRL_TYPE_ERROR; cmd_entry->cmd->reply = "BSC closed the connection"; ctrl_cmd_send(&cmd_entry->ccon->write_queue, cmd_entry->cmd); bsc_nat_ctrl_del_pending(cmd_entry); } /* close endpoints allocated by this BSC */ bsc_mgcp_clear_endpoints_for(connection); osmo_fd_unregister(&connection->write_queue.bfd); close(connection->write_queue.bfd.fd); osmo_wqueue_clear(&connection->write_queue); llist_del(&connection->list_entry); if (connection->pending_msg) { LOGP(DNAT, LOGL_ERROR, "Dropping partial message on connection %d.\n", connection->cfg ? connection->cfg->nr : -1); msgb_free(connection->pending_msg); connection->pending_msg = NULL; } talloc_free(connection); } static void bsc_maybe_close(struct bsc_connection *bsc) { struct nat_sccp_connection *sccp; if (!bsc->nat->blocked) return; /* are there any connections left */ llist_for_each_entry(sccp, &bsc->nat->sccp_connections, list_entry) if (sccp->bsc == bsc) return; /* nothing left, close the BSC */ LOGP(DNAT, LOGL_NOTICE, "Cleaning up BSC %d in blocking mode.\n", bsc->cfg ? bsc->cfg->nr : -1); bsc_close_connection(bsc); } static void ipaccess_close_bsc(void *data) { struct sockaddr_in sock; socklen_t len = sizeof(sock); struct bsc_connection *conn = data; getpeername(conn->write_queue.bfd.fd, (struct sockaddr *) &sock, &len); LOGP(DNAT, LOGL_ERROR, "BSC on %s didn't respond to identity request. Closing.\n", inet_ntoa(sock.sin_addr)); bsc_close_connection(conn); } /* Wishful thinking to generate a constant time compare */ static int constant_time_cmp(const uint8_t *exp, const uint8_t *rel, const int count) { int x = 0, i; for (i = 0; i < count; ++i) x |= exp[i] ^ rel[i]; return x != 0; } static int verify_key(struct bsc_connection *conn, struct bsc_config *conf, const uint8_t *key, const int keylen) { struct osmo_auth_vector vec; struct osmo_sub_auth_data auth = { .type = OSMO_AUTH_TYPE_GSM, .algo = OSMO_AUTH_ALG_MILENAGE, }; /* expect a specific keylen */ if (keylen != 8) { LOGP(DNAT, LOGL_ERROR, "Key length is wrong: %d for bsc nr %d\n", keylen, conf->nr); return 0; } memcpy(auth.u.umts.opc, conf->key, 16); memcpy(auth.u.umts.k, conf->key, 16); memset(auth.u.umts.amf, 0, 2); auth.u.umts.sqn = 0; memset(&vec, 0, sizeof(vec)); osmo_auth_gen_vec(&vec, &auth, conn->last_rand); if (vec.res_len != 8) { LOGP(DNAT, LOGL_ERROR, "Res length is wrong: %d for bsc nr %d\n", vec.res_len, conf->nr); return 0; } return constant_time_cmp(vec.res, key, 8) == 0; } static void ipaccess_auth_bsc(struct tlv_parsed *tvp, struct bsc_connection *bsc) { struct bsc_config *conf; const char *token = (const char *) TLVP_VAL(tvp, IPAC_IDTAG_UNITNAME); int len = TLVP_LEN(tvp, IPAC_IDTAG_UNITNAME); const uint8_t *xres = TLVP_VAL(tvp, 0x24); const int xlen = TLVP_LEN(tvp, 0x24); if (bsc->cfg) { LOGP(DNAT, LOGL_ERROR, "Reauth on fd %d bsc nr %d\n", bsc->write_queue.bfd.fd, bsc->cfg->nr); return; } if (len <= 0) { LOGP(DNAT, LOGL_ERROR, "Token with length zero on fd: %d\n", bsc->write_queue.bfd.fd); return; } if (token[len - 1] != '\0') { LOGP(DNAT, LOGL_ERROR, "Token not null terminated on fd: %d\n", bsc->write_queue.bfd.fd); return; } /* * New systems have fixed the structure of the message but * we need to support old ones too. */ if (len >= 2 && token[len - 2] == '\0') len -= 1; conf = bsc_config_by_token(bsc->nat, token, len); if (!conf) { LOGP(DNAT, LOGL_ERROR, "No bsc found for token '%s' len %d on fd: %d.\n", token, bsc->write_queue.bfd.fd, len); bsc_close_connection(bsc); return; } /* We have set a key and expect it to be present */ if (conf->key_present && !verify_key(bsc, conf, xres, xlen - 1)) { LOGP(DNAT, LOGL_ERROR, "Wrong key for bsc nr %d fd: %d.\n", conf->nr, bsc->write_queue.bfd.fd); bsc_close_connection(bsc); return; } rate_ctr_inc(&conf->stats.ctrg->ctr[BCFG_CTR_NET_RECONN]); bsc->authenticated = 1; bsc->cfg = conf; osmo_timer_del(&bsc->id_timeout); LOGP(DNAT, LOGL_NOTICE, "Authenticated bsc nr: %d on fd %d\n", conf->nr, bsc->write_queue.bfd.fd); start_ping_pong(bsc); } static void handle_con_stats(struct nat_sccp_connection *con) { struct rate_ctr_group *ctrg; int id = bsc_conn_type_to_ctr(con); if (id == -1) return; if (!con->bsc || !con->bsc->cfg) return; ctrg = con->bsc->cfg->stats.ctrg; rate_ctr_inc(&ctrg->ctr[id]); } static int forward_sccp_to_msc(struct bsc_connection *bsc, struct msgb *msg) { int con_filter = 0; char *imsi = NULL; struct bsc_msc_connection *con_msc = NULL; struct bsc_connection *con_bsc = NULL; int con_type; struct bsc_nat_parsed *parsed; struct bsc_filter_reject_cause cause; /* Parse and filter messages */ parsed = bsc_nat_parse(msg); if (!parsed) { LOGP(DNAT, LOGL_ERROR, "Can not parse msg from BSC.\n"); msgb_free(msg); return -1; } if (bsc_nat_filter_ipa(DIR_MSC, msg, parsed)) goto exit; /* * check authentication after filtering to not reject auth * responses coming from the BSC. We have to make sure that * nothing from the exit path will forward things to the MSC */ if (!bsc->authenticated) { LOGP(DNAT, LOGL_ERROR, "BSC is not authenticated.\n"); msgb_free(msg); return -1; } /* modify the SCCP entries */ if (parsed->ipa_proto == IPAC_PROTO_SCCP) { int filter; struct nat_sccp_connection *con; switch (parsed->sccp_type) { case SCCP_MSG_TYPE_CR: memset(&cause, 0, sizeof(cause)); filter = bsc_nat_filter_sccp_cr(bsc, msg, parsed, &con_type, &imsi, &cause); if (filter < 0) { if (imsi) bsc_nat_inform_reject(bsc, imsi); bsc_stat_reject(filter, bsc, 0); goto exit3; } if (!create_sccp_src_ref(bsc, parsed)) goto exit2; con = patch_sccp_src_ref_to_msc(msg, parsed, bsc); con->msc_con = bsc->nat->msc_con; con_msc = con->msc_con; con->filter_state.con_type = con_type; con->filter_state.imsi_checked = filter; bsc_nat_extract_lac(bsc, con, parsed, msg); if (imsi) con->filter_state.imsi = talloc_steal(con, imsi); imsi = NULL; con_bsc = con->bsc; handle_con_stats(con); break; case SCCP_MSG_TYPE_RLSD: case SCCP_MSG_TYPE_CREF: case SCCP_MSG_TYPE_DT1: case SCCP_MSG_TYPE_CC: case SCCP_MSG_TYPE_IT: con = patch_sccp_src_ref_to_msc(msg, parsed, bsc); if (con) { /* only filter non local connections */ if (!con->con_local) { memset(&cause, 0, sizeof(cause)); filter = bsc_nat_filter_dt(bsc, msg, con, parsed, &cause); if (filter < 0) { if (con->filter_state.imsi) bsc_nat_inform_reject(bsc, con->filter_state.imsi); bsc_stat_reject(filter, bsc, 1); bsc_send_con_release(bsc, con, &cause); con = NULL; goto exit2; } /* hand data to a side channel */ if (bsc_ussd_check(con, parsed, msg) == 1) con->con_local = NAT_CON_END_USSD; /* * Optionally rewrite setup message. This can * replace the msg and the parsed structure becomes * invalid. */ msg = bsc_nat_rewrite_msg(bsc->nat, msg, parsed, con->filter_state.imsi); talloc_free(parsed); parsed = NULL; } else if (con->con_local == NAT_CON_END_USSD) { bsc_ussd_check(con, parsed, msg); } con_bsc = con->bsc; con_msc = con->msc_con; con_filter = con->con_local; } break; case SCCP_MSG_TYPE_RLC: con = patch_sccp_src_ref_to_msc(msg, parsed, bsc); if (con) { con_bsc = con->bsc; con_msc = con->msc_con; con_filter = con->con_local; } remove_sccp_src_ref(bsc, msg, parsed); bsc_maybe_close(bsc); break; case SCCP_MSG_TYPE_UDT: /* simply forward everything */ con = NULL; break; default: LOGP(DNAT, LOGL_ERROR, "Not forwarding to msc sccp type: 0x%x\n", parsed->sccp_type); con = NULL; goto exit2; break; } } else if (parsed->ipa_proto == IPAC_PROTO_MGCP_OLD) { bsc_mgcp_forward(bsc, msg); goto exit2; } else { LOGP(DNAT, LOGL_ERROR, "Not forwarding unknown stream id: 0x%x\n", parsed->ipa_proto); goto exit2; } if (con_msc && con_bsc != bsc) { LOGP(DNAT, LOGL_ERROR, "The connection belongs to a different BTS: input: %d con: %d\n", bsc->cfg->nr, con_bsc->cfg->nr); goto exit2; } /* do not forward messages to the MSC */ if (con_filter) goto exit2; if (!con_msc) { LOGP(DNAT, LOGL_ERROR, "Not forwarding data bsc_nr: %d ipa: %d type: 0x%x\n", bsc->cfg->nr, parsed ? parsed->ipa_proto : -1, parsed ? parsed->sccp_type : -1); goto exit2; } /* send the non-filtered but maybe modified msg */ queue_for_msc(con_msc, msg); if (parsed) talloc_free(parsed); return 0; exit: /* if we filter out the reset send an ack to the BSC */ if (parsed->bssap == 0 && parsed->gsm_type == BSS_MAP_MSG_RESET) { send_reset_ack(bsc); send_reset_ack(bsc); } else if (parsed->ipa_proto == IPAC_PROTO_IPACCESS) { /* do we know who is handling this? */ if (msg->l2h[0] == IPAC_MSGT_ID_RESP && msgb_l2len(msg) > 2) { struct tlv_parsed tvp; int ret; ret = ipa_ccm_idtag_parse_off(&tvp, (unsigned char *) msg->l2h + 2, msgb_l2len(msg) - 2, 0); if (ret < 0) { LOGP(DNAT, LOGL_ERROR, "ignoring IPA response " "message with malformed TLVs\n"); return ret; } if (TLVP_PRESENT(&tvp, IPAC_IDTAG_UNITNAME)) ipaccess_auth_bsc(&tvp, bsc); } goto exit2; } exit2: if (imsi) talloc_free(imsi); talloc_free(parsed); msgb_free(msg); return -1; exit3: /* send a SCCP Connection Refused */ if (imsi) talloc_free(imsi); bsc_send_con_refuse(bsc, parsed, con_type, &cause); talloc_free(parsed); msgb_free(msg); return -1; } static int ipaccess_bsc_read_cb(struct osmo_fd *bfd) { struct bsc_connection *bsc = bfd->data; struct msgb *msg = NULL; struct ipaccess_head *hh; struct ipaccess_head_ext *hh_ext; int ret; ret = ipa_msg_recv_buffered(bfd->fd, &msg, &bsc->pending_msg); if (ret <= 0) { if (ret == -EAGAIN) return 0; if (ret == 0) LOGP(DNAT, LOGL_ERROR, "The connection to the BSC Nr: %d was lost. Cleaning it\n", bsc->cfg ? bsc->cfg->nr : -1); else LOGP(DNAT, LOGL_ERROR, "Stream error on BSC Nr: %d. Failed to parse ip access message: %d\n", bsc->cfg ? bsc->cfg->nr : -1, ret); bsc_close_connection(bsc); return -1; } LOGP(DNAT, LOGL_DEBUG, "MSG from BSC: %s proto: %d\n", osmo_hexdump(msg->data, msg->len), msg->l2h[0]); /* Handle messages from the BSC */ hh = (struct ipaccess_head *) msg->data; /* stop the pong timeout */ if (hh->proto == IPAC_PROTO_IPACCESS) { if (msg->l2h[0] == IPAC_MSGT_PONG) { osmo_timer_del(&bsc->pong_timeout); msgb_free(msg); return 0; } else if (msg->l2h[0] == IPAC_MSGT_PING) { send_pong(bsc); msgb_free(msg); return 0; } /* Message contains the ipaccess_head_ext header, investigate further */ } else if (hh->proto == IPAC_PROTO_OSMO && msg->len > sizeof(*hh) + sizeof(*hh_ext)) { hh_ext = (struct ipaccess_head_ext *) hh->data; /* l2h is where the actual command data is expected */ msg->l2h = hh_ext->data; if (hh_ext->proto == IPAC_PROTO_EXT_CTRL) return bsc_nat_handle_ctrlif_msg(bsc, msg); } /* FIXME: Currently no PONG is sent to the BSC */ /* FIXME: Currently no ID ACK is sent to the BSC */ forward_sccp_to_msc(bsc, msg); return 0; } static int ipaccess_listen_bsc_cb(struct osmo_fd *bfd, unsigned int what) { struct bsc_connection *bsc; int fd, rc, on; struct sockaddr_in sa; socklen_t sa_len = sizeof(sa); if (!(what & BSC_FD_READ)) return 0; fd = accept(bfd->fd, (struct sockaddr *) &sa, &sa_len); if (fd < 0) { perror("accept"); return fd; } /* count the reconnect */ osmo_counter_inc(nat->stats.bsc.reconn); /* * if we are not connected to a msc... just close the socket */ if (!bsc_nat_msc_is_connected(nat)) { LOGP(DNAT, LOGL_NOTICE, "Disconnecting BSC due lack of MSC connection.\n"); close(fd); return 0; } if (nat->blocked) { LOGP(DNAT, LOGL_NOTICE, "Disconnecting BSC due NAT being blocked.\n"); close(fd); return 0; } on = 1; rc = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)); if (rc != 0) LOGP(DNAT, LOGL_ERROR, "Failed to set TCP_NODELAY: %s\n", strerror(errno)); rc = setsockopt(fd, IPPROTO_IP, IP_TOS, &nat->bsc_ip_dscp, sizeof(nat->bsc_ip_dscp)); if (rc != 0) LOGP(DNAT, LOGL_ERROR, "Failed to set IP_TOS: %s\n", strerror(errno)); /* todo... do something with the connection */ /* todo... use GNUtls to see if we want to trust this as a BTS */ /* * */ bsc = bsc_connection_alloc(nat); if (!bsc) { LOGP(DNAT, LOGL_ERROR, "Failed to allocate BSC struct.\n"); close(fd); return -1; } bsc->write_queue.bfd.data = bsc; bsc->write_queue.bfd.fd = fd; bsc->write_queue.read_cb = ipaccess_bsc_read_cb; bsc->write_queue.write_cb = bsc_write_cb; bsc->write_queue.bfd.when = BSC_FD_READ; if (osmo_fd_register(&bsc->write_queue.bfd) < 0) { LOGP(DNAT, LOGL_ERROR, "Failed to register BSC fd.\n"); close(fd); talloc_free(bsc); return -2; } LOGP(DNAT, LOGL_NOTICE, "BSC connection on %d with IP: %s\n", fd, inet_ntoa(sa.sin_addr)); llist_add(&bsc->list_entry, &nat->bsc_connections); bsc->last_id = 0; send_id_ack(bsc); send_id_req(nat, bsc); send_mgcp_reset(bsc); /* * start the hangup timer */ bsc->id_timeout.data = bsc; bsc->id_timeout.cb = ipaccess_close_bsc; osmo_timer_schedule(&bsc->id_timeout, nat->auth_timeout, 0); return 0; } static void print_usage() { printf("Usage: bsc_nat\n"); } static void print_help() { printf(" Some useful help...\n"); printf(" -h --help this text\n"); printf(" -d option --debug=DRLL:DCC:DMM:DRR:DRSL:DNM enable debugging\n"); printf(" -D --daemonize Fork the process into a background daemon\n"); printf(" -s --disable-color\n"); printf(" -c --config-file filename The config file to use.\n"); printf(" -m --msc=IP. The address of the MSC.\n"); printf(" -l --local=IP. The local address of this BSC.\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'}, {"msc", 1, 0, 'm'}, {"local", 1, 0, 'l'}, {0, 0, 0, 0} }; c = getopt_long(argc, argv, "hd:sTPc:m:l:D", 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 'm': msc_ip = optarg; break; case 'l': inet_aton(optarg, &local_addr); break; default: /* ignore */ break; } } } static void signal_handler(int signal) { switch (signal) { 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_full(tall_bsc_ctx, stderr); break; default: break; } } static void sccp_close_unconfirmed(void *_data) { int destroyed = 0; struct bsc_connection *bsc, *bsc_tmp; struct nat_sccp_connection *conn, *tmp1; struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); llist_for_each_entry_safe(conn, tmp1, &nat->sccp_connections, list_entry) { if (conn->has_remote_ref) continue; int diff = (now.tv_sec - conn->creation_time.tv_sec) / 60; if (diff < SCCP_CLOSE_TIME_TIMEOUT) continue; LOGP(DNAT, LOGL_ERROR, "SCCP connection 0x%x/0x%x was never confirmed on bsc nr. %d\n", sccp_src_ref_to_int(&conn->real_ref), sccp_src_ref_to_int(&conn->patched_ref), conn->bsc->cfg->nr); sccp_connection_destroy(conn); destroyed = 1; } if (!destroyed) goto out; /* now close out any BSC */ llist_for_each_entry_safe(bsc, bsc_tmp, &nat->bsc_connections, list_entry) bsc_maybe_close(bsc); out: osmo_timer_schedule(&sccp_close, SCCP_CLOSE_TIME, 0); } extern void *tall_msgb_ctx; extern void *tall_ctr_ctx; static void talloc_init_ctx() { tall_bsc_ctx = talloc_named_const(NULL, 0, "nat"); tall_msgb_ctx = talloc_named_const(tall_bsc_ctx, 0, "msgb"); tall_ctr_ctx = talloc_named_const(tall_bsc_ctx, 0, "counter"); } extern int bsc_vty_go_parent(struct vty *vty); static struct vty_app_info vty_info = { .name = "OsmoBSCNAT", .version = PACKAGE_VERSION, .go_parent_cb = bsc_vty_go_parent, .is_config_node = bsc_vty_is_config_node, }; int main(int argc, char **argv) { int rc; talloc_init_ctx(); osmo_init_logging(&log_info); nat = bsc_nat_alloc(); if (!nat) { fprintf(stderr, "Failed to allocate the BSC nat.\n"); return -4; } nat->mgcp_cfg = mgcp_config_alloc(); if (!nat->mgcp_cfg) { fprintf(stderr, "Failed to allocate MGCP cfg.\n"); return -5; } /* We need to add mode-set for amr codecs */ nat->sdp_ensure_amr_mode_set = 1; vty_info.copyright = openbsc_copyright; vty_init(&vty_info); logging_vty_add_cmds(&log_info); bsc_nat_vty_init(nat); /* parse options */ local_addr.s_addr = INADDR_ANY; handle_options(argc, argv); rate_ctr_init(tall_bsc_ctx); /* init vty and parse */ telnet_init(tall_bsc_ctx, NULL, OSMO_VTY_PORT_BSC_NAT); if (mgcp_parse_config(config_file, nat->mgcp_cfg, MGCP_BSC_NAT) < 0) { fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file); return -3; } /* over rule the VTY config */ if (msc_ip) bsc_nat_set_msc_ip(nat, msc_ip); /* seed the PRNG */ srand(time(NULL)); /* * Setup the MGCP code.. */ if (bsc_mgcp_nat_init(nat) != 0) return -4; /* connect to the MSC */ nat->msc_con = bsc_msc_create(nat, &nat->dests); if (!nat->msc_con) { fprintf(stderr, "Creating a bsc_msc_connection failed.\n"); exit(1); } nat->ctrl = bsc_nat_controlif_setup(nat, 4250); if (!nat->ctrl) { fprintf(stderr, "Creating the control interface failed.\n"); exit(1); } nat->msc_con->name = "main MSC"; nat->msc_con->connection_loss = msc_connection_was_lost; nat->msc_con->connected = msc_connection_connected; nat->msc_con->write_queue.read_cb = ipaccess_msc_read_cb; nat->msc_con->write_queue.write_cb = ipaccess_msc_write_cb;; nat->msc_con->write_queue.bfd.data = nat->msc_con; bsc_msc_connect(nat->msc_con); /* wait for the BSC */ rc = make_sock(&bsc_listen, IPPROTO_TCP, ntohl(local_addr.s_addr), 5000, 0, ipaccess_listen_bsc_cb, nat); if (rc != 0) { fprintf(stderr, "Failed to listen for BSC.\n"); exit(1); } rc = bsc_ussd_init(nat); if (rc != 0) { LOGP(DNAT, LOGL_ERROR, "Failed to bind the USSD socket.\n"); exit(1); } signal(SIGABRT, &signal_handler); signal(SIGUSR1, &signal_handler); osmo_init_ignore_signals(); if (daemonize) { rc = osmo_daemonize(); if (rc < 0) { perror("Error during daemonize"); exit(1); } } /* recycle timer */ sccp_set_log_area(DSCCP); sccp_close.cb = sccp_close_unconfirmed; sccp_close.data = NULL; osmo_timer_schedule(&sccp_close, SCCP_CLOSE_TIME, 0); while (1) { osmo_select_main(0); } return 0; } /* Close all connections handed out to the USSD module */ int bsc_ussd_close_connections(struct bsc_nat *nat) { struct nat_sccp_connection *con; llist_for_each_entry(con, &nat->sccp_connections, list_entry) { if (con->con_local != NAT_CON_END_USSD) continue; if (!con->bsc) continue; nat_send_clrc_bsc(con); nat_send_rlsd_bsc(con); } return 0; } openbsc-0.15.0/openbsc/src/osmo-bsc_nat/bsc_nat_ctrl.c000066400000000000000000000305421265565154000226500ustar00rootroot00000000000000/* * (C) 2011-2012 by Holger Hans Peter Freyther * (C) 2011-2012 by On-Waves * (C) 2011 by Daniel Willmann * 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define NAT_MAX_CTRL_ID 65535 static struct bsc_nat *g_nat; static int bsc_id_unused(int id, struct bsc_connection *bsc) { struct bsc_cmd_list *pending; llist_for_each_entry(pending, &bsc->cmd_pending, list_entry) { if (pending->nat_id == id) return 0; } return 1; } static int get_next_free_bsc_id(struct bsc_connection *bsc) { int new_id, overflow = 0; new_id = bsc->last_id; do { new_id++; if (new_id == NAT_MAX_CTRL_ID) { new_id = 1; overflow++; } if (bsc_id_unused(new_id, bsc)) { bsc->last_id = new_id; return new_id; } } while (overflow != 2); return -1; } void bsc_nat_ctrl_del_pending(struct bsc_cmd_list *pending) { llist_del(&pending->list_entry); osmo_timer_del(&pending->timeout); talloc_free(pending->cmd); talloc_free(pending); } static struct bsc_cmd_list *bsc_get_pending(struct bsc_connection *bsc, char *id_str) { struct bsc_cmd_list *cmd_entry; int id = atoi(id_str); if (id == 0) return NULL; llist_for_each_entry(cmd_entry, &bsc->cmd_pending, list_entry) { if (cmd_entry->nat_id == id) { return cmd_entry; } } return NULL; } int bsc_nat_handle_ctrlif_msg(struct bsc_connection *bsc, struct msgb *msg) { struct ctrl_cmd *cmd; struct bsc_cmd_list *pending; char *var, *id; cmd = ctrl_cmd_parse(bsc, msg); msgb_free(msg); if (!cmd) { cmd = talloc_zero(bsc, struct ctrl_cmd); if (!cmd) { LOGP(DNAT, LOGL_ERROR, "OOM!\n"); return -ENOMEM; } cmd->type = CTRL_TYPE_ERROR; cmd->id = "err"; cmd->reply = "Failed to parse command."; goto err; } if (bsc->cfg && !llist_empty(&bsc->cfg->lac_list)) { if (cmd->variable) { var = talloc_asprintf(cmd, "net.0.bsc.%i.%s", bsc->cfg->nr, cmd->variable); if (!var) { cmd->type = CTRL_TYPE_ERROR; cmd->reply = "OOM"; goto err; } talloc_free(cmd->variable); cmd->variable = var; } /* We have to handle TRAPs before matching pending */ if (cmd->type == CTRL_TYPE_TRAP) { ctrl_cmd_send_to_all(bsc->nat->ctrl, cmd); talloc_free(cmd); return 0; } /* Find the pending command */ pending = bsc_get_pending(bsc, cmd->id); if (pending) { id = talloc_strdup(cmd, pending->cmd->id); if (!id) { cmd->type = CTRL_TYPE_ERROR; cmd->reply = "OOM"; goto err; } cmd->id = id; ctrl_cmd_send(&pending->ccon->write_queue, cmd); bsc_nat_ctrl_del_pending(pending); } else { /* We need to handle TRAPS here */ if ((cmd->type != CTRL_TYPE_ERROR) && (cmd->type != CTRL_TYPE_TRAP)) { LOGP(DNAT, LOGL_NOTICE, "Got control message " "from BSC without pending entry\n"); cmd->type = CTRL_TYPE_ERROR; cmd->reply = "No request outstanding"; goto err; } } } talloc_free(cmd); return 0; err: ctrl_cmd_send(&bsc->write_queue, cmd); talloc_free(cmd); return 0; } static void pending_timeout_cb(void *data) { struct bsc_cmd_list *pending = data; LOGP(DNAT, LOGL_ERROR, "Command timed out\n"); pending->cmd->type = CTRL_TYPE_ERROR; pending->cmd->reply = "Command timed out"; ctrl_cmd_send(&pending->ccon->write_queue, pending->cmd); bsc_nat_ctrl_del_pending(pending); } static void ctrl_conn_closed_cb(struct ctrl_connection *connection) { struct bsc_connection *bsc; struct bsc_cmd_list *pending, *tmp; llist_for_each_entry(bsc, &g_nat->bsc_connections, list_entry) { llist_for_each_entry_safe(pending, tmp, &bsc->cmd_pending, list_entry) { if (pending->ccon == connection) bsc_nat_ctrl_del_pending(pending); } } } static int extract_bsc_nr_variable(char *variable, unsigned int *nr, char **bsc_variable) { char *nr_str, *tmp, *saveptr = NULL; tmp = strtok_r(variable, ".", &saveptr); tmp = strtok_r(NULL, ".", &saveptr); tmp = strtok_r(NULL, ".", &saveptr); nr_str = strtok_r(NULL, ".", &saveptr); if (!nr_str) return 0; *nr = atoi(nr_str); tmp = strtok_r(NULL, "\0", &saveptr); if (!tmp) return 0; *bsc_variable = tmp; return 1; } static int forward_to_bsc(struct ctrl_cmd *cmd) { int ret = CTRL_CMD_HANDLED; struct ctrl_cmd *bsc_cmd = NULL; struct bsc_connection *bsc; struct bsc_cmd_list *pending; unsigned int nr; char *bsc_variable; /* Skip over the beginning (bsc.) */ if (!extract_bsc_nr_variable(cmd->variable, &nr, &bsc_variable)) { cmd->reply = "command incomplete"; goto err; } llist_for_each_entry(bsc, &g_nat->bsc_connections, list_entry) { if (!bsc->cfg) continue; if (!bsc->authenticated) continue; if (bsc->cfg->nr == nr) { /* Add pending command to list */ pending = talloc_zero(bsc, struct bsc_cmd_list); if (!pending) { cmd->reply = "OOM"; goto err; } pending->nat_id = get_next_free_bsc_id(bsc); if (pending->nat_id < 0) { cmd->reply = "No free ID found"; goto err; } bsc_cmd = ctrl_cmd_cpy(bsc, cmd); if (!bsc_cmd) { cmd->reply = "Could not forward command"; goto err; } talloc_free(bsc_cmd->id); bsc_cmd->id = talloc_asprintf(bsc_cmd, "%i", pending->nat_id); if (!bsc_cmd->id) { cmd->reply = "OOM"; goto err; } talloc_free(bsc_cmd->variable); bsc_cmd->variable = talloc_strdup(bsc_cmd, bsc_variable); if (!bsc_cmd->variable) { cmd->reply = "OOM"; goto err; } if (ctrl_cmd_send(&bsc->write_queue, bsc_cmd)) { cmd->reply = "Sending failed"; goto err; } pending->ccon = cmd->ccon; pending->ccon->closed_cb = ctrl_conn_closed_cb; pending->cmd = cmd; /* Setup the timeout */ pending->timeout.data = pending; pending->timeout.cb = pending_timeout_cb; /* TODO: Make timeout configurable */ osmo_timer_schedule(&pending->timeout, 10, 0); llist_add_tail(&pending->list_entry, &bsc->cmd_pending); goto done; } } /* We end up here if there's no bsc to handle our LAC */ cmd->reply = "no BSC with this nr"; err: ret = CTRL_CMD_ERROR; done: talloc_free(bsc_cmd); return ret; } CTRL_CMD_DEFINE(fwd_cmd, "net 0 bsc *"); static int get_fwd_cmd(struct ctrl_cmd *cmd, void *data) { return forward_to_bsc(cmd); } static int set_fwd_cmd(struct ctrl_cmd *cmd, void *data) { return forward_to_bsc(cmd); } static int verify_fwd_cmd(struct ctrl_cmd *cmd, const char *value, void *data) { return 0; } static int extract_bsc_cfg_variable(struct ctrl_cmd *cmd, struct bsc_config **cfg, char **bsc_variable) { unsigned int nr; if (!extract_bsc_nr_variable(cmd->variable, &nr, bsc_variable)) { cmd->reply = "command incomplete"; return 0; } *cfg = bsc_config_num(g_nat, nr); if (!*cfg) { cmd->reply = "Unknown BSC"; return 0; } return 1; } CTRL_CMD_DEFINE(net_cfg_cmd, "net 0 bsc_cfg *"); static int get_net_cfg_cmd(struct ctrl_cmd *cmd, void *data) { char *bsc_variable; struct bsc_config *bsc_cfg; if (!extract_bsc_cfg_variable(cmd, &bsc_cfg, &bsc_variable)) return CTRL_CMD_ERROR; if (strcmp(bsc_variable, "access-list-name") == 0) { cmd->reply = talloc_asprintf(cmd, "%s", bsc_cfg->acc_lst_name ? bsc_cfg->acc_lst_name : ""); return CTRL_CMD_REPLY; } cmd->reply = "unknown command"; return CTRL_CMD_ERROR; } static int set_net_cfg_cmd(struct ctrl_cmd *cmd, void *data) { char *bsc_variable; struct bsc_config *bsc_cfg; if (!extract_bsc_cfg_variable(cmd, &bsc_cfg, &bsc_variable)) return CTRL_CMD_ERROR; if (strcmp(bsc_variable, "access-list-name") == 0) { bsc_replace_string(bsc_cfg, &bsc_cfg->acc_lst_name, cmd->value); cmd->reply = talloc_asprintf(cmd, "%s", bsc_cfg->acc_lst_name ? bsc_cfg->acc_lst_name : ""); return CTRL_CMD_REPLY; } else if (strcmp(bsc_variable, "no-access-list-name") == 0) { talloc_free(bsc_cfg->acc_lst_name); bsc_cfg->acc_lst_name = NULL; cmd->reply = ""; return CTRL_CMD_REPLY; } cmd->reply = "unknown command"; return CTRL_CMD_ERROR; } static int verify_net_cfg_cmd(struct ctrl_cmd *cmd, const char *value, void *data) { return 0; } CTRL_CMD_DEFINE(net_cfg_acc_cmd, "net 0 add allow access-list *"); static const char *extract_acc_name(const char *var) { char *str; str = strstr(var, "net.0.add.allow.access-list."); if (!str) return NULL; str += strlen("net.0.add.allow.access-list."); if (strlen(str) == 0) return NULL; return str; } static int get_net_cfg_acc_cmd(struct ctrl_cmd *cmd, void *data) { cmd->reply = "Append only"; return CTRL_CMD_ERROR; } static int set_net_cfg_acc_cmd(struct ctrl_cmd *cmd, void *data) { const char *access_name = extract_acc_name(cmd->variable); struct bsc_msg_acc_lst *acc; struct bsc_msg_acc_lst_entry *entry; const char *value = cmd->value; int rc; /* Should have been caught by verify_net_cfg_acc_cmd */ acc = bsc_msg_acc_lst_find(&g_nat->access_lists, access_name); if (!acc) { cmd->reply = "Access list not found"; return CTRL_CMD_ERROR; } entry = bsc_msg_acc_lst_entry_create(acc); if (!entry) { cmd->reply = "OOM"; return CTRL_CMD_ERROR; } rc = gsm_parse_reg(acc, &entry->imsi_allow_re, &entry->imsi_allow, 1, &value); if (rc != 0) { cmd->reply = "Failed to compile expression"; return CTRL_CMD_ERROR; } cmd->reply = "IMSI allow added to access list"; return CTRL_CMD_REPLY; } static int verify_net_cfg_acc_cmd(struct ctrl_cmd *cmd, const char *value, void *data) { const char *access_name = extract_acc_name(cmd->variable); struct bsc_msg_acc_lst *acc = bsc_msg_acc_lst_find(&g_nat->access_lists, access_name); if (!acc) { cmd->reply = "Access list not known"; return -1; } return 0; } CTRL_CMD_DEFINE(net_save_cmd, "net 0 save-configuration"); static int verify_net_save_cmd(struct ctrl_cmd *cmd, const char *v, void *d) { return 0; } static int set_net_save_cmd(struct ctrl_cmd *cmd, void *data) { int rc = osmo_vty_save_config_file(); cmd->reply = talloc_asprintf(cmd, "%d", rc); if (!cmd->reply) { cmd->reply = "OOM"; return CTRL_CMD_ERROR; } return CTRL_CMD_REPLY; } static int get_net_save_cmd(struct ctrl_cmd *cmd, void *data) { cmd->reply = "Write only attribute"; return CTRL_CMD_ERROR; } struct ctrl_handle *bsc_nat_controlif_setup(struct bsc_nat *nat, int port) { struct ctrl_handle *ctrl; int rc; ctrl = bsc_controlif_setup(NULL, OSMO_CTRL_PORT_BSC_NAT); if (!ctrl) { fprintf(stderr, "Failed to initialize the control interface. Exiting.\n"); return NULL; } rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_fwd_cmd); if (rc) { fprintf(stderr, "Failed to install the control command. Exiting.\n"); goto error; } rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_cfg_cmd); if (rc) { fprintf(stderr, "Failed to install the net cfg command. Exiting.\n"); goto error; } rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_cfg_acc_cmd); if (rc) { fprintf(stderr, "Failed to install the net acc command. Exiting.\n"); goto error; } rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_save_cmd); if (rc) { fprintf(stderr, "Failed to install the net save command. Exiting.\n"); goto error; } g_nat = nat; return ctrl; error: osmo_fd_unregister(&ctrl->listen_fd); close(ctrl->listen_fd.fd); talloc_free(ctrl); return NULL; } void bsc_nat_inform_reject(struct bsc_connection *conn, const char *imsi) { struct ctrl_cmd *cmd; cmd = ctrl_cmd_create(conn, CTRL_TYPE_TRAP); if (!cmd) { LOGP(DCTRL, LOGL_ERROR, "Failed to create TRAP command.\n"); return; } cmd->id = "0"; cmd->variable = talloc_asprintf(cmd, "net.0.bsc.%d.notification-rejection-v1", conn->cfg->nr); cmd->reply = talloc_asprintf(cmd, "imsi=%s", imsi); ctrl_cmd_send_to_all(conn->cfg->nat->ctrl, cmd); talloc_free(cmd); } openbsc-0.15.0/openbsc/src/osmo-bsc_nat/bsc_nat_filter.c000066400000000000000000000070431265565154000231710ustar00rootroot00000000000000/* * (C) 2010-2015 by Holger Hans Peter Freyther * (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 . * */ #include #include #include #include #include #include #include #include /* Filter out CR data... */ int bsc_nat_filter_sccp_cr(struct bsc_connection *bsc, struct msgb *msg, struct bsc_nat_parsed *parsed, int *con_type, char **imsi, struct bsc_filter_reject_cause *cause) { struct bsc_filter_request req; struct tlv_parsed tp; struct gsm48_hdr *hdr48; int hdr48_len; int len; *con_type = FLT_CON_TYPE_NONE; cause->cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED; cause->lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED; *imsi = NULL; if (parsed->gsm_type != BSS_MAP_MSG_COMPLETE_LAYER_3) { LOGP(DNAT, LOGL_ERROR, "Rejecting CR message due wrong GSM Type %d\n", parsed->gsm_type); return -1; } /* the parsed has had some basic l3 length check */ len = msg->l3h[1]; if (msgb_l3len(msg) - 3 < len) { LOGP(DNAT, LOGL_ERROR, "The CR Data has not enough space...\n"); return -1; } msg->l4h = &msg->l3h[3]; len -= 1; tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l4h, len, 0, 0); if (!TLVP_PRESENT(&tp, GSM0808_IE_LAYER_3_INFORMATION)) { LOGP(DNAT, LOGL_ERROR, "CR Data does not contain layer3 information.\n"); return -1; } hdr48_len = TLVP_LEN(&tp, GSM0808_IE_LAYER_3_INFORMATION); if (hdr48_len < sizeof(*hdr48)) { LOGP(DNAT, LOGL_ERROR, "GSM48 header does not fit.\n"); return -1; } hdr48 = (struct gsm48_hdr *) TLVP_VAL(&tp, GSM0808_IE_LAYER_3_INFORMATION); req.ctx = bsc; req.black_list = &bsc->nat->imsi_black_list; req.access_lists = &bsc->nat->access_lists; req.local_lst_name = bsc->cfg->acc_lst_name; req.global_lst_name = bsc->nat->acc_lst_name; req.bsc_nr = bsc->cfg->nr; return bsc_msg_filter_initial(hdr48, hdr48_len, &req, con_type, imsi, 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) { uint32_t len; struct gsm48_hdr *hdr48; struct bsc_filter_request req; cause->cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED; cause->lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED; if (con->filter_state.imsi_checked) return 0; /* only care about DTAP messages */ if (parsed->bssap != BSSAP_MSG_DTAP) return 0; hdr48 = bsc_unpack_dtap(parsed, msg, &len); if (!hdr48) return -1; req.ctx = bsc; req.black_list = &bsc->nat->imsi_black_list; req.access_lists = &bsc->nat->access_lists; req.local_lst_name = bsc->cfg->acc_lst_name; req.global_lst_name = bsc->nat->acc_lst_name; req.bsc_nr = bsc->cfg->nr; return bsc_msg_filter_data(hdr48, len, &req, &con->filter_state, cause); } openbsc-0.15.0/openbsc/src/osmo-bsc_nat/bsc_nat_rewrite.c000066400000000000000000000456331265565154000233740ustar00rootroot00000000000000/* * Message rewriting functionality */ /* * (C) 2010-2013 by Holger Hans Peter Freyther * (C) 2010-2013 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include static char *trie_lookup(struct nat_rewrite *trie, const char *number, regoff_t off, void *ctx) { struct nat_rewrite_rule *rule; if (!trie) { LOGP(DCC, LOGL_ERROR, "Asked to do a table lookup but no table.\n"); return NULL; } rule = nat_rewrite_lookup(trie, number); if (!rule) { LOGP(DCC, LOGL_DEBUG, "Couldn't find a prefix rule for %s\n", number); return NULL; } return talloc_asprintf(ctx, "%s%s", rule->rewrite, &number[off]); } static char *match_and_rewrite_number(void *ctx, const char *number, const char *imsi, struct llist_head *list, struct nat_rewrite *trie) { struct bsc_nat_num_rewr_entry *entry; char *new_number = NULL; /* need to find a replacement and then fix it */ llist_for_each_entry(entry, list, list) { regmatch_t matches[2]; /* check the IMSI match */ if (regexec(&entry->msisdn_reg, imsi, 0, NULL, 0) != 0) continue; /* this regexp matches... */ if (regexec(&entry->num_reg, number, 2, matches, 0) == 0 && matches[1].rm_eo != -1) { if (entry->is_prefix_lookup) new_number = trie_lookup(trie, number, matches[1].rm_so, ctx); else new_number = talloc_asprintf(ctx, "%s%s", entry->replace, &number[matches[1].rm_so]); } if (new_number) break; } return new_number; } static char *rewrite_isdn_number(struct bsc_nat *nat, struct llist_head *rewr_list, void *ctx, const char *imsi, struct gsm_mncc_number *called) { char int_number[sizeof(called->number) + 2]; char *number = called->number; if (llist_empty(&nat->num_rewr)) { LOGP(DCC, LOGL_DEBUG, "Rewrite rules empty.\n"); return NULL; } /* only ISDN plan */ if (called->plan != 1) { LOGP(DCC, LOGL_DEBUG, "Called plan is not 1 it was %d\n", called->plan); return NULL; } /* international, prepend */ if (called->type == 1) { int_number[0] = '+'; memcpy(&int_number[1], number, strlen(number) + 1); number = int_number; } return match_and_rewrite_number(ctx, number, imsi, rewr_list, nat->num_rewr_trie); } static void update_called_number(struct gsm_mncc_number *called, const char *chosen_number) { if (strncmp(chosen_number, "00", 2) == 0) { called->type = 1; strncpy(called->number, chosen_number + 2, sizeof(called->number)); } else { /* rewrite international to unknown */ if (called->type == 1) called->type = 0; strncpy(called->number, chosen_number, sizeof(called->number)); } called->number[sizeof(called->number) - 1] = '\0'; } /** * Rewrite non global numbers... according to rules based on the IMSI */ static struct msgb *rewrite_setup(struct bsc_nat *nat, struct msgb *msg, struct bsc_nat_parsed *parsed, const char *imsi, struct gsm48_hdr *hdr48, const uint32_t len) { struct tlv_parsed tp; unsigned int payload_len; struct gsm_mncc_number called; struct msgb *out; char *new_number_pre = NULL, *new_number_post = NULL, *chosen_number; uint8_t *outptr; const uint8_t *msgptr; int sec_len; /* decode and rewrite the message */ payload_len = len - sizeof(*hdr48); tlv_parse(&tp, &gsm48_att_tlvdef, hdr48->data, payload_len, 0, 0); /* no number, well let us ignore it */ if (!TLVP_PRESENT(&tp, GSM48_IE_CALLED_BCD)) return NULL; memset(&called, 0, sizeof(called)); gsm48_decode_called(&called, TLVP_VAL(&tp, GSM48_IE_CALLED_BCD) - 1); /* check if it looks international and stop */ LOGP(DCC, LOGL_DEBUG, "Pre-Rewrite for IMSI(%s) Plan(%d) Type(%d) Number(%s)\n", imsi, called.plan, called.type, called.number); new_number_pre = rewrite_isdn_number(nat, &nat->num_rewr, msg, imsi, &called); if (!new_number_pre) { LOGP(DCC, LOGL_DEBUG, "No IMSI(%s) match found, returning message.\n", imsi); return NULL; } if (strlen(new_number_pre) > sizeof(called.number)) { LOGP(DCC, LOGL_ERROR, "Number %s is too long for structure.\n", new_number_pre); talloc_free(new_number_pre); return NULL; } update_called_number(&called, new_number_pre); /* another run through the re-write engine with other rules */ LOGP(DCC, LOGL_DEBUG, "Post-Rewrite for IMSI(%s) Plan(%d) Type(%d) Number(%s)\n", imsi, called.plan, called.type, called.number); new_number_post = rewrite_isdn_number(nat, &nat->num_rewr_post, msg, imsi, &called); chosen_number = new_number_post ? new_number_post : new_number_pre; if (strlen(chosen_number) > sizeof(called.number)) { LOGP(DCC, LOGL_ERROR, "Number %s is too long for structure.\n", chosen_number); talloc_free(new_number_pre); talloc_free(new_number_post); return NULL; } /* * Need to create a new message now based on the old onew * with a new number. We can sadly not patch this in place * so we will need to regenerate it. */ out = msgb_alloc_headroom(4096, 128, "changed-setup"); if (!out) { LOGP(DCC, LOGL_ERROR, "Failed to allocate.\n"); talloc_free(new_number_pre); talloc_free(new_number_post); return NULL; } /* copy the header */ outptr = msgb_put(out, sizeof(*hdr48)); memcpy(outptr, hdr48, sizeof(*hdr48)); /* copy everything up to the number */ sec_len = TLVP_VAL(&tp, GSM48_IE_CALLED_BCD) - 2 - &hdr48->data[0]; outptr = msgb_put(out, sec_len); memcpy(outptr, &hdr48->data[0], sec_len); /* create the new number */ update_called_number(&called, chosen_number); LOGP(DCC, LOGL_DEBUG, "Chosen number for IMSI(%s) is Plan(%d) Type(%d) Number(%s)\n", imsi, called.plan, called.type, called.number); gsm48_encode_called(out, &called); /* copy thre rest */ msgptr = TLVP_VAL(&tp, GSM48_IE_CALLED_BCD) + TLVP_LEN(&tp, GSM48_IE_CALLED_BCD); sec_len = payload_len - (msgptr - &hdr48->data[0]); outptr = msgb_put(out, sec_len); memcpy(outptr, msgptr, sec_len); talloc_free(new_number_pre); talloc_free(new_number_post); return out; } /** * Find a new SMSC address, returns an allocated string that needs to be * freed or is NULL. */ static char *find_new_smsc(struct bsc_nat *nat, void *ctx, const char *imsi, const char *smsc_addr, const char *dest_nr) { struct bsc_nat_num_rewr_entry *entry; char *new_number = NULL; uint8_t dest_match = llist_empty(&nat->tpdest_match); /* We will find a new number now */ llist_for_each_entry(entry, &nat->smsc_rewr, list) { regmatch_t matches[2]; /* check the IMSI match */ if (regexec(&entry->msisdn_reg, imsi, 0, NULL, 0) != 0) continue; /* this regexp matches... */ if (regexec(&entry->num_reg, smsc_addr, 2, matches, 0) == 0 && matches[1].rm_eo != -1) new_number = talloc_asprintf(ctx, "%s%s", entry->replace, &smsc_addr[matches[1].rm_so]); if (new_number) break; } if (!new_number) return NULL; /* * now match the number against another list */ llist_for_each_entry(entry, &nat->tpdest_match, list) { /* check the IMSI match */ if (regexec(&entry->msisdn_reg, imsi, 0, NULL, 0) != 0) continue; if (regexec(&entry->num_reg, dest_nr, 0, NULL, 0) == 0) { dest_match = 1; break; } } if (!dest_match) { talloc_free(new_number); return NULL; } return new_number; } /** * Clear the TP-SRR from the TPDU header */ static uint8_t sms_new_tpdu_hdr(struct bsc_nat *nat, const char *imsi, const char *dest_nr, uint8_t hdr) { struct bsc_nat_num_rewr_entry *entry; /* We will find a new number now */ llist_for_each_entry(entry, &nat->sms_clear_tp_srr, list) { /* check the IMSI match */ if (regexec(&entry->msisdn_reg, imsi, 0, NULL, 0) != 0) continue; if (regexec(&entry->num_reg, dest_nr, 0, NULL, 0) != 0) continue; /* matched phone number and imsi */ return hdr & ~0x20; } return hdr; } /** * Check if we need to rewrite the number. For this SMS. */ static char *sms_new_dest_nr(struct bsc_nat *nat, void *ctx, const char *imsi, const char *dest_nr) { return match_and_rewrite_number(ctx, dest_nr, imsi, &nat->sms_num_rewr, NULL); } /** * This is a helper for GSM 04.11 8.2.5.2 Destination address element */ void sms_encode_addr_element(struct msgb *out, const char *new_number, int format, int tp_data) { uint8_t new_addr_len; uint8_t new_addr[26]; /* * Copy the new number. We let libosmocore encode it, then set * the extension followed after the length. Depending on if * we want to write RP we will let the TLV code add the * length for us or we need to use strlen... This is not very clear * as of 03.40 and 04.11. */ new_addr_len = gsm48_encode_bcd_number(new_addr, ARRAY_SIZE(new_addr), 1, new_number); new_addr[1] = format; if (tp_data) { uint8_t *data = msgb_put(out, new_addr_len); memcpy(data, new_addr, new_addr_len); data[0] = strlen(new_number); } else { msgb_lv_put(out, new_addr_len - 1, new_addr + 1); } } static struct msgb *sms_create_new(uint8_t type, uint8_t ref, struct gsm48_hdr *old_hdr48, const uint8_t *orig_addr_ptr, int orig_addr_len, const char *new_number, const uint8_t *data_ptr, int data_len, uint8_t tpdu_first_byte, const int old_dest_len, const char *new_dest_nr) { struct gsm48_hdr *new_hdr48; struct msgb *out; /* * We need to re-create the patched structure. This is why we have * saved the above pointers. */ out = msgb_alloc_headroom(4096, 128, "changed-smsc"); if (!out) { LOGP(DNAT, LOGL_ERROR, "Failed to allocate.\n"); return NULL; } out->l2h = out->data; msgb_v_put(out, GSM411_MT_RP_DATA_MO); msgb_v_put(out, ref); msgb_lv_put(out, orig_addr_len, orig_addr_ptr); sms_encode_addr_element(out, new_number, 0x91, 0); /* Patch the TPDU from here on */ /** * Do we need to put a new TP-Destination-Address (TP-DA) here or * can we copy the old thing? For the TP-DA we need to find out the * new size. */ if (new_dest_nr) { uint8_t *data, *new_size; /* reserve the size and write the header */ new_size = msgb_put(out, 1); out->l3h = new_size + 1; msgb_v_put(out, tpdu_first_byte); msgb_v_put(out, data_ptr[1]); /* encode the new number and put it */ if (strncmp(new_dest_nr, "00", 2) == 0) sms_encode_addr_element(out, new_dest_nr + 2, 0x91, 1); else sms_encode_addr_element(out, new_dest_nr, 0x81, 1); /* Copy the rest after the TP-DS */ data = msgb_put(out, data_len - 2 - 1 - old_dest_len); memcpy(data, &data_ptr[2 + 1 + old_dest_len], data_len - 2 - 1 - old_dest_len); /* fill in the new size */ new_size[0] = msgb_l3len(out); } else { msgb_v_put(out, data_len); msgb_tv_fixed_put(out, tpdu_first_byte, data_len - 1, &data_ptr[1]); } /* prepend GSM 04.08 header */ new_hdr48 = (struct gsm48_hdr *) msgb_push(out, sizeof(*new_hdr48) + 1); memcpy(new_hdr48, old_hdr48, sizeof(*old_hdr48)); new_hdr48->data[0] = msgb_l2len(out); return out; } /** * Parse the SMS and check if it needs to be rewritten */ static struct msgb *rewrite_sms(struct bsc_nat *nat, struct msgb *msg, struct bsc_nat_parsed *parsed, const char *imsi, struct gsm48_hdr *hdr48, const uint32_t len) { unsigned int payload_len; unsigned int cp_len; uint8_t ref; uint8_t orig_addr_len, *orig_addr_ptr; uint8_t dest_addr_len, *dest_addr_ptr; uint8_t data_len, *data_ptr; char smsc_addr[30]; uint8_t dest_len, orig_dest_len; char _dest_nr[30]; char *dest_nr; char *new_dest_nr; char *new_number = NULL; uint8_t tpdu_hdr; struct msgb *out; payload_len = len - sizeof(*hdr48); if (payload_len < 1) { LOGP(DNAT, LOGL_ERROR, "SMS too short for things. %d\n", payload_len); return NULL; } cp_len = hdr48->data[0]; if (payload_len + 1 < cp_len) { LOGP(DNAT, LOGL_ERROR, "SMS RPDU can not fit in: %d %d\n", cp_len, payload_len); return NULL; } if (hdr48->data[1] != GSM411_MT_RP_DATA_MO) return NULL; if (cp_len < 5) { LOGP(DNAT, LOGL_ERROR, "RD-DATA can not fit in the CP len: %d\n", cp_len); return NULL; } /* RP */ ref = hdr48->data[2]; orig_addr_len = hdr48->data[3]; orig_addr_ptr = &hdr48->data[4]; /* the +1 is for checking if the following element has some space */ if (cp_len < 3 + orig_addr_len + 1) { LOGP(DNAT, LOGL_ERROR, "RP-Originator addr does not fit: %d\n", orig_addr_len); return NULL; } dest_addr_len = hdr48->data[3 + orig_addr_len + 1]; dest_addr_ptr = &hdr48->data[3 + orig_addr_len + 2]; if (cp_len < 3 + orig_addr_len + 1 + dest_addr_len + 1) { LOGP(DNAT, LOGL_ERROR, "RP-Destination addr does not fit: %d\n", dest_addr_len); return NULL; } gsm48_decode_bcd_number(smsc_addr, ARRAY_SIZE(smsc_addr), dest_addr_ptr - 1, 1); data_len = hdr48->data[3 + orig_addr_len + 1 + dest_addr_len + 1]; data_ptr = &hdr48->data[3 + orig_addr_len + 1 + dest_addr_len + 2]; if (cp_len < 3 + orig_addr_len + 1 + dest_addr_len + 1 + data_len) { LOGP(DNAT, LOGL_ERROR, "RP-Data does not fit: %d\n", data_len); return NULL; } if (data_len < 3) { LOGP(DNAT, LOGL_ERROR, "SMS-SUBMIT is too short.\n"); return NULL; } /* TP-PDU starts here */ if ((data_ptr[0] & 0x03) != GSM340_SMS_SUBMIT_MS2SC) return NULL; /* * look into the phone number. The length is in semi-octets, we will * need to add the byte for the number type as well. */ orig_dest_len = data_ptr[2]; dest_len = ((orig_dest_len + 1) / 2) + 1; if (data_len < dest_len + 3 || dest_len < 2) { LOGP(DNAT, LOGL_ERROR, "SMS-SUBMIT can not have TP-DestAddr.\n"); return NULL; } if ((data_ptr[3] & 0x80) == 0) { LOGP(DNAT, LOGL_ERROR, "TP-DestAddr has extension. Not handled.\n"); return NULL; } if ((data_ptr[3] & 0x0F) == 0) { LOGP(DNAT, LOGL_ERROR, "TP-DestAddr is of unknown type.\n"); return NULL; } /** * Besides of what I think I read in GSM 03.40 and 04.11 the TP-DA * contains the semi-octets as length (strlen), change it to the * the number of bytes, but then change it back. */ data_ptr[2] = dest_len; gsm48_decode_bcd_number(_dest_nr + 2, ARRAY_SIZE(_dest_nr) - 2, &data_ptr[2], 1); data_ptr[2] = orig_dest_len; if ((data_ptr[3] & 0x70) == 0x10) { _dest_nr[0] = _dest_nr[1] = '0'; dest_nr = &_dest_nr[0]; } else { dest_nr = &_dest_nr[2]; } /** * Call functions to rewrite the data */ tpdu_hdr = sms_new_tpdu_hdr(nat, imsi, dest_nr, data_ptr[0]); new_number = find_new_smsc(nat, msg, imsi, smsc_addr, dest_nr); new_dest_nr = sms_new_dest_nr(nat, msg, imsi, dest_nr); if (tpdu_hdr == data_ptr[0] && !new_number && !new_dest_nr) return NULL; out = sms_create_new(GSM411_MT_RP_DATA_MO, ref, hdr48, orig_addr_ptr, orig_addr_len, new_number ? new_number : smsc_addr, data_ptr, data_len, tpdu_hdr, dest_len, new_dest_nr); talloc_free(new_number); talloc_free(new_dest_nr); return out; } struct msgb *bsc_nat_rewrite_msg(struct bsc_nat *nat, struct msgb *msg, struct bsc_nat_parsed *parsed, const char *imsi) { struct gsm48_hdr *hdr48; uint32_t len; uint8_t msg_type, proto; struct msgb *new_msg = NULL, *sccp; uint8_t link_id; if (!imsi || strlen(imsi) < 5) return msg; /* only care about DTAP messages */ if (parsed->bssap != BSSAP_MSG_DTAP) return msg; if (!parsed->dest_local_ref) return msg; hdr48 = bsc_unpack_dtap(parsed, msg, &len); if (!hdr48) return msg; link_id = msg->l3h[1]; proto = hdr48->proto_discr & 0x0f; msg_type = hdr48->msg_type & 0xbf; if (proto == GSM48_PDISC_CC && msg_type == GSM48_MT_CC_SETUP) new_msg = rewrite_setup(nat, msg, parsed, imsi, hdr48, len); else if (proto == GSM48_PDISC_SMS && msg_type == GSM411_MT_CP_DATA) new_msg = rewrite_sms(nat, msg, parsed, imsi, hdr48, len); if (!new_msg) return msg; /* wrap with DTAP, SCCP, then IPA. TODO: Stop copying */ gsm0808_prepend_dtap_header(new_msg, link_id); sccp = sccp_create_dt1(parsed->dest_local_ref, new_msg->data, new_msg->len); talloc_free(new_msg); if (!sccp) { LOGP(DNAT, LOGL_ERROR, "Failed to allocate.\n"); return msg; } ipa_prepend_header(sccp, IPAC_PROTO_SCCP); /* the parsed hangs off from msg but it needs to survive */ talloc_steal(sccp, parsed); msgb_free(msg); return sccp; } static void num_rewr_free_data(struct bsc_nat_num_rewr_entry *entry) { regfree(&entry->msisdn_reg); regfree(&entry->num_reg); talloc_free(entry->replace); } void bsc_nat_num_rewr_entry_adapt(void *ctx, struct llist_head *head, const struct osmo_config_list *list) { struct bsc_nat_num_rewr_entry *entry, *tmp; struct osmo_config_entry *cfg_entry; /* free the old data */ llist_for_each_entry_safe(entry, tmp, head, list) { num_rewr_free_data(entry); llist_del(&entry->list); talloc_free(entry); } if (!list) return; llist_for_each_entry(cfg_entry, &list->entry, list) { char *regexp; if (cfg_entry->text[0] == '+') { LOGP(DNAT, LOGL_ERROR, "Plus is not allowed in the number\n"); continue; } entry = talloc_zero(ctx, struct bsc_nat_num_rewr_entry); if (!entry) { LOGP(DNAT, LOGL_ERROR, "Allocation of the num_rewr entry failed.\n"); continue; } entry->replace = talloc_strdup(entry, cfg_entry->text); if (!entry->replace) { LOGP(DNAT, LOGL_ERROR, "Failed to copy the replacement text.\n"); talloc_free(entry); continue; } if (strcmp("prefix_lookup", entry->replace) == 0) entry->is_prefix_lookup = 1; /* we will now build a regexp string */ if (cfg_entry->mcc[0] == '^') { regexp = talloc_strdup(entry, cfg_entry->mcc); } else { regexp = talloc_asprintf(entry, "^%s%s", cfg_entry->mcc[0] == '*' ? "[0-9][0-9][0-9]" : cfg_entry->mcc, cfg_entry->mnc[0] == '*' ? "[0-9][0-9]" : cfg_entry->mnc); } if (!regexp) { LOGP(DNAT, LOGL_ERROR, "Failed to create a regexp string.\n"); talloc_free(entry); continue; } if (regcomp(&entry->msisdn_reg, regexp, 0) != 0) { LOGP(DNAT, LOGL_ERROR, "Failed to compile regexp '%s'\n", regexp); talloc_free(regexp); talloc_free(entry); continue; } talloc_free(regexp); if (regcomp(&entry->num_reg, cfg_entry->option, REG_EXTENDED) != 0) { LOGP(DNAT, LOGL_ERROR, "Failed to compile regexp '%s'\n", cfg_entry->option); regfree(&entry->msisdn_reg); talloc_free(entry); continue; } /* we have copied the number */ llist_add_tail(&entry->list, head); } } openbsc-0.15.0/openbsc/src/osmo-bsc_nat/bsc_nat_rewrite_trie.c000066400000000000000000000141221265565154000244040ustar00rootroot00000000000000/* Handling for loading a re-write file/database */ /* * (C) 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 . * */ #include #include #include #include #include #include #include #include #include #define CHECK_IS_DIGIT_OR_FAIL(prefix, pos) \ if (!isdigit(prefix[pos]) && prefix[pos] != '+') { \ LOGP(DNAT, LOGL_ERROR, \ "Prefix(%s) contains non ascii text at(%d=%c)\n", \ prefix, pos, prefix[pos]); \ goto fail; \ } #define TO_INT(c) \ ((c) == '+' ? 10 : ((c - '0') % 10)) static void insert_rewrite_node(struct nat_rewrite_rule *rule, struct nat_rewrite *root) { struct nat_rewrite_rule *new = &root->rule; const int len = strlen(rule->prefix); int i; if (len <= 0) { LOGP(DNAT, LOGL_ERROR, "An empty prefix does not make sense.\n"); goto fail; } for (i = 0; i < len - 1; ++i) { int pos; /* check if the input is valid */ CHECK_IS_DIGIT_OR_FAIL(rule->prefix, i); /* check if the next node is already valid */ pos = TO_INT(rule->prefix[i]); if (!new->rules[pos]) { new->rules[pos] = talloc_zero(root, struct nat_rewrite_rule); if (!new->rules[pos]) { LOGP(DNAT, LOGL_ERROR, "Failed to allocate memory.\n"); goto fail; } new->rules[pos]->empty = 1; } /* we continue here */ new = new->rules[pos]; } /* new now points to the place where we want to add it */ int pos; /* check if the input is valid */ CHECK_IS_DIGIT_OR_FAIL(rule->prefix, (len - 1)); /* check if the next node is already valid */ pos = TO_INT(rule->prefix[len - 1]); if (!new->rules[pos]) new->rules[pos] = rule; else if (new->rules[pos]->empty) { /* copy over entries */ new->rules[pos]->empty = 0; memcpy(new->rules[pos]->prefix, rule->prefix, sizeof(rule->prefix)); memcpy(new->rules[pos]->rewrite, rule->rewrite, sizeof(rule->rewrite)); talloc_free(rule); } else { LOGP(DNAT, LOGL_ERROR, "Prefix(%s) is already installed\n", rule->prefix); goto fail; } root->prefixes += 1; return; fail: talloc_free(rule); return; } static void handle_line(struct nat_rewrite *rewrite, char *line) { char *split; struct nat_rewrite_rule *rule; size_t size_prefix, size_end, len; /* Find the ',' in the line */ len = strlen(line); split = strstr(line, ","); if (!split) { LOGP(DNAT, LOGL_ERROR, "Line doesn't contain ','\n"); return; } /* Check if there is space for the rewrite rule */ size_prefix = split - line; if (len - size_prefix <= 2) { LOGP(DNAT, LOGL_ERROR, "No rewrite available.\n"); return; } /* Continue after the ',' to the end */ split = &line[size_prefix + 1]; size_end = strlen(split) - 1; /* Check if both strings can fit into the static array */ if (size_prefix > sizeof(rule->prefix) - 1) { LOGP(DNAT, LOGL_ERROR, "Prefix is too long with %zu\n", size_prefix); return; } if (size_end > sizeof(rule->rewrite) - 1) { LOGP(DNAT, LOGL_ERROR, "Rewrite is too long with %zu on %s\n", size_end, &line[size_prefix + 1]); return; } /* Now create the entry and insert it into the trie */ rule = talloc_zero(rewrite, struct nat_rewrite_rule); if (!rule) { LOGP(DNAT, LOGL_ERROR, "Can not allocate memory\n"); return; } memcpy(rule->prefix, line, size_prefix); assert(size_prefix < sizeof(rule->prefix)); rule->prefix[size_prefix] = '\0'; memcpy(rule->rewrite, split, size_end); assert(size_end < sizeof(rule->rewrite)); rule->rewrite[size_end] = '\0'; /* now insert and balance the tree */ insert_rewrite_node(rule, rewrite); } struct nat_rewrite *nat_rewrite_parse(void *ctx, const char *filename) { FILE *file; char *line = NULL; size_t n = 2342; struct nat_rewrite *res; file = fopen(filename, "r"); if (!file) return NULL; res = talloc_zero(ctx, struct nat_rewrite); if (!res) { fclose(file); return NULL; } /* mark the root as empty */ res->rule.empty = 1; while (getline(&line, &n, file) != -1) { handle_line(res, line); } free(line); fclose(file); return res; } /** * Simple find that tries to do a longest match... */ struct nat_rewrite_rule *nat_rewrite_lookup(struct nat_rewrite *rewrite, const char *prefix) { struct nat_rewrite_rule *rule = &rewrite->rule; struct nat_rewrite_rule *last = NULL; const int len = OSMO_MIN(strlen(prefix), (sizeof(rule->prefix) - 1)); int i; for (i = 0; rule && i < len; ++i) { int pos; CHECK_IS_DIGIT_OR_FAIL(prefix, i); pos = TO_INT(prefix[i]); rule = rule->rules[pos]; if (rule && !rule->empty) last = rule; } return last; fail: return NULL; } static void nat_rewrite_dump_rec(struct nat_rewrite_rule *rule) { int i; if (!rule->empty) printf("%s,%s\n", rule->prefix, rule->rewrite); for (i = 0; i < ARRAY_SIZE(rule->rules); ++i) { if (!rule->rules[i]) continue; nat_rewrite_dump_rec(rule->rules[i]); } } void nat_rewrite_dump(struct nat_rewrite *rewrite) { nat_rewrite_dump_rec(&rewrite->rule); } static void nat_rewrite_dump_rec_vty(struct vty *vty, struct nat_rewrite_rule *rule) { int i; if (!rule->empty) vty_out(vty, "%s,%s%s", rule->prefix, rule->rewrite, VTY_NEWLINE); for (i = 0; i < ARRAY_SIZE(rule->rules); ++i) { if (!rule->rules[i]) continue; nat_rewrite_dump_rec_vty(vty, rule->rules[i]); } } void nat_rewrite_dump_vty(struct vty *vty, struct nat_rewrite *rewrite) { nat_rewrite_dump_rec_vty(vty, &rewrite->rule); } openbsc-0.15.0/openbsc/src/osmo-bsc_nat/bsc_nat_utils.c000066400000000000000000000341271265565154000230470ustar00rootroot00000000000000 /* BSC Multiplexer/NAT Utilities */ /* * (C) 2010-2011 by Holger Hans Peter Freyther * (C) 2010-2011 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static const struct rate_ctr_desc bsc_cfg_ctr_description[] = { [BCFG_CTR_SCCP_CONN] = { "sccp.conn", "SCCP Connections "}, [BCFG_CTR_SCCP_CALLS] = { "sccp.calls", "SCCP Assignment Commands "}, [BCFG_CTR_NET_RECONN] = { "net.reconnects", "Network reconnects "}, [BCFG_CTR_DROPPED_SCCP] = { "dropped.sccp", "Dropped SCCP connections."}, [BCFG_CTR_DROPPED_CALLS] = { "dropped.calls", "Dropped active calls. "}, [BCFG_CTR_REJECTED_CR] = { "rejected.cr", "Rejected CR due filter "}, [BCFG_CTR_REJECTED_MSG] = { "rejected.msg", "Rejected MSG due filter "}, [BCFG_CTR_ILL_PACKET] = { "rejected.ill", "Rejected due parse error "}, [BCFG_CTR_CON_TYPE_LU] = { "conn.lu", "Conn Location Update "}, [BCFG_CTR_CON_CMSERV_RQ] = { "conn.rq", "Conn CM Service Req "}, [BCFG_CTR_CON_PAG_RESP] = { "conn.pag", "Conn Paging Response "}, [BCFG_CTR_CON_SSA] = { "conn.ssa", "Conn USSD "}, [BCFG_CTR_CON_OTHER] = { "conn.other", "Conn Other "}, }; static const struct rate_ctr_group_desc bsc_cfg_ctrg_desc = { .group_name_prefix = "nat.bsc", .group_description = "NAT BSC Statistics", .num_ctr = ARRAY_SIZE(bsc_cfg_ctr_description), .ctr_desc = bsc_cfg_ctr_description, }; struct bsc_nat *bsc_nat_alloc(void) { struct bsc_nat *nat = talloc_zero(tall_bsc_ctx, struct bsc_nat); if (!nat) return NULL; nat->main_dest = talloc_zero(nat, struct bsc_msc_dest); if (!nat->main_dest) { talloc_free(nat); return NULL; } INIT_LLIST_HEAD(&nat->sccp_connections); INIT_LLIST_HEAD(&nat->bsc_connections); INIT_LLIST_HEAD(&nat->paging_groups); INIT_LLIST_HEAD(&nat->bsc_configs); INIT_LLIST_HEAD(&nat->access_lists); INIT_LLIST_HEAD(&nat->dests); INIT_LLIST_HEAD(&nat->num_rewr); INIT_LLIST_HEAD(&nat->num_rewr_post); INIT_LLIST_HEAD(&nat->smsc_rewr); INIT_LLIST_HEAD(&nat->tpdest_match); INIT_LLIST_HEAD(&nat->sms_clear_tp_srr); INIT_LLIST_HEAD(&nat->sms_num_rewr); nat->stats.sccp.conn = osmo_counter_alloc("nat.sccp.conn"); nat->stats.sccp.calls = osmo_counter_alloc("nat.sccp.calls"); nat->stats.bsc.reconn = osmo_counter_alloc("nat.bsc.conn"); nat->stats.bsc.auth_fail = osmo_counter_alloc("nat.bsc.auth_fail"); nat->stats.msc.reconn = osmo_counter_alloc("nat.msc.conn"); nat->stats.ussd.reconn = osmo_counter_alloc("nat.ussd.conn"); nat->auth_timeout = 2; nat->ping_timeout = 20; nat->pong_timeout = 5; llist_add(&nat->main_dest->list, &nat->dests); nat->main_dest->ip = talloc_strdup(nat, "127.0.0.1"); nat->main_dest->port = 5000; return nat; } void bsc_nat_free(struct bsc_nat *nat) { struct bsc_config *cfg, *tmp; struct bsc_msg_acc_lst *lst, *tmp_lst; llist_for_each_entry_safe(cfg, tmp, &nat->bsc_configs, entry) bsc_config_free(cfg); llist_for_each_entry_safe(lst, tmp_lst, &nat->access_lists, list) bsc_msg_acc_lst_delete(lst); bsc_nat_num_rewr_entry_adapt(nat, &nat->num_rewr, NULL); bsc_nat_num_rewr_entry_adapt(nat, &nat->num_rewr_post, NULL); bsc_nat_num_rewr_entry_adapt(nat, &nat->sms_clear_tp_srr, NULL); bsc_nat_num_rewr_entry_adapt(nat, &nat->sms_num_rewr, NULL); bsc_nat_num_rewr_entry_adapt(nat, &nat->tpdest_match, NULL); osmo_counter_free(nat->stats.sccp.conn); osmo_counter_free(nat->stats.sccp.calls); osmo_counter_free(nat->stats.bsc.reconn); osmo_counter_free(nat->stats.bsc.auth_fail); osmo_counter_free(nat->stats.msc.reconn); osmo_counter_free(nat->stats.ussd.reconn); talloc_free(nat->mgcp_cfg); talloc_free(nat); } void bsc_nat_set_msc_ip(struct bsc_nat *nat, const char *ip) { bsc_replace_string(nat, &nat->main_dest->ip, ip); } struct bsc_connection *bsc_connection_alloc(struct bsc_nat *nat) { struct bsc_connection *con = talloc_zero(nat, struct bsc_connection); if (!con) return NULL; con->nat = nat; osmo_wqueue_init(&con->write_queue, 100); INIT_LLIST_HEAD(&con->cmd_pending); INIT_LLIST_HEAD(&con->pending_dlcx); return con; } struct bsc_config *bsc_config_alloc(struct bsc_nat *nat, const char *token) { struct bsc_config *conf = talloc_zero(nat, struct bsc_config); if (!conf) return NULL; conf->token = talloc_strdup(conf, token); conf->nr = nat->num_bsc; conf->nat = nat; conf->max_endpoints = 32; conf->paging_group = PAGIN_GROUP_UNASSIGNED; INIT_LLIST_HEAD(&conf->lac_list); llist_add_tail(&conf->entry, &nat->bsc_configs); ++nat->num_bsc; conf->stats.ctrg = rate_ctr_group_alloc(conf, &bsc_cfg_ctrg_desc, conf->nr); if (!conf->stats.ctrg) { llist_del(&conf->entry); talloc_free(conf); return NULL; } return conf; } struct bsc_config *bsc_config_by_token(struct bsc_nat *nat, const char *token, int len) { struct bsc_config *conf; llist_for_each_entry(conf, &nat->bsc_configs, entry) { /* * Add the '\0' of the token for the memcmp, the IPA messages * for some reason added null termination. */ const int token_len = strlen(conf->token) + 1; if (token_len == len && memcmp(conf->token, token, token_len) == 0) return conf; } return NULL; } void bsc_config_free(struct bsc_config *cfg) { llist_del(&cfg->entry); rate_ctr_group_free(cfg->stats.ctrg); talloc_free(cfg); } static void _add_lac(void *ctx, struct llist_head *list, int _lac) { struct bsc_lac_entry *lac; llist_for_each_entry(lac, list, entry) if (lac->lac == _lac) return; lac = talloc_zero(ctx, struct bsc_lac_entry); if (!lac) { LOGP(DNAT, LOGL_ERROR, "Failed to allocate.\n"); return; } lac->lac = _lac; llist_add_tail(&lac->entry, list); } static void _del_lac(struct llist_head *list, int _lac) { struct bsc_lac_entry *lac; llist_for_each_entry(lac, list, entry) if (lac->lac == _lac) { llist_del(&lac->entry); talloc_free(lac); return; } } void bsc_config_add_lac(struct bsc_config *cfg, int _lac) { _add_lac(cfg, &cfg->lac_list, _lac); } void bsc_config_del_lac(struct bsc_config *cfg, int _lac) { _del_lac(&cfg->lac_list, _lac); } struct bsc_nat_paging_group *bsc_nat_paging_group_create(struct bsc_nat *nat, int group) { struct bsc_nat_paging_group *pgroup; pgroup = talloc_zero(nat, struct bsc_nat_paging_group); if (!pgroup) { LOGP(DNAT, LOGL_ERROR, "Failed to allocate a paging group.\n"); return NULL; } pgroup->nr = group; INIT_LLIST_HEAD(&pgroup->lists); llist_add_tail(&pgroup->entry, &nat->paging_groups); return pgroup; } void bsc_nat_paging_group_delete(struct bsc_nat_paging_group *pgroup) { llist_del(&pgroup->entry); talloc_free(pgroup); } struct bsc_nat_paging_group *bsc_nat_paging_group_num(struct bsc_nat *nat, int group) { struct bsc_nat_paging_group *pgroup; llist_for_each_entry(pgroup, &nat->paging_groups, entry) if (pgroup->nr == group) return pgroup; return NULL; } void bsc_nat_paging_group_add_lac(struct bsc_nat_paging_group *pgroup, int lac) { _add_lac(pgroup, &pgroup->lists, lac); } void bsc_nat_paging_group_del_lac(struct bsc_nat_paging_group *pgroup, int lac) { _del_lac(&pgroup->lists, lac); } int bsc_config_handles_lac(struct bsc_config *cfg, int lac_nr) { struct bsc_nat_paging_group *pgroup; struct bsc_lac_entry *entry; llist_for_each_entry(entry, &cfg->lac_list, entry) if (entry->lac == lac_nr) return 1; /* now lookup the paging group */ pgroup = bsc_nat_paging_group_num(cfg->nat, cfg->paging_group); if (!pgroup) return 0; llist_for_each_entry(entry, &pgroup->lists, entry) if (entry->lac == lac_nr) return 1; return 0; } void sccp_connection_destroy(struct nat_sccp_connection *conn) { LOGP(DNAT, LOGL_DEBUG, "Destroy 0x%x <-> 0x%x mapping for con %p\n", sccp_src_ref_to_int(&conn->real_ref), sccp_src_ref_to_int(&conn->patched_ref), conn->bsc); bsc_mgcp_dlcx(conn); llist_del(&conn->list_entry); talloc_free(conn); } int bsc_nat_find_paging(struct msgb *msg, const uint8_t **out_data, int *out_leng) { int data_length; const uint8_t *data; struct tlv_parsed tp; if (!msg->l3h || msgb_l3len(msg) < 3) { LOGP(DNAT, LOGL_ERROR, "Paging message is too short.\n"); return -1; } tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 3, msgb_l3len(msg) - 3, 0, 0); if (!TLVP_PRESENT(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST)) { LOGP(DNAT, LOGL_ERROR, "No CellIdentifier List inside paging msg.\n"); return -2; } data_length = TLVP_LEN(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST); data = TLVP_VAL(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST); /* No need to try a different BSS */ if (data[0] == CELL_IDENT_BSS) { return -3; } else if (data[0] != CELL_IDENT_LAC) { LOGP(DNAT, LOGL_ERROR, "Unhandled cell ident discrminator: %d\n", data[0]); return -4; } *out_data = &data[1]; *out_leng = data_length - 1; return 0; } int bsc_write_mgcp(struct bsc_connection *bsc, const uint8_t *data, unsigned int length) { struct msgb *msg; if (length > 4096 - 128) { LOGP(DLINP, LOGL_ERROR, "Can not send message of that size.\n"); return -1; } msg = msgb_alloc_headroom(4096, 128, "to-bsc"); if (!msg) { LOGP(DLINP, LOGL_ERROR, "Failed to allocate memory for BSC msg.\n"); return -1; } /* copy the data */ msg->l3h = msgb_put(msg, length); memcpy(msg->l3h, data, length); return bsc_write(bsc, msg, IPAC_PROTO_MGCP_OLD); } int bsc_write(struct bsc_connection *bsc, struct msgb *msg, int proto) { return bsc_do_write(&bsc->write_queue, msg, proto); } int bsc_do_write(struct osmo_wqueue *queue, struct msgb *msg, int proto) { /* prepend the header */ ipa_prepend_header(msg, proto); return bsc_write_msg(queue, msg); } int bsc_write_msg(struct osmo_wqueue *queue, struct msgb *msg) { if (osmo_wqueue_enqueue(queue, msg) != 0) { LOGP(DLINP, LOGL_ERROR, "Failed to enqueue the write.\n"); msgb_free(msg); return -1; } return 0; } struct gsm48_hdr *bsc_unpack_dtap(struct bsc_nat_parsed *parsed, struct msgb *msg, uint32_t *len) { /* gsm_type is actually the size of the dtap */ *len = parsed->gsm_type; if (*len < msgb_l3len(msg) - 3) { LOGP(DNAT, LOGL_ERROR, "Not enough space for DTAP.\n"); return NULL; } if (msgb_l3len(msg) - 3 < msg->l3h[2]) { LOGP(DNAT, LOGL_ERROR, "GSM48 payload does not fit: %d %d\n", msg->l3h[2], msgb_l3len(msg) - 3); return NULL; } msg->l4h = &msg->l3h[3]; return (struct gsm48_hdr *) msg->l4h; } static const char *con_types [] = { [FLT_CON_TYPE_NONE] = "n/a", [FLT_CON_TYPE_LU] = "Location Update", [FLT_CON_TYPE_CM_SERV_REQ] = "CM Serv Req", [FLT_CON_TYPE_PAG_RESP] = "Paging Response", [FLT_CON_TYPE_SSA] = "Supplementar Service Activation", [FLT_CON_TYPE_LOCAL_REJECT] = "Local Reject", [FLT_CON_TYPE_OTHER] = "Other", }; const char *bsc_con_type_to_string(int type) { return con_types[type]; } int bsc_nat_msc_is_connected(struct bsc_nat *nat) { return nat->msc_con->is_connected; } static const int con_to_ctr[] = { [FLT_CON_TYPE_NONE] = -1, [FLT_CON_TYPE_LU] = BCFG_CTR_CON_TYPE_LU, [FLT_CON_TYPE_CM_SERV_REQ] = BCFG_CTR_CON_CMSERV_RQ, [FLT_CON_TYPE_PAG_RESP] = BCFG_CTR_CON_PAG_RESP, [FLT_CON_TYPE_SSA] = BCFG_CTR_CON_SSA, [FLT_CON_TYPE_LOCAL_REJECT] = -1, [FLT_CON_TYPE_OTHER] = BCFG_CTR_CON_OTHER, }; int bsc_conn_type_to_ctr(struct nat_sccp_connection *conn) { return con_to_ctr[conn->filter_state.con_type]; } int bsc_write_cb(struct osmo_fd *bfd, struct msgb *msg) { int rc; rc = write(bfd->fd, msg->data, msg->len); if (rc != msg->len) LOGP(DNAT, LOGL_ERROR, "Failed to write message to the BSC.\n"); return rc; } static void extract_lac(const uint8_t *data, uint16_t *lac, uint16_t *ci) { memcpy(lac, &data[0], sizeof(*lac)); memcpy(ci, &data[2], sizeof(*ci)); *lac = ntohs(*lac); *ci = ntohs(*ci); } int bsc_nat_extract_lac(struct bsc_connection *bsc, struct nat_sccp_connection *con, struct bsc_nat_parsed *parsed, struct msgb *msg) { int data_length; const uint8_t *data; struct tlv_parsed tp; uint16_t lac, ci; if (parsed->gsm_type != BSS_MAP_MSG_COMPLETE_LAYER_3) { LOGP(DNAT, LOGL_ERROR, "Can only extract LAC from Complete Layer3\n"); return -1; } if (!msg->l3h || msgb_l3len(msg) < 3) { LOGP(DNAT, LOGL_ERROR, "Complete Layer3 mssage is too short.\n"); return -1; } tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 3, msgb_l3len(msg) - 3, 0, 0); if (!TLVP_PRESENT(&tp, GSM0808_IE_CELL_IDENTIFIER)) { LOGP(DNAT, LOGL_ERROR, "No CellIdentifier List inside paging msg.\n"); return -2; } data_length = TLVP_LEN(&tp, GSM0808_IE_CELL_IDENTIFIER); data = TLVP_VAL(&tp, GSM0808_IE_CELL_IDENTIFIER); /* Attemt to get the LAC/CI from it */ if (data[0] == CELL_IDENT_WHOLE_GLOBAL) { if (data_length != 8) { LOGP(DNAT, LOGL_ERROR, "Ident too short: %d\n", data_length); return -3; } extract_lac(&data[1 + 3], &lac, &ci); } else if (data[0] == CELL_IDENT_LAC_AND_CI) { if (data_length != 5) { LOGP(DNAT, LOGL_ERROR, "Ident too short: %d\n", data_length); return -3; } extract_lac(&data[1], &lac, &ci); } else { LOGP(DNAT, LOGL_ERROR, "Unhandled cell identifier: %d\n", data[0]); return -1; } con->lac = lac; con->ci = ci; return 0; } openbsc-0.15.0/openbsc/src/osmo-bsc_nat/bsc_nat_vty.c000066400000000000000000001051371265565154000225310ustar00rootroot00000000000000/* OpenBSC NAT interface to quagga VTY */ /* (C) 2010-2015 by Holger Hans Peter Freyther * (C) 2010-2015 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static struct bsc_nat *_nat; #define BSC_STR "Information about BSCs\n" #define MGCP_STR "MGCP related status\n" #define PAGING_STR "Paging\n" #define SMSC_REWRITE "SMSC Rewriting\n" static struct cmd_node nat_node = { NAT_NODE, "%s(config-nat)# ", 1, }; static struct cmd_node bsc_node = { NAT_BSC_NODE, "%s(config-nat-bsc)# ", 1, }; static struct cmd_node pgroup_node = { PGROUP_NODE, "%s(config-nat-paging-group)# ", 1, }; static int config_write_pgroup(struct vty *vty) { return CMD_SUCCESS; } static void dump_lac(struct vty *vty, struct llist_head *head) { struct bsc_lac_entry *lac; llist_for_each_entry(lac, head, entry) vty_out(vty, " location_area_code %u%s", lac->lac, VTY_NEWLINE); } static void write_pgroup_lst(struct vty *vty, struct bsc_nat_paging_group *pgroup) { vty_out(vty, " paging-group %d%s", pgroup->nr, VTY_NEWLINE); dump_lac(vty, &pgroup->lists); } static int config_write_nat(struct vty *vty) { struct bsc_msg_acc_lst *lst; struct bsc_nat_paging_group *pgroup; vty_out(vty, "nat%s", VTY_NEWLINE); vty_out(vty, " msc ip %s%s", _nat->main_dest->ip, VTY_NEWLINE); vty_out(vty, " msc port %d%s", _nat->main_dest->port, VTY_NEWLINE); vty_out(vty, " timeout auth %d%s", _nat->auth_timeout, VTY_NEWLINE); vty_out(vty, " timeout ping %d%s", _nat->ping_timeout, VTY_NEWLINE); vty_out(vty, " timeout pong %d%s", _nat->pong_timeout, VTY_NEWLINE); if (_nat->token) vty_out(vty, " token %s%s", _nat->token, VTY_NEWLINE); vty_out(vty, " ip-dscp %d%s", _nat->bsc_ip_dscp, VTY_NEWLINE); if (_nat->acc_lst_name) vty_out(vty, " access-list-name %s%s", _nat->acc_lst_name, VTY_NEWLINE); if (_nat->imsi_black_list_fn) vty_out(vty, " imsi-black-list-file-name %s%s", _nat->imsi_black_list_fn, VTY_NEWLINE); if (_nat->ussd_lst_name) vty_out(vty, " ussd-list-name %s%s", _nat->ussd_lst_name, VTY_NEWLINE); if (_nat->ussd_query) vty_out(vty, " ussd-query %s%s", _nat->ussd_query, VTY_NEWLINE); if (_nat->ussd_token) vty_out(vty, " ussd-token %s%s", _nat->ussd_token, VTY_NEWLINE); if (_nat->ussd_local) vty_out(vty, " ussd-local-ip %s%s", _nat->ussd_local, VTY_NEWLINE); if (_nat->num_rewr_name) vty_out(vty, " number-rewrite %s%s", _nat->num_rewr_name, VTY_NEWLINE); if (_nat->num_rewr_post_name) vty_out(vty, " number-rewrite-post %s%s", _nat->num_rewr_post_name, VTY_NEWLINE); if (_nat->smsc_rewr_name) vty_out(vty, " rewrite-smsc addr %s%s", _nat->smsc_rewr_name, VTY_NEWLINE); if (_nat->tpdest_match_name) vty_out(vty, " rewrite-smsc tp-dest-match %s%s", _nat->tpdest_match_name, VTY_NEWLINE); if (_nat->sms_clear_tp_srr_name) vty_out(vty, " sms-clear-tp-srr %s%s", _nat->sms_clear_tp_srr_name, VTY_NEWLINE); if (_nat->sms_num_rewr_name) vty_out(vty, " sms-number-rewrite %s%s", _nat->sms_num_rewr_name, VTY_NEWLINE); if (_nat->num_rewr_trie_name) vty_out(vty, " prefix-tree %s%s", _nat->num_rewr_trie_name, VTY_NEWLINE); llist_for_each_entry(lst, &_nat->access_lists, list) bsc_msg_acc_lst_write(vty, lst); llist_for_each_entry(pgroup, &_nat->paging_groups, entry) write_pgroup_lst(vty, pgroup); if (_nat->mgcp_ipa) vty_out(vty, " use-msc-ipa-for-mgcp%s", VTY_NEWLINE); vty_out(vty, " %ssdp-ensure-amr-mode-set%s", _nat->sdp_ensure_amr_mode_set ? "" : "no ", VTY_NEWLINE); return CMD_SUCCESS; } static void config_write_bsc_single(struct vty *vty, struct bsc_config *bsc) { vty_out(vty, " bsc %u%s", bsc->nr, VTY_NEWLINE); vty_out(vty, " token %s%s", bsc->token, VTY_NEWLINE); if (bsc->key_present) vty_out(vty, " auth-key %s%s", osmo_hexdump(bsc->key, 16), VTY_NEWLINE); dump_lac(vty, &bsc->lac_list); if (bsc->description) vty_out(vty, " description %s%s", bsc->description, VTY_NEWLINE); if (bsc->acc_lst_name) vty_out(vty, " access-list-name %s%s", bsc->acc_lst_name, VTY_NEWLINE); vty_out(vty, " max-endpoints %d%s", bsc->max_endpoints, VTY_NEWLINE); if (bsc->paging_group != -1) vty_out(vty, " paging group %d%s", bsc->paging_group, VTY_NEWLINE); vty_out(vty, " paging forbidden %d%s", bsc->forbid_paging, VTY_NEWLINE); switch (bsc->osmux) { case OSMUX_USAGE_ON: vty_out(vty, " osmux on%s", VTY_NEWLINE); break; case OSMUX_USAGE_ONLY: vty_out(vty, " osmux only%s", VTY_NEWLINE); break; } } static int config_write_bsc(struct vty *vty) { struct bsc_config *bsc; llist_for_each_entry(bsc, &_nat->bsc_configs, entry) config_write_bsc_single(vty, bsc); return CMD_SUCCESS; } DEFUN(show_sccp, show_sccp_cmd, "show sccp connections", SHOW_STR "Display information about SCCP\n" "All active connections\n") { struct nat_sccp_connection *con; vty_out(vty, "Listing all open SCCP connections%s", VTY_NEWLINE); llist_for_each_entry(con, &_nat->sccp_connections, list_entry) { vty_out(vty, "For BSC Nr: %d BSC ref: 0x%x; MUX ref: 0x%x; Network has ref: %d ref: 0x%x MSC/BSC mux: 0x%x/0x%x type: %s%s", con->bsc->cfg ? con->bsc->cfg->nr : -1, sccp_src_ref_to_int(&con->real_ref), sccp_src_ref_to_int(&con->patched_ref), con->has_remote_ref, sccp_src_ref_to_int(&con->remote_ref), con->msc_endp, con->bsc_endp, bsc_con_type_to_string(con->filter_state.con_type), VTY_NEWLINE); } return CMD_SUCCESS; } DEFUN(show_bsc, show_bsc_cmd, "show bsc connections", SHOW_STR BSC_STR "All active connections\n") { struct bsc_connection *con; struct sockaddr_in sock; socklen_t len = sizeof(sock); llist_for_each_entry(con, &_nat->bsc_connections, list_entry) { getpeername(con->write_queue.bfd.fd, (struct sockaddr *) &sock, &len); vty_out(vty, "BSC nr: %d auth: %d fd: %d peername: %s pending-stats: %u%s", con->cfg ? con->cfg->nr : -1, con->authenticated, con->write_queue.bfd.fd, inet_ntoa(sock.sin_addr), con->pending_dlcx_count, VTY_NEWLINE); } return CMD_SUCCESS; } DEFUN(show_bsc_mgcp, show_bsc_mgcp_cmd, "show bsc mgcp NR", SHOW_STR BSC_STR MGCP_STR "Identifier of the BSC\n") { struct bsc_connection *con; int nr = atoi(argv[0]); int i, j, endp; llist_for_each_entry(con, &_nat->bsc_connections, list_entry) { int max; if (!con->cfg) continue; if (con->cfg->nr != nr) continue; /* this bsc has no audio endpoints yet */ if (!con->_endpoint_status) continue; vty_out(vty, "MGCP Status for %d%s", con->cfg->nr, VTY_NEWLINE); max = bsc_mgcp_nr_multiplexes(con->max_endpoints); for (i = 0; i < max; ++i) { for (j = 1; j < 32; ++j) { endp = mgcp_timeslot_to_endpoint(i, j); vty_out(vty, " Endpoint 0x%x %s%s", endp, con->_endpoint_status[endp] == 0 ? "free" : "allocated", VTY_NEWLINE); } } break; } return CMD_SUCCESS; } DEFUN(show_bsc_cfg, show_bsc_cfg_cmd, "show bsc config", SHOW_STR BSC_STR "Configuration of BSCs\n") { struct bsc_config *conf; llist_for_each_entry(conf, &_nat->bsc_configs, entry) { vty_out(vty, "BSC token: '%s' nr: %u%s", conf->token, conf->nr, VTY_NEWLINE); if (conf->acc_lst_name) vty_out(vty, " access-list: %s%s", conf->acc_lst_name, VTY_NEWLINE); vty_out(vty, " paging forbidden: %d%s", conf->forbid_paging, VTY_NEWLINE); if (conf->description) vty_out(vty, " description: %s%s", conf->description, VTY_NEWLINE); else vty_out(vty, " No description.%s", VTY_NEWLINE); } return CMD_SUCCESS; } static void dump_stat_total(struct vty *vty, struct bsc_nat *nat) { vty_out(vty, "NAT statistics%s", VTY_NEWLINE); vty_out(vty, " SCCP Connections %lu total, %lu calls%s", osmo_counter_get(nat->stats.sccp.conn), osmo_counter_get(nat->stats.sccp.calls), VTY_NEWLINE); vty_out(vty, " MSC Connections %lu%s", osmo_counter_get(nat->stats.msc.reconn), VTY_NEWLINE); vty_out(vty, " MSC Connected: %d%s", bsc_nat_msc_is_connected(nat), VTY_NEWLINE); vty_out(vty, " BSC Connections %lu total, %lu auth failed.%s", osmo_counter_get(nat->stats.bsc.reconn), osmo_counter_get(nat->stats.bsc.auth_fail), VTY_NEWLINE); } static void dump_stat_bsc(struct vty *vty, struct bsc_config *conf) { int connected = 0; struct bsc_connection *con; vty_out(vty, " BSC nr: %d%s", conf->nr, VTY_NEWLINE); vty_out_rate_ctr_group(vty, " ", conf->stats.ctrg); llist_for_each_entry(con, &conf->nat->bsc_connections, list_entry) { if (con->cfg != conf) continue; connected = 1; break; } vty_out(vty, " Connected: %d%s", connected, VTY_NEWLINE); } DEFUN(show_stats, show_stats_cmd, "show statistics [NR]", SHOW_STR "Display network statistics\n" "Number of the BSC\n") { struct bsc_config *conf; int nr = -1; if (argc == 1) nr = atoi(argv[0]); dump_stat_total(vty, _nat); llist_for_each_entry(conf, &_nat->bsc_configs, entry) { if (argc == 1 && nr != conf->nr) continue; dump_stat_bsc(vty, conf); } return CMD_SUCCESS; } DEFUN(show_stats_lac, show_stats_lac_cmd, "show statistics-by-lac <0-65535>", SHOW_STR "Display network statistics by lac\n" "The lac of the BSC\n") { int lac; struct bsc_config *conf; lac = atoi(argv[0]); dump_stat_total(vty, _nat); llist_for_each_entry(conf, &_nat->bsc_configs, entry) { if (!bsc_config_handles_lac(conf, lac)) continue; dump_stat_bsc(vty, conf); } return CMD_SUCCESS; } DEFUN(show_msc, show_msc_cmd, "show msc connection", SHOW_STR "MSC related information\n" "Status of the A-link connection\n") { if (!_nat->msc_con) { vty_out(vty, "The MSC is not yet configured.%s", VTY_NEWLINE); return CMD_WARNING; } vty_out(vty, "MSC is connected: %d%s", bsc_nat_msc_is_connected(_nat), VTY_NEWLINE); return CMD_SUCCESS; } DEFUN(close_bsc, close_bsc_cmd, "close bsc connection BSC_NR", "Close\n" "A-link\n" "Connection\n" "Identifier of the BSC\n") { struct bsc_connection *bsc; int bsc_nr = atoi(argv[0]); llist_for_each_entry(bsc, &_nat->bsc_connections, list_entry) { if (!bsc->cfg || bsc->cfg->nr != bsc_nr) continue; bsc_close_connection(bsc); break; } return CMD_SUCCESS; } DEFUN(cfg_nat, cfg_nat_cmd, "nat", "Configure the NAT") { vty->index = _nat; vty->node = NAT_NODE; return CMD_SUCCESS; } DEFUN(cfg_nat_msc_ip, cfg_nat_msc_ip_cmd, "msc ip A.B.C.D", "MSC related configuration\n" "Configure the IP address\n" IP_STR) { bsc_nat_set_msc_ip(_nat, argv[0]); return CMD_SUCCESS; } DEFUN(cfg_nat_msc_port, cfg_nat_msc_port_cmd, "msc port <1-65500>", "MSC related configuration\n" "Configure the port\n" "Port number\n") { _nat->main_dest->port = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_nat_auth_time, cfg_nat_auth_time_cmd, "timeout auth <1-256>", "Timeout configuration\n" "Authentication timeout\n" "Timeout in seconds\n") { _nat->auth_timeout = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_nat_ping_time, cfg_nat_ping_time_cmd, "timeout ping NR", "Timeout configuration\n" "Time between two pings\n" "Timeout in seconds\n") { _nat->ping_timeout = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_nat_pong_time, cfg_nat_pong_time_cmd, "timeout pong NR", "Timeout configuration\n" "Waiting for pong timeout\n" "Timeout in seconds\n") { _nat->pong_timeout = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_nat_token, cfg_nat_token_cmd, "token TOKEN", "Authentication token configuration\n" "Token of the BSC, currently transferred in cleartext\n") { bsc_replace_string(_nat, &_nat->token, argv[0]); return CMD_SUCCESS; } DEFUN(cfg_nat_bsc_ip_dscp, cfg_nat_bsc_ip_dscp_cmd, "ip-dscp <0-255>", "Set the IP DSCP for the BSCs to use\n" "Set the IP_TOS attribute") { _nat->bsc_ip_dscp = atoi(argv[0]); return CMD_SUCCESS; } ALIAS_DEPRECATED(cfg_nat_bsc_ip_dscp, cfg_nat_bsc_ip_tos_cmd, "ip-tos <0-255>", "Use ip-dscp in the future.\n" "Set the DSCP\n") DEFUN(cfg_nat_acc_lst_name, cfg_nat_acc_lst_name_cmd, "access-list-name NAME", "Set the name of the access list to use.\n" "The name of the to be used access list.") { bsc_replace_string(_nat, &_nat->acc_lst_name, argv[0]); return CMD_SUCCESS; } DEFUN(cfg_nat_no_acc_lst_name, cfg_nat_no_acc_lst_name_cmd, "no access-list-name", NO_STR "Remove the access list from the NAT.\n") { if (_nat->acc_lst_name) { talloc_free(_nat->acc_lst_name); _nat->acc_lst_name = NULL; } return CMD_SUCCESS; } DEFUN(cfg_nat_imsi_black_list_fn, cfg_nat_imsi_black_list_fn_cmd, "imsi-black-list-file-name NAME", "IMSI black listing\n" "Filename IMSI and reject-cause\n") { bsc_replace_string(_nat, &_nat->imsi_black_list_fn, argv[0]); if (_nat->imsi_black_list_fn) { int rc; struct osmo_config_list *rewr = NULL; rewr = osmo_config_list_parse(_nat, _nat->imsi_black_list_fn); rc = bsc_filter_barr_adapt(_nat, &_nat->imsi_black_list, rewr); if (rc != 0) { vty_out(vty, "%%There was an error parsing the list." " Please see the error log.%s", VTY_NEWLINE); return CMD_WARNING; } return CMD_SUCCESS; } bsc_filter_barr_adapt(_nat, &_nat->imsi_black_list, NULL); return CMD_SUCCESS; } DEFUN(cfg_nat_no_imsi_black_list_fn, cfg_nat_no_imsi_black_list_fn_cmd, "no imsi-black-list-file-name", NO_STR "Remove the imsi-black-list\n") { talloc_free(_nat->imsi_black_list_fn); _nat->imsi_black_list_fn = NULL; bsc_filter_barr_adapt(_nat, &_nat->imsi_black_list, NULL); return CMD_SUCCESS; } static int replace_rules(struct bsc_nat *nat, char **name, struct llist_head *head, const char *file) { struct osmo_config_list *rewr = NULL; bsc_replace_string(nat, name, file); if (*name) { rewr = osmo_config_list_parse(nat, *name); bsc_nat_num_rewr_entry_adapt(nat, head, rewr); talloc_free(rewr); return CMD_SUCCESS; } else { bsc_nat_num_rewr_entry_adapt(nat, head, NULL); return CMD_SUCCESS; } } DEFUN(cfg_nat_number_rewrite, cfg_nat_number_rewrite_cmd, "number-rewrite FILENAME", "Set the file with rewriting rules.\n" "Filename") { return replace_rules(_nat, &_nat->num_rewr_name, &_nat->num_rewr, argv[0]); } DEFUN(cfg_nat_no_number_rewrite, cfg_nat_no_number_rewrite_cmd, "no number-rewrite", NO_STR "Set the file with rewriting rules.\n") { talloc_free(_nat->num_rewr_name); _nat->num_rewr_name = NULL; bsc_nat_num_rewr_entry_adapt(NULL, &_nat->num_rewr, NULL); return CMD_SUCCESS; } DEFUN(cfg_nat_number_rewrite_post, cfg_nat_number_rewrite_post_cmd, "number-rewrite-post FILENAME", "Set the file with post-routing rewriting rules.\n" "Filename") { return replace_rules(_nat, &_nat->num_rewr_post_name, &_nat->num_rewr_post, argv[0]); } DEFUN(cfg_nat_no_number_rewrite_post, cfg_nat_no_number_rewrite_post_cmd, "no number-rewrite-post", NO_STR "Set the file with post-routing rewriting rules.\n") { talloc_free(_nat->num_rewr_post_name); _nat->num_rewr_post_name = NULL; bsc_nat_num_rewr_entry_adapt(NULL, &_nat->num_rewr_post, NULL); return CMD_SUCCESS; } DEFUN(cfg_nat_smsc_addr, cfg_nat_smsc_addr_cmd, "rewrite-smsc addr FILENAME", SMSC_REWRITE "The SMSC Address to match and replace in RP-DATA\n" "File with rules for the SMSC Address replacing\n") { return replace_rules(_nat, &_nat->smsc_rewr_name, &_nat->smsc_rewr, argv[0]); } DEFUN(cfg_nat_smsc_tpdest, cfg_nat_smsc_tpdest_cmd, "rewrite-smsc tp-dest-match FILENAME", SMSC_REWRITE "Match TP-Destination of a SMS.\n" "File with rules for matching MSISDN and TP-DEST\n") { return replace_rules(_nat, &_nat->tpdest_match_name, &_nat->tpdest_match, argv[0]); } DEFUN(cfg_nat_sms_clear_tpsrr, cfg_nat_sms_clear_tpsrr_cmd, "sms-clear-tp-srr FILENAME", "SMS TPDU Sender Report Request clearing\n" "Files with rules for matching MSISDN\n") { return replace_rules(_nat, &_nat->sms_clear_tp_srr_name, &_nat->sms_clear_tp_srr, argv[0]); } DEFUN(cfg_nat_no_sms_clear_tpsrr, cfg_nat_no_sms_clear_tpsrr_cmd, "no sms-clear-tp-srr", NO_STR "SMS TPDU Sender Report Request clearing\n") { talloc_free(_nat->sms_clear_tp_srr_name); _nat->sms_clear_tp_srr_name = NULL; bsc_nat_num_rewr_entry_adapt(NULL, &_nat->sms_clear_tp_srr, NULL); return CMD_SUCCESS; } DEFUN(cfg_nat_sms_number_rewrite, cfg_nat_sms_number_rewrite_cmd, "sms-number-rewrite FILENAME", "SMS TP-DA Number rewriting\n" "Files with rules for matching MSISDN\n") { return replace_rules(_nat, &_nat->sms_num_rewr_name, &_nat->sms_num_rewr, argv[0]); } DEFUN(cfg_nat_no_sms_number_rewrite, cfg_nat_no_sms_number_rewrite_cmd, "no sms-number-rewrite", NO_STR "Disable SMS TP-DA rewriting\n") { talloc_free(_nat->sms_num_rewr_name); _nat->sms_num_rewr_name = NULL; bsc_nat_num_rewr_entry_adapt(NULL, &_nat->sms_num_rewr, NULL); return CMD_SUCCESS; } DEFUN(cfg_nat_prefix_trie, cfg_nat_prefix_trie_cmd, "prefix-tree FILENAME", "Prefix tree for number rewriting\n" "File to load\n") { /* give up the old data */ talloc_free(_nat->num_rewr_trie); _nat->num_rewr_trie = NULL; /* replace the file name */ bsc_replace_string(_nat, &_nat->num_rewr_trie_name, argv[0]); if (!_nat->num_rewr_trie_name) { vty_out(vty, "%% prefix-tree no filename is present.%s", VTY_NEWLINE); return CMD_WARNING; } _nat->num_rewr_trie = nat_rewrite_parse(_nat, _nat->num_rewr_trie_name); if (!_nat->num_rewr_trie) { vty_out(vty, "%% prefix-tree parsing has failed.%s", VTY_NEWLINE); return CMD_WARNING; } vty_out(vty, "%% prefix-tree loaded %zu rules.%s", _nat->num_rewr_trie->prefixes, VTY_NEWLINE); return CMD_SUCCESS; } DEFUN(cfg_nat_no_prefix_trie, cfg_nat_no_prefix_trie_cmd, "no prefix-tree", NO_STR "Prefix tree for number rewriting\n") { talloc_free(_nat->num_rewr_trie); _nat->num_rewr_trie = NULL; talloc_free(_nat->num_rewr_trie_name); _nat->num_rewr_trie_name = NULL; return CMD_SUCCESS; } DEFUN(show_prefix_tree, show_prefix_tree_cmd, "show prefix-tree", SHOW_STR "Prefix tree for number rewriting\n") { if (!_nat->num_rewr_trie) { vty_out(vty, "%% there is now prefix tree loaded.%s", VTY_NEWLINE); return CMD_WARNING; } nat_rewrite_dump_vty(vty, _nat->num_rewr_trie); return CMD_SUCCESS; } DEFUN(cfg_nat_ussd_lst_name, cfg_nat_ussd_lst_name_cmd, "ussd-list-name NAME", "Set the name of the access list to check for IMSIs for USSD message\n" "The name of the access list for HLR USSD handling") { bsc_replace_string(_nat, &_nat->ussd_lst_name, argv[0]); return CMD_SUCCESS; } DEFUN(cfg_nat_ussd_query, cfg_nat_ussd_query_cmd, "ussd-query REGEXP", "Set the USSD query to match with the ussd-list-name\n" "The query to match") { if (gsm_parse_reg(_nat, &_nat->ussd_query_re, &_nat->ussd_query, argc, argv) != 0) return CMD_WARNING; return CMD_SUCCESS; } DEFUN(cfg_nat_ussd_token, cfg_nat_ussd_token_cmd, "ussd-token TOKEN", "Set the token used to identify the USSD module\n" "Secret key\n") { bsc_replace_string(_nat, &_nat->ussd_token, argv[0]); return CMD_SUCCESS; } DEFUN(cfg_nat_ussd_local, cfg_nat_ussd_local_cmd, "ussd-local-ip A.B.C.D", "Set the IP to listen for the USSD Provider\n" "IP Address\n") { bsc_replace_string(_nat, &_nat->ussd_local, argv[0]); return CMD_SUCCESS; } DEFUN(cfg_nat_use_ipa_for_mgcp, cfg_nat_use_ipa_for_mgcp_cmd, "use-msc-ipa-for-mgcp", "This needs to be set at start. Handle MGCP messages through " "the IPA protocol and not through the UDP socket.\n") { if (_nat->mgcp_cfg->data) vty_out(vty, "%%the setting will not be applied right now.%s", VTY_NEWLINE); _nat->mgcp_ipa = 1; return CMD_SUCCESS; } DEFUN(cfg_nat_sdp_amr_mode_set, cfg_nat_sdp_amr_mode_set_cmd, "sdp-ensure-amr-mode-set", "Ensure that SDP records include a mode-set\n") { _nat->sdp_ensure_amr_mode_set = 1; return CMD_SUCCESS; } DEFUN(cfg_nat_no_sdp_amr_mode_set, cfg_nat_no_sdp_amr_mode_set_cmd, "no sdp-ensure-amr-mode-set", NO_STR "Ensure that SDP records include a mode-set\n") { _nat->sdp_ensure_amr_mode_set = 0; return CMD_SUCCESS; } /* per BSC configuration */ DEFUN(cfg_bsc, cfg_bsc_cmd, "bsc BSC_NR", "BSC configuration\n" "Identifier of the BSC\n") { int bsc_nr = atoi(argv[0]); struct bsc_config *bsc; if (bsc_nr > _nat->num_bsc) { vty_out(vty, "%% The next unused BSC number is %u%s", _nat->num_bsc, VTY_NEWLINE); return CMD_WARNING; } else if (bsc_nr == _nat->num_bsc) { /* allocate a new one */ bsc = bsc_config_alloc(_nat, "unknown"); } else bsc = bsc_config_num(_nat, bsc_nr); if (!bsc) return CMD_WARNING; vty->index = bsc; vty->node = NAT_BSC_NODE; return CMD_SUCCESS; } DEFUN(cfg_bsc_token, cfg_bsc_token_cmd, "token TOKEN", "Authentication token configuration\n" "Token of the BSC, currently transferred in cleartext\n") { struct bsc_config *conf = vty->index; bsc_replace_string(conf, &conf->token, argv[0]); return CMD_SUCCESS; } DEFUN(cfg_bsc_auth_key, cfg_bsc_auth_key_cmd, "auth-key KEY", "Authentication (secret) key configuration\n" "Non null security key\n") { struct bsc_config *conf = vty->index; memset(conf->key, 0, sizeof(conf->key)); osmo_hexparse(argv[0], conf->key, sizeof(conf->key)); conf->key_present = 1; return CMD_SUCCESS; } DEFUN(cfg_bsc_no_auth_key, cfg_bsc_no_auth_key_cmd, "no auth-key", NO_STR "Authentication (secret) key configuration\n") { struct bsc_config *conf = vty->index; memset(conf->key, 0, sizeof(conf->key)); conf->key_present = 0; return CMD_SUCCESS; } DEFUN(cfg_bsc_lac, cfg_bsc_lac_cmd, "location_area_code <0-65535>", "Add the Location Area Code (LAC) of this BSC\n" "LAC\n") { struct bsc_config *tmp; struct bsc_config *conf = vty->index; int lac = atoi(argv[0]); if (lac == GSM_LAC_RESERVED_DETACHED || lac == GSM_LAC_RESERVED_ALL_BTS) { vty_out(vty, "%% LAC %d is reserved by GSM 04.08%s", lac, VTY_NEWLINE); return CMD_WARNING; } /* verify that the LACs are unique */ llist_for_each_entry(tmp, &_nat->bsc_configs, entry) { if (bsc_config_handles_lac(tmp, lac)) { vty_out(vty, "%% LAC %d is already used.%s", lac, VTY_NEWLINE); return CMD_ERR_INCOMPLETE; } } bsc_config_add_lac(conf, lac); return CMD_SUCCESS; } DEFUN(cfg_bsc_no_lac, cfg_bsc_no_lac_cmd, "no location_area_code <0-65535>", NO_STR "Remove the Location Area Code (LAC) of this BSC\n" "LAC\n") { int lac = atoi(argv[0]); struct bsc_config *conf = vty->index; bsc_config_del_lac(conf, lac); return CMD_SUCCESS; } DEFUN(show_bar_lst, show_bar_lst_cmd, "show imsi-black-list", SHOW_STR "IMSIs barred from the network\n") { struct rb_node *node; vty_out(vty, "IMSIs barred from the network:%s", VTY_NEWLINE); for (node = rb_first(&_nat->imsi_black_list); node; node = rb_next(node)) { struct bsc_filter_barr_entry *entry; entry = rb_entry(node, struct bsc_filter_barr_entry, node); vty_out(vty, " IMSI(%s) CM-Reject-Cause(%d) LU-Reject-Cause(%d)%s", entry->imsi, entry->cm_reject_cause, entry->lu_reject_cause, VTY_NEWLINE); } return CMD_SUCCESS; } DEFUN(cfg_bsc_acc_lst_name, cfg_bsc_acc_lst_name_cmd, "access-list-name NAME", "Set the name of the access list to use.\n" "The name of the to be used access list.") { struct bsc_config *conf = vty->index; bsc_replace_string(conf, &conf->acc_lst_name, argv[0]); return CMD_SUCCESS; } DEFUN(cfg_bsc_no_acc_lst_name, cfg_bsc_no_acc_lst_name_cmd, "no access-list-name", NO_STR "Do not use an access-list for the BSC.\n") { struct bsc_config *conf = vty->index; if (conf->acc_lst_name) { talloc_free(conf->acc_lst_name); conf->acc_lst_name = NULL; } return CMD_SUCCESS; } DEFUN(cfg_bsc_max_endps, cfg_bsc_max_endps_cmd, "max-endpoints <1-1024>", "Highest endpoint to use (exclusively)\n" "Number of ports\n") { struct bsc_config *conf = vty->index; conf->max_endpoints = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_bsc_paging, cfg_bsc_paging_cmd, "paging forbidden (0|1)", PAGING_STR "Forbid sending PAGING REQUESTS to the BSC.\n" "Do not forbid\n" "Forbid\n") { struct bsc_config *conf = vty->index; if (strcmp("1", argv[0]) == 0) conf->forbid_paging = 1; else conf->forbid_paging = 0; return CMD_SUCCESS; } DEFUN(cfg_bsc_desc, cfg_bsc_desc_cmd, "description DESC", "Provide a description for the given BSC.\n" "Description\n") { struct bsc_config *conf = vty->index; bsc_replace_string(conf, &conf->description, argv[0]); return CMD_SUCCESS; } DEFUN(cfg_bsc_paging_grp, cfg_bsc_paging_grp_cmd, "paging group <0-1000>", PAGING_STR "Use a paging group\n" "Paging Group to use\n") { struct bsc_config *conf = vty->index; conf->paging_group = atoi(argv[0]); return CMD_SUCCESS; } ALIAS_DEPRECATED(cfg_bsc_paging_grp, cfg_bsc_old_grp_cmd, "paging-group <0-1000>", "Use a paging group\n" "Paging Group to use\n") DEFUN(cfg_bsc_no_paging_grp, cfg_bsc_no_paging_grp_cmd, "no paging group", NO_STR PAGING_STR "Disable the usage of a paging group.\n") { struct bsc_config *conf = vty->index; conf->paging_group = PAGIN_GROUP_UNASSIGNED; return CMD_SUCCESS; } DEFUN(test_regex, test_regex_cmd, "test regex PATTERN STRING", "Test utilities\n" "Regexp testing\n" "The regexp pattern\n" "The string to match\n") { regex_t reg; char *str = NULL; memset(®, 0, sizeof(reg)); if (gsm_parse_reg(_nat, ®, &str, 1, argv) != 0) return CMD_WARNING; vty_out(vty, "String matches allow pattern: %d%s", regexec(®, argv[1], 0, NULL, 0) == 0, VTY_NEWLINE); talloc_free(str); regfree(®); return CMD_SUCCESS; } DEFUN(set_last_endp, set_last_endp_cmd, "set bsc last-used-endpoint <0-9999999999> <0-1024>", "Set a value\n" "Operate on a BSC\n" "Last used endpoint for an assignment\n" "BSC configuration number\n" "Endpoint number used\n") { struct bsc_connection *con; int nr = atoi(argv[0]); int endp = atoi(argv[1]); llist_for_each_entry(con, &_nat->bsc_connections, list_entry) { if (!con->cfg) continue; if (con->cfg->nr != nr) continue; con->last_endpoint = endp; vty_out(vty, "Updated the last endpoint for %d to %d.%s", con->cfg->nr, con->last_endpoint, VTY_NEWLINE); } return CMD_SUCCESS; } DEFUN(block_new_conn, block_new_conn_cmd, "nat-block (block|unblock)", "Block the NAT for new connections\n" "Block\n" "Unblock\n") { _nat->blocked = argv[0][0] == 'b'; vty_out(vty, "%%Going to %s the NAT.%s", _nat->blocked ? "block" : "unblock", VTY_NEWLINE); return CMD_SUCCESS; } /* paging group */ DEFUN(cfg_nat_pgroup, cfg_nat_pgroup_cmd, "paging-group <0-1000>", "Create a Paging Group\n" "Number of the Group\n") { int group = atoi(argv[0]); struct bsc_nat_paging_group *pgroup; pgroup = bsc_nat_paging_group_num(_nat, group); if (!pgroup) pgroup = bsc_nat_paging_group_create(_nat, group); if (!pgroup) { vty_out(vty, "Failed to create the group.%s", VTY_NEWLINE); return CMD_WARNING; } vty->index = pgroup; vty->node = PGROUP_NODE; return CMD_SUCCESS; } DEFUN(cfg_nat_no_pgroup, cfg_nat_no_pgroup_cmd, "no paging-group <0-1000>", NO_STR "Delete paging-group\n" "Paging-group number\n") { int group = atoi(argv[0]); struct bsc_nat_paging_group *pgroup; pgroup = bsc_nat_paging_group_num(_nat, group); if (!pgroup) { vty_out(vty, "No such paging group %d.%s", group, VTY_NEWLINE); return CMD_WARNING; } bsc_nat_paging_group_delete(pgroup); return CMD_SUCCESS; } DEFUN(cfg_pgroup_lac, cfg_pgroup_lac_cmd, "location_area_code <0-65535>", "Add the Location Area Code (LAC)\n" "LAC\n") { struct bsc_nat_paging_group *pgroup = vty->index; int lac = atoi(argv[0]); if (lac == GSM_LAC_RESERVED_DETACHED || lac == GSM_LAC_RESERVED_ALL_BTS) { vty_out(vty, "%% LAC %d is reserved by GSM 04.08%s", lac, VTY_NEWLINE); return CMD_WARNING; } bsc_nat_paging_group_add_lac(pgroup, lac); return CMD_SUCCESS; } DEFUN(cfg_pgroup_no_lac, cfg_pgroup_no_lac_cmd, "no location_area_code <0-65535>", NO_STR "Remove the Location Area Code (LAC)\n" "LAC\n") { int lac = atoi(argv[0]); struct bsc_nat_paging_group *pgroup = vty->index; bsc_nat_paging_group_del_lac(pgroup, lac); return CMD_SUCCESS; } DEFUN(show_ussd_connection, show_ussd_connection_cmd, "show ussd-connection", SHOW_STR "USSD connection related information\n") { vty_out(vty, "The USSD side channel provider is %sconnected and %sauthorized.%s", _nat->ussd_con ? "" : "not ", _nat->ussd_con && _nat->ussd_con->authorized? "" : "not ", VTY_NEWLINE); return CMD_SUCCESS; } #define OSMUX_STR "RTP multiplexing\n" DEFUN(cfg_bsc_osmux, cfg_bsc_osmux_cmd, "osmux (on|off|only)", OSMUX_STR "Enable OSMUX\n" "Disable OSMUX\n" "Only OSMUX\n") { struct bsc_config *conf = vty->index; int old = conf->osmux; if (strcmp(argv[0], "on") == 0) conf->osmux = OSMUX_USAGE_ON; else if (strcmp(argv[0], "off") == 0) conf->osmux = OSMUX_USAGE_OFF; else if (strcmp(argv[0], "only") == 0) conf->osmux = OSMUX_USAGE_ONLY; if (old == 0 && conf->osmux > 0 && !conf->nat->mgcp_cfg->osmux_init) { LOGP(DMGCP, LOGL_NOTICE, "Setting up OSMUX socket\n"); if (osmux_init(OSMUX_ROLE_BSC_NAT, conf->nat->mgcp_cfg) < 0) { LOGP(DMGCP, LOGL_ERROR, "Cannot init OSMUX\n"); vty_out(vty, "%% failed to create Osmux socket%s", VTY_NEWLINE); return CMD_WARNING; } } else if (old > 0 && conf->osmux == 0) { LOGP(DMGCP, LOGL_NOTICE, "Disabling OSMUX socket\n"); /* Don't stop the socket, we may already have ongoing voice * flows already using Osmux. This just switch indicates that * new upcoming flows should use RTP. */ } return CMD_SUCCESS; } int bsc_nat_vty_init(struct bsc_nat *nat) { _nat = nat; /* show commands */ install_element_ve(&show_sccp_cmd); install_element_ve(&show_bsc_cmd); install_element_ve(&show_bsc_cfg_cmd); install_element_ve(&show_stats_cmd); install_element_ve(&show_stats_lac_cmd); install_element_ve(&close_bsc_cmd); install_element_ve(&show_msc_cmd); install_element_ve(&test_regex_cmd); install_element_ve(&show_bsc_mgcp_cmd); install_element_ve(&show_bar_lst_cmd); install_element_ve(&show_prefix_tree_cmd); install_element_ve(&show_ussd_connection_cmd); install_element(ENABLE_NODE, &set_last_endp_cmd); install_element(ENABLE_NODE, &block_new_conn_cmd); /* nat group */ install_element(CONFIG_NODE, &cfg_nat_cmd); install_node(&nat_node, config_write_nat); vty_install_default(NAT_NODE); install_element(NAT_NODE, &cfg_nat_msc_ip_cmd); install_element(NAT_NODE, &cfg_nat_msc_port_cmd); install_element(NAT_NODE, &cfg_nat_auth_time_cmd); install_element(NAT_NODE, &cfg_nat_ping_time_cmd); install_element(NAT_NODE, &cfg_nat_pong_time_cmd); install_element(NAT_NODE, &cfg_nat_token_cmd); install_element(NAT_NODE, &cfg_nat_bsc_ip_dscp_cmd); install_element(NAT_NODE, &cfg_nat_bsc_ip_tos_cmd); install_element(NAT_NODE, &cfg_nat_acc_lst_name_cmd); install_element(NAT_NODE, &cfg_nat_no_acc_lst_name_cmd); install_element(NAT_NODE, &cfg_nat_imsi_black_list_fn_cmd); install_element(NAT_NODE, &cfg_nat_no_imsi_black_list_fn_cmd); install_element(NAT_NODE, &cfg_nat_ussd_lst_name_cmd); install_element(NAT_NODE, &cfg_nat_ussd_query_cmd); install_element(NAT_NODE, &cfg_nat_ussd_token_cmd); install_element(NAT_NODE, &cfg_nat_ussd_local_cmd); install_element(NAT_NODE, &cfg_nat_use_ipa_for_mgcp_cmd); bsc_msg_lst_vty_init(nat, &nat->access_lists, NAT_NODE); /* number rewriting */ install_element(NAT_NODE, &cfg_nat_number_rewrite_cmd); install_element(NAT_NODE, &cfg_nat_no_number_rewrite_cmd); install_element(NAT_NODE, &cfg_nat_number_rewrite_post_cmd); install_element(NAT_NODE, &cfg_nat_no_number_rewrite_post_cmd); install_element(NAT_NODE, &cfg_nat_smsc_addr_cmd); install_element(NAT_NODE, &cfg_nat_smsc_tpdest_cmd); install_element(NAT_NODE, &cfg_nat_sms_clear_tpsrr_cmd); install_element(NAT_NODE, &cfg_nat_no_sms_clear_tpsrr_cmd); install_element(NAT_NODE, &cfg_nat_sms_number_rewrite_cmd); install_element(NAT_NODE, &cfg_nat_no_sms_number_rewrite_cmd); install_element(NAT_NODE, &cfg_nat_prefix_trie_cmd); install_element(NAT_NODE, &cfg_nat_no_prefix_trie_cmd); install_element(NAT_NODE, &cfg_nat_sdp_amr_mode_set_cmd); install_element(NAT_NODE, &cfg_nat_no_sdp_amr_mode_set_cmd); install_element(NAT_NODE, &cfg_nat_pgroup_cmd); install_element(NAT_NODE, &cfg_nat_no_pgroup_cmd); install_node(&pgroup_node, config_write_pgroup); vty_install_default(PGROUP_NODE); install_element(PGROUP_NODE, &cfg_pgroup_lac_cmd); install_element(PGROUP_NODE, &cfg_pgroup_no_lac_cmd); /* BSC subgroups */ install_element(NAT_NODE, &cfg_bsc_cmd); install_node(&bsc_node, config_write_bsc); vty_install_default(NAT_BSC_NODE); install_element(NAT_BSC_NODE, &cfg_bsc_token_cmd); install_element(NAT_BSC_NODE, &cfg_bsc_auth_key_cmd); install_element(NAT_BSC_NODE, &cfg_bsc_no_auth_key_cmd); install_element(NAT_BSC_NODE, &cfg_bsc_lac_cmd); install_element(NAT_BSC_NODE, &cfg_bsc_no_lac_cmd); install_element(NAT_BSC_NODE, &cfg_bsc_paging_cmd); install_element(NAT_BSC_NODE, &cfg_bsc_desc_cmd); install_element(NAT_BSC_NODE, &cfg_bsc_acc_lst_name_cmd); install_element(NAT_BSC_NODE, &cfg_bsc_no_acc_lst_name_cmd); install_element(NAT_BSC_NODE, &cfg_bsc_max_endps_cmd); install_element(NAT_BSC_NODE, &cfg_bsc_old_grp_cmd); install_element(NAT_BSC_NODE, &cfg_bsc_paging_grp_cmd); install_element(NAT_BSC_NODE, &cfg_bsc_no_paging_grp_cmd); install_element(NAT_BSC_NODE, &cfg_bsc_osmux_cmd); mgcp_vty_init(); return 0; } /* called by the telnet interface... we have our own init above */ int bsc_vty_init(const struct log_info *cat) { logging_vty_add_cmds(cat); return 0; } openbsc-0.15.0/openbsc/src/osmo-bsc_nat/bsc_sccp.c000066400000000000000000000156711265565154000220000ustar00rootroot00000000000000/* SCCP patching and handling routines */ /* * (C) 2010 by Holger Hans Peter Freyther * (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 . * */ #include #include #include #include #include #include #include static int equal(struct sccp_source_reference *ref1, struct sccp_source_reference *ref2) { return memcmp(ref1, ref2, sizeof(*ref1)) == 0; } /* * SCCP patching below */ /* check if we are using this ref for patched already */ static int sccp_ref_is_free(struct sccp_source_reference *ref, struct bsc_nat *nat) { struct nat_sccp_connection *conn; llist_for_each_entry(conn, &nat->sccp_connections, list_entry) { if (memcmp(ref, &conn->patched_ref, sizeof(*ref)) == 0) return -1; } return 0; } /* copied from sccp.c */ static int assign_src_local_reference(struct sccp_source_reference *ref, struct bsc_nat *nat) { static uint32_t last_ref = 0x50000; int wrapped = 0; do { struct sccp_source_reference reference; reference.octet1 = (last_ref >> 0) & 0xff; reference.octet2 = (last_ref >> 8) & 0xff; reference.octet3 = (last_ref >> 16) & 0xff; ++last_ref; /* do not use the reversed word and wrap around */ if ((last_ref & 0x00FFFFFF) == 0x00FFFFFF) { LOGP(DNAT, LOGL_NOTICE, "Wrapped searching for a free code\n"); last_ref = 0; ++wrapped; } if (sccp_ref_is_free(&reference, nat) == 0) { *ref = reference; return 0; } } while (wrapped != 2); LOGP(DNAT, LOGL_ERROR, "Finding a free reference failed\n"); return -1; } struct nat_sccp_connection *create_sccp_src_ref(struct bsc_connection *bsc, struct bsc_nat_parsed *parsed) { struct nat_sccp_connection *conn; /* Some commercial BSCs like to reassign there SRC ref */ llist_for_each_entry(conn, &bsc->nat->sccp_connections, list_entry) { if (conn->bsc != bsc) continue; if (memcmp(&conn->real_ref, parsed->src_local_ref, sizeof(conn->real_ref)) != 0) continue; /* the BSC has reassigned the SRC ref and we failed to keep track */ memset(&conn->remote_ref, 0, sizeof(conn->remote_ref)); if (assign_src_local_reference(&conn->patched_ref, bsc->nat) != 0) { LOGP(DNAT, LOGL_ERROR, "BSC %d reused src ref: %d and we failed to generate a new id.\n", bsc->cfg->nr, sccp_src_ref_to_int(parsed->src_local_ref)); bsc_mgcp_dlcx(conn); llist_del(&conn->list_entry); talloc_free(conn); return NULL; } else { clock_gettime(CLOCK_MONOTONIC, &conn->creation_time); bsc_mgcp_dlcx(conn); return conn; } } conn = talloc_zero(bsc->nat, struct nat_sccp_connection); if (!conn) { LOGP(DNAT, LOGL_ERROR, "Memory allocation failure.\n"); return NULL; } conn->bsc = bsc; clock_gettime(CLOCK_MONOTONIC, &conn->creation_time); conn->real_ref = *parsed->src_local_ref; if (assign_src_local_reference(&conn->patched_ref, bsc->nat) != 0) { LOGP(DNAT, LOGL_ERROR, "Failed to assign a ref.\n"); talloc_free(conn); return NULL; } bsc_mgcp_init(conn); llist_add_tail(&conn->list_entry, &bsc->nat->sccp_connections); rate_ctr_inc(&bsc->cfg->stats.ctrg->ctr[BCFG_CTR_SCCP_CONN]); osmo_counter_inc(bsc->cfg->nat->stats.sccp.conn); LOGP(DNAT, LOGL_DEBUG, "Created 0x%x <-> 0x%x mapping for con %p\n", sccp_src_ref_to_int(&conn->real_ref), sccp_src_ref_to_int(&conn->patched_ref), bsc); return conn; } int update_sccp_src_ref(struct nat_sccp_connection *sccp, struct bsc_nat_parsed *parsed) { if (!parsed->dest_local_ref || !parsed->src_local_ref) { LOGP(DNAT, LOGL_ERROR, "CC MSG should contain both local and dest address.\n"); return -1; } sccp->remote_ref = *parsed->src_local_ref; sccp->has_remote_ref = 1; LOGP(DNAT, LOGL_DEBUG, "Updating 0x%x to remote 0x%x on %p\n", sccp_src_ref_to_int(&sccp->patched_ref), sccp_src_ref_to_int(&sccp->remote_ref), sccp->bsc); return 0; } void remove_sccp_src_ref(struct bsc_connection *bsc, struct msgb *msg, struct bsc_nat_parsed *parsed) { struct nat_sccp_connection *conn; llist_for_each_entry(conn, &bsc->nat->sccp_connections, list_entry) { if (memcmp(parsed->src_local_ref, &conn->patched_ref, sizeof(conn->patched_ref)) == 0) { sccp_connection_destroy(conn); return; } } LOGP(DNAT, LOGL_ERROR, "Can not remove connection: 0x%x\n", sccp_src_ref_to_int(parsed->src_local_ref)); } /* * We have a message from the MSC to the BSC. The MSC is using * an address that was assigned by the MUX, we need to update the * dest reference to the real network. */ struct nat_sccp_connection *patch_sccp_src_ref_to_bsc(struct msgb *msg, struct bsc_nat_parsed *parsed, struct bsc_nat *nat) { struct nat_sccp_connection *conn; if (!parsed->dest_local_ref) { LOGP(DNAT, LOGL_ERROR, "MSG should contain dest_local_ref.\n"); return NULL; } llist_for_each_entry(conn, &nat->sccp_connections, list_entry) { if (!equal(parsed->dest_local_ref, &conn->patched_ref)) continue; /* Change the dest address to the real one */ *parsed->dest_local_ref = conn->real_ref; return conn; } return NULL; } /* * These are message to the MSC. We will need to find the BSC * Connection by either the SRC or the DST local reference. * * In case of a CR we need to work by the SRC local reference * in all other cases we need to work by the destination local * reference.. */ struct nat_sccp_connection *patch_sccp_src_ref_to_msc(struct msgb *msg, struct bsc_nat_parsed *parsed, struct bsc_connection *bsc) { struct nat_sccp_connection *conn; llist_for_each_entry(conn, &bsc->nat->sccp_connections, list_entry) { if (conn->bsc != bsc) continue; if (parsed->src_local_ref) { if (equal(parsed->src_local_ref, &conn->real_ref)) { *parsed->src_local_ref = conn->patched_ref; return conn; } } else if (parsed->dest_local_ref) { if (equal(parsed->dest_local_ref, &conn->remote_ref)) return conn; } else { LOGP(DNAT, LOGL_ERROR, "Header has neither loc/dst ref.\n"); return NULL; } } return NULL; } struct nat_sccp_connection *bsc_nat_find_con_by_bsc(struct bsc_nat *nat, struct sccp_source_reference *ref) { struct nat_sccp_connection *conn; llist_for_each_entry(conn, &nat->sccp_connections, list_entry) { if (memcmp(ref, &conn->real_ref, sizeof(*ref)) == 0) return conn; } return NULL; } openbsc-0.15.0/openbsc/src/osmo-bsc_nat/bsc_ussd.c000066400000000000000000000265171265565154000220270ustar00rootroot00000000000000/* USSD Filter Code */ /* * (C) 2010-2011 by Holger Hans Peter Freyther * (C) 2010-2011 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define USSD_LAC_IE 0 #define USSD_CI_IE 1 static void ussd_auth_con(struct tlv_parsed *, struct bsc_nat_ussd_con *); static struct bsc_nat_ussd_con *bsc_nat_ussd_alloc(struct bsc_nat *nat) { struct bsc_nat_ussd_con *con; con = talloc_zero(nat, struct bsc_nat_ussd_con); if (!con) return NULL; con->nat = nat; return con; } static void bsc_nat_ussd_destroy(struct bsc_nat_ussd_con *con) { if (con->nat->ussd_con == con) { bsc_ussd_close_connections(con->nat); con->nat->ussd_con = NULL; } close(con->queue.bfd.fd); osmo_fd_unregister(&con->queue.bfd); osmo_timer_del(&con->auth_timeout); osmo_wqueue_clear(&con->queue); msgb_free(con->pending_msg); talloc_free(con); } static void ussd_pong(struct bsc_nat_ussd_con *conn) { struct msgb *msg; msg = msgb_alloc_headroom(4096, 128, "pong message"); if (!msg) { LOGP(DNAT, LOGL_ERROR, "Failed to allocate pong msg\n"); return; } msgb_v_put(msg, IPAC_MSGT_PONG); bsc_do_write(&conn->queue, msg, IPAC_PROTO_IPACCESS); } static int forward_sccp(struct bsc_nat *nat, struct msgb *msg) { struct nat_sccp_connection *con; struct bsc_nat_parsed *parsed; parsed = bsc_nat_parse(msg); if (!parsed) { LOGP(DNAT, LOGL_ERROR, "Can not parse msg from USSD.\n"); msgb_free(msg); return -1; } if (!parsed->dest_local_ref) { LOGP(DNAT, LOGL_ERROR, "No destination local reference.\n"); msgb_free(msg); return -1; } con = bsc_nat_find_con_by_bsc(nat, parsed->dest_local_ref); if (!con || !con->bsc) { LOGP(DNAT, LOGL_ERROR, "No active connection found.\n"); msgb_free(msg); return -1; } talloc_free(parsed); bsc_write_msg(&con->bsc->write_queue, msg); return 0; } static int ussd_read_cb(struct osmo_fd *bfd) { struct bsc_nat_ussd_con *conn = bfd->data; struct msgb *msg = NULL; struct ipaccess_head *hh; int ret; ret = ipa_msg_recv_buffered(bfd->fd, &msg, &conn->pending_msg); if (ret <= 0) { if (ret == -EAGAIN) return 0; LOGP(DNAT, LOGL_ERROR, "USSD Connection was lost.\n"); bsc_nat_ussd_destroy(conn); return -1; } LOGP(DNAT, LOGL_NOTICE, "MSG from USSD: %s proto: %d\n", osmo_hexdump(msg->data, msg->len), msg->l2h[0]); hh = (struct ipaccess_head *) msg->data; if (hh->proto == IPAC_PROTO_IPACCESS) { if (msg->l2h[0] == IPAC_MSGT_ID_RESP) { struct tlv_parsed tvp; int ret; ret = ipa_ccm_idtag_parse(&tvp, (unsigned char *) msg->l2h + 2, msgb_l2len(msg) - 2); if (ret < 0) { LOGP(DNAT, LOGL_ERROR, "ignoring IPA response " "message with malformed TLVs\n"); msgb_free(msg); return ret; } if (TLVP_PRESENT(&tvp, IPAC_IDTAG_UNITNAME)) ussd_auth_con(&tvp, conn); } else if (msg->l2h[0] == IPAC_MSGT_PING) { LOGP(DNAT, LOGL_DEBUG, "Got USSD ping request.\n"); ussd_pong(conn); } else { LOGP(DNAT, LOGL_NOTICE, "Got unknown IPACCESS message 0x%02x.\n", msg->l2h[0]); } msgb_free(msg); } else if (hh->proto == IPAC_PROTO_SCCP) { forward_sccp(conn->nat, msg); } else { msgb_free(msg); } return 0; } static void ussd_auth_cb(void *_data) { LOGP(DNAT, LOGL_ERROR, "USSD module didn't authenticate\n"); bsc_nat_ussd_destroy((struct bsc_nat_ussd_con *) _data); } static void ussd_auth_con(struct tlv_parsed *tvp, struct bsc_nat_ussd_con *conn) { const char *token; int len; if (!conn->nat->ussd_token) { LOGP(DNAT, LOGL_ERROR, "No USSD token set. Closing\n"); bsc_nat_ussd_destroy(conn); return; } token = (const char *) TLVP_VAL(tvp, IPAC_IDTAG_UNITNAME); len = TLVP_LEN(tvp, IPAC_IDTAG_UNITNAME); /* last byte should be a NULL */ if (strlen(conn->nat->ussd_token) != len - 1) goto disconnect; /* compare everything including the null byte */ if (memcmp(conn->nat->ussd_token, token, len) != 0) goto disconnect; /* it is authenticated now */ if (conn->nat->ussd_con && conn->nat->ussd_con != conn) bsc_nat_ussd_destroy(conn->nat->ussd_con); LOGP(DNAT, LOGL_ERROR, "USSD token specified. USSD provider is connected.\n"); osmo_timer_del(&conn->auth_timeout); conn->authorized = 1; conn->nat->ussd_con = conn; return; disconnect: LOGP(DNAT, LOGL_ERROR, "Wrong USSD token by client: %d\n", conn->queue.bfd.fd); bsc_nat_ussd_destroy(conn); } static void ussd_start_auth(struct bsc_nat_ussd_con *conn) { struct msgb *msg; conn->auth_timeout.data = conn; conn->auth_timeout.cb = ussd_auth_cb; osmo_timer_schedule(&conn->auth_timeout, conn->nat->auth_timeout, 0); msg = msgb_alloc_headroom(4096, 128, "auth message"); if (!msg) { LOGP(DNAT, LOGL_ERROR, "Failed to allocate auth msg\n"); return; } msgb_v_put(msg, IPAC_MSGT_ID_GET); bsc_do_write(&conn->queue, msg, IPAC_PROTO_IPACCESS); } static int ussd_listen_cb(struct osmo_fd *bfd, unsigned int what) { struct bsc_nat_ussd_con *conn; struct bsc_nat *nat; struct sockaddr_in sa; socklen_t sa_len = sizeof(sa); int fd; if (!(what & BSC_FD_READ)) return 0; fd = accept(bfd->fd, (struct sockaddr *) &sa, &sa_len); if (fd < 0) { perror("accept"); return fd; } nat = (struct bsc_nat *) bfd->data; osmo_counter_inc(nat->stats.ussd.reconn); conn = bsc_nat_ussd_alloc(nat); if (!conn) { LOGP(DNAT, LOGL_ERROR, "Failed to allocate USSD con struct.\n"); close(fd); return -1; } osmo_wqueue_init(&conn->queue, 10); conn->queue.bfd.data = conn; conn->queue.bfd.fd = fd; conn->queue.bfd.when = BSC_FD_READ; conn->queue.read_cb = ussd_read_cb; conn->queue.write_cb = bsc_write_cb; if (osmo_fd_register(&conn->queue.bfd) < 0) { LOGP(DNAT, LOGL_ERROR, "Failed to register USSD fd.\n"); bsc_nat_ussd_destroy(conn); return -1; } LOGP(DNAT, LOGL_NOTICE, "USSD Connection on %d with IP: %s\n", fd, inet_ntoa(sa.sin_addr)); /* do authentication */ ussd_start_auth(conn); return 0; } int bsc_ussd_init(struct bsc_nat *nat) { struct in_addr addr; addr.s_addr = INADDR_ANY; if (nat->ussd_local) inet_aton(nat->ussd_local, &addr); nat->ussd_listen.data = nat; return make_sock(&nat->ussd_listen, IPPROTO_TCP, ntohl(addr.s_addr), 5001, 0, ussd_listen_cb, nat); } static int forward_ussd_simple(struct nat_sccp_connection *con, struct msgb *input) { struct msgb *copy; struct bsc_nat_ussd_con *ussd; if (!con->bsc->nat->ussd_con) return -1; copy = msgb_alloc_headroom(4096, 128, "forward bts"); if (!copy) { LOGP(DNAT, LOGL_ERROR, "Allocation failed, not forwarding.\n"); return -1; } /* copy the data into the copy */ copy->l2h = msgb_put(copy, msgb_l2len(input)); memcpy(copy->l2h, input->l2h, msgb_l2len(input)); /* send it out */ ussd = con->bsc->nat->ussd_con; bsc_do_write(&ussd->queue, copy, IPAC_PROTO_SCCP); return 0; } static int forward_ussd(struct nat_sccp_connection *con, const struct ussd_request *req, struct msgb *input) { struct msgb *msg, *copy; struct ipac_msgt_sccp_state *state; struct bsc_nat_ussd_con *ussd; uint16_t lac, ci; if (!con->bsc->nat->ussd_con) return -1; msg = msgb_alloc_headroom(4096, 128, "forward ussd"); if (!msg) { LOGP(DNAT, LOGL_ERROR, "Allocation failed, not forwarding.\n"); return -1; } copy = msgb_alloc_headroom(4096, 128, "forward bts"); if (!copy) { LOGP(DNAT, LOGL_ERROR, "Allocation failed, not forwarding.\n"); msgb_free(msg); return -1; } copy->l2h = msgb_put(copy, msgb_l2len(input)); memcpy(copy->l2h, input->l2h, msgb_l2len(input)); msg->l2h = msgb_put(msg, 1); msg->l2h[0] = IPAC_MSGT_SCCP_OLD; /* fill out the data */ state = (struct ipac_msgt_sccp_state *) msgb_put(msg, sizeof(*state)); state->trans_id = req->transaction_id; state->invoke_id = req->invoke_id; memcpy(&state->src_ref, &con->remote_ref, sizeof(con->remote_ref)); memcpy(&state->dst_ref, &con->real_ref, sizeof(con->real_ref)); memcpy(state->imsi, con->filter_state.imsi, strlen(con->filter_state.imsi)); /* add additional tag/values */ lac = htons(con->lac); ci = htons(con->ci); msgb_tv_fixed_put(msg, USSD_LAC_IE, sizeof(lac), (const uint8_t *) &lac); msgb_tv_fixed_put(msg, USSD_CI_IE, sizeof(ci), (const uint8_t *) &ci); ussd = con->bsc->nat->ussd_con; bsc_do_write(&ussd->queue, msg, IPAC_PROTO_IPACCESS); bsc_do_write(&ussd->queue, copy, IPAC_PROTO_SCCP); return 0; } int bsc_ussd_check(struct nat_sccp_connection *con, struct bsc_nat_parsed *parsed, struct msgb *msg) { uint32_t len; uint8_t msg_type; uint8_t proto; uint8_t ti; struct gsm48_hdr *hdr48; struct bsc_msg_acc_lst *lst; struct ussd_request req; /* * various checks to avoid the decoding work. Right now we only want to * decode if the connection was created for USSD, we do have a USSD access * list, a query, a IMSI and such... */ if (con->filter_state.con_type != FLT_CON_TYPE_SSA) return 0; if (!con->filter_state.imsi) return 0; /* We have not verified the IMSI yet */ if (!con->authorized) return 0; if (!con->bsc->nat->ussd_lst_name) return 0; if (!con->bsc->nat->ussd_query) return 0; if (parsed->bssap != BSSAP_MSG_DTAP) return 0; if (strlen(con->filter_state.imsi) >= GSM_IMSI_LENGTH) return 0; hdr48 = bsc_unpack_dtap(parsed, msg, &len); if (!hdr48) return 0; proto = hdr48->proto_discr & 0x0f; msg_type = hdr48->msg_type & 0xbf; ti = (hdr48->proto_discr & 0x70) >> 4; if (proto != GSM48_PDISC_NC_SS) return 0; if (msg_type == GSM0480_MTYPE_REGISTER) { /* now check if it is a IMSI we care about */ lst = bsc_msg_acc_lst_find(&con->bsc->nat->access_lists, con->bsc->nat->ussd_lst_name); if (!lst) return 0; if (bsc_msg_acc_lst_check_allow(lst, con->filter_state.imsi) != 0) return 0; /* now decode the message and see if we really want to handle it */ memset(&req, 0, sizeof(req)); if (gsm0480_decode_ussd_request(hdr48, len, &req) != 1) return 0; if (req.text[0] == 0xff) return 0; if (regexec(&con->bsc->nat->ussd_query_re, req.text, 0, NULL, 0) == REG_NOMATCH) return 0; /* found a USSD query for our subscriber */ LOGP(DNAT, LOGL_NOTICE, "Found USSD query for %s\n", con->filter_state.imsi); con->ussd_ti[ti] = 1; if (forward_ussd(con, &req, msg) != 0) return 0; return 1; } else if (msg_type == GSM0480_MTYPE_FACILITY && con->ussd_ti[ti]) { LOGP(DNAT, LOGL_NOTICE, "Forwarding message part of TI: %d %s\n", ti, con->filter_state.imsi); if (forward_ussd_simple(con, msg) != 0) return 0; return 1; } return 0; } openbsc-0.15.0/openbsc/src/osmo-nitb/000077500000000000000000000000001265565154000173665ustar00rootroot00000000000000openbsc-0.15.0/openbsc/src/osmo-nitb/Makefile.am000066400000000000000000000013361265565154000214250ustar00rootroot00000000000000AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) AM_CFLAGS=-Wall $(COVERAGE_CFLAGS) \ $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) \ $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(LIBSMPP34_CFLAGS) AM_LDFLAGS = $(COVERAGE_LDFLAGS) bin_PROGRAMS = osmo-nitb osmo_nitb_SOURCES = bsc_hack.c osmo_nitb_LDADD = \ $(top_builddir)/src/libbsc/libbsc.a \ $(top_builddir)/src/libmsc/libmsc.a \ $(top_builddir)/src/libbsc/libbsc.a \ $(top_builddir)/src/libtrau/libtrau.a \ $(top_builddir)/src/libcommon/libcommon.a \ -ldbi $(LIBCRYPT) \ $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOCORE_LIBS) \ $(LIBOSMOCTRL_LIBS) $(LIBOSMOABIS_LIBS) $(LIBSMPP34_LIBS) $(LIBCRYPTO_LIBS) openbsc-0.15.0/openbsc/src/osmo-nitb/bsc_hack.c000066400000000000000000000217521265565154000212760ustar00rootroot00000000000000/* A hackish minimal BSC (+MSC +HLR) implementation */ /* (C) 2008-2010 by Harald Welte * (C) 2009-2012 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 . * */ #include #include #include #include #include #include #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../../bscconfig.h" /* MCC and MNC for the Location Area Identifier */ struct gsm_network *bsc_gsmnet = 0; static const char *database_name = "hlr.sqlite3"; static const char *config_file = "openbsc.cfg"; static const char *rf_ctrl_name = NULL; extern const char *openbsc_copyright; static int daemonize = 0; static int use_mncc_sock = 0; static int use_db_counter = 1; /* timer to store statistics */ #define DB_SYNC_INTERVAL 60, 0 #define EXPIRE_INTERVAL 10, 0 static struct osmo_timer_list db_sync_timer; static void create_pcap_file(char *file) { mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; int fd = open(file, O_WRONLY|O_TRUNC|O_CREAT, mode); if (fd < 0) { perror("Failed to open file for pcap"); return; } e1_set_pcap_fd(fd); } static void print_usage() { printf("Usage: osmo-nitb\n"); } static void print_help() { printf(" Some useful help...\n"); printf(" -h --help This text.\n"); printf(" -d option --debug=DRLL:DCC:DMM:DRR:DRSL:DNM 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(" -l --database db-name The database to use.\n"); printf(" -a --authorize-everyone Authorize every new subscriber. Dangerous!\n"); printf(" -T --timestamp Prefix every log line with a timestamp.\n"); printf(" -V --version Print the version of OpenBSC.\n"); printf(" -P --rtp-proxy Enable the RTP Proxy code inside OpenBSC.\n"); printf(" -e --log-level number Set a global loglevel.\n"); printf(" -m --mncc-sock Disable built-in MNCC handler and offer socket.\n"); printf(" -C --no-dbcounter Disable regular syncing of counters to database.\n"); printf(" -r --rf-ctl NAME A unix domain socket to listen for cmds.\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'}, {"database", 1, 0, 'l'}, {"authorize-everyone", 0, 0, 'a'}, {"pcap", 1, 0, 'p'}, {"timestamp", 0, 0, 'T'}, {"version", 0, 0, 'V' }, {"rtp-proxy", 0, 0, 'P'}, {"log-level", 1, 0, 'e'}, {"mncc-sock", 0, 0, 'm'}, {"no-dbcounter", 0, 0, 'C'}, {"rf-ctl", 1, 0, 'r'}, {0, 0, 0, 0} }; c = getopt_long(argc, argv, "hd:Dsl:ar:p:TPVc:e:mCr:", 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 'l': database_name = optarg; break; case 'c': config_file = optarg; break; case 'p': create_pcap_file(optarg); break; case 'T': log_set_print_timestamp(osmo_stderr_target, 1); break; case 'P': ipacc_rtp_direct = 0; break; case 'e': log_set_log_level(osmo_stderr_target, atoi(optarg)); break; case 'm': use_mncc_sock = 1; break; case 'C': use_db_counter = 0; break; case 'V': print_version(1); exit(0); break; case 'r': rf_ctrl_name = optarg; break; default: /* ignore */ break; } } } extern void *tall_vty_ctx; static void signal_handler(int signal) { fprintf(stdout, "signal %u received\n", signal); switch (signal) { case SIGINT: bsc_shutdown_net(bsc_gsmnet); osmo_signal_dispatch(SS_L_GLOBAL, S_L_GLOBAL_SHUTDOWN, NULL); sleep(3); 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; } } /* timer handling */ static int _db_store_counter(struct osmo_counter *counter, void *data) { return db_store_counter(counter); } static void db_sync_timer_cb(void *data) { /* store counters to database and re-schedule */ osmo_counters_for_each(_db_store_counter, NULL); osmo_timer_schedule(&db_sync_timer, DB_SYNC_INTERVAL); } static void subscr_expire_cb(void *data) { subscr_expire(bsc_gsmnet->subscr_group); osmo_timer_schedule(&bsc_gsmnet->subscr_expire_timer, EXPIRE_INTERVAL); } void talloc_ctx_init(void); extern int bsc_vty_go_parent(struct vty *vty); static struct vty_app_info vty_info = { .name = "OpenBSC", .version = PACKAGE_VERSION, .go_parent_cb = bsc_vty_go_parent, .is_config_node = bsc_vty_is_config_node, }; int main(int argc, char **argv) { int rc; vty_info.copyright = openbsc_copyright; tall_bsc_ctx = talloc_named_const(NULL, 1, "openbsc"); talloc_ctx_init(); on_dso_load_token(); on_dso_load_rrlp(); on_dso_load_ho_dec(); libosmo_abis_init(tall_bsc_ctx); osmo_init_logging(&log_info); bts_init(); /* This needs to precede handle_options() */ vty_init(&vty_info); bsc_vty_init(&log_info); #ifdef BUILD_SMPP if (smpp_openbsc_init(tall_bsc_ctx, 0) < 0) return -1; #endif /* parse options */ handle_options(argc, argv); /* internal MNCC handler or MNCC socket? */ if (use_mncc_sock) { rc = bsc_bootstrap_network(mncc_sock_from_cc, config_file); if (rc >= 0) mncc_sock_init(bsc_gsmnet); } else rc = bsc_bootstrap_network(int_mncc_recv, config_file); if (rc < 0) exit(1); #ifdef BUILD_SMPP smpp_openbsc_set_net(bsc_gsmnet); #endif bsc_api_init(bsc_gsmnet, msc_bsc_api()); bsc_gsmnet->ctrl = bsc_controlif_setup(bsc_gsmnet, OSMO_CTRL_PORT_NITB_BSC); if (!bsc_gsmnet->ctrl) { printf("Failed to initialize control interface. Exiting.\n"); return -1; } if (bsc_base_ctrl_cmds_install() != 0) { printf("Failed to initialize the BSC control commands.\n"); return -1; } if (msc_ctrl_cmds_install() != 0) { printf("Failed to initialize the MSC control commands.\n"); return -1; } /* seed the PRNG */ srand(time(NULL)); bsc_gsmnet->bsc_data->rf_ctrl = osmo_bsc_rf_create(rf_ctrl_name, bsc_gsmnet); if (!bsc_gsmnet->bsc_data->rf_ctrl) { fprintf(stderr, "Failed to create the RF service.\n"); exit(1); } if (db_init(database_name)) { printf("DB: Failed to init database. Please check the option settings.\n"); return -1; } printf("DB: Database initialized.\n"); if (db_prepare()) { printf("DB: Failed to prepare database.\n"); return -1; } printf("DB: Database prepared.\n"); /* setup the timer */ db_sync_timer.cb = db_sync_timer_cb; db_sync_timer.data = NULL; if (use_db_counter) osmo_timer_schedule(&db_sync_timer, DB_SYNC_INTERVAL); bsc_gsmnet->subscr_expire_timer.cb = subscr_expire_cb; bsc_gsmnet->subscr_expire_timer.data = NULL; osmo_timer_schedule(&bsc_gsmnet->subscr_expire_timer, EXPIRE_INTERVAL); signal(SIGINT, &signal_handler); signal(SIGABRT, &signal_handler); signal(SIGUSR1, &signal_handler); signal(SIGUSR2, &signal_handler); osmo_init_ignore_signals(); /* start the SMS queue */ if (sms_queue_start(bsc_gsmnet, 20) != 0) return -1; if (daemonize) { rc = osmo_daemonize(); if (rc < 0) { perror("Error during daemonize"); exit(1); } } while (1) { log_reset_context(); osmo_select_main(0); } } openbsc-0.15.0/openbsc/src/utils/000077500000000000000000000000001265565154000166175ustar00rootroot00000000000000openbsc-0.15.0/openbsc/src/utils/Makefile.am000066400000000000000000000027201265565154000206540ustar00rootroot00000000000000AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) $(SQLITE3_CFLAGS) AM_LDFLAGS = $(COVERAGE_LDFLAGS) noinst_HEADERS = meas_db.h bin_PROGRAMS = bs11_config isdnsync if HAVE_SQLITE3 bin_PROGRAMS += osmo-meas-pcap2db osmo-meas-udp2db endif if HAVE_LIBCDK bin_PROGRAMS += meas_vis endif if BUILD_SMPP noinst_PROGRAMS = smpp_mirror endif bs11_config_SOURCES = bs11_config.c bs11_config_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ $(top_builddir)/src/libtrau/libtrau.a \ $(top_builddir)/src/libcommon/libcommon.a \ $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOABIS_LIBS) isdnsync_SOURCES = isdnsync.c smpp_mirror_SOURCES = smpp_mirror.c smpp_mirror_LDADD = $(top_builddir)/src/libcommon/libcommon.a \ $(LIBOSMOCORE_LIBS) $(LIBSMPP34_LIBS) meas_vis_SOURCES = meas_vis.c meas_vis_LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) -lcdk -lncurses meas_vis_CFLAGS = $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) osmo_meas_pcap2db_SOURCES = meas_pcap2db.c meas_db.c osmo_meas_pcap2db_LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) -lpcap $(SQLITE3_LIBS) osmo_meas_pcap2db_CFLAGS = $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) osmo_meas_udp2db_SOURCES = meas_udp2db.c meas_db.c osmo_meas_udp2db_LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(SQLITE3_LIBS) osmo_meas_udp2db_CFLAGS = $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) openbsc-0.15.0/openbsc/src/utils/bs11_config.c000066400000000000000000000622561265565154000210710ustar00rootroot00000000000000/* Siemens BS-11 microBTS configuration tool */ /* (C) 2009-2010 by Harald Welte * All Rights Reserved * * This software is based on ideas (but not code) of BS11Config * (C) 2009 by Dieter Spaar * * 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void *tall_bs11cfg_ctx; static struct e1inp_sign_link *oml_link; /* state of our bs11_config application */ enum bs11cfg_state { STATE_NONE, STATE_LOGON_WAIT, STATE_LOGON_ACK, STATE_SWLOAD, STATE_QUERY, }; static enum bs11cfg_state bs11cfg_state = STATE_NONE; static char *command, *value; struct osmo_timer_list status_timer; static const uint8_t obj_li_attr[] = { NM_ATT_BS11_BIT_ERR_THESH, 0x09, 0x00, NM_ATT_BS11_L1_PROT_TYPE, 0x00, NM_ATT_BS11_LINE_CFG, 0x00, }; static const uint8_t obj_bbsig0_attr[] = { NM_ATT_BS11_RSSI_OFFS, 0x02, 0x00, 0x00, NM_ATT_BS11_DIVERSITY, 0x01, 0x00, }; static const uint8_t obj_pa0_attr[] = { NM_ATT_BS11_TXPWR, 0x01, BS11_TRX_POWER_GSM_30mW, }; static const char *trx1_password = "1111111111"; #define TEI_OML 25 static const uint8_t too_fast[] = { 0x12, 0x80, 0x00, 0x00, 0x02, 0x02 }; /* dummy function to keep gsm_data.c happy */ struct osmo_counter *osmo_counter_alloc(const char *name) { return NULL; } int handle_serial_msg(struct msgb *rx_msg); /* create all objects for an initial configuration */ static int create_objects(struct gsm_bts *bts) { fprintf(stdout, "Crating Objects for minimal config\n"); abis_nm_bs11_create_object(bts, BS11_OBJ_LI, 0, sizeof(obj_li_attr), obj_li_attr); abis_nm_bs11_create_object(bts, BS11_OBJ_GPSU, 0, 0, NULL); abis_nm_bs11_create_object(bts, BS11_OBJ_ALCO, 0, 0, NULL); abis_nm_bs11_create_object(bts, BS11_OBJ_CCLK, 0, 0, NULL); abis_nm_bs11_create_object(bts, BS11_OBJ_BBSIG, 0, sizeof(obj_bbsig0_attr), obj_bbsig0_attr); abis_nm_bs11_create_object(bts, BS11_OBJ_PA, 0, sizeof(obj_pa0_attr), obj_pa0_attr); abis_nm_bs11_create_envaBTSE(bts, 0); abis_nm_bs11_create_envaBTSE(bts, 1); abis_nm_bs11_create_envaBTSE(bts, 2); abis_nm_bs11_create_envaBTSE(bts, 3); abis_nm_bs11_conn_oml_tei(bts, 0, 1, 0xff, TEI_OML); abis_nm_bs11_set_trx_power(bts->c0, BS11_TRX_POWER_GSM_30mW); sleep(1); abis_nm_bs11_set_trx1_pw(bts, trx1_password); sleep(1); return 0; } static int create_trx1(struct gsm_bts *bts) { uint8_t bbsig1_attr[sizeof(obj_bbsig0_attr)+12]; uint8_t *cur = bbsig1_attr; struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, 1); if (!trx) trx = gsm_bts_trx_alloc(bts); fprintf(stdout, "Crating Objects for TRX1\n"); abis_nm_bs11_set_trx1_pw(bts, trx1_password); sleep(1); cur = tlv_put(cur, NM_ATT_BS11_PASSWORD, 10, (uint8_t *)trx1_password); memcpy(cur, obj_bbsig0_attr, sizeof(obj_bbsig0_attr)); abis_nm_bs11_create_object(bts, BS11_OBJ_BBSIG, 1, sizeof(bbsig1_attr), bbsig1_attr); abis_nm_bs11_create_object(bts, BS11_OBJ_PA, 1, sizeof(obj_pa0_attr), obj_pa0_attr); abis_nm_bs11_set_trx_power(trx, BS11_TRX_POWER_GSM_30mW); return 0; } static char *serial_port = "/dev/ttyUSB0"; static char *fname_safety = "BTSBMC76.SWI"; static char *fname_software = "HS011106.SWL"; static int delay_ms = 0; static int win_size = 8; static int param_disconnect = 0; static int param_restart = 0; static int param_forced = 0; static struct gsm_bts *g_bts; static int file_is_readable(const char *fname) { int rc; struct stat st; rc = stat(fname, &st); if (rc < 0) return 0; if (S_ISREG(st.st_mode) && (st.st_mode & S_IRUSR)) return 1; return 0; } static int percent; static int percent_old; /* callback function passed to the ABIS OML code */ static int swload_cbfn(unsigned int hook, unsigned int event, struct msgb *msg, void *data, void *param) { if (hook != GSM_HOOK_NM_SWLOAD) return 0; switch (event) { case NM_MT_LOAD_INIT_ACK: fprintf(stdout, "Software Load Initiate ACK\n"); break; case NM_MT_LOAD_INIT_NACK: fprintf(stderr, "ERROR: Software Load Initiate NACK\n"); exit(5); break; case NM_MT_LOAD_END_ACK: if (data) { /* we did a safety load and must activate it */ abis_nm_software_activate(g_bts, fname_safety, swload_cbfn, g_bts); sleep(5); } break; case NM_MT_LOAD_END_NACK: fprintf(stderr, "ERROR: Software Load End NACK\n"); exit(3); break; case NM_MT_ACTIVATE_SW_NACK: fprintf(stderr, "ERROR: Activate Software NACK\n"); exit(4); break; case NM_MT_ACTIVATE_SW_ACK: bs11cfg_state = STATE_NONE; break; case NM_MT_LOAD_SEG_ACK: percent = abis_nm_software_load_status(g_bts); if (percent > percent_old) printf("Software Download Progress: %d%%\n", percent); percent_old = percent; break; } return 0; } static const struct value_string bs11_linkst_names[] = { { 0, "Down" }, { 1, "Up" }, { 2, "Restoring" }, { 0, NULL } }; static const char *linkstate_name(uint8_t linkstate) { return get_value_string(bs11_linkst_names, linkstate); } static const struct value_string mbccu_load_names[] = { { 0, "No Load" }, { 1, "Load BTSCAC" }, { 2, "Load BTSDRX" }, { 3, "Load BTSBBX" }, { 4, "Load BTSARC" }, { 5, "Load" }, { 0, NULL } }; static const char *mbccu_load_name(uint8_t linkstate) { return get_value_string(mbccu_load_names, linkstate); } static const char *bts_phase_name(uint8_t phase) { switch (phase) { case BS11_STATE_WARM_UP: case BS11_STATE_WARM_UP_2: return "Warm Up"; break; case BS11_STATE_LOAD_SMU_SAFETY: return "Load SMU Safety"; break; case BS11_STATE_LOAD_SMU_INTENDED: return "Load SMU Intended"; break; case BS11_STATE_LOAD_MBCCU: return "Load MBCCU"; break; case BS11_STATE_SOFTWARE_RQD: return "Software required"; break; case BS11_STATE_WAIT_MIN_CFG: case BS11_STATE_WAIT_MIN_CFG_2: return "Wait minimal config"; break; case BS11_STATE_MAINTENANCE: return "Maintenance"; break; case BS11_STATE_NORMAL: return "Normal"; break; case BS11_STATE_ABIS_LOAD: return "Abis load"; break; default: return "Unknown"; break; } } static const char *trx_power_name(uint8_t pwr) { switch (pwr) { case BS11_TRX_POWER_GSM_2W: return "2W (GSM)"; case BS11_TRX_POWER_GSM_250mW: return "250mW (GSM)"; case BS11_TRX_POWER_GSM_80mW: return "80mW (GSM)"; case BS11_TRX_POWER_GSM_30mW: return "30mW (GSM)"; case BS11_TRX_POWER_DCS_3W: return "3W (DCS)"; case BS11_TRX_POWER_DCS_1W6: return "1.6W (DCS)"; case BS11_TRX_POWER_DCS_500mW: return "500mW (DCS)"; case BS11_TRX_POWER_DCS_160mW: return "160mW (DCS)"; default: return "unknown value"; } } static const char *pll_mode_name(uint8_t mode) { switch (mode) { case BS11_LI_PLL_LOCKED: return "E1 Locked"; case BS11_LI_PLL_STANDALONE: return "Standalone"; default: return "unknown"; } } static const char *cclk_acc_name(uint8_t acc) { switch (acc) { case 0: /* Out of the demanded +/- 0.05ppm */ return "Medium"; case 1: /* Synchronized with Abis, within demanded tolerance +/- 0.05ppm */ return "High"; default: return "unknown"; } } static const char *bport_lcfg_name(uint8_t lcfg) { switch (lcfg) { case BS11_LINE_CFG_STAR: return "Star"; case BS11_LINE_CFG_MULTIDROP: return "Multi-Drop"; default: return "unknown"; } } static const char *obj_name(struct abis_om_fom_hdr *foh) { static char retbuf[256]; retbuf[0] = 0; switch (foh->obj_class) { case NM_OC_BS11: strcat(retbuf, "BS11 "); switch (foh->obj_inst.bts_nr) { case BS11_OBJ_PA: sprintf(retbuf+strlen(retbuf), "Power Amplifier %d ", foh->obj_inst.ts_nr); break; case BS11_OBJ_LI: sprintf(retbuf+strlen(retbuf), "Line Interface "); break; case BS11_OBJ_CCLK: sprintf(retbuf+strlen(retbuf), "CCLK "); break; } break; case NM_OC_SITE_MANAGER: strcat(retbuf, "SITE MANAGER "); break; case NM_OC_BS11_BPORT: sprintf(retbuf+strlen(retbuf), "BPORT%u ", foh->obj_inst.bts_nr); break; } return retbuf; } static void print_state(struct tlv_parsed *tp) { if (TLVP_PRESENT(tp, NM_ATT_BS11_BTS_STATE)) { uint8_t phase, mbccu; if (TLVP_LEN(tp, NM_ATT_BS11_BTS_STATE) >= 1) { phase = *TLVP_VAL(tp, NM_ATT_BS11_BTS_STATE); printf("PHASE: %u %-20s ", phase & 0xf, bts_phase_name(phase)); } if (TLVP_LEN(tp, NM_ATT_BS11_BTS_STATE) >= 2) { mbccu = *(TLVP_VAL(tp, NM_ATT_BS11_BTS_STATE)+1); printf("MBCCU0: %-11s MBCCU1: %-11s ", mbccu_load_name(mbccu & 0xf), mbccu_load_name(mbccu >> 4)); } } if (TLVP_PRESENT(tp, NM_ATT_BS11_E1_STATE) && TLVP_LEN(tp, NM_ATT_BS11_E1_STATE) >= 1) { uint8_t e1_state = *TLVP_VAL(tp, NM_ATT_BS11_E1_STATE); printf("Abis-link: %-9s ", linkstate_name(e1_state & 0xf)); } printf("\n"); } static int print_attr(struct tlv_parsed *tp) { if (TLVP_PRESENT(tp, NM_ATT_BS11_ESN_PCB_SERIAL)) { printf("\tBS-11 ESN PCB Serial Number: %s\n", TLVP_VAL(tp, NM_ATT_BS11_ESN_PCB_SERIAL)); } if (TLVP_PRESENT(tp, NM_ATT_BS11_ESN_HW_CODE_NO)) { printf("\tBS-11 ESN Hardware Code Number: %s\n", TLVP_VAL(tp, NM_ATT_BS11_ESN_HW_CODE_NO)+6); } if (TLVP_PRESENT(tp, NM_ATT_BS11_ESN_FW_CODE_NO)) { printf("\tBS-11 ESN Firmware Code Number: %s\n", TLVP_VAL(tp, NM_ATT_BS11_ESN_FW_CODE_NO)+6); } #if 0 if (TLVP_PRESENT(tp, NM_ATT_BS11_BOOT_SW_VERS)) { printf("BS-11 Boot Software Version: %s\n", TLVP_VAL(tp, NM_ATT_BS11_BOOT_SW_VERS)+6); } #endif if (TLVP_PRESENT(tp, NM_ATT_ABIS_CHANNEL) && TLVP_LEN(tp, NM_ATT_ABIS_CHANNEL) >= 3) { const uint8_t *chan = TLVP_VAL(tp, NM_ATT_ABIS_CHANNEL); printf("\tE1 Channel: Port=%u Timeslot=%u ", chan[0], chan[1]); if (chan[2] == 0xff) printf("(Full Slot)\n"); else printf("Subslot=%u\n", chan[2]); } if (TLVP_PRESENT(tp, NM_ATT_TEI)) printf("\tTEI: %d\n", *TLVP_VAL(tp, NM_ATT_TEI)); if (TLVP_PRESENT(tp, NM_ATT_BS11_TXPWR) && TLVP_LEN(tp, NM_ATT_BS11_TXPWR) >= 1) { printf("\tTRX Power: %s\n", trx_power_name(*TLVP_VAL(tp, NM_ATT_BS11_TXPWR))); } if (TLVP_PRESENT(tp, NM_ATT_BS11_PLL_MODE) && TLVP_LEN(tp, NM_ATT_BS11_PLL_MODE) >= 1) { printf("\tPLL Mode: %s\n", pll_mode_name(*TLVP_VAL(tp, NM_ATT_BS11_PLL_MODE))); } if (TLVP_PRESENT(tp, NM_ATT_BS11_PLL) && TLVP_LEN(tp, NM_ATT_BS11_PLL) >= 4) { const uint8_t *vp = TLVP_VAL(tp, NM_ATT_BS11_PLL); printf("\tPLL Set Value=%d, Work Value=%d\n", vp[0] << 8 | vp[1], vp[2] << 8 | vp[3]); } if (TLVP_PRESENT(tp, NM_ATT_BS11_CCLK_ACCURACY) && TLVP_LEN(tp, NM_ATT_BS11_CCLK_ACCURACY) >= 1) { const uint8_t *acc = TLVP_VAL(tp, NM_ATT_BS11_CCLK_ACCURACY); printf("\tCCLK Accuracy: %s (%d)\n", cclk_acc_name(*acc), *acc); } if (TLVP_PRESENT(tp, NM_ATT_BS11_CCLK_TYPE) && TLVP_LEN(tp, NM_ATT_BS11_CCLK_TYPE) >= 1) { const uint8_t *acc = TLVP_VAL(tp, NM_ATT_BS11_CCLK_TYPE); printf("\tCCLK Type=%d\n", *acc); } if (TLVP_PRESENT(tp, NM_ATT_BS11_LINE_CFG) && TLVP_LEN(tp, NM_ATT_BS11_LINE_CFG) >= 1) { const uint8_t *lcfg = TLVP_VAL(tp, NM_ATT_BS11_LINE_CFG); printf("\tLine Configuration: %s (%d)\n", bport_lcfg_name(*lcfg), *lcfg); } return 0; } static void cmd_query(void) { struct gsm_bts_trx *trx = g_bts->c0; bs11cfg_state = STATE_QUERY; abis_nm_bs11_get_serno(g_bts); abis_nm_bs11_get_oml_tei_ts(g_bts); abis_nm_bs11_get_pll_mode(g_bts); abis_nm_bs11_get_cclk(g_bts); abis_nm_bs11_get_trx_power(trx); trx = gsm_bts_trx_num(g_bts, 1); if (trx) abis_nm_bs11_get_trx_power(trx); abis_nm_bs11_get_bport_line_cfg(g_bts, 0); abis_nm_bs11_get_bport_line_cfg(g_bts, 1); sleep(1); abis_nm_bs11_factory_logon(g_bts, 0); command = NULL; } /* handle a response from the BTS to a GET STATE command */ static int handle_state_resp(enum abis_bs11_phase state) { int rc = 0; switch (state) { case BS11_STATE_WARM_UP: case BS11_STATE_LOAD_SMU_SAFETY: case BS11_STATE_LOAD_SMU_INTENDED: case BS11_STATE_LOAD_MBCCU: break; case BS11_STATE_SOFTWARE_RQD: bs11cfg_state = STATE_SWLOAD; /* send safety load. Use g_bts as private 'param' * argument, so our swload_cbfn can distinguish * a safety load from a regular software */ if (file_is_readable(fname_safety)) rc = abis_nm_software_load(g_bts, 0xff, fname_safety, win_size, param_forced, swload_cbfn, g_bts); else fprintf(stderr, "No valid Safety Load file \"%s\"\n", fname_safety); break; case BS11_STATE_WAIT_MIN_CFG: case BS11_STATE_WAIT_MIN_CFG_2: bs11cfg_state = STATE_SWLOAD; rc = create_objects(g_bts); break; case BS11_STATE_MAINTENANCE: if (command) { if (!strcmp(command, "disconnect")) abis_nm_bs11_factory_logon(g_bts, 0); else if (!strcmp(command, "reconnect")) rc = abis_nm_bs11_bsc_disconnect(g_bts, 1); else if (!strcmp(command, "software") && bs11cfg_state != STATE_SWLOAD) { bs11cfg_state = STATE_SWLOAD; /* send software (FIXME: over A-bis?) */ if (file_is_readable(fname_software)) rc = abis_nm_bs11_load_swl(g_bts, fname_software, win_size, param_forced, swload_cbfn); else fprintf(stderr, "No valid Software file \"%s\"\n", fname_software); } else if (!strcmp(command, "delete-trx1")) { printf("Locing BBSIG and PA objects of TRX1\n"); abis_nm_chg_adm_state(g_bts, NM_OC_BS11, BS11_OBJ_BBSIG, 0, 1, NM_STATE_LOCKED); abis_nm_chg_adm_state(g_bts, NM_OC_BS11, BS11_OBJ_PA, 0, 1, NM_STATE_LOCKED); sleep(1); printf("Deleting BBSIG and PA objects of TRX1\n"); abis_nm_bs11_delete_object(g_bts, BS11_OBJ_BBSIG, 1); abis_nm_bs11_delete_object(g_bts, BS11_OBJ_PA, 1); sleep(1); abis_nm_bs11_factory_logon(g_bts, 0); command = NULL; } else if (!strcmp(command, "create-trx1")) { create_trx1(g_bts); sleep(1); abis_nm_bs11_factory_logon(g_bts, 0); command = NULL; } else if (!strcmp(command, "pll-e1-locked")) { abis_nm_bs11_set_pll_locked(g_bts, 1); sleep(1); abis_nm_bs11_factory_logon(g_bts, 0); command = NULL; } else if (!strcmp(command, "pll-standalone")) { abis_nm_bs11_set_pll_locked(g_bts, 0); sleep(1); abis_nm_bs11_factory_logon(g_bts, 0); command = NULL; } else if (!strcmp(command, "pll-setvalue")) { abis_nm_bs11_set_pll(g_bts, atoi(value)); sleep(1); abis_nm_bs11_factory_logon(g_bts, 0); command = NULL; } else if (!strcmp(command, "pll-workvalue")) { /* To set the work value we need to login as FIELD */ abis_nm_bs11_factory_logon(g_bts, 0); sleep(1); abis_nm_bs11_infield_logon(g_bts, 1); sleep(1); abis_nm_bs11_set_pll(g_bts, atoi(value)); sleep(1); abis_nm_bs11_infield_logon(g_bts, 0); command = NULL; } else if (!strcmp(command, "oml-tei")) { abis_nm_bs11_conn_oml_tei(g_bts, 0, 1, 0xff, TEI_OML); command = NULL; } else if (!strcmp(command, "restart")) { abis_nm_bs11_restart(g_bts); command = NULL; } else if (!strcmp(command, "query")) { cmd_query(); } else if (!strcmp(command, "create-bport1")) { abis_nm_bs11_create_bport(g_bts, 1); sleep(1); abis_nm_bs11_factory_logon(g_bts, 0); command = NULL; } else if (!strcmp(command, "delete-bport1")) { abis_nm_chg_adm_state(g_bts, NM_OC_BS11_BPORT, 1, 0xff, 0xff, NM_STATE_LOCKED); sleep(1); abis_nm_bs11_delete_bport(g_bts, 1); sleep(1); abis_nm_bs11_factory_logon(g_bts, 0); command = NULL; } else if (!strcmp(command, "bport0-star")) { abis_nm_bs11_set_bport_line_cfg(g_bts, 0, BS11_LINE_CFG_STAR); sleep(1); abis_nm_bs11_factory_logon(g_bts, 0); command = NULL; } else if (!strcmp(command, "bport0-multidrop")) { abis_nm_bs11_set_bport_line_cfg(g_bts, 0, BS11_LINE_CFG_MULTIDROP); sleep(1); abis_nm_bs11_factory_logon(g_bts, 0); command = NULL; } else if (!strcmp(command, "bport1-multidrop")) { abis_nm_bs11_set_bport_line_cfg(g_bts, 1, BS11_LINE_CFG_MULTIDROP); sleep(1); abis_nm_bs11_factory_logon(g_bts, 0); command = NULL; } } break; case BS11_STATE_NORMAL: if (command) { if (!strcmp(command, "reconnect")) abis_nm_bs11_factory_logon(g_bts, 0); else if (!strcmp(command, "disconnect")) abis_nm_bs11_bsc_disconnect(g_bts, 0); else if (!strcmp(command, "query")) { cmd_query(); } } else if (param_disconnect) { param_disconnect = 0; abis_nm_bs11_bsc_disconnect(g_bts, 0); if (param_restart) { param_restart = 0; abis_nm_bs11_restart(g_bts); } } break; default: break; } return rc; } /* handle a fully-received message/packet from the RS232 port */ static int abis_nm_bs11cfg_rcvmsg(struct msgb *rx_msg) { struct e1inp_sign_link *link = rx_msg->dst; struct abis_om_hdr *oh; struct abis_om_fom_hdr *foh; struct tlv_parsed tp; int rc = -1; #if 0 if (rx_msg->len < LAPD_HDR_LEN + sizeof(struct abis_om_fom_hdr) + sizeof(struct abis_om_hdr)) { if (!memcmp(rx_msg->data + 2, too_fast, sizeof(too_fast))) { fprintf(stderr, "BS11 tells us we're too " "fast, try --delay bigger than %u\n", delay_ms); return -E2BIG; } else fprintf(stderr, "unknown BS11 message\n"); } #endif oh = (struct abis_om_hdr *) msgb_l2(rx_msg); foh = (struct abis_om_fom_hdr *) oh->data; switch (foh->msg_type) { case NM_MT_BS11_LMT_LOGON_ACK: printf("LMT LOGON: ACK\n\n"); if (bs11cfg_state == STATE_NONE) bs11cfg_state = STATE_LOGON_ACK; rc = abis_nm_bs11_get_state(g_bts); break; case NM_MT_BS11_LMT_LOGOFF_ACK: printf("LMT LOGOFF: ACK\n"); exit(0); break; case NM_MT_BS11_GET_STATE_ACK: rc = abis_nm_tlv_parse(&tp, g_bts, foh->data, oh->length-sizeof(*foh)); print_state(&tp); if (TLVP_PRESENT(&tp, NM_ATT_BS11_BTS_STATE) && TLVP_LEN(&tp, NM_ATT_BS11_BTS_STATE) >= 1) rc = handle_state_resp(*TLVP_VAL(&tp, NM_ATT_BS11_BTS_STATE)); break; case NM_MT_GET_ATTR_RESP: printf("\n%sATTRIBUTES:\n", obj_name(foh)); abis_nm_tlv_parse(&tp, g_bts, foh->data, oh->length-sizeof(*foh)); rc = print_attr(&tp); //osmo_hexdump(foh->data, oh->length-sizeof(*foh)); break; case NM_MT_BS11_SET_ATTR_ACK: printf("SET ATTRIBUTE ObjClass=0x%02x ObjInst=(%d,%d,%d) ACK\n", foh->obj_class, foh->obj_inst.bts_nr, foh->obj_inst.trx_nr, foh->obj_inst.ts_nr); rc = 0; break; case NM_MT_BS11_SET_ATTR_NACK: printf("SET ATTRIBUTE ObjClass=0x%02x ObjInst=(%d,%d,%d) NACK\n", foh->obj_class, foh->obj_inst.bts_nr, foh->obj_inst.trx_nr, foh->obj_inst.ts_nr); break; case NM_MT_GET_ATTR_NACK: printf("\n%sGET ATTR NACK\n", obj_name(foh)); break; case NM_MT_BS11_CREATE_OBJ_ACK: printf("\n%sCREATE OBJECT ACK\n", obj_name(foh)); break; case NM_MT_BS11_CREATE_OBJ_NACK: printf("\n%sCREATE OBJECT NACK\n", obj_name(foh)); break; case NM_MT_BS11_DELETE_OBJ_ACK: printf("\n%sDELETE OBJECT ACK\n", obj_name(foh)); break; case NM_MT_BS11_DELETE_OBJ_NACK: printf("\n%sDELETE OBJECT NACK\n", obj_name(foh)); break; default: rc = abis_nm_rcvmsg(rx_msg); } if (rc < 0) { perror("ERROR in main loop"); //break; } /* flush the queue of pending messages to be sent. */ abis_nm_queue_send_next(link->trx->bts); if (rc == 1) return rc; switch (bs11cfg_state) { case STATE_NONE: abis_nm_bs11_factory_logon(g_bts, 1); break; case STATE_LOGON_ACK: osmo_timer_schedule(&status_timer, 5, 0); break; default: break; } return rc; } void status_timer_cb(void *data) { abis_nm_bs11_get_state(g_bts); } static void print_banner(void) { printf("bs11_config (C) 2009-2010 by Harald Welte and Dieter Spaar\n"); printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n"); } static void print_help(void) { printf("bs11_config [options] [command]\n"); printf("\nSupported options:\n"); printf("\t-h --help\t\t\tPrint this help text\n"); printf("\t-p --port \t\tSpecify serial port\n"); printf("\t-s --software \t\tSpecify Software file\n"); printf("\t-S --safety \t\tSpecify Safety Load file\n"); printf("\t-d --delay \t\t\tSpecify delay in milliseconds\n"); printf("\t-D --disconnect\t\t\tDisconnect BTS from BSC\n"); printf("\t-w --win-size \t\tSpecify Window Size\n"); printf("\t-f --forced\t\t\tForce Software Load\n"); printf("\nSupported commands:\n"); printf("\tquery\t\t\tQuery the BS-11 about serial number and configuration\n"); printf("\tdisconnect\t\tDisconnect A-bis link (go into administrative state)\n"); printf("\tresconnect\t\tReconnect A-bis link (go into normal state)\n"); printf("\trestart\t\t\tRestart the BTS\n"); printf("\tsoftware\t\tDownload Software (only in administrative state)\n"); printf("\tcreate-trx1\t\tCreate objects for TRX1 (Danger: Your BS-11 might overheat)\n"); printf("\tdelete-trx1\t\tDelete objects for TRX1\n"); printf("\tpll-e1-locked\t\tSet the PLL to be locked to E1 clock\n"); printf("\tpll-standalone\t\tSet the PLL to be in standalone mode\n"); printf("\tpll-setvalue \tSet the PLL set value\n"); printf("\tpll-workvalue \tSet the PLL work value\n"); printf("\toml-tei\t\t\tSet OML E1 TS and TEI\n"); printf("\tbport0-star\t\tSet BPORT0 line config to star\n"); printf("\tbport0-multidrop\tSet BPORT0 line config to multidrop\n"); printf("\tbport1-multidrop\tSet BPORT1 line config to multidrop\n"); printf("\tcreate-bport1\t\tCreate BPORT1 object\n"); printf("\tdelete-bport1\t\tDelete BPORT1 object\n"); } static void handle_options(int argc, char **argv) { int option_index = 0; print_banner(); while (1) { int c; static struct option long_options[] = { { "help", 0, 0, 'h' }, { "port", 1, 0, 'p' }, { "software", 1, 0, 's' }, { "safety", 1, 0, 'S' }, { "delay", 1, 0, 'd' }, { "disconnect", 0, 0, 'D' }, { "win-size", 1, 0, 'w' }, { "forced", 0, 0, 'f' }, { "restart", 0, 0, 'r' }, { "debug", 1, 0, 'b'}, }; c = getopt_long(argc, argv, "hp:s:S:td:Dw:fra:", long_options, &option_index); if (c == -1) break; switch (c) { case 'h': print_help(); exit(0); case 'p': serial_port = optarg; break; case 'b': log_parse_category_mask(osmo_stderr_target, optarg); break; case 's': fname_software = optarg; break; case 'S': fname_safety = optarg; break; case 'd': delay_ms = atoi(optarg); break; case 'w': win_size = atoi(optarg); break; case 'D': param_disconnect = 1; break; case 'f': param_forced = 1; break; case 'r': param_disconnect = 1; param_restart = 1; break; default: break; } } if (optind < argc) command = argv[optind]; if (optind+1 < argc) value = argv[optind+1]; } static int num_sigint; static void signal_handler(int signal) { fprintf(stdout, "\nsignal %u received\n", signal); switch (signal) { case SIGINT: num_sigint++; abis_nm_bs11_factory_logon(g_bts, 0); if (num_sigint >= 3) exit(0); break; } } static int bs11cfg_sign_link(struct msgb *msg) { msg->dst = oml_link; return abis_nm_bs11cfg_rcvmsg(msg); } struct e1inp_line_ops bs11cfg_e1inp_line_ops = { .sign_link = bs11cfg_sign_link, }; extern int bts_model_bs11_init(void); int main(int argc, char **argv) { struct gsm_network *gsmnet; struct e1inp_line *line; osmo_init_logging(&log_info); handle_options(argc, argv); bts_model_bs11_init(); gsmnet = gsm_network_init(1, 1, NULL); if (!gsmnet) { fprintf(stderr, "Unable to allocate gsm network\n"); exit(1); } g_bts = gsm_bts_alloc_register(gsmnet, GSM_BTS_TYPE_BS11, HARDCODED_TSC, HARDCODED_BSIC); /* Override existing OML callback handler to set our own. */ g_bts->model->oml_rcvmsg = abis_nm_bs11cfg_rcvmsg; libosmo_abis_init(tall_bs11cfg_ctx); /* Initialize virtual E1 line over rs232. */ line = talloc_zero(tall_bs11cfg_ctx, struct e1inp_line); if (!line) { fprintf(stderr, "Unable to allocate memory for virtual E1 line\n"); exit(1); } /* set the serial port. */ bs11cfg_e1inp_line_ops.cfg.rs232.port = serial_port; bs11cfg_e1inp_line_ops.cfg.rs232.delay = delay_ms; line->driver = e1inp_driver_find("rs232"); if (!line->driver) { fprintf(stderr, "cannot find `rs232' driver, giving up.\n"); exit(1); } e1inp_line_bind_ops(line, &bs11cfg_e1inp_line_ops); /* configure and create signalling link for OML. */ e1inp_ts_config_sign(&line->ts[0], line); g_bts->oml_link = oml_link = e1inp_sign_link_create(&line->ts[0], E1INP_SIGN_OML, g_bts->c0, TEI_OML, 0); e1inp_line_update(line); signal(SIGINT, &signal_handler); abis_nm_bs11_factory_logon(g_bts, 1); //abis_nm_bs11_get_serno(g_bts); status_timer.cb = status_timer_cb; while (1) { if (osmo_select_main(0) < 0) break; } abis_nm_bs11_factory_logon(g_bts, 0); exit(0); } openbsc-0.15.0/openbsc/src/utils/isdnsync.c000066400000000000000000000110571265565154000206210ustar00rootroot00000000000000/* isdnsync.c * * Author Andreas Eversberg * * 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 . * */ #include #include #include #include #include #include #include #include #include #include "mISDNif.h" #define MISDN_OLD_AF_COMPATIBILITY #define AF_COMPATIBILITY_FUNC #include "compat_af_isdn.h" int card = 0; int sock = -1; int mISDN_open(void) { int fd, ret; struct mISDN_devinfo devinfo; struct sockaddr_mISDN l2addr; fd = socket(PF_ISDN, SOCK_RAW, ISDN_P_BASE); if (fd < 0) { fprintf(stderr, "could not open socket (%s)\n", strerror(errno)); return fd; } devinfo.id = card; ret = ioctl(fd, IMGETDEVINFO, &devinfo); if (ret < 0) { fprintf(stderr,"could not send IOCTL IMGETCOUNT (%s)\n", strerror(errno)); close(fd); return ret; } close(fd); if (!(devinfo.Dprotocols & (1 << ISDN_P_TE_S0)) && !(devinfo.Dprotocols & (1 << ISDN_P_TE_E1))) { fprintf(stderr,"Interface does not support TE mode (%s)\n", strerror(errno)); return ret; } fd = socket(PF_ISDN, SOCK_DGRAM, ISDN_P_LAPD_TE); if (fd < 0) { fprintf(stderr,"could not open ISDN_P_LAPD_TE socket (%s)\n", strerror(errno)); return fd; } l2addr.family = AF_ISDN; l2addr.dev = card; l2addr.channel = 0; l2addr.sapi = 0; l2addr.tei = 0; ret = bind(fd, (struct sockaddr *)&l2addr, sizeof(l2addr)); if (ret < 0) { fprintf(stderr,"could not bind socket for card %d (%s)\n", card, strerror(errno)); close(fd); return ret; } sock = fd; return sock; } void mISDN_handle(void) { int ret; fd_set rfd; struct timeval tv; struct sockaddr_mISDN addr; socklen_t alen; unsigned char buffer[2048]; struct mISDNhead *hh = (struct mISDNhead *)buffer; int l1 = 0, l2 = 0, tei = 0; while(1) { again: FD_ZERO(&rfd); FD_SET(sock, &rfd); tv.tv_sec = 2; tv.tv_usec = 0; ret = select(sock+1, &rfd, NULL, NULL, &tv); if (ret < 0) { if (errno == EINTR) continue; fprintf(stderr, "%s aborted: %s\n", __FUNCTION__, strerror(errno)); break; } if (FD_ISSET(sock, &rfd)) { alen = sizeof(addr); ret = recvfrom(sock, buffer, sizeof(buffer), 0, (struct sockaddr *) &addr, &alen); if (ret < 0) { fprintf(stderr, "%s read socket error %s\n", __FUNCTION__, strerror(errno)); } else if (ret < MISDN_HEADER_LEN) { fprintf(stderr, "%s read socket shor frame\n", __FUNCTION__); } else { switch(hh->prim) { case MPH_ACTIVATE_IND: case PH_ACTIVATE_IND: if (!l1) { printf("PH_ACTIVATE\n"); printf("*** Sync available from interface :-)\n"); l1 = 1; } goto again; break; case MPH_DEACTIVATE_IND: case PH_DEACTIVATE_IND: if (l1) { printf("PH_DEACTIVATE\n"); printf("*** Lost sync on interface :-(\n"); l1 = 0; } goto again; break; case DL_ESTABLISH_IND: case DL_ESTABLISH_CNF: printf("DL_ESTABLISH\n"); l2 = 1; goto again; break; case DL_RELEASE_IND: case DL_RELEASE_CNF: printf("DL_RELEASE\n"); l2 = 0; goto again; break; case DL_INFORMATION_IND: printf("DL_INFORMATION (tei %d sapi %d)\n", addr.tei, addr.sapi); tei = 1; break; default: // printf("prim %x\n", hh->prim); goto again; } } } if (tei && !l2) { hh->prim = DL_ESTABLISH_REQ; printf("-> activating layer 2\n"); sendto(sock, buffer, MISDN_HEADER_LEN, 0, (struct sockaddr *) &addr, alen); } } } int main(int argc, char *argv[]) { int ret; if (argc <= 1) { printf("Usage: %s \n\n", argv[0]); printf("Opens given card number in TE-mode PTP and tries to keep layer 2 established.\n"); printf("This keeps layer 1 activated to retrieve a steady sync signal from network.\n"); return(0); } card = atoi(argv[1]); init_af_isdn(); if ((ret = mISDN_open() < 0)) return(ret); mISDN_handle(); close(sock); return 0; } openbsc-0.15.0/openbsc/src/utils/meas_db.c000066400000000000000000000214001265565154000203520ustar00rootroot00000000000000/* Routines for storing measurement reports in SQLite3 database */ /* (C) 2012 by Harald Welte * * 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 . * */ #include #include #include #include #include #include #include #include "meas_db.h" #define INS_MR "INSERT INTO meas_rep (time, imsi, name, scenario, nr, bs_power, ms_timing_offset, fpc, ms_l1_pwr, ms_l1_ta) VALUES (?,?,?,?,?,?,?,?,?,?)" #define INS_UD "INSERT INTO meas_rep_unidir (meas_id, rx_lev_full, rx_lev_sub, rx_qual_full, rx_qual_sub, dtx, uplink) VALUES (?,?,?,?,?,?,?)" #define UPD_MR "UPDATE meas_rep SET ul_unidir=?, dl_unidir=? WHERE id=?" struct meas_db_state { sqlite3 *db; sqlite3_stmt *stmt_ins_ud; sqlite3_stmt *stmt_ins_mr; sqlite3_stmt *stmt_upd_mr; }; /* macros to check for SQLite3 result codes */ #define _SCK_OK(db, call, exp) \ do { \ int rc = call; \ if (rc != exp) { \ fprintf(stderr,"SQL Error in line %u: %s\n", \ __LINE__, sqlite3_errmsg(db)); \ goto err_io; \ } \ } while (0) #define SCK_OK(db, call) _SCK_OK(db, call, SQLITE_OK) #define SCK_DONE(db, call) _SCK_OK(db, call, SQLITE_DONE) static int _insert_ud(struct meas_db_state *st, unsigned long meas_id, int dtx, int uplink, const struct gsm_meas_rep_unidir *ud) { unsigned long rowid; SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_ud, 1, meas_id)); SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_ud, 2, rxlev2dbm(ud->full.rx_lev))); SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_ud, 3, rxlev2dbm(ud->sub.rx_lev))); SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_ud, 4, ud->full.rx_qual)); SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_ud, 5, ud->sub.rx_qual)); SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_ud, 6, dtx)); SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_ud, 7, uplink)); SCK_DONE(st->db, sqlite3_step(st->stmt_ins_ud)); SCK_OK(st->db, sqlite3_reset(st->stmt_ins_ud)); return sqlite3_last_insert_rowid(st->db); err_io: exit(1); } /* insert a measurement report into the database */ int meas_db_insert(struct meas_db_state *st, const char *imsi, const char *name, unsigned long timestamp, const char *scenario, const struct gsm_meas_rep *mr) { int rc; sqlite3_int64 rowid, ul_rowid, dl_rowid; SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 1, timestamp)); if (imsi) SCK_OK(st->db, sqlite3_bind_text(st->stmt_ins_mr, 2, imsi, -1, SQLITE_STATIC)); else SCK_OK(st->db, sqlite3_bind_null(st->stmt_ins_mr, 2)); if (name) SCK_OK(st->db, sqlite3_bind_text(st->stmt_ins_mr, 3, name, -1, SQLITE_STATIC)); else SCK_OK(st->db, sqlite3_bind_null(st->stmt_ins_mr, 3)); if (scenario) SCK_OK(st->db, sqlite3_bind_text(st->stmt_ins_mr, 4, scenario, -1, SQLITE_STATIC)); else SCK_OK(st->db, sqlite3_bind_null(st->stmt_ins_mr, 4)); SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 5, mr->nr)); SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 6, mr->bs_power)); if (mr->flags & MEAS_REP_F_MS_TO) SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 7, mr->ms_timing_offset)); else SCK_OK(st->db, sqlite3_bind_null(st->stmt_ins_mr, 7)); if (mr->flags & MEAS_REP_F_FPC) SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 8, 1)); else SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 8, 0)); if (mr->flags & MEAS_REP_F_MS_L1) { SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 9, mr->ms_l1.pwr)); SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 10, mr->ms_l1.ta)); } SCK_DONE(st->db, sqlite3_step(st->stmt_ins_mr)); SCK_OK(st->db, sqlite3_reset(st->stmt_ins_mr)); rowid = sqlite3_last_insert_rowid(st->db); /* insert uplink measurement */ ul_rowid = _insert_ud(st, rowid, mr->flags & MEAS_REP_F_UL_DTX, 1, &mr->ul); SCK_OK(st->db, sqlite3_bind_int(st->stmt_upd_mr, 1, ul_rowid)); /* insert downlink measurement, if present */ if (mr->flags & MEAS_REP_F_DL_VALID) { dl_rowid = _insert_ud(st, rowid, mr->flags & MEAS_REP_F_DL_DTX, 0, &mr->dl); SCK_OK(st->db, sqlite3_bind_int(st->stmt_upd_mr, 2, dl_rowid)); } else SCK_OK(st->db, sqlite3_bind_null(st->stmt_upd_mr, 2)); /* update meas_rep with the id's of the unidirectional * measurements */ SCK_OK(st->db, sqlite3_bind_int(st->stmt_upd_mr, 3, rowid)); SCK_DONE(st->db, sqlite3_step(st->stmt_upd_mr)); SCK_OK(st->db, sqlite3_reset(st->stmt_upd_mr)); return 0; err_io: return -EIO; } int meas_db_begin(struct meas_db_state *st) { SCK_OK(st->db, sqlite3_exec(st->db, "BEGIN", NULL, NULL, NULL)); return 0; err_io: return -EIO; } int meas_db_commit(struct meas_db_state *st) { SCK_OK(st->db, sqlite3_exec(st->db, "COMMIT", NULL, NULL, NULL)); return 0; err_io: return -EIO; } static const char *create_stmts[] = { "CREATE TABLE IF NOT EXISTS meas_rep (" "id INTEGER PRIMARY KEY AUTOINCREMENT," "time TIMESTAMP," "imsi TEXT," "name TEXT," "scenario TEXT," "nr INTEGER," "bs_power INTEGER NOT NULL," "ms_timing_offset INTEGER," "fpc INTEGER NOT NULL DEFAULT 0," "ul_unidir INTEGER REFERENCES meas_rep_unidir(id)," "dl_unidir INTEGER REFERENCES meas_rep_unidir(id)," "ms_l1_pwr INTEGER," "ms_l1_ta INTEGER" ")", "CREATE TABLE IF NOT EXISTS meas_rep_unidir (" "id INTEGER PRIMARY KEY AUTOINCREMENT," "meas_id INTEGER NOT NULL REFERENCES meas_rep(id)," "rx_lev_full INTEGER NOT NULL," "rx_lev_sub INTEGER NOT NULL," "rx_qual_full INTEGER NOT NULL," "rx_qual_sub INTEGER NOT NULL," "dtx BOOLEAN NOT NULL DEFAULT 0," "uplink BOOLEAN NOT NULL" ")", "CREATE VIEW IF NOT EXISTS path_loss AS " "SELECT " "meas_rep.id, " "datetime(time,'unixepoch') AS timestamp, " "imsi, " "name, " "scenario, " "ms_timing_offset, " "ms_l1_ta, " "fpc, " "ms_l1_pwr, " "ud_ul.rx_lev_full AS ul_rx_lev_full, " "ms_l1_pwr-ud_ul.rx_lev_full AS ul_path_loss_full, " "ud_ul.rx_lev_sub ul_rx_lev_sub, " "ms_l1_pwr-ud_ul.rx_lev_sub AS ul_path_loss_sub, " "ud_ul.rx_qual_full AS ul_rx_qual_full, " "ud_ul.rx_qual_sub AS ul_rx_qual_sub, " "bs_power, " "ud_dl.rx_lev_full AS dl_rx_lev_full, " "bs_power-ud_dl.rx_lev_full AS dl_path_loss_full, " "ud_dl.rx_lev_sub AS dl_rx_lev_sub, " "bs_power-ud_dl.rx_lev_sub AS dl_path_loss_sub, " "ud_dl.rx_qual_full AS dl_rx_qual_full, " "ud_dl.rx_qual_sub AS dl_rx_qual_sub " "FROM " "meas_rep, " "meas_rep_unidir AS ud_dl, " "meas_rep_unidir AS ud_ul " "WHERE " "ud_ul.id = meas_rep.ul_unidir AND " "ud_dl.id = meas_rep.dl_unidir", "CREATE VIEW IF NOT EXISTS overview AS " "SELECT " "id," "timestamp," "imsi," "name," "scenario," "ms_l1_pwr," "ul_rx_lev_full," "ul_path_loss_full," "ul_rx_qual_full," "bs_power," "dl_rx_lev_full," "dl_path_loss_full," "dl_rx_qual_full " "FROM path_loss", }; static int check_create_tbl(struct meas_db_state *st) { int i, rc; for (i = 0; i < ARRAY_SIZE(create_stmts); i++) { SCK_OK(st->db, sqlite3_exec(st->db, create_stmts[i], NULL, NULL, NULL)); } return 0; err_io: return -EIO; } #define PREP_CHK(db, stmt, ptr) \ do { \ int rc; \ rc = sqlite3_prepare_v2(db, stmt, strlen(stmt)+1, \ ptr, NULL); \ if (rc != SQLITE_OK) { \ fprintf(stderr, "Error during prepare of '%s': %s\n", \ stmt, sqlite3_errmsg(db)); \ goto err_io; \ } \ } while (0) struct meas_db_state *meas_db_open(void *ctx, const char *fname) { int rc; struct meas_db_state *st = talloc_zero(ctx, struct meas_db_state); if (!st) return NULL; rc = sqlite3_open_v2(fname, &st->db, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, NULL); if (rc != SQLITE_OK) { fprintf(stderr, "Unable to open DB: %s\n", sqlite3_errmsg(st->db)); goto err_io; } rc = check_create_tbl(st); PREP_CHK(st->db, INS_MR, &st->stmt_ins_mr); PREP_CHK(st->db, INS_UD, &st->stmt_ins_ud); PREP_CHK(st->db, UPD_MR, &st->stmt_upd_mr); return st; err_io: talloc_free(st); return NULL; } void meas_db_close(struct meas_db_state *st) { sqlite3_finalize(st->stmt_ins_mr); sqlite3_finalize(st->stmt_ins_ud); sqlite3_finalize(st->stmt_upd_mr); sqlite3_close_v2(st->db); talloc_free(st); } openbsc-0.15.0/openbsc/src/utils/meas_db.h000066400000000000000000000007201265565154000203610ustar00rootroot00000000000000#ifndef OPENBSC_MEAS_DB_H #define OPENBSC_MEAS_DB_H struct meas_db_state; struct meas_db_state *meas_db_open(void *ctx, const char *fname); void meas_db_close(struct meas_db_state *st); int meas_db_begin(struct meas_db_state *st); int meas_db_commit(struct meas_db_state *st); int meas_db_insert(struct meas_db_state *st, const char *imsi, const char *name, unsigned long timestamp, const char *scenario, const struct gsm_meas_rep *mr); #endif openbsc-0.15.0/openbsc/src/utils/meas_pcap2db.c000066400000000000000000000056611265565154000213130ustar00rootroot00000000000000/* read PCAP file with meas_feed data and write it to sqlite3 database */ /* (C) 2012 by Harald Welte * * 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "meas_db.h" static struct meas_db_state *db; static void handle_mfm(const struct pcap_pkthdr *h, const struct meas_feed_meas *mfm) { const char *scenario; if (strlen(mfm->scenario)) scenario = mfm->scenario; else scenario = NULL; meas_db_insert(db, mfm->imsi, mfm->name, h->ts.tv_sec, scenario, &mfm->mr); } static void pcap_cb(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes) { const char *cur = bytes; const struct iphdr *ip; const struct udphdr *udp; const struct meas_feed_meas *mfm; uint16_t udplen; if (h->caplen < 14+20+8) return; /* Check if there is IPv4 in the Ethernet */ if (cur[12] != 0x08 || cur[13] != 0x00) return; cur += 14; /* ethernet header */ ip = (struct iphdr *) cur; if (ip->version != 4) return; cur += ip->ihl * 4; if (ip->protocol != IPPROTO_UDP) return; udp = (struct udphdr *) cur; if (udp->dest != htons(8888)) return; udplen = ntohs(udp->len); if (udplen != sizeof(*udp) + sizeof(*mfm)) return; cur += sizeof(*udp); mfm = (const struct meas_feed_meas *) cur; handle_mfm(h, mfm); } int main(int argc, char **argv) { char errbuf[PCAP_ERRBUF_SIZE+1]; char *pcap_fname, *db_fname; pcap_t *pc; int rc; if (argc < 3) { fprintf(stderr, "You need to specify PCAP and database file\n"); exit(2); } pcap_fname = argv[1]; db_fname = argv[2]; pc = pcap_open_offline(pcap_fname, errbuf); if (!pc) { fprintf(stderr, "Cannot open %s: %s\n", pcap_fname, errbuf); exit(1); } db = meas_db_open(NULL, db_fname); if (!db) exit(0); rc = meas_db_begin(db); if (rc < 0) { fprintf(stderr, "Error during BEGIN\n"); exit(1); } pcap_loop(pc, 0 , pcap_cb, NULL); meas_db_commit(db); exit(0); } openbsc-0.15.0/openbsc/src/utils/meas_udp2db.c000066400000000000000000000052441265565154000211550ustar00rootroot00000000000000/* liesten to meas_feed on UDP and write it to sqlite3 database */ /* (C) 2012 by Harald Welte * * 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "meas_db.h" static struct osmo_fd udp_ofd; static struct meas_db_state *db; static int handle_msg(struct msgb *msg) { struct meas_feed_hdr *mfh = (struct meas_feed_hdr *) msgb_data(msg); struct meas_feed_meas *mfm = (struct meas_feed_meas *) msgb_data(msg); const char *scenario; time_t now = time(NULL); if (mfh->version != MEAS_FEED_VERSION) return -EINVAL; if (mfh->msg_type != MEAS_FEED_MEAS) return -EINVAL; if (strlen(mfm->scenario)) scenario = mfm->scenario; else scenario = NULL; meas_db_insert(db, mfm->imsi, mfm->name, now, scenario, &mfm->mr); return 0; } static int udp_fd_cb(struct osmo_fd *ofd, unsigned int what) { int rc; if (what & BSC_FD_READ) { struct msgb *msg = msgb_alloc(1024, "UDP Rx"); rc = read(ofd->fd, msgb_data(msg), msgb_tailroom(msg)); if (rc < 0) return rc; msgb_put(msg, rc); handle_msg(msg); msgb_free(msg); } return 0; } int main(int argc, char **argv) { char *db_fname; int rc; if (argc < 2) { fprintf(stderr, "You have to specify the database file name\n"); exit(2); } db_fname = argv[1]; udp_ofd.cb = udp_fd_cb; rc = osmo_sock_init_ofd(&udp_ofd, AF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, 8888, OSMO_SOCK_F_BIND); if (rc < 0) { fprintf(stderr, "Unable to create UDP listen socket\n"); exit(1); } db = meas_db_open(NULL, db_fname); if (!db) { fprintf(stderr, "Unable to open database\n"); exit(1); } /* FIXME: timer-based BEGIN/COMMIT */ while (1) { osmo_select_main(0); }; meas_db_close(db); exit(0); } openbsc-0.15.0/openbsc/src/utils/meas_vis.c000066400000000000000000000141111265565154000205670ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include struct ms_state_uni { CDKSLIDER *cdk; CDKLABEL *cdk_label; time_t last_update; char label[32]; char *_lbl[1]; }; struct ms_state { struct llist_head list; char name[31+1]; char imsi[15+1]; struct gsm_meas_rep mr; struct ms_state_uni ul; struct ms_state_uni dl; }; struct state { struct osmo_fd udp_ofd; struct llist_head ms_list; CDKSCREEN *cdkscreen; WINDOW *curses_win; CDKLABEL *cdk_title; char *title; CDKLABEL *cdk_header; char header[256]; }; static struct state g_st; struct ms_state *find_ms(const char *imsi) { struct ms_state *ms; llist_for_each_entry(ms, &g_st.ms_list, list) { if (!strcmp(ms->imsi, imsi)) return ms; } return NULL; } static struct ms_state *find_alloc_ms(const char *imsi) { struct ms_state *ms; ms = find_ms(imsi); if (!ms) { ms = talloc_zero(NULL, struct ms_state); strncpy(ms->imsi, imsi, sizeof(ms->imsi)-1); ms->ul._lbl[0] = ms->ul.label; ms->dl._lbl[0] = ms->dl.label; llist_add_tail(&ms->list, &g_st.ms_list); } return ms; } static int handle_meas(struct msgb *msg) { struct meas_feed_meas *mfm = (struct meas_feed_meas *) msgb_data(msg); struct ms_state *ms = find_alloc_ms(mfm->imsi); time_t now = time(NULL); strncpy(ms->name, mfm->name, sizeof(ms->imsi)-1); memcpy(&ms->mr, &mfm->mr, sizeof(ms->mr)); ms->ul.last_update = now; if (ms->mr.flags & MEAS_REP_F_DL_VALID) ms->dl.last_update = now; /* move to head of list */ llist_del(&ms->list); llist_add(&ms->list, &g_st.ms_list); return 0; } static int handle_msg(struct msgb *msg) { struct meas_feed_hdr *mfh = (struct meas_feed_hdr *) msgb_data(msg); if (mfh->version != MEAS_FEED_VERSION) return -EINVAL; switch (mfh->msg_type) { case MEAS_FEED_MEAS: handle_meas(msg); break; default: break; } } static int udp_fd_cb(struct osmo_fd *ofd, unsigned int what) { int rc; if (what & BSC_FD_READ) { struct msgb *msg = msgb_alloc(1024, "UDP Rx"); rc = read(ofd->fd, msgb_data(msg), msgb_tailroom(msg)); if (rc < 0) return rc; msgb_put(msg, rc); handle_msg(msg); msgb_free(msg); } return 0; } static void destroy_dir(struct ms_state_uni *uni) { if (uni->cdk) { destroyCDKSlider(uni->cdk); uni->cdk = NULL; } if (uni->cdk_label) { destroyCDKLabel(uni->cdk_label); uni->cdk_label = NULL; } } #define DIR_UL 0 #define DIR_DL 1 static const char *dir_str[2] = { [DIR_UL] = "UL", [DIR_DL] = "DL", }; static int colpair_by_qual(uint8_t rx_qual) { if (rx_qual == 0) return 24; else if (rx_qual <= 4) return 32; else return 16; } static int colpair_by_lev(int rx_lev) { if (rx_lev < -95) return 16; else if (rx_lev < -80) return 32; else return 24; } void write_uni(struct ms_state *ms, struct ms_state_uni *msu, struct gsm_rx_lev_qual *lq, int dir, int row) { char label[128]; time_t now = time(NULL); int qual_col = colpair_by_qual(lq->rx_qual); int lev_col = colpair_by_lev(rxlev2dbm(lq->rx_lev)); int color, pwr; if (dir == DIR_UL) { pwr = ms->mr.ms_l1.pwr; } else { pwr = ms->mr.bs_power; } color = A_REVERSE | COLOR_PAIR(lev_col) | ' '; snprintf(label, sizeof(label), "%s %s ", ms->imsi, dir_str[dir]); msu->cdk = newCDKSlider(g_st.cdkscreen, 0, row, NULL, label, color, COLS-40, rxlev2dbm(lq->rx_lev), -110, -47, 1, 2, FALSE, FALSE); //IsVisibleObj(ms->ul.cdk) = FALSE; snprintf(msu->label, sizeof(msu->label), "%1d %3d %2u %2u %4u", qual_col, lq->rx_qual, qual_col, pwr, ms->mr.ms_l1.ta, ms->mr.ms_timing_offset, now - msu->last_update); msu->cdk_label = newCDKLabel(g_st.cdkscreen, RIGHT, row, msu->_lbl, 1, FALSE, FALSE); } static void update_sliders(void) { int num_vis_sliders = 0; struct ms_state *ms; #define HEADER_LINES 2 /* remove all sliders */ llist_for_each_entry(ms, &g_st.ms_list, list) { destroy_dir(&ms->ul); destroy_dir(&ms->dl); } llist_for_each_entry(ms, &g_st.ms_list, list) { struct gsm_rx_lev_qual *lq; unsigned int row = HEADER_LINES + num_vis_sliders*3; if (ms->mr.flags & MEAS_REP_F_UL_DTX) lq = &ms->mr.ul.sub; else lq = &ms->mr.ul.full; write_uni(ms, &ms->ul, lq, DIR_UL, row); if (ms->mr.flags & MEAS_REP_F_DL_DTX) lq = &ms->mr.dl.sub; else lq = &ms->mr.dl.full; write_uni(ms, &ms->dl, lq, DIR_DL, row+1); num_vis_sliders++; if (num_vis_sliders >= LINES/3) break; } refreshCDKScreen(g_st.cdkscreen); } const struct value_string col_strs[] = { { COLOR_WHITE, "white" }, { COLOR_RED, "red" }, { COLOR_GREEN, "green" }, { COLOR_YELLOW, "yellow" }, { COLOR_BLUE, "blue" }, { COLOR_MAGENTA,"magenta" }, { COLOR_CYAN, "cyan" }, { COLOR_BLACK, "black" }, { 0, NULL } }; int main(int argc, char **argv) { int rc; char *header[1]; char *title[1]; printf("sizeof(gsm_meas_rep)=%u\n", sizeof(struct gsm_meas_rep)); printf("sizeof(meas_feed_meas)=%u\n", sizeof(struct meas_feed_meas)); INIT_LLIST_HEAD(&g_st.ms_list); g_st.curses_win = initscr(); g_st.cdkscreen = initCDKScreen(g_st.curses_win); initCDKColor(); g_st.title = "OpenBSC link quality monitor"; title[0] = g_st.title; g_st.cdk_title = newCDKLabel(g_st.cdkscreen, CENTER, 0, title, 1, FALSE, FALSE); snprintf(g_st.header, sizeof(g_st.header), "Q Pwr TA TO Time"); header[0] = g_st.header; g_st.cdk_header = newCDKLabel(g_st.cdkscreen, RIGHT, 1, header, 1, FALSE, FALSE); #if 0 int i; for (i = 0; i < 64; i++) { short f, b; pair_content(i, &f, &b); attron(COLOR_PAIR(i)); printw("%u: %u (%s) ", i, f, get_value_string(col_strs, f)); printw("%u (%s)\n\r", b, get_value_string(col_strs, b)); } refresh(); getch(); exit(0); #endif g_st.udp_ofd.cb = udp_fd_cb; rc = osmo_sock_init_ofd(&g_st.udp_ofd, AF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, 8888, OSMO_SOCK_F_BIND); if (rc < 0) exit(1); while (1) { osmo_select_main(0); update_sliders(); }; exit(0); } openbsc-0.15.0/openbsc/src/utils/smpp_mirror.c000066400000000000000000000200401265565154000213300ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* FIXME: merge with smpp_smsc.c */ #define SMPP_SYS_ID_LEN 16 enum esme_read_state { READ_ST_IN_LEN = 0, READ_ST_IN_MSG = 1, }; /* FIXME: merge with smpp_smsc.c */ struct esme { struct osmo_fd ofd; uint32_t own_seq_nr; struct osmo_wqueue wqueue; enum esme_read_state read_state; uint32_t read_len; uint32_t read_idx; struct msgb *read_msg; uint8_t smpp_version; char system_id[SMPP_SYS_ID_LEN+1]; char password[SMPP_SYS_ID_LEN+1]; }; /* FIXME: merge with smpp_smsc.c */ #define SMPP34_UNPACK(rc, type, str, data, len) \ memset(str, 0, sizeof(*str)); \ rc = smpp34_unpack(type, str, data, len) #define INIT_RESP(type, resp, req) { \ memset((resp), 0, sizeof(*(resp))); \ (resp)->command_length = 0; \ (resp)->command_id = type; \ (resp)->command_status = ESME_ROK; \ (resp)->sequence_number = (req)->sequence_number; \ } #define PACK_AND_SEND(esme, ptr) pack_and_send(esme, (ptr)->command_id, ptr) static inline uint32_t smpp_msgb_cmdid(struct msgb *msg) { uint8_t *tmp = msgb_data(msg) + 4; return ntohl(*(uint32_t *)tmp); } static uint32_t esme_inc_seq_nr(struct esme *esme) { esme->own_seq_nr++; if (esme->own_seq_nr > 0x7fffffff) esme->own_seq_nr = 1; return esme->own_seq_nr; } static int pack_and_send(struct esme *esme, uint32_t type, void *ptr) { struct msgb *msg = msgb_alloc(4096, "SMPP_Tx"); int rc, rlen; if (!msg) return -ENOMEM; rc = smpp34_pack(type, msg->tail, msgb_tailroom(msg), &rlen, ptr); if (rc != 0) { LOGP(DSMPP, LOGL_ERROR, "[%s] Error during smpp34_pack(): %s\n", esme->system_id, smpp34_strerror); msgb_free(msg); return -EINVAL; } msgb_put(msg, rlen); if (osmo_wqueue_enqueue(&esme->wqueue, msg) != 0) { LOGP(DSMPP, LOGL_ERROR, "[%s] Write queue full. Dropping message\n", esme->system_id); msgb_free(msg); return -EAGAIN; } return 0; } /* FIXME: merge with smpp_smsc.c */ static int smpp_handle_deliver(struct esme *esme, struct msgb *msg) { struct deliver_sm_t deliver; struct deliver_sm_resp_t deliver_r; struct submit_sm_t submit; int rc; memset(&deliver, 0, sizeof(deliver)); SMPP34_UNPACK(rc, DELIVER_SM, &deliver, msgb_data(msg), msgb_length(msg)); if (rc < 0) return rc; INIT_RESP(DELIVER_SM_RESP, &deliver_r, &deliver); PACK_AND_SEND(esme, &deliver_r); memset(&submit, 0, sizeof(submit)); submit.command_id = SUBMIT_SM; submit.command_status = ESME_ROK; submit.sequence_number = esme_inc_seq_nr(esme); submit.dest_addr_ton = deliver.source_addr_ton; submit.dest_addr_npi = deliver.source_addr_npi; memcpy(submit.destination_addr, deliver.source_addr, OSMO_MIN(sizeof(submit.destination_addr), sizeof(deliver.source_addr))); submit.source_addr_ton = deliver.dest_addr_ton; submit.source_addr_npi = deliver.dest_addr_npi; memcpy(submit.source_addr, deliver.destination_addr, OSMO_MIN(sizeof(submit.source_addr), sizeof(deliver.destination_addr))); submit.esm_class = deliver.esm_class; submit.protocol_id = deliver.protocol_id; submit.priority_flag = deliver.priority_flag; memcpy(submit.schedule_delivery_time, deliver.schedule_delivery_time, OSMO_MIN(sizeof(submit.schedule_delivery_time), sizeof(deliver.schedule_delivery_time))); memcpy(submit.validity_period, deliver.validity_period, OSMO_MIN(sizeof(submit.validity_period), sizeof(deliver.validity_period))); submit.registered_delivery = deliver.registered_delivery; submit.replace_if_present_flag = deliver.replace_if_present_flag; submit.data_coding = deliver.data_coding; submit.sm_default_msg_id = deliver.sm_default_msg_id; submit.sm_length = deliver.sm_length; memcpy(submit.short_message, deliver.short_message, OSMO_MIN(sizeof(submit.short_message), sizeof(deliver.short_message))); /* FIXME: TLV? */ return PACK_AND_SEND(esme, &submit); } static int bind_transceiver(struct esme *esme) { struct bind_transceiver_t bind; memset(&bind, 0, sizeof(bind)); bind.command_id = BIND_TRANSCEIVER; bind.sequence_number = esme_inc_seq_nr(esme); snprintf((char *)bind.system_id, sizeof(bind.system_id), "%s", esme->system_id); snprintf((char *)bind.password, sizeof(bind.password), "%s", esme->password); snprintf((char *)bind.system_type, sizeof(bind.system_type), "mirror"); bind.interface_version = esme->smpp_version; return PACK_AND_SEND(esme, &bind); } static int smpp_pdu_rx(struct esme *esme, struct msgb *msg) { uint32_t cmd_id = smpp_msgb_cmdid(msg); int rc; switch (cmd_id) { case DELIVER_SM: rc = smpp_handle_deliver(esme, msg); break; default: LOGP(DSMPP, LOGL_NOTICE, "unhandled case %d\n", cmd_id); rc = 0; break; } return rc; } /* FIXME: merge with smpp_smsc.c */ static int esme_read_cb(struct osmo_fd *ofd) { struct esme *esme = ofd->data; uint32_t len; uint8_t *lenptr = (uint8_t *) &len; uint8_t *cur; struct msgb *msg; int rdlen; int rc; switch (esme->read_state) { case READ_ST_IN_LEN: rdlen = sizeof(uint32_t) - esme->read_idx; rc = read(ofd->fd, lenptr + esme->read_idx, rdlen); if (rc < 0) { LOGP(DSMPP, LOGL_ERROR, "[%s] read returned %d\n", esme->system_id, rc); } else if (rc == 0) { goto dead_socket; } else esme->read_idx += rc; if (esme->read_idx >= sizeof(uint32_t)) { esme->read_len = ntohl(len); msg = msgb_alloc(esme->read_len, "SMPP Rx"); if (!msg) return -ENOMEM; esme->read_msg = msg; cur = msgb_put(msg, sizeof(uint32_t)); memcpy(cur, lenptr, sizeof(uint32_t)); esme->read_state = READ_ST_IN_MSG; esme->read_idx = sizeof(uint32_t); } break; case READ_ST_IN_MSG: msg = esme->read_msg; rdlen = esme->read_len - esme->read_idx; rc = read(ofd->fd, msg->tail, OSMO_MIN(rdlen, msgb_tailroom(msg))); if (rc < 0) { LOGP(DSMPP, LOGL_ERROR, "[%s] read returned %d\n", esme->system_id, rc); } else if (rc == 0) { goto dead_socket; } else { esme->read_idx += rc; msgb_put(msg, rc); } if (esme->read_idx >= esme->read_len) { rc = smpp_pdu_rx(esme, esme->read_msg); esme->read_msg = NULL; esme->read_idx = 0; esme->read_len = 0; esme->read_state = READ_ST_IN_LEN; } break; } return 0; dead_socket: msgb_free(esme->read_msg); osmo_fd_unregister(&esme->wqueue.bfd); close(esme->wqueue.bfd.fd); esme->wqueue.bfd.fd = -1; exit(2342); return 0; } static int esme_write_cb(struct osmo_fd *ofd, struct msgb *msg) { struct esme *esme = ofd->data; int rc; rc = write(ofd->fd, msgb_data(msg), msgb_length(msg)); if (rc == 0) { osmo_fd_unregister(&esme->wqueue.bfd); close(esme->wqueue.bfd.fd); esme->wqueue.bfd.fd = -1; exit(99); } else if (rc < msgb_length(msg)) { LOGP(DSMPP, LOGL_ERROR, "[%s] Short write\n", esme->system_id); return 0; } return 0; } static int smpp_esme_init(struct esme *esme, const char *host, uint16_t port) { int rc; if (port == 0) port = 2775; esme->own_seq_nr = rand(); esme_inc_seq_nr(esme); osmo_wqueue_init(&esme->wqueue, 10); esme->wqueue.bfd.data = esme; esme->wqueue.read_cb = esme_read_cb; esme->wqueue.write_cb = esme_write_cb; rc = osmo_sock_init_ofd(&esme->wqueue.bfd, AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP, host, port, OSMO_SOCK_F_CONNECT); if (rc < 0) return rc; return bind_transceiver(esme); } int main(int argc, char **argv) { struct esme esme; char *host = "localhost"; int port = 0; int rc; memset(&esme, 0, sizeof(esme)); osmo_init_logging(&log_info); snprintf((char *) esme.system_id, sizeof(esme.system_id), "mirror"); snprintf((char *) esme.password, sizeof(esme.password), "mirror"); esme.smpp_version = 0x34; if (argc >= 2) host = argv[1]; if (argc >= 3) port = atoi(argv[2]); rc = smpp_esme_init(&esme, host, port); if (rc < 0) exit(1); while (1) { osmo_select_main(0); } exit(0); } openbsc-0.15.0/openbsc/tests/000077500000000000000000000000001265565154000160325ustar00rootroot00000000000000openbsc-0.15.0/openbsc/tests/Makefile.am000066400000000000000000000044661265565154000201000ustar00rootroot00000000000000SUBDIRS = gsm0408 db channel mgcp gprs abis gbproxy trau subscr if BUILD_NAT SUBDIRS += bsc-nat bsc-nat-trie endif if BUILD_BSC SUBDIRS += bsc endif if BUILD_SMPP SUBDIRS += smpp endif if HAVE_LIBGTP SUBDIRS += sgsn endif # The `:;' works around a Bash 3.2 bug when the output is not writeable. $(srcdir)/package.m4: $(top_srcdir)/configure.ac :;{ \ echo '# Signature of the current package.' && \ echo 'm4_define([AT_PACKAGE_NAME],' && \ echo ' [$(PACKAGE_NAME)])' && \ echo 'm4_define([AT_PACKAGE_TARNAME],' && \ echo ' [$(PACKAGE_TARNAME)])' && \ echo 'm4_define([AT_PACKAGE_VERSION],' && \ echo ' [$(PACKAGE_VERSION)])' && \ echo 'm4_define([AT_PACKAGE_STRING],' && \ echo ' [$(PACKAGE_STRING)])' && \ echo 'm4_define([AT_PACKAGE_BUGREPORT],' && \ echo ' [$(PACKAGE_BUGREPORT)])'; \ echo 'm4_define([AT_PACKAGE_URL],' && \ echo ' [$(PACKAGE_URL)])'; \ } >'$(srcdir)/package.m4' EXTRA_DIST = testsuite.at $(srcdir)/package.m4 $(TESTSUITE) vty_test_runner.py ctrl_test_runner.py smpp_test_runner.py TESTSUITE = $(srcdir)/testsuite DISTCLEANFILES = atconfig if ENABLE_EXT_TESTS python-tests: $(BUILT_SOURCES) osmotestvty.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v osmotestconfig.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v $(PYTHON) $(srcdir)/vty_test_runner.py -w $(abs_top_builddir) -v $(PYTHON) $(srcdir)/ctrl_test_runner.py -w $(abs_top_builddir) -v if BUILD_SMPP $(PYTHON) $(srcdir)/smpp_test_runner.py -w $(abs_top_builddir) -v endif rm -f $(top_builddir)/hlr.sqlite3 else python-tests: $(BUILT_SOURCES) echo "Not running python-based tests (determined at configure-time)" endif check-local: atconfig $(TESTSUITE) $(SHELL) '$(TESTSUITE)' $(TESTSUITEFLAGS) $(MAKE) $(AM_MAKEFLAGS) python-tests installcheck-local: atconfig $(TESTSUITE) $(SHELL) '$(TESTSUITE)' AUTOTEST_PATH='$(bindir)' \ $(TESTSUITEFLAGS) clean-local: test ! -f '$(TESTSUITE)' || \ $(SHELL) '$(TESTSUITE)' --clean AUTOM4TE = $(SHELL) $(top_srcdir)/missing --run autom4te AUTOTEST = $(AUTOM4TE) --language=autotest $(TESTSUITE): $(srcdir)/testsuite.at $(srcdir)/package.m4 $(AUTOTEST) -I '$(srcdir)' -o $@.tmp $@.at mv $@.tmp $@ openbsc-0.15.0/openbsc/tests/abis/000077500000000000000000000000001265565154000167505ustar00rootroot00000000000000openbsc-0.15.0/openbsc/tests/abis/Makefile.am000066400000000000000000000007731265565154000210130ustar00rootroot00000000000000AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOABIS_CFLAGS) \ $(LIBOSMOGSM_CFLAGS) $(COVERAGE_CFLAGS) EXTRA_DIST = abis_test.ok noinst_PROGRAMS = abis_test abis_test_SOURCES = abis_test.c abis_test_LDADD = \ $(top_builddir)/src/libbsc/libbsc.a \ $(top_builddir)/src/libcommon/libcommon.a \ $(top_builddir)/src/libbsc/libbsc.a \ $(top_builddir)/src/libtrau/libtrau.a \ $(LIBOSMOCORE_LIBS) $(LIBOSMOABIS_LIBS) \ $(LIBOSMOGSM_LIBS) openbsc-0.15.0/openbsc/tests/abis/abis_test.c000066400000000000000000000113141265565154000210710ustar00rootroot00000000000000/* * (C) 2012 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 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 . * */ #include #include #include #include #include #include #include static const uint8_t simple_config[] = { /*0, 13, */ 66, 18, 0, 3, 1, 2, 3, 19, 0, 3, 3, 4, 5, }; static const uint8_t dual_config[] = { /*0, 26, */ 66, 18, 0, 3, 1, 2, 3, 19, 0, 3, 3, 4, 5, 66, 18, 0, 3, 9, 7, 5, 19, 0, 3, 6, 7, 8, }; static const uint8_t load_config[] = { 0x42, 0x12, 0x00, 0x08, 0x31, 0x36, 0x38, 0x64, 0x34, 0x37, 0x32, 0x00, 0x13, 0x00, 0x0b, 0x76, 0x32, 0x30, 0x30, 0x62, 0x31, 0x34, 0x33, 0x64, 0x30, 0x00, 0x42, 0x12, 0x00, 0x08, 0x31, 0x36, 0x38, 0x64, 0x34, 0x37, 0x32, 0x00, 0x13, 0x00, 0x0b, 0x76, 0x32, 0x30, 0x30, 0x62, 0x31, 0x34, 0x33, 0x64, 0x31, 0x00 }; static void test_simple_sw_config(void) { struct abis_nm_sw_descr descr[1]; int rc; rc = abis_nm_parse_sw_config(simple_config, ARRAY_SIZE(simple_config), &descr[0], ARRAY_SIZE(descr)); if (rc != 1) { printf("FAILED to parse the File Id/File version\n"); abort(); } if (descr[0].len != 13) { printf("WRONG SIZE: %zu\n", descr[0].len); abort(); } printf("Start: %u len: %zu\n", descr[0].start - simple_config, descr[0].len); printf("file_id: %s\n", osmo_hexdump(descr[0].file_id, descr[0].file_id_len)); printf("file_ver: %s\n", osmo_hexdump(descr[0].file_ver, descr[0].file_ver_len)); } static void test_simple_sw_short(void) { struct abis_nm_sw_descr descr[1]; int i; for (i = 1; i < ARRAY_SIZE(simple_config); ++i) { int rc = abis_nm_parse_sw_config(simple_config, ARRAY_SIZE(simple_config) - i, &descr[0], ARRAY_SIZE(descr)); if (rc >= 1) { printf("SHOULD not have parsed: %d\n", rc); abort(); } } } static void test_dual_sw_config(void) { struct abis_nm_sw_descr descr[2]; int rc; rc = abis_nm_parse_sw_config(dual_config, ARRAY_SIZE(dual_config), &descr[0], ARRAY_SIZE(descr)); if (rc != 2) { printf("FAILED to parse the File Id/File version\n"); abort(); } if (descr[0].len != 13) { printf("WRONG SIZE0: %zu\n", descr[0].len); abort(); } if (descr[1].len != 13) { printf("WRONG SIZE1: %zu\n", descr[1].len); abort(); } printf("Start: %u len: %zu\n", descr[0].start - dual_config, descr[0].len); printf("file_id: %s\n", osmo_hexdump(descr[0].file_id, descr[0].file_id_len)); printf("file_ver: %s\n", osmo_hexdump(descr[0].file_ver, descr[0].file_ver_len)); printf("Start: %u len: %zu\n", descr[1].start - dual_config, descr[1].len); printf("file_id: %s\n", osmo_hexdump(descr[1].file_id, descr[1].file_id_len)); printf("file_ver: %s\n", osmo_hexdump(descr[1].file_ver, descr[1].file_ver_len)); } static void test_sw_selection(void) { struct abis_nm_sw_descr descr[8], tmp; int rc, pos; rc = abis_nm_parse_sw_config(load_config, ARRAY_SIZE(load_config), &descr[0], ARRAY_SIZE(descr)); if (rc != 2) { printf("FAILED to parse the File Id/File version\n"); abort(); } printf("Start: %u len: %zu\n", descr[0].start - load_config, descr[0].len); printf("file_id: %s\n", osmo_hexdump(descr[0].file_id, descr[0].file_id_len)); printf("file_ver: %s\n", osmo_hexdump(descr[0].file_ver, descr[0].file_ver_len)); printf("Start: %u len: %zu\n", descr[1].start - load_config, descr[1].len); printf("file_id: %s\n", osmo_hexdump(descr[1].file_id, descr[1].file_id_len)); printf("file_ver: %s\n", osmo_hexdump(descr[1].file_ver, descr[1].file_ver_len)); /* start */ pos = abis_nm_select_newest_sw(descr, rc); if (pos != 1) { printf("Selected the wrong version: %d\n", pos); abort(); } printf("SELECTED: %d\n", pos); /* shuffle */ tmp = descr[0]; descr[0] = descr[1]; descr[1] = tmp; pos = abis_nm_select_newest_sw(descr, rc); if (pos != 0) { printf("Selected the wrong version: %d\n", pos); abort(); } printf("SELECTED: %d\n", pos); } int main(int argc, char **argv) { osmo_init_logging(&log_info); test_simple_sw_config(); test_simple_sw_short(); test_dual_sw_config(); test_sw_selection(); return EXIT_SUCCESS; } openbsc-0.15.0/openbsc/tests/abis/abis_test.ok000066400000000000000000000006051265565154000212610ustar00rootroot00000000000000Start: 0 len: 13 file_id: 01 02 03 file_ver: 03 04 05 Start: 0 len: 13 file_id: 01 02 03 file_ver: 03 04 05 Start: 13 len: 13 file_id: 09 07 05 file_ver: 06 07 08 Start: 0 len: 26 file_id: 31 36 38 64 34 37 32 00 file_ver: 76 32 30 30 62 31 34 33 64 30 00 Start: 26 len: 26 file_id: 31 36 38 64 34 37 32 00 file_ver: 76 32 30 30 62 31 34 33 64 31 00 SELECTED: 1 SELECTED: 0 openbsc-0.15.0/openbsc/tests/atlocal.in000066400000000000000000000003201265565154000177740ustar00rootroot00000000000000enable_nat_test='@osmo_ac_build_nat@' enable_smpp_test='@osmo_ac_build_smpp@' enable_bsc_test='@osmo_ac_build_bsc@' enable_mgcp_transcoding_test='@osmo_ac_mgcp_transcoding@' enable_sgsn_test='@found_libgtp@' openbsc-0.15.0/openbsc/tests/bsc-nat-trie/000077500000000000000000000000001265565154000203225ustar00rootroot00000000000000openbsc-0.15.0/openbsc/tests/bsc-nat-trie/Makefile.am000066400000000000000000000013221265565154000223540ustar00rootroot00000000000000AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOSCCP_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) AM_LDFLAGS = $(COVERAGE_LDFLAGS) EXTRA_DIST = bsc_nat_trie_test.ok prefixes.csv noinst_PROGRAMS = bsc_nat_trie_test bsc_nat_trie_test_SOURCES = bsc_nat_trie_test.c \ $(top_srcdir)/src/osmo-bsc_nat/bsc_nat_rewrite_trie.c bsc_nat_trie_test_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ $(top_builddir)/src/libmgcp/libmgcp.a \ $(top_builddir)/src/libtrau/libtrau.a \ $(top_builddir)/src/libcommon/libcommon.a \ $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) -lrt \ $(LIBOSMOSCCP_LIBS) $(LIBOSMOVTY_LIBS) \ $(LIBOSMOABIS_LIBS) openbsc-0.15.0/openbsc/tests/bsc-nat-trie/bsc_nat_trie_test.c000066400000000000000000000065321265565154000241670ustar00rootroot00000000000000/* * (C) 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 . * */ #include #include #include #include #include #include #include int main(int argc, char **argv) { struct nat_rewrite *trie; osmo_init_logging(&log_info); printf("Testing the trie\n"); trie = nat_rewrite_parse(NULL, "prefixes.csv"); OSMO_ASSERT(trie); /* verify that it has been parsed */ OSMO_ASSERT(trie->prefixes == 17); printf("Dumping the internal trie\n"); nat_rewrite_dump(trie); /* now do the matching... */ OSMO_ASSERT(!nat_rewrite_lookup(trie, "")); OSMO_ASSERT(!nat_rewrite_lookup(trie, "2")); OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "1")->rewrite, "1") == 0); OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "12")->rewrite, "2") == 0); OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "123")->rewrite, "3") == 0); OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "1234")->rewrite, "4") == 0); OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "12345")->rewrite, "5") == 0); OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "123456")->rewrite, "6") == 0); OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "1234567")->rewrite, "7") == 0); OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "12345678")->rewrite, "8") == 0); OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "123456789")->rewrite, "9") == 0); OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "1234567890")->rewrite, "10") == 0); OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "13")->rewrite, "11") == 0); OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "14")->rewrite, "12") == 0); OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "15")->rewrite, "13") == 0); OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "16")->rewrite, "14") == 0); OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "823455")->rewrite, "15") == 0); OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "82")->rewrite, "16") == 0); OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "+49123445")->rewrite, "17") == 0); /* match a prefix */ OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "121")->rewrite, "2") == 0); OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "1292323")->rewrite, "2") == 0); OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "12345678901")->rewrite, "10") == 0); OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "160")->rewrite, "14") == 0); OSMO_ASSERT(strcmp(nat_rewrite_lookup(trie, "12345678901123452123123")->rewrite, "10") == 0); /* invalid input */ OSMO_ASSERT(!nat_rewrite_lookup(trie, "12abc")); talloc_free(trie); trie = nat_rewrite_parse(NULL, "does_not_exist.csv"); OSMO_ASSERT(!trie); printf("Done with the tests.\n"); return 0; } openbsc-0.15.0/openbsc/tests/bsc-nat-trie/bsc_nat_trie_test.ok000066400000000000000000000003101265565154000243420ustar00rootroot00000000000000Testing the trie Dumping the internal trie 1,1 12,2 123,3 1234,4 12345,5 123456,6 1234567,7 12345678,8 123456789,9 1234567890,10 13,11 14,12 15,13 16,14 82,16 823455,15 +49123,17 Done with the tests. openbsc-0.15.0/openbsc/tests/bsc-nat-trie/prefixes.csv000066400000000000000000000003361265565154000226660ustar00rootroot000000000000001,1 12,2 123,3 1234,4 12345,5 123456,6 1234567,7 12345678,8 123456789,9 1234567890,10 13,11 14,12 15,13 16,14 823455,15 82,16 +49123,17 1ABC,18 12345678901234567890,19 ,20 14A,21 124,324324324234 1234567890,10 no line 99, openbsc-0.15.0/openbsc/tests/bsc-nat/000077500000000000000000000000001265565154000173615ustar00rootroot00000000000000openbsc-0.15.0/openbsc/tests/bsc-nat/Makefile.am000066400000000000000000000022331265565154000214150ustar00rootroot00000000000000AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOCTRL_LIBS) $(LIBOSMOSCCP_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(LIBOSMONETIF_CFLAGS) $(COVERAGE_CFLAGS) AM_LDFLAGS = $(COVERAGE_LDFLAGS) EXTRA_DIST = bsc_nat_test.ok bsc_data.c barr.cfg barr_dup.cfg prefixes.csv noinst_PROGRAMS = bsc_nat_test bsc_nat_test_SOURCES = bsc_nat_test.c \ $(top_srcdir)/src/osmo-bsc_nat/bsc_filter.c \ $(top_srcdir)/src/osmo-bsc_nat/bsc_sccp.c \ $(top_srcdir)/src/osmo-bsc_nat/bsc_nat_utils.c \ $(top_srcdir)/src/osmo-bsc_nat/bsc_nat_rewrite.c \ $(top_srcdir)/src/osmo-bsc_nat/bsc_nat_rewrite_trie.c \ $(top_srcdir)/src/osmo-bsc_nat/bsc_mgcp_utils.c \ $(top_srcdir)/src/osmo-bsc_nat/bsc_nat_filter.c bsc_nat_test_LDADD = \ $(top_builddir)/src/libfilter/libfilter.a \ $(top_builddir)/src/libbsc/libbsc.a \ $(top_builddir)/src/libmgcp/libmgcp.a \ $(top_builddir)/src/libtrau/libtrau.a \ $(top_builddir)/src/libcommon/libcommon.a \ $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) -lrt \ $(LIBOSMOSCCP_LIBS) $(LIBOSMOVTY_LIBS) \ $(LIBOSMOABIS_LIBS) $(LIBOSMONETIF_LIBS) \ $(LIBOSMOCTRL_LIBS) openbsc-0.15.0/openbsc/tests/bsc-nat/barr.cfg000066400000000000000000000002501265565154000207650ustar00rootroot0000000000000012123124:3:2: 12123123:3:1: 12123128:3:6: 12123125:3:3: 12123127:3:5: 12123126:3:4: 12123120:3:4: 12123119:3:4: 12123118:3:4: 12123117:3:4: 12123116:3:4: 12123115:3:4: openbsc-0.15.0/openbsc/tests/bsc-nat/barr_dup.cfg000066400000000000000000000000341265565154000216350ustar00rootroot0000000000000012123124:3:2: 12123124:3:2: openbsc-0.15.0/openbsc/tests/bsc-nat/bsc_data.c000066400000000000000000000223701265565154000212710ustar00rootroot00000000000000/* test data */ /* BSC -> MSC, CR */ static const uint8_t bsc_cr[] = { 0x00, 0x2e, 0xfd, 0x01, 0x00, 0x00, 0x15, 0x02, 0x02, 0x04, 0x02, 0x42, 0xfe, 0x0f, 0x21, 0x00, 0x1f, 0x57, 0x05, 0x08, 0x00, 0x72, 0xf4, 0x80, 0x20, 0x1c, 0xc3, 0x51, 0x17, 0x12, 0x05, 0x08, 0x20, 0x72, 0xf4, 0x90, 0x20, 0x1d, 0x50, 0x08, 0x29, 0x47, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00 }; static const uint8_t bsc_cr_patched[] = { 0x00, 0x2e, 0xfd, 0x01, 0x00, 0x00, 0x05, 0x02, 0x02, 0x04, 0x02, 0x42, 0xfe, 0x0f, 0x21, 0x00, 0x1f, 0x57, 0x05, 0x08, 0x00, 0x72, 0xf4, 0x80, 0x20, 0x1c, 0xc3, 0x51, 0x17, 0x12, 0x05, 0x08, 0x20, 0x72, 0xf4, 0x90, 0x20, 0x1d, 0x50, 0x08, 0x29, 0x47, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00 }; /* CC, MSC -> BSC */ static const uint8_t msc_cc[] = { 0x00, 0x0a, 0xfd, 0x02, 0x00, 0x00, 0x05, 0x01, 0x1f, 0xe4, 0x02, 0x01, 0x00 }; static const uint8_t msc_cc_patched[] = { 0x00, 0x0a, 0xfd, 0x02, 0x00, 0x00, 0x15, 0x01, 0x1f, 0xe4, 0x02, 0x01, 0x00 }; /* Classmark, BSC -> MSC */ static const uint8_t bsc_dtap[] = { 0x00, 0x17, 0xfd, 0x06, 0x01, 0x1f, 0xe4, 0x00, 0x01, 0x10, 0x00, 0x0e, 0x54, 0x12, 0x03, 0x50, 0x18, 0x93, 0x13, 0x06, 0x60, 0x14, 0x45, 0x00, 0x81, 0x00 }; static const uint8_t bsc_dtap_patched[] = { 0x00, 0x17, 0xfd, 0x06, 0x01, 0x1f, 0xe4, 0x00, 0x01, 0x10, 0x00, 0x0e, 0x54, 0x12, 0x03, 0x50, 0x18, 0x93, 0x13, 0x06, 0x60, 0x14, 0x45, 0x00, 0x81, 0x00 }; /* Clear command, MSC -> BSC */ static const uint8_t msc_dtap[] = { 0x00, 0x0d, 0xfd, 0x06, 0x00, 0x00, 0x05, 0x00, 0x01, 0x06, 0x00, 0x04, 0x20, 0x04, 0x01, 0x09 }; static const uint8_t msc_dtap_patched[] = { 0x00, 0x0d, 0xfd, 0x06, 0x00, 0x00, 0x15, 0x00, 0x01, 0x06, 0x00, 0x04, 0x20, 0x04, 0x01, 0x09 }; /*RLSD, MSC -> BSC */ static const uint8_t msc_rlsd[] = { 0x00, 0x0a, 0xfd, 0x04, 0x00, 0x00, 0x05, 0x01, 0x1f, 0xe4, 0x00, 0x01, 0x00 }; static const uint8_t msc_rlsd_patched[] = { 0x00, 0x0a, 0xfd, 0x04, 0x00, 0x00, 0x15, 0x01, 0x1f, 0xe4, 0x00, 0x01, 0x00 }; /* RLC, BSC -> MSC */ static const uint8_t bsc_rlc[] = { 0x00, 0x07, 0xfd, 0x05, 0x01, 0x1f, 0xe4, 0x00, 0x00, 0x15 }; static const uint8_t bsc_rlc_patched[] = { 0x00, 0x07, 0xfd, 0x05, 0x01, 0x1f, 0xe4, 0x00, 0x00, 0x05 }; /* a paging command */ static const uint8_t paging_by_lac_cmd[] = { 0x00, 0x22, 0xfd, 0x09, 0x00, 0x03, 0x07, 0x0b, 0x04, 0x43, 0x02, 0x00, 0xfe, 0x04, 0x43, 0x5c, 0x00, 0xfe, 0x12, 0x00, 0x10, 0x52, 0x08, 0x08, 0x29, 0x47, 0x10, 0x02, 0x01, 0x50, 0x02, 0x30, 0x1a, 0x03, 0x05, 0x20, 0x15 }; /* an assignment command */ static const uint8_t ass_cmd[] = { 0x00, 0x12, 0xfd, 0x06, 0x00, 0x00, 0x49, 0x00, 0x01, 0x0b, 0x00, 0x09, 0x01, 0x0b, 0x03, 0x01, 0x0a, 0x11, 0x01, 0x00, 0x01 }; /* identity response */ static const uint8_t id_resp[] = { 0x00, 0x15, 0xfd, 0x06, 0x01, 0x1c, 0xdc, 0x00, 0x01, 0x0e, 0x01, 0x00, 0x0b, 0x05, 0x59, 0x08, 0x29, 0x40, 0x21, 0x03, 0x07, 0x48, 0x66, 0x31 }; /* sms code msg */ static const uint8_t smsc_rewrite[] = { 0x00, 0x30, 0xfd, 0x06, 0x01, 0x13, 0x1e, 0x00, 0x01, 0x29, 0x01, 0x03, 0x26, 0x09, 0x01, 0x23, 0x00, 0x0c, 0x00, 0x07, 0x91, 0x36, 0x19, 0x08, 0x00, 0x10, 0x50, 0x17, 0x21, 0x0c, 0x0f, 0x81, 0x00, 0x94, 0x51, 0x87, 0x86, 0x78, 0x46, 0xf5, 0x00, 0x00, 0x09, 0xcc, 0xb7, 0xbd, 0x0c, 0xca, 0xbf, 0xeb, 0x20 }; static const uint8_t smsc_rewrite_patched[] = { 0x00, 0x31, 0xfd, 0x06, 0x01, 0x13, 0x1e, 0x00, 0x01, 0x2a, 0x01, 0x03, 0x27, 0x09, 0x01, 0x24, 0x00, 0x0c, 0x00, 0x08, 0x91, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0xf7, 0x17, 0x01, 0x0c, 0x0f, 0x81, 0x00, 0x94, 0x51, 0x87, 0x86, 0x78, 0x46, 0xf5, 0x00, 0x00, 0x09, 0xcc, 0xb7, 0xbd, 0x0c, 0xca, 0xbf, 0xeb, 0x20 }; static const uint8_t smsc_rewrite_patched_hdr[] = { 0x00, 0x30, 0xfd, 0x06, 0x01, 0x13, 0x1e, 0x00, 0x01, 0x29, 0x01, 0x03, 0x26, 0x09, 0x01, 0x23, 0x00, 0x0c, 0x00, 0x07, 0x91, 0x36, 0x19, 0x08, 0x00, 0x10, 0x50, 0x17, 0x01, 0x0c, 0x0f, 0x81, 0x00, 0x94, 0x51, 0x87, 0x86, 0x78, 0x46, 0xf5, 0x00, 0x00, 0x09, 0xcc, 0xb7, 0xbd, 0x0c, 0xca, 0xbf, 0xeb, 0x20 }; static const uint8_t smsc_rewrite_num_patched[] = { 0x00, 0x2f, 0xfd, 0x06, 0x01, 0x13, 0x1e, 0x00, 0x01, 0x28, 0x01, 0x03, 0x25, 0x09, 0x01, 0x22, 0x00, 0x0c, 0x00, 0x07, 0x91, 0x36, 0x19, 0x08, 0x00, 0x10, 0x50, 0x16, 0x21, 0x0c, 0x0d, 0x91, 0x23, 0x51, 0x87, 0x86, 0x78, 0x46, 0xf5, 0x00, 0x00, 0x09, 0xcc, 0xb7, 0xbd, 0x0c, 0xca, 0xbf, 0xeb, 0x20 }; static const uint8_t smsc_rewrite_num_patched_tp_srr[] = { 0x00, 0x2f, 0xfd, 0x06, 0x01, 0x13, 0x1e, 0x00, 0x01, 0x28, 0x01, 0x03, 0x25, 0x09, 0x01, 0x22, 0x00, 0x0c, 0x00, 0x07, 0x91, 0x36, 0x19, 0x08, 0x00, 0x10, 0x50, 0x16, 0x01, 0x0c, 0x0d, 0x91, 0x23, 0x51, 0x87, 0x86, 0x78, 0x46, 0xf5, 0x00, 0x00, 0x09, 0xcc, 0xb7, 0xbd, 0x0c, 0xca, 0xbf, 0xeb, 0x20 }; /* * MGCP messages */ /* nothing to patch */ static const char crcx[] = "CRCX 23265295 8@mgw MGCP 1.0\r\nC: 394b0439fb\r\nL: p:20, a:AMR, nt:IN\r\nM: recvonly\r\n"; static const char crcx_patched[] = "CRCX 23265295 1e@mgw MGCP 1.0\r\nC: 394b0439fb\r\nL: p:20, a:AMR, nt:IN\r\nM: recvonly\r\n"; /* patch the ip and port */ static const char crcx_resp[] = "200 23265295\r\nI: 1\r\n\r\nv=0\r\nc=IN IP4 172.16.18.2\r\nm=audio 4002 RTP/AVP 98 3\r\na=rtpmap:98 AMR/8000\r\n"; static const char crcx_resp_patched[] = "200 23265295\r\nI: 1\r\n\r\nv=0\r\nc=IN IP4 10.0.0.1\r\nm=audio 999 RTP/AVP 98 3\r\na=rtpmap:98 AMR/8000\r\na=fmtp:98 mode-set=2\r\n"; /* patch the ip and port */ static const char mdcx[] = "MDCX 23330829 8@mgw MGCP 1.0\r\nC: 394b0439fb\r\nI: 1\r\nL: p:20, a:AMR, nt:IN\r\nM: recvonly\r\n\r\nv=0\r\no=- 1049380491 0 IN IP4 172.16.18.2\r\ns=-\r\nc=IN IP4 172.16.18.2\r\nt=0 0\r\nm=audio 4410 RTP/AVP 126\r\na=rtpmap:126 AMR/8000/1\r\na=fmtp:126 mode-set=2;start-mode=0\r\na=ptime:20\r\na=recvonly\r\nm=image 4412 udptl t38\r\na=T38FaxVersion:0\r\na=T38MaxBitRate:14400\r\n"; static const char mdcx_patched[] = "MDCX 23330829 1e@mgw MGCP 1.0\r\nC: 394b0439fb\r\nI: 1\r\nL: p:20, a:AMR, nt:IN\r\nM: recvonly\r\n\r\nv=0\r\no=- 1049380491 0 IN IP4 172.16.18.2\r\ns=-\r\nc=IN IP4 10.0.0.23\r\nt=0 0\r\nm=audio 6666 RTP/AVP 126\r\na=rtpmap:126 AMR/8000/1\r\na=fmtp:126 mode-set=2;start-mode=0\r\na=ptime:20\r\na=recvonly\r\nm=image 4412 udptl t38\r\na=T38FaxVersion:0\r\na=T38MaxBitRate:14400\r\n"; static const char mdcx_resp[] = "200 23330829\r\n\r\nv=0\r\nc=IN IP4 172.16.18.2\r\nm=audio 4002 RTP/AVP 98\r\na=rtpmap:98 AMR/8000\r\n"; static const char mdcx_resp_patched[] = "200 23330829\r\n\r\nv=0\r\nc=IN IP4 10.0.0.23\r\nm=audio 5555 RTP/AVP 98\r\na=rtpmap:98 AMR/8000\r\na=fmtp:98 mode-set=2\r\n"; /* different line ending */ static const char mdcx_resp2[] = "200 33330829\n\nv=0\nc=IN IP4 172.16.18.2\nm=audio 4002 RTP/AVP 98\na=rtpmap:98 AMR/8000\n"; static const char mdcx_resp_patched2[] = "200 33330829\n\nv=0\nc=IN IP4 10.0.0.23\nm=audio 5555 RTP/AVP 98\na=rtpmap:98 AMR/8000\na=fmtp:98 mode-set=2\n"; static const char mdcx_resp_patched2_noamr[] = "200 33330829\n\nv=0\nc=IN IP4 10.0.0.23\nm=audio 5555 RTP/AVP 98\na=rtpmap:98 AMR/8000\n"; struct mgcp_patch_test { const char *orig; const char *patch; const char *ip; const int port; const int payload_type; const int ensure_mode_set; }; static const struct mgcp_patch_test mgcp_messages[] = { { .orig = crcx, .patch = crcx_patched, .ip = "0.0.0.0", .port = 2323, .ensure_mode_set = 1, }, { .orig = crcx_resp, .patch = crcx_resp_patched, .ip = "10.0.0.1", .port = 999, .payload_type = 98, .ensure_mode_set = 1, }, { .orig = mdcx, .patch = mdcx_patched, .ip = "10.0.0.23", .port = 6666, .payload_type = 126, .ensure_mode_set = 1, }, { .orig = mdcx_resp, .patch = mdcx_resp_patched, .ip = "10.0.0.23", .port = 5555, .payload_type = 98, .ensure_mode_set = 1, }, { .orig = mdcx_resp2, .patch = mdcx_resp_patched2, .ip = "10.0.0.23", .port = 5555, .payload_type = 98, .ensure_mode_set = 1, }, { .orig = mdcx_resp2, .patch = mdcx_resp_patched2_noamr, .ip = "10.0.0.23", .port = 5555, .payload_type = 98, .ensure_mode_set = 0, }, }; /* CC Setup messages */ static const uint8_t cc_setup_national[] = { 0x00, 0x20, 0xfd, 0x06, 0x01, 0x12, 0x6d, 0x00, 0x01, 0x19, 0x01, 0x00, 0x16, 0x03, 0x05, 0x04, 0x06, 0x60, 0x04, 0x02, 0x00, 0x05, 0x81, 0x5e, 0x06, 0x81, 0x10, 0x27, 0x33, 0x63, 0x66, 0x15, 0x02, 0x11, 0x01 }; static const uint8_t cc_setup_national_patched[] = { 0x00, 0x21, 0xfd, 0x06, 0x01, 0x12, 0x6d, 0x00, 0x01, 0x1a, 0x01, 0x00, 0x17, 0x03, 0x05, 0x04, 0x06, 0x60, 0x04, 0x02, 0x00, 0x05, 0x81, 0x5e, 0x07, 0x91, 0x94, 0x71, 0x32, 0x33, 0x66, 0xf6, 0x15, 0x02, 0x11, 0x01 }; /* patch the phone number of cc_setup_national_patched */ static const uint8_t cc_setup_national_patched_patched[] = { 0x00, 0x21, 0xfd, 0x06, 0x01, 0x12, 0x6d, 0x00, 0x01, 0x1a, 0x01, 0x00, 0x17, 0x03, 0x05, 0x04, 0x06, 0x60, 0x04, 0x02, 0x00, 0x05, 0x81, 0x5e, 0x07, 0x91, 0x63, 0x71, 0x32, 0x33, 0x66, 0xf6, 0x15, 0x02, 0x11, 0x01 }; static const uint8_t cc_setup_international[] = { 0x00, 0x22, 0xfd, 0x06, 0x01, 0x13, 0xe7, 0x00, 0x01, 0x1b, 0x01, 0x00, 0x18, 0x03, 0x45, 0x04, 0x06, 0x60, 0x04, 0x02, 0x00, 0x05, 0x81, 0x5e, 0x08, 0x81, 0x00, 0x94, 0x71, 0x33, 0x63, 0x66, 0x03, 0x15, 0x02, 0x11, 0x01 }; static const uint8_t cc_setup_national_again[] = { 0x00, 0x22, 0xfd, 0x06, 0x01, 0x12, 0x6d, 0x00, 0x01, 0x1b, 0x01, 0x00, 0x18, 0x03, 0x05, 0x04, 0x06, 0x60, 0x04, 0x02, 0x00, 0x05, 0x81, 0x5e, 0x08, 0x81, 0x63, 0x94, 0x71, 0x32, 0x33, 0x66, 0xf6, 0x15, 0x02, 0x11, 0x01 }; openbsc-0.15.0/openbsc/tests/bsc-nat/bsc_nat_test.c000066400000000000000000001217161265565154000222050ustar00rootroot00000000000000/* * BSC NAT Message filtering * * (C) 2010-2013 by Holger Hans Peter Freyther * (C) 2010-2013 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 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include /* test messages for ipa */ static uint8_t ipa_id[] = { 0x00, 0x01, 0xfe, 0x06, }; /* SCCP messages are below */ static uint8_t gsm_reset[] = { 0x00, 0x12, 0xfd, 0x09, 0x00, 0x03, 0x05, 0x07, 0x02, 0x42, 0xfe, 0x02, 0x42, 0xfe, 0x06, 0x00, 0x04, 0x30, 0x04, 0x01, 0x20, }; static const uint8_t gsm_reset_ack[] = { 0x00, 0x13, 0xfd, 0x09, 0x00, 0x03, 0x07, 0x0b, 0x04, 0x43, 0x01, 0x00, 0xfe, 0x04, 0x43, 0x5c, 0x00, 0xfe, 0x03, 0x00, 0x01, 0x31, }; static const uint8_t gsm_paging[] = { 0x00, 0x20, 0xfd, 0x09, 0x00, 0x03, 0x07, 0x0b, 0x04, 0x43, 0x01, 0x00, 0xfe, 0x04, 0x43, 0x5c, 0x00, 0xfe, 0x10, 0x00, 0x0e, 0x52, 0x08, 0x08, 0x29, 0x47, 0x10, 0x02, 0x01, 0x31, 0x97, 0x61, 0x1a, 0x01, 0x06, }; /* BSC -> MSC connection open */ static const uint8_t bssmap_cr[] = { 0x00, 0x2c, 0xfd, 0x01, 0x01, 0x02, 0x03, 0x02, 0x02, 0x04, 0x02, 0x42, 0xfe, 0x0f, 0x1f, 0x00, 0x1d, 0x57, 0x05, 0x08, 0x00, 0x72, 0xf4, 0x80, 0x20, 0x12, 0xc3, 0x50, 0x17, 0x10, 0x05, 0x24, 0x11, 0x03, 0x33, 0x19, 0xa2, 0x08, 0x29, 0x47, 0x10, 0x02, 0x01, 0x31, 0x97, 0x61, 0x00 }; /* MSC -> BSC connection confirm */ static const uint8_t bssmap_cc[] = { 0x00, 0x0a, 0xfd, 0x02, 0x01, 0x02, 0x03, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00, }; /* MSC -> BSC released */ static const uint8_t bssmap_released[] = { 0x00, 0x0e, 0xfd, 0x04, 0x00, 0x00, 0x03, 0x01, 0x02, 0x03, 0x00, 0x01, 0x0f, 0x02, 0x23, 0x42, 0x00, }; /* BSC -> MSC released */ static const uint8_t bssmap_release_complete[] = { 0x00, 0x07, 0xfd, 0x05, 0x01, 0x02, 0x03, 0x00, 0x00, 0x03 }; /* both directions IT timer */ static const uint8_t connnection_it[] = { 0x00, 0x0b, 0xfd, 0x10, 0x01, 0x02, 0x03, 0x01, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, }; /* error in both directions */ static const uint8_t proto_error[] = { 0x00, 0x05, 0xfd, 0x0f, 0x22, 0x33, 0x44, 0x00, }; /* MGCP wrap... */ static const uint8_t mgcp_msg[] = { 0x00, 0x03, 0xfc, 0x20, 0x20, 0x20, }; /* location updating request */ static const uint8_t bss_lu[] = { 0x00, 0x2e, 0xfd, 0x01, 0x91, 0x45, 0x14, 0x02, 0x02, 0x04, 0x02, 0x42, 0xfe, 0x0f, 0x21, 0x00, 0x1f, 0x57, 0x05, 0x08, 0x00, 0x72, 0xf4, 0x80, 0x20, 0x14, 0xc3, 0x50, 0x17, 0x12, 0x05, 0x08, 0x70, 0x72, 0xf4, 0x80, 0xff, 0xfe, 0x30, 0x08, 0x29, 0x44, 0x50, 0x12, 0x03, 0x24, 0x01, 0x95, 0x00 }; /* paging response */ static const uint8_t pag_resp[] = { 0x00, 0x2c, 0xfd, 0x01, 0xe5, 0x68, 0x14, 0x02, 0x02, 0x04, 0x02, 0x42, 0xfe, 0x0f, 0x1f, 0x00, 0x1d, 0x57, 0x05, 0x08, 0x00, 0x72, 0xf4, 0x80, 0x20, 0x16, 0xc3, 0x50, 0x17, 0x10, 0x06, 0x27, 0x01, 0x03, 0x30, 0x18, 0x96, 0x08, 0x29, 0x26, 0x30, 0x32, 0x11, 0x42, 0x01, 0x19, 0x00 }; struct filter_result { const uint8_t *data; const uint16_t length; const int dir; const int result; }; static const struct filter_result results[] = { { .data = ipa_id, .length = ARRAY_SIZE(ipa_id), .dir = DIR_MSC, .result = 1, }, { .data = gsm_reset, .length = ARRAY_SIZE(gsm_reset), .dir = DIR_MSC, .result = 1, }, { .data = gsm_reset_ack, .length = ARRAY_SIZE(gsm_reset_ack), .dir = DIR_BSC, .result = 1, }, { .data = gsm_paging, .length = ARRAY_SIZE(gsm_paging), .dir = DIR_BSC, .result = 0, }, { .data = bssmap_cr, .length = ARRAY_SIZE(bssmap_cr), .dir = DIR_MSC, .result = 0, }, { .data = bssmap_cc, .length = ARRAY_SIZE(bssmap_cc), .dir = DIR_BSC, .result = 0, }, { .data = bssmap_released, .length = ARRAY_SIZE(bssmap_released), .dir = DIR_MSC, .result = 0, }, { .data = bssmap_release_complete, .length = ARRAY_SIZE(bssmap_release_complete), .dir = DIR_BSC, .result = 0, }, { .data = mgcp_msg, .length = ARRAY_SIZE(mgcp_msg), .dir = DIR_MSC, .result = 0, }, { .data = connnection_it, .length = ARRAY_SIZE(connnection_it), .dir = DIR_BSC, .result = 0, }, { .data = connnection_it, .length = ARRAY_SIZE(connnection_it), .dir = DIR_MSC, .result = 0, }, { .data = proto_error, .length = ARRAY_SIZE(proto_error), .dir = DIR_BSC, .result = 0, }, { .data = proto_error, .length = ARRAY_SIZE(proto_error), .dir = DIR_MSC, .result = 0, }, }; static void test_filter(void) { int i; /* start testinh with proper messages */ printf("Testing BSS Filtering.\n"); for (i = 0; i < ARRAY_SIZE(results); ++i) { int result; struct bsc_nat_parsed *parsed; struct msgb *msg = msgb_alloc(4096, "test-message"); printf("Going to test item: %d\n", i); memcpy(msg->data, results[i].data, results[i].length); msg->l2h = msgb_put(msg, results[i].length); parsed = bsc_nat_parse(msg); if (!parsed) { printf("FAIL: Failed to parse the message\n"); continue; } result = bsc_nat_filter_ipa(results[i].dir, msg, parsed); if (result != results[i].result) { printf("FAIL: Not the expected result got: %d wanted: %d\n", result, results[i].result); } msgb_free(msg); } } #include "bsc_data.c" static void copy_to_msg(struct msgb *msg, const uint8_t *data, unsigned int length) { msgb_reset(msg); msg->l2h = msgb_put(msg, length); memcpy(msg->l2h, data, msgb_l2len(msg)); } static void verify_msg(struct msgb *out, const uint8_t *ref, int ref_len) { if (out->len != ref_len) { printf("FAIL: The size should match: %d vs. %d\n", out->len, ref_len); printf("%s\n", osmo_hexdump(out->data, out->len)); printf("Wanted\n"); printf("%s\n", osmo_hexdump(ref, ref_len)); abort(); } if (memcmp(out->data, ref, out->len) != 0) { printf("FAIL: the data should be changed.\n"); printf("%s\n", osmo_hexdump(out->data, out->len)); printf("Wanted\n"); printf("%s\n", osmo_hexdump(ref, ref_len)); abort(); } } #define VERIFY(con_found, con, msg, ver, str) \ if (!con_found) { \ printf("Failed to find connection.\n"); \ abort(); \ } \ if (con_found->bsc != con) { \ printf("Got connection of the wrong BSC: %d\n", \ con_found->bsc->cfg->nr); \ abort(); \ } \ if (memcmp(msg->data, ver, sizeof(ver)) != 0) { \ printf("Failed to patch the %s msg.\n", str); \ abort(); \ } /* test conn tracking once */ static void test_contrack() { struct bsc_nat *nat; struct bsc_connection *con; struct nat_sccp_connection *con_found; struct nat_sccp_connection *rc_con; struct bsc_nat_parsed *parsed; struct msgb *msg; printf("Testing connection tracking.\n"); nat = bsc_nat_alloc(); con = bsc_connection_alloc(nat); con->cfg = bsc_config_alloc(nat, "foo"); bsc_config_add_lac(con->cfg, 23); bsc_config_add_lac(con->cfg, 49); bsc_config_add_lac(con->cfg, 42); bsc_config_del_lac(con->cfg, 49); bsc_config_add_lac(con->cfg, 1111); msg = msgb_alloc(4096, "test"); /* 1.) create a connection */ copy_to_msg(msg, bsc_cr, sizeof(bsc_cr)); parsed = bsc_nat_parse(msg); con_found = patch_sccp_src_ref_to_msc(msg, parsed, con); if (con_found != NULL) { printf("Con should not exist realref(%u)\n", sccp_src_ref_to_int(&con_found->real_ref)); abort(); } rc_con = create_sccp_src_ref(con, parsed); if (!rc_con) { printf("Failed to create a ref\n"); abort(); } con_found = patch_sccp_src_ref_to_msc(msg, parsed, con); if (!con_found) { printf("Failed to find connection.\n"); abort(); } if (con_found->bsc != con) { printf("Got connection of the wrong BSC: %d\n", con_found->bsc->cfg->nr); abort(); } if (con_found != rc_con) { printf("Failed to find the right connection.\n"); abort(); } if (memcmp(msg->data, bsc_cr_patched, sizeof(bsc_cr_patched)) != 0) { printf("Failed to patch the BSC CR msg.\n"); abort(); } talloc_free(parsed); /* 2.) get the cc */ copy_to_msg(msg, msc_cc, sizeof(msc_cc)); parsed = bsc_nat_parse(msg); con_found = patch_sccp_src_ref_to_bsc(msg, parsed, nat); VERIFY(con_found, con, msg, msc_cc_patched, "MSC CC"); if (update_sccp_src_ref(con_found, parsed) != 0) { printf("Failed to update the SCCP con.\n"); abort(); } /* 3.) send some data */ copy_to_msg(msg, bsc_dtap, sizeof(bsc_dtap)); parsed = bsc_nat_parse(msg); con_found = patch_sccp_src_ref_to_msc(msg, parsed, con); VERIFY(con_found, con, msg, bsc_dtap_patched, "BSC DTAP"); /* 4.) receive some data */ copy_to_msg(msg, msc_dtap, sizeof(msc_dtap)); parsed = bsc_nat_parse(msg); con_found = patch_sccp_src_ref_to_bsc(msg, parsed, nat); VERIFY(con_found, con, msg, msc_dtap_patched, "MSC DTAP"); /* 5.) close the connection */ copy_to_msg(msg, msc_rlsd, sizeof(msc_rlsd)); parsed = bsc_nat_parse(msg); con_found = patch_sccp_src_ref_to_bsc(msg, parsed, nat); VERIFY(con_found, con, msg, msc_rlsd_patched, "MSC RLSD"); /* 6.) confirm the connection close */ copy_to_msg(msg, bsc_rlc, sizeof(bsc_rlc)); parsed = bsc_nat_parse(msg); con_found = patch_sccp_src_ref_to_msc(msg, parsed, con); if (!con_found) { printf("Failed to find connection.\n"); abort(); } if (con_found->bsc != con) { printf("Got connection of the wrong BSC: %d\n", con_found->bsc->cfg->nr); abort(); } if (memcmp(msg->data, bsc_rlc_patched, sizeof(bsc_rlc_patched)) != 0) { printf("Failed to patch the BSC CR msg.\n"); abort(); } remove_sccp_src_ref(con, msg, parsed); talloc_free(parsed); copy_to_msg(msg, bsc_rlc, sizeof(bsc_rlc)); parsed = bsc_nat_parse(msg); con_found = patch_sccp_src_ref_to_msc(msg, parsed, con); /* verify that it is gone */ if (con_found != NULL) { printf("Con should not exist real_ref(%u)\n", sccp_src_ref_to_int(&con_found->real_ref)); abort(); } talloc_free(parsed); bsc_config_free(con->cfg); bsc_nat_free(nat); msgb_free(msg); } static void test_paging(void) { struct bsc_nat *nat; struct bsc_connection *con; struct bsc_config *cfg; printf("Testing paging by lac.\n"); nat = bsc_nat_alloc(); con = bsc_connection_alloc(nat); cfg = bsc_config_alloc(nat, "unknown"); con->cfg = cfg; bsc_config_add_lac(cfg, 23); con->authenticated = 1; llist_add(&con->list_entry, &nat->bsc_connections); /* Test it by not finding it */ if (bsc_config_handles_lac(cfg, 8213) != 0) { printf("Should not be handled.\n"); abort(); } /* Test by finding it */ bsc_config_del_lac(cfg, 23); bsc_config_add_lac(cfg, 8213); if (bsc_config_handles_lac(cfg, 8213) == 0) { printf("Should have found it.\n"); abort(); } bsc_nat_free(nat); } static void test_mgcp_allocations(void) { #if 0 struct bsc_connection *bsc; struct bsc_nat *nat; struct nat_sccp_connection con; int i, j, multiplex; printf("Testing MGCP.\n"); memset(&con, 0, sizeof(con)); nat = bsc_nat_alloc(); nat->bsc_endpoints = talloc_zero_array(nat, struct bsc_endpoint, 65); nat->mgcp_cfg = mgcp_config_alloc(); nat->mgcp_cfg->trunk.number_endpoints = 64; bsc = bsc_connection_alloc(nat); bsc->cfg = bsc_config_alloc(nat, "foo"); bsc->cfg->max_endpoints = 60; bsc_config_add_lac(bsc->cfg, 2323); bsc->last_endpoint = 0x22; con.bsc = bsc; bsc_init_endps_if_needed(bsc); i = 1; do { if (bsc_assign_endpoint(bsc, &con) != 0) { printf("failed to allocate... on iteration %d\n", i); break; } ++i; } while(1); multiplex = bsc_mgcp_nr_multiplexes(bsc->cfg->max_endpoints); for (i = 0; i < multiplex; ++i) { for (j = 0; j < 32; ++j) printf("%d", bsc->_endpoint_status[i*32 + j]); printf(": %d of %d\n", i*32 + 32, 32 * 8); } #endif } static void test_mgcp_ass_tracking(void) { struct bsc_connection *bsc; struct bsc_nat *nat; struct nat_sccp_connection con; struct bsc_nat_parsed *parsed; struct msgb *msg; printf("Testing MGCP.\n"); memset(&con, 0, sizeof(con)); nat = bsc_nat_alloc(); nat->bsc_endpoints = talloc_zero_array(nat, struct bsc_endpoint, 33); nat->mgcp_cfg = mgcp_config_alloc(); nat->mgcp_cfg->trunk.number_endpoints = 64; mgcp_endpoints_allocate(&nat->mgcp_cfg->trunk); bsc = bsc_connection_alloc(nat); bsc->cfg = bsc_config_alloc(nat, "foo"); bsc_config_add_lac(bsc->cfg, 2323); bsc->last_endpoint = 0x1e; con.bsc = bsc; msg = msgb_alloc(4096, "foo"); copy_to_msg(msg, ass_cmd, sizeof(ass_cmd)); parsed = bsc_nat_parse(msg); if (msg->l2h[16] != 0 || msg->l2h[17] != 0x1) { printf("Input is not as expected.. %s 0x%x\n", osmo_hexdump(msg->l2h, msgb_l2len(msg)), msg->l2h[17]); abort(); } if (bsc_mgcp_assign_patch(&con, msg) != 0) { printf("Failed to handle assignment.\n"); abort(); } if (con.msc_endp != 1) { printf("Timeslot should be 1.\n"); abort(); } if (con.bsc_endp != 0x1) { printf("Assigned timeslot should have been 1.\n"); abort(); } if (con.bsc->_endpoint_status[0x1] != 1) { printf("The status on the BSC is wrong.\n"); abort(); } int multiplex, timeslot; mgcp_endpoint_to_timeslot(0x1, &multiplex, ×lot); uint16_t cic = htons(timeslot & 0x1f); if (memcmp(&cic, &msg->l2h[16], sizeof(cic)) != 0) { printf("Message was not patched properly\n"); printf("data cic: 0x%x %s\n", cic, osmo_hexdump(msg->l2h, msgb_l2len(msg))); abort(); } talloc_free(parsed); bsc_mgcp_dlcx(&con); if (con.bsc_endp != -1 || con.msc_endp != -1 || con.bsc->_endpoint_status[1] != 0 || con.bsc->last_endpoint != 0x1) { printf("Clearing should remove the mapping.\n"); abort(); } bsc_config_free(bsc->cfg); bsc_nat_free(nat); } /* test the code to find a given connection */ static void test_mgcp_find(void) { struct bsc_nat *nat; struct bsc_connection *con; struct nat_sccp_connection *sccp_con; printf("Testing finding of a BSC Connection\n"); nat = bsc_nat_alloc(); con = bsc_connection_alloc(nat); llist_add(&con->list_entry, &nat->bsc_connections); sccp_con = talloc_zero(con, struct nat_sccp_connection); sccp_con->msc_endp = 12; sccp_con->bsc_endp = 12; sccp_con->bsc = con; llist_add(&sccp_con->list_entry, &nat->sccp_connections); if (bsc_mgcp_find_con(nat, 11) != NULL) { printf("Found the wrong connection.\n"); abort(); } if (bsc_mgcp_find_con(nat, 12) != sccp_con) { printf("Didn't find the connection\n"); abort(); } /* free everything */ bsc_nat_free(nat); } static void test_mgcp_rewrite(void) { int i; struct msgb *output; printf("Testing rewriting MGCP messages.\n"); for (i = 0; i < ARRAY_SIZE(mgcp_messages); ++i) { const char *orig = mgcp_messages[i].orig; const char *patc = mgcp_messages[i].patch; const char *ip = mgcp_messages[i].ip; const int port = mgcp_messages[i].port; const int expected_payload_type = mgcp_messages[i].payload_type; const int ensure_mode_set = mgcp_messages[i].ensure_mode_set; int payload_type = -1; char *input = strdup(orig); output = bsc_mgcp_rewrite(input, strlen(input), 0x1e, ip, port, -1, &payload_type, ensure_mode_set); if (payload_type != -1) { fprintf(stderr, "Found media payload type %d in SDP data\n", payload_type); if (payload_type != expected_payload_type) { printf("Wrong payload type %d (expected %d)\n", payload_type, expected_payload_type); abort(); } } if (msgb_l2len(output) != strlen(patc)) { printf("Wrong sizes for test: %d %u != %zu != %zu\n", i, msgb_l2len(output), strlen(patc), strlen(orig)); printf("String '%s' vs '%s'\n", (const char *) output->l2h, patc); abort(); } if (memcmp(output->l2h, patc, msgb_l2len(output)) != 0) { printf("Broken on %d msg: '%s'\n", i, (const char *) output->l2h); abort(); } msgb_free(output); free(input); } } static void test_mgcp_parse(void) { int code, ci; char transaction[60]; printf("Testing MGCP response parsing.\n"); if (bsc_mgcp_parse_response(crcx_resp, &code, transaction) != 0) { printf("Failed to parse CRCX resp.\n"); abort(); } if (code != 200) { printf("Failed to parse the CODE properly. Got: %d\n", code); abort(); } if (strcmp(transaction, "23265295") != 0) { printf("Failed to parse transaction id: '%s'\n", transaction); abort(); } ci = bsc_mgcp_extract_ci(crcx_resp); if (ci != 1) { printf("Failed to parse the CI. Got: %d\n", ci); abort(); } } struct cr_filter { const uint8_t *data; int length; int result; int contype; const char *bsc_imsi_allow; const char *bsc_imsi_deny; const char *nat_imsi_deny; int nat_cm_reject_cause; int nat_lu_reject_cause; int bsc_cm_reject_cause; int bsc_lu_reject_cause; int want_cm_reject_cause; int want_lu_reject_cause; }; static struct cr_filter cr_filter[] = { { .data = bssmap_cr, .length = sizeof(bssmap_cr), .result = 1, .contype = FLT_CON_TYPE_CM_SERV_REQ, .nat_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, .nat_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, .bsc_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, .bsc_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, .want_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, .want_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, }, { .data = bss_lu, .length = sizeof(bss_lu), .result = 1, .contype = FLT_CON_TYPE_LU, .nat_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, .nat_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, .bsc_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, .bsc_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, .want_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, .want_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, }, { .data = pag_resp, .length = sizeof(pag_resp), .result = 1, .contype = FLT_CON_TYPE_PAG_RESP, .nat_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, .nat_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, .bsc_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, .bsc_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, .want_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, .want_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, }, { /* nat deny is before blank/null BSC */ .data = bss_lu, .length = sizeof(bss_lu), .result = -3, .nat_imsi_deny = "[0-9]*", .contype = FLT_CON_TYPE_LU, .nat_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, .nat_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, .bsc_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, .bsc_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, .want_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, .want_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, }, { /* BSC allow is before NAT deny */ .data = bss_lu, .length = sizeof(bss_lu), .result = 1, .nat_imsi_deny = "[0-9]*", .bsc_imsi_allow = "2440[0-9]*", .contype = FLT_CON_TYPE_LU, .nat_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, .nat_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, .bsc_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, .bsc_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, .want_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, .want_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, }, { /* BSC allow is before NAT deny */ .data = bss_lu, .length = sizeof(bss_lu), .result = 1, .bsc_imsi_allow = "[0-9]*", .nat_imsi_deny = "[0-9]*", .contype = FLT_CON_TYPE_LU, .nat_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, .nat_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, .bsc_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, .bsc_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, .want_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, .want_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, }, { /* filter as deny is first */ .data = bss_lu, .length = sizeof(bss_lu), .result = 1, .bsc_imsi_deny = "[0-9]*", .bsc_imsi_allow = "[0-9]*", .nat_imsi_deny = "[0-9]*", .contype = FLT_CON_TYPE_LU, .nat_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, .nat_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, .bsc_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, .bsc_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, .want_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, .want_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, }, { /* deny by nat rule */ .data = bss_lu, .length = sizeof(bss_lu), .result = -3, .bsc_imsi_deny = "000[0-9]*", .nat_imsi_deny = "[0-9]*", .contype = FLT_CON_TYPE_LU, .nat_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, .nat_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, .bsc_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, .bsc_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, .want_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, .want_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, }, { /* deny by nat rule */ .data = bss_lu, .length = sizeof(bss_lu), .result = -3, .bsc_imsi_deny = "000[0-9]*", .nat_imsi_deny = "[0-9]*", .contype = FLT_CON_TYPE_LU, .nat_cm_reject_cause = 0x23, .nat_lu_reject_cause = 0x42, .bsc_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, .bsc_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, .want_lu_reject_cause = 0x42, .want_cm_reject_cause = 0x23, }, { /* deny by bsc rule */ .data = bss_lu, .length = sizeof(bss_lu), .result = -2, .bsc_imsi_deny = "[0-9]*", .contype = FLT_CON_TYPE_LU, .nat_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, .nat_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, .bsc_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, .bsc_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, .want_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, .want_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, }, { /* deny by bsc rule */ .data = bss_lu, .length = sizeof(bss_lu), .result = -2, .bsc_imsi_deny = "[0-9]*", .contype = FLT_CON_TYPE_LU, .nat_cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, .nat_lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED, .bsc_cm_reject_cause = 0x42, .bsc_lu_reject_cause = 0x23, .want_lu_reject_cause = 0x23, .want_cm_reject_cause = 0x42, }, }; static void test_cr_filter() { int i, res, contype; struct msgb *msg = msgb_alloc(4096, "test_cr_filter"); struct bsc_nat_parsed *parsed; struct bsc_msg_acc_lst *nat_lst, *bsc_lst; struct bsc_msg_acc_lst_entry *nat_entry, *bsc_entry; struct bsc_filter_reject_cause cause; struct bsc_nat *nat = bsc_nat_alloc(); struct bsc_connection *bsc = bsc_connection_alloc(nat); bsc->cfg = bsc_config_alloc(nat, "foo"); bsc_config_add_lac(bsc->cfg, 1234); bsc->cfg->acc_lst_name = "bsc"; nat->acc_lst_name = "nat"; nat_lst = bsc_msg_acc_lst_get(nat, &nat->access_lists, "nat"); bsc_lst = bsc_msg_acc_lst_get(nat, &nat->access_lists, "bsc"); bsc_entry = bsc_msg_acc_lst_entry_create(bsc_lst); nat_entry = bsc_msg_acc_lst_entry_create(nat_lst); /* test the default value as we are going to overwrite it */ OSMO_ASSERT(bsc_entry->cm_reject_cause == GSM48_REJECT_PLMN_NOT_ALLOWED); OSMO_ASSERT(bsc_entry->lu_reject_cause == GSM48_REJECT_PLMN_NOT_ALLOWED); for (i = 0; i < ARRAY_SIZE(cr_filter); ++i) { char *imsi; msgb_reset(msg); copy_to_msg(msg, cr_filter[i].data, cr_filter[i].length); bsc_entry->cm_reject_cause = cr_filter[i].bsc_cm_reject_cause; bsc_entry->lu_reject_cause = cr_filter[i].bsc_lu_reject_cause; nat_entry->cm_reject_cause = cr_filter[i].nat_cm_reject_cause; nat_entry->lu_reject_cause = cr_filter[i].nat_lu_reject_cause; if (gsm_parse_reg(nat_entry, &nat_entry->imsi_deny_re, &nat_entry->imsi_deny, cr_filter[i].nat_imsi_deny ? 1 : 0, &cr_filter[i].nat_imsi_deny) != 0) abort(); if (gsm_parse_reg(bsc_entry, &bsc_entry->imsi_allow_re, &bsc_entry->imsi_allow, cr_filter[i].bsc_imsi_allow ? 1 : 0, &cr_filter[i].bsc_imsi_allow) != 0) abort(); if (gsm_parse_reg(bsc_entry, &bsc_entry->imsi_deny_re, &bsc_entry->imsi_deny, cr_filter[i].bsc_imsi_deny ? 1 : 0, &cr_filter[i].bsc_imsi_deny) != 0) abort(); parsed = bsc_nat_parse(msg); if (!parsed) { printf("FAIL: Failed to parse the message\n"); abort(); } memset(&cause, 0, sizeof(cause)); res = bsc_nat_filter_sccp_cr(bsc, msg, parsed, &contype, &imsi, &cause); if (res != cr_filter[i].result) { printf("FAIL: Wrong result %d for test %d.\n", res, i); abort(); } OSMO_ASSERT(cause.cm_reject_cause == cr_filter[i].want_cm_reject_cause); OSMO_ASSERT(cause.lu_reject_cause == cr_filter[i].want_lu_reject_cause); if (contype != cr_filter[i].contype) { printf("FAIL: Wrong contype %d for test %d.\n", res, contype); abort(); } talloc_steal(parsed, imsi); talloc_free(parsed); } msgb_free(msg); bsc_nat_free(nat); } static void test_dt_filter() { int i; struct msgb *msg = msgb_alloc(4096, "test_dt_filter"); struct bsc_nat_parsed *parsed; struct bsc_filter_reject_cause cause; struct bsc_nat *nat = bsc_nat_alloc(); struct bsc_connection *bsc = bsc_connection_alloc(nat); struct nat_sccp_connection *con = talloc_zero(0, struct nat_sccp_connection); bsc->cfg = bsc_config_alloc(nat, "foo"); bsc_config_add_lac(bsc->cfg, 23); con->bsc = bsc; msgb_reset(msg); copy_to_msg(msg, id_resp, ARRAY_SIZE(id_resp)); parsed = bsc_nat_parse(msg); if (!parsed) { printf("FAIL: Could not parse ID resp\n"); abort(); } if (parsed->bssap != BSSAP_MSG_DTAP) { printf("FAIL: It should be dtap\n"); abort(); } /* gsm_type is actually the size of the dtap */ if (parsed->gsm_type < msgb_l3len(msg) - 3) { printf("FAIL: Not enough space for the content\n"); abort(); } memset(&cause, 0, sizeof(cause)); if (bsc_nat_filter_dt(bsc, msg, con, parsed, &cause) != 1) { printf("FAIL: Should have passed..\n"); abort(); } /* just some basic length checking... */ for (i = ARRAY_SIZE(id_resp); i >= 0; --i) { msgb_reset(msg); copy_to_msg(msg, id_resp, ARRAY_SIZE(id_resp)); parsed = bsc_nat_parse(msg); if (!parsed) continue; con->filter_state.imsi_checked = 0; memset(&cause, 0, sizeof(cause)); bsc_nat_filter_dt(bsc, msg, con, parsed, &cause); } msgb_free(msg); bsc_nat_free(nat); } static void test_setup_rewrite() { struct msgb *msg = msgb_alloc(4096, "test_dt_filter"); struct msgb *out; struct bsc_nat_parsed *parsed; const char *imsi = "27408000001234"; struct bsc_nat *nat = bsc_nat_alloc(); /* a fake list */ struct osmo_config_list entries; struct osmo_config_entry entry; INIT_LLIST_HEAD(&entries.entry); entry.mcc = "274"; entry.mnc = "08"; entry.option = "^0([1-9])"; entry.text = "0049"; llist_add_tail(&entry.list, &entries.entry); bsc_nat_num_rewr_entry_adapt(nat, &nat->num_rewr, &entries); /* verify that nothing changed */ msgb_reset(msg); copy_to_msg(msg, cc_setup_international, ARRAY_SIZE(cc_setup_international)); parsed = bsc_nat_parse(msg); if (!parsed) { printf("FAIL: Could not parse ID resp\n"); abort(); } out = bsc_nat_rewrite_msg(nat, msg, parsed, imsi); if (msg != out) { printf("FAIL: The message should not have been changed\n"); abort(); } verify_msg(out, cc_setup_international, ARRAY_SIZE(cc_setup_international)); talloc_free(parsed); /* verify that something in the message changes */ msgb_reset(msg); copy_to_msg(msg, cc_setup_national, ARRAY_SIZE(cc_setup_national)); parsed = bsc_nat_parse(msg); if (!parsed) { printf("FAIL: Could not parse ID resp\n"); abort(); } out = bsc_nat_rewrite_msg(nat, msg, parsed, imsi); if (!out) { printf("FAIL: A new message should be created.\n"); abort(); } if (msg == out) { printf("FAIL: The message should have changed\n"); abort(); } verify_msg(out, cc_setup_national_patched, ARRAY_SIZE(cc_setup_national_patched)); msgb_free(out); /* Make sure that a wildcard is matching */ entry.mnc = "*"; bsc_nat_num_rewr_entry_adapt(nat, &nat->num_rewr, &entries); msg = msgb_alloc(4096, "test_dt_filter"); copy_to_msg(msg, cc_setup_national, ARRAY_SIZE(cc_setup_national)); parsed = bsc_nat_parse(msg); if (!parsed) { printf("FAIL: Could not parse ID resp\n"); abort(); } out = bsc_nat_rewrite_msg(nat, msg, parsed, imsi); if (!out) { printf("FAIL: A new message should be created.\n"); abort(); } if (msg == out) { printf("FAIL: The message should have changed\n"); abort(); } verify_msg(out, cc_setup_national_patched, ARRAY_SIZE(cc_setup_national_patched)); msgb_free(out); /* Make sure that a wildcard is matching */ entry.mnc = "09"; bsc_nat_num_rewr_entry_adapt(nat, &nat->num_rewr, &entries); msg = msgb_alloc(4096, "test_dt_filter"); copy_to_msg(msg, cc_setup_national, ARRAY_SIZE(cc_setup_national)); parsed = bsc_nat_parse(msg); if (!parsed) { printf("FAIL: Could not parse ID resp\n"); abort(); } out = bsc_nat_rewrite_msg(nat, msg, parsed, imsi); if (out != msg) { printf("FAIL: The message should be unchanged.\n"); abort(); } verify_msg(out, cc_setup_national, ARRAY_SIZE(cc_setup_national)); msgb_free(out); /* Now see what happens to an international number */ entry.mnc = "*"; entry.option = "^\\+[0-9][0-9]([1-9])"; entry.text = "0036"; bsc_nat_num_rewr_entry_adapt(nat, &nat->num_rewr, &entries); msg = msgb_alloc(4096, "test_dt_filter"); copy_to_msg(msg, cc_setup_national_patched, ARRAY_SIZE(cc_setup_national_patched)); parsed = bsc_nat_parse(msg); if (!parsed) { printf("FAIL: Could not parse ID resp %d\n", __LINE__); abort(); } out = bsc_nat_rewrite_msg(nat, msg, parsed, imsi); if (!out) { printf("FAIL: A new message should be created %d.\n", __LINE__); abort(); } if (msg == out) { printf("FAIL: The message should have changed %d\n", __LINE__); abort(); } verify_msg(out, cc_setup_national_patched_patched, ARRAY_SIZE(cc_setup_national_patched_patched)); msgb_free(out); /* go from international back to national */ entry.mnc = "*"; entry.option = "^\\+([0-9])"; entry.text = "36"; bsc_nat_num_rewr_entry_adapt(nat, &nat->num_rewr, &entries); msg = msgb_alloc(4096, "test_dt_filter"); copy_to_msg(msg, cc_setup_national_patched, ARRAY_SIZE(cc_setup_national_patched)); parsed = bsc_nat_parse(msg); if (!parsed) { printf("FAIL: Could not parse ID resp %d\n", __LINE__); abort(); } out = bsc_nat_rewrite_msg(nat, msg, parsed, imsi); if (!out) { printf("FAIL: A new message should be created %d.\n", __LINE__); abort(); } if (msg == out) { printf("FAIL: The message should have changed %d\n", __LINE__); abort(); } verify_msg(out, cc_setup_national_again, ARRAY_SIZE(cc_setup_national_again)); msgb_free(out); bsc_nat_num_rewr_entry_adapt(nat, &nat->num_rewr, NULL); bsc_nat_free(nat); } static void test_setup_rewrite_prefix(void) { struct msgb *msg = msgb_alloc(4096, "test_dt_filter"); struct msgb *out; struct bsc_nat_parsed *parsed; const char *imsi = "27408000001234"; struct bsc_nat *nat = bsc_nat_alloc(); /* a fake list */ struct osmo_config_list entries; struct osmo_config_entry entry; INIT_LLIST_HEAD(&entries.entry); entry.mcc = "274"; entry.mnc = "08"; entry.option = "^0([1-9])"; entry.text = "prefix_lookup"; llist_add_tail(&entry.list, &entries.entry); bsc_nat_num_rewr_entry_adapt(nat, &nat->num_rewr, &entries); nat->num_rewr_trie = nat_rewrite_parse(nat, "prefixes.csv"); msgb_reset(msg); copy_to_msg(msg, cc_setup_national, ARRAY_SIZE(cc_setup_national)); parsed = bsc_nat_parse(msg); if (!parsed) { printf("FAIL: Could not parse ID resp\n"); abort(); } out = bsc_nat_rewrite_msg(nat, msg, parsed, imsi); if (!out) { printf("FAIL: A new message should be created.\n"); abort(); } if (msg == out) { printf("FAIL: The message should have changed\n"); abort(); } verify_msg(out, cc_setup_national_patched, ARRAY_SIZE(cc_setup_national_patched)); msgb_free(out); bsc_nat_num_rewr_entry_adapt(nat, &nat->num_rewr, NULL); bsc_nat_free(nat); } static void test_setup_rewrite_post(void) { struct msgb *msg = msgb_alloc(4096, "test_dt_filter"); struct msgb *out; struct bsc_nat_parsed *parsed; const char *imsi = "27408000001234"; struct bsc_nat *nat = bsc_nat_alloc(); /* a fake list */ struct osmo_config_list entries; struct osmo_config_entry entry; struct osmo_config_list entries_post; struct osmo_config_entry entry_post; INIT_LLIST_HEAD(&entries.entry); entry.mcc = "274"; entry.mnc = "08"; entry.option = "^0([1-9])"; entry.text = "0049"; llist_add_tail(&entry.list, &entries.entry); bsc_nat_num_rewr_entry_adapt(nat, &nat->num_rewr, &entries); /* attempt to undo the previous one */ INIT_LLIST_HEAD(&entries_post.entry); entry_post.mcc = "274"; entry_post.mnc = "08"; entry_post.option = "^\\+49([1-9])"; entry_post.text = "prefix_lookup"; llist_add_tail(&entry_post.list, &entries_post.entry); bsc_nat_num_rewr_entry_adapt(nat, &nat->num_rewr_post, &entries_post); nat->num_rewr_trie = nat_rewrite_parse(nat, "prefixes.csv"); msgb_reset(msg); copy_to_msg(msg, cc_setup_national, ARRAY_SIZE(cc_setup_national)); parsed = bsc_nat_parse(msg); if (!parsed) { printf("FAIL: Could not parse ID resp\n"); abort(); } out = bsc_nat_rewrite_msg(nat, msg, parsed, imsi); if (!out) { printf("FAIL: A new message should be created.\n"); abort(); } if (msg == out) { printf("FAIL: The message should have changed\n"); abort(); } verify_msg(out, cc_setup_national, ARRAY_SIZE(cc_setup_national)); msgb_free(out); bsc_nat_free(nat); } static void test_sms_smsc_rewrite() { struct msgb *msg = msgb_alloc(4096, "SMSC rewrite"), *out; struct bsc_nat_parsed *parsed; const char *imsi = "515039900406700"; struct bsc_nat *nat = bsc_nat_alloc(); /* a fake list */ struct osmo_config_list smsc_entries, dest_entries, clear_entries; struct osmo_config_entry smsc_entry, dest_entry, clear_entry; INIT_LLIST_HEAD(&smsc_entries.entry); INIT_LLIST_HEAD(&dest_entries.entry); INIT_LLIST_HEAD(&clear_entries.entry); smsc_entry.mcc = "^515039"; smsc_entry.option = "639180000105()"; smsc_entry.text = "6666666666667"; llist_add_tail(&smsc_entry.list, &smsc_entries.entry); dest_entry.mcc = "515"; dest_entry.mnc = "03"; dest_entry.option = "^0049"; dest_entry.text = ""; llist_add_tail(&dest_entry.list, &dest_entries.entry); clear_entry.mcc = "^515039"; clear_entry.option = "^0049"; clear_entry.text = ""; llist_add_tail(&clear_entry.list, &clear_entries.entry); bsc_nat_num_rewr_entry_adapt(nat, &nat->smsc_rewr, &smsc_entries); bsc_nat_num_rewr_entry_adapt(nat, &nat->tpdest_match, &dest_entries); bsc_nat_num_rewr_entry_adapt(nat, &nat->sms_clear_tp_srr, &clear_entries); printf("Testing SMSC rewriting.\n"); /* * Check if the SMSC address is changed */ copy_to_msg(msg, smsc_rewrite, ARRAY_SIZE(smsc_rewrite)); parsed = bsc_nat_parse(msg); if (!parsed) { printf("FAIL: Could not parse SMS\n"); abort(); } out = bsc_nat_rewrite_msg(nat, msg, parsed, imsi); if (out == msg) { printf("FAIL: This should have changed.\n"); abort(); } verify_msg(out, smsc_rewrite_patched, ARRAY_SIZE(smsc_rewrite_patched)); msgb_free(out); /* clear out the filter for SMSC */ printf("Attempting to only rewrite the HDR\n"); bsc_nat_num_rewr_entry_adapt(nat, &nat->smsc_rewr, NULL); msg = msgb_alloc(4096, "SMSC rewrite"); copy_to_msg(msg, smsc_rewrite, ARRAY_SIZE(smsc_rewrite)); parsed = bsc_nat_parse(msg); if (!parsed) { printf("FAIL: Could not parse SMS\n"); abort(); } out = bsc_nat_rewrite_msg(nat, msg, parsed, imsi); if (out == msg) { printf("FAIL: This should have changed.\n"); abort(); } verify_msg(out, smsc_rewrite_patched_hdr, ARRAY_SIZE(smsc_rewrite_patched_hdr)); msgb_free(out); /* clear out the next filter */ printf("Attempting to change nothing.\n"); bsc_nat_num_rewr_entry_adapt(nat, &nat->sms_clear_tp_srr, NULL); msg = msgb_alloc(4096, "SMSC rewrite"); copy_to_msg(msg, smsc_rewrite, ARRAY_SIZE(smsc_rewrite)); parsed = bsc_nat_parse(msg); if (!parsed) { printf("FAIL: Could not parse SMS\n"); abort(); } out = bsc_nat_rewrite_msg(nat, msg, parsed, imsi); if (out != msg) { printf("FAIL: This should not have changed.\n"); abort(); } verify_msg(out, smsc_rewrite, ARRAY_SIZE(smsc_rewrite)); msgb_free(out); bsc_nat_free(nat); } static void test_sms_number_rewrite(void) { struct msgb *msg, *out; struct bsc_nat_parsed *parsed; const char *imsi = "515039900406700"; struct bsc_nat *nat = bsc_nat_alloc(); /* a fake list */ struct osmo_config_list num_entries, clear_entries; struct osmo_config_entry num_entry, clear_entry; INIT_LLIST_HEAD(&num_entries.entry); num_entry.mcc = "^515039"; num_entry.option = "^0049()"; num_entry.text = "0032"; llist_add_tail(&num_entry.list, &num_entries.entry); bsc_nat_num_rewr_entry_adapt(nat, &nat->sms_num_rewr, &num_entries); printf("Testing SMS TP-DA rewriting.\n"); /* * Check if the SMSC address is changed */ msg = msgb_alloc(4096, "SMSC rewrite"); copy_to_msg(msg, smsc_rewrite, ARRAY_SIZE(smsc_rewrite)); parsed = bsc_nat_parse(msg); if (!parsed) { printf("FAIL: Could not parse SMS\n"); abort(); } out = bsc_nat_rewrite_msg(nat, msg, parsed, imsi); if (out == msg) { printf("FAIL: This should have changed.\n"); abort(); } verify_msg(out, smsc_rewrite_num_patched, ARRAY_SIZE(smsc_rewrite_num_patched)); msgb_free(out); /* * Now with TP-SRR rewriting enabled */ INIT_LLIST_HEAD(&clear_entries.entry); clear_entry.mcc = "^515039"; clear_entry.option = ""; clear_entry.text = ""; llist_add_tail(&clear_entry.list, &clear_entries.entry); bsc_nat_num_rewr_entry_adapt(nat, &nat->sms_clear_tp_srr, &clear_entries); msg = msgb_alloc(4096, "SMSC rewrite"); copy_to_msg(msg, smsc_rewrite, ARRAY_SIZE(smsc_rewrite)); parsed = bsc_nat_parse(msg); if (!parsed) { printf("FAIL: Could not parse SMS\n"); abort(); } out = bsc_nat_rewrite_msg(nat, msg, parsed, imsi); if (out == msg) { printf("FAIL: This should have changed.\n"); abort(); } verify_msg(out, smsc_rewrite_num_patched_tp_srr, ARRAY_SIZE(smsc_rewrite_num_patched_tp_srr)); msgb_free(out); bsc_nat_free(nat); } static void test_barr_list_parsing(void) { int rc; int cm, lu; struct rb_node *node; struct rb_root root = RB_ROOT; struct osmo_config_list *lst = osmo_config_list_parse(NULL, "barr.cfg"); if (lst == NULL) abort(); rc = bsc_filter_barr_adapt(NULL, &root, lst); if (rc != 0) abort(); talloc_free(lst); for (node = rb_first(&root); node; node = rb_next(node)) { struct bsc_filter_barr_entry *entry; entry = rb_entry(node, struct bsc_filter_barr_entry, node); printf("IMSI: %s CM: %d LU: %d\n", entry->imsi, entry->cm_reject_cause, entry->lu_reject_cause); } /* do the look up now.. */ rc = bsc_filter_barr_find(&root, "12123119", &cm, &lu); if (!rc) { printf("Failed to find the IMSI.\n"); abort(); } if (cm != 3 || lu != 4) { printf("Found CM(%d) and LU(%d)\n", cm, lu); abort(); } /* empty and check that it is empty */ bsc_filter_barr_adapt(NULL, &root, NULL); if (!RB_EMPTY_ROOT(&root)) { printf("Failed to empty the list.\n"); abort(); } /* check that dup results in an error */ lst = osmo_config_list_parse(NULL, "barr_dup.cfg"); if (lst == NULL) { printf("Failed to parse list with dups\n"); abort(); } rc = bsc_filter_barr_adapt(NULL, &root, lst); if (rc != -1) { printf("It should have failed due dup\n"); abort(); } talloc_free(lst); /* dump for reference */ for (node = rb_first(&root); node; node = rb_next(node)) { struct bsc_filter_barr_entry *entry; entry = rb_entry(node, struct bsc_filter_barr_entry, node); printf("IMSI: %s CM: %d LU: %d\n", entry->imsi, entry->cm_reject_cause, entry->lu_reject_cause); } rc = bsc_filter_barr_adapt(NULL, &root, NULL); } static void test_nat_extract_lac() { int res; struct bsc_connection *bsc; struct bsc_nat *nat; struct nat_sccp_connection con; struct bsc_nat_parsed *parsed; struct msgb *msg = msgb_alloc(4096, "test-message"); printf("Testing LAC extraction from SCCP CR\n"); /* initialize the testcase */ nat = bsc_nat_alloc(); bsc = bsc_connection_alloc(nat); bsc->cfg = bsc_config_alloc(nat, "foo"); memset(&con, 0, sizeof(con)); con.bsc = bsc; /* create the SCCP CR */ msg->l2h = msgb_put(msg, ARRAY_SIZE(bssmap_cr)); memcpy(msg->l2h, bssmap_cr, ARRAY_SIZE(bssmap_cr)); /* parse it and pass it on */ parsed = bsc_nat_parse(msg); res = bsc_nat_extract_lac(bsc, &con, parsed, msg); OSMO_ASSERT(res == 0); /* verify the LAC */ OSMO_ASSERT(con.lac == 8210); OSMO_ASSERT(con.ci == 50000); bsc_nat_free(nat); } int main(int argc, char **argv) { sccp_set_log_area(DSCCP); osmo_init_logging(&log_info); test_filter(); test_contrack(); test_paging(); test_mgcp_ass_tracking(); test_mgcp_find(); test_mgcp_rewrite(); test_mgcp_parse(); test_cr_filter(); test_dt_filter(); test_setup_rewrite(); test_setup_rewrite_prefix(); test_setup_rewrite_post(); test_sms_smsc_rewrite(); test_sms_number_rewrite(); test_mgcp_allocations(); test_barr_list_parsing(); test_nat_extract_lac(); printf("Testing execution completed.\n"); return 0; } /* stub */ void bsc_nat_send_mgcp_to_msc(struct bsc_nat *nat, struct msgb *msg) { abort(); } openbsc-0.15.0/openbsc/tests/bsc-nat/bsc_nat_test.ok000066400000000000000000000017641265565154000223740ustar00rootroot00000000000000Testing BSS Filtering. Going to test item: 0 Going to test item: 1 Going to test item: 2 Going to test item: 3 Going to test item: 4 Going to test item: 5 Going to test item: 6 Going to test item: 7 Going to test item: 8 Going to test item: 9 Going to test item: 10 Going to test item: 11 Going to test item: 12 Testing connection tracking. Testing paging by lac. Testing MGCP. Testing finding of a BSC Connection Testing rewriting MGCP messages. Testing MGCP response parsing. Testing SMSC rewriting. Attempting to only rewrite the HDR Attempting to change nothing. Testing SMS TP-DA rewriting. IMSI: 12123115 CM: 3 LU: 4 IMSI: 12123116 CM: 3 LU: 4 IMSI: 12123117 CM: 3 LU: 4 IMSI: 12123118 CM: 3 LU: 4 IMSI: 12123119 CM: 3 LU: 4 IMSI: 12123120 CM: 3 LU: 4 IMSI: 12123123 CM: 3 LU: 1 IMSI: 12123124 CM: 3 LU: 2 IMSI: 12123125 CM: 3 LU: 3 IMSI: 12123126 CM: 3 LU: 4 IMSI: 12123127 CM: 3 LU: 5 IMSI: 12123128 CM: 3 LU: 6 IMSI: 12123124 CM: 3 LU: 2 Testing LAC extraction from SCCP CR Testing execution completed. openbsc-0.15.0/openbsc/tests/bsc-nat/prefixes.csv000066400000000000000000000000201265565154000217130ustar00rootroot000000000000000172,0049 +49,0 openbsc-0.15.0/openbsc/tests/bsc/000077500000000000000000000000001265565154000166015ustar00rootroot00000000000000openbsc-0.15.0/openbsc/tests/bsc/Makefile.am000066400000000000000000000012701265565154000206350ustar00rootroot00000000000000AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOSCCP_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) AM_LDFLAGS = $(COVERAGE_LDFLAGS) EXTRA_DIST = bsc_test.ok noinst_PROGRAMS = bsc_test bsc_test_SOURCES = bsc_test.c \ $(top_srcdir)/src/osmo-bsc/osmo_bsc_filter.c bsc_test_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ $(top_builddir)/src/libmsc/libmsc.a \ $(top_builddir)/src/libmgcp/libmgcp.a \ $(top_builddir)/src/libtrau/libtrau.a \ $(top_builddir)/src/libcommon/libcommon.a \ $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) -lrt \ $(LIBOSMOSCCP_LIBS) $(LIBOSMOVTY_LIBS) \ $(LIBOSMOABIS_LIBS) openbsc-0.15.0/openbsc/tests/bsc/bsc_test.c000066400000000000000000000124711265565154000205600ustar00rootroot00000000000000/* * BSC Message filtering * * (C) 2013 by sysmocom s.f.m.c. GmbH * Written by Jacob Erlbeck * (C) 2010-2013 by Holger Hans Peter Freyther * (C) 2010-2013 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 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 . * */ #include #include #include #include #include #include #include #include #include #include #include enum test { TEST_SCAN_TO_BTS, TEST_SCAN_TO_MSC, }; /* GSM 04.08 MM INFORMATION test message */ static uint8_t gsm48_mm_info_nn_tzt[] = { 0x05, 0x32, 0x45, 0x08, 0x80, 0x4f, 0x77, 0xeb, 0x1a, 0xb6, 0x97, 0xe7, 0x47, 0x31, 0x90, 0x61, 0x11, 0x02, 0x73, 0x00, }; static uint8_t gsm48_mm_info_nn_tzt_out[] = { 0x05, 0x32, 0x45, 0x08, 0x80, 0x4f, 0x77, 0xeb, 0x1a, 0xb6, 0x97, 0xe7, 0x47, 0x31, 0x90, 0x61, 0x11, 0x02, 0x73, 0x1a, }; static uint8_t gsm48_mm_info_nn_tzt_dst[] = { 0x05, 0x32, 0x45, 0x08, 0x80, 0x4f, 0x77, 0xeb, 0x1a, 0xb6, 0x97, 0xe7, 0x47, 0x31, 0x90, 0x61, 0x11, 0x02, 0x73, 0x00, 0x49, 0x01, 0x00, }; static uint8_t gsm48_mm_info_nn_tzt_dst_out[] = { 0x05, 0x32, 0x45, 0x08, 0x80, 0x4f, 0x77, 0xeb, 0x1a, 0xb6, 0x97, 0xe7, 0x47, 0x31, 0x90, 0x61, 0x11, 0x02, 0x73, 0x1a, 0x49, 0x01, 0x02, }; struct test_definition { const uint8_t *data; const uint16_t length; const int dir; const int result; const uint8_t *out_data; const uint16_t out_length; const char* params; const int n_params; }; static int get_int(const char *params, size_t nmemb, const char *key, int def, int *is_set) { const char *kv = NULL; kv = strstr(params, key); if (kv) { kv += strlen(key) + 1; fprintf(stderr, "get_int(%s) -> %d\n", key, atoi(kv)); if (is_set) *is_set = 1; } return kv ? atoi(kv) : def; } static const struct test_definition test_scan_defs[] = { { .data = gsm48_mm_info_nn_tzt_dst, .length = ARRAY_SIZE(gsm48_mm_info_nn_tzt), .dir = TEST_SCAN_TO_BTS, .result = 0, .out_data = gsm48_mm_info_nn_tzt_dst_out, .out_length = ARRAY_SIZE(gsm48_mm_info_nn_tzt_out), .params = "tz_hr=-5 tz_mn=15 tz_dst=2", .n_params = 3, }, { .data = gsm48_mm_info_nn_tzt_dst, .length = ARRAY_SIZE(gsm48_mm_info_nn_tzt_dst), .dir = TEST_SCAN_TO_BTS, .result = 0, .out_data = gsm48_mm_info_nn_tzt_dst_out, .out_length = ARRAY_SIZE(gsm48_mm_info_nn_tzt_dst_out), .params = "tz_hr=-5 tz_mn=15 tz_dst=2", .n_params = 3, }, }; static void test_scan(void) { int i; struct gsm_network *net; struct gsm_bts *bts; struct osmo_bsc_sccp_con *sccp_con; struct osmo_msc_data *msc; struct gsm_subscriber_connection *conn; net = talloc_zero(NULL, struct gsm_network); bts = talloc_zero(net, struct gsm_bts); sccp_con = talloc_zero(net, struct osmo_bsc_sccp_con); msc = talloc_zero(net, struct osmo_msc_data); conn = talloc_zero(net, struct gsm_subscriber_connection); bts->network = net; sccp_con->msc = msc; conn->bts = bts; conn->sccp_con = sccp_con; /* start testinh with proper messages */ printf("Testing BTS<->MSC message scan.\n"); for (i = 0; i < ARRAY_SIZE(test_scan_defs); ++i) { const struct test_definition *test_def = &test_scan_defs[i]; int result; struct msgb *msg = msgb_alloc(4096, "test-message"); int is_set = 0; bts->tz.hr = get_int(test_def->params, test_def->n_params, "tz_hr", 0, &is_set); bts->tz.mn = get_int(test_def->params, test_def->n_params, "tz_mn", 0, &is_set); bts->tz.dst = get_int(test_def->params, test_def->n_params, "tz_dst", 0, &is_set); bts->tz.override = get_int(test_def->params, test_def->n_params, "tz_dst", is_set ? 1 : 0, NULL); printf("Going to test item: %d\n", i); msg->l3h = msgb_put(msg, test_def->length); memcpy(msg->l3h, test_def->data, test_def->length); switch (test_def->dir) { case TEST_SCAN_TO_BTS: result = bsc_scan_msc_msg(conn, msg); break; case TEST_SCAN_TO_MSC: result = bsc_scan_msc_msg(conn, msg); break; default: abort(); break; } if (result != test_def->result) { printf("FAIL: Not the expected result, got: %d wanted: %d\n", result, test_def->result); goto out; } if (msgb_l3len(msg) != test_def->out_length) { printf("FAIL: Not the expected message size, got: %d wanted: %d\n", msgb_l3len(msg), test_def->out_length); goto out; } if (memcmp(msgb_l3(msg), test_def->out_data, test_def->out_length) != 0) { printf("FAIL: Not the expected message\n"); goto out; } out: msgb_free(msg); } talloc_free(net); } int main(int argc, char **argv) { osmo_init_logging(&log_info); test_scan(); printf("Testing execution completed.\n"); return 0; } openbsc-0.15.0/openbsc/tests/bsc/bsc_test.ok000066400000000000000000000001511265565154000207370ustar00rootroot00000000000000Testing BTS<->MSC message scan. Going to test item: 0 Going to test item: 1 Testing execution completed. openbsc-0.15.0/openbsc/tests/channel/000077500000000000000000000000001265565154000174425ustar00rootroot00000000000000openbsc-0.15.0/openbsc/tests/channel/Makefile.am000066400000000000000000000007101265565154000214740ustar00rootroot00000000000000AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOABIS_CFLAGS) EXTRA_DIST = channel_test.ok noinst_PROGRAMS = channel_test channel_test_SOURCES = channel_test.c channel_test_LDADD = \ $(top_builddir)/src/libbsc/libbsc.a \ $(top_builddir)/src/libmsc/libmsc.a \ $(top_builddir)/src/libcommon/libcommon.a \ $(LIBOSMOCORE_LIBS) \ -ldbi $(LIBOSMOGSM_LIBS) $(LIBCRYPTO_LIBS) openbsc-0.15.0/openbsc/tests/channel/channel_test.c000066400000000000000000000053341265565154000222620ustar00rootroot00000000000000/* * (C) 2009 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 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 . * */ #include #include #include #include #include #include #include #include static int s_end = 0; static struct gsm_subscriber_connection s_conn; static void *s_data; static gsm_cbfn *s_cbfn; /* our handler */ static int subscr_cb(unsigned int hook, unsigned int event, struct msgb *msg, void *data, void *param) { assert(hook == 101); assert(event == 200); assert(msg == (void*)0x1323L); assert(data == &s_conn); assert(param == (void*)0x2342L); printf("Reached, didn't crash, test passed\n"); s_end = true; return 0; } /* mock object for testing, directly invoke the cb... maybe later through the timer */ int paging_request(struct gsm_bts *bts, struct gsm_subscriber *subscriber, int type, gsm_cbfn *cbfn, void *data) { s_data = data; s_cbfn = cbfn; /* claim we have patched */ return 1; } int main(int argc, char **argv) { struct gsm_network *network; struct gsm_bts *bts; osmo_init_logging(&log_info); printf("Testing the gsm_subscriber chan logic\n"); /* Create a dummy network */ network = gsm_network_init(1, 1, NULL); if (!network) exit(1); bts = gsm_bts_alloc(network); bts->location_area_code = 23; /* Create a dummy subscriber */ struct gsm_subscriber *subscr = subscr_alloc(); subscr->lac = 23; subscr->group = network->subscr_group; OSMO_ASSERT(subscr->group); OSMO_ASSERT(subscr->group->net == network); /* Ask for a channel... */ subscr_request_channel(subscr, RSL_CHANNEED_TCH_F, subscr_cb, (void*)0x2342L); s_cbfn(101, 200, (void*)0x1323L, &s_conn, s_data); OSMO_ASSERT(s_end); return EXIT_SUCCESS; } void _abis_nm_sendmsg() {} void sms_alloc() {} void sms_free() {} void gsm_net_update_ctype(struct gsm_network *network) {} void gsm48_secure_channel() {} void paging_request_stop() {} void vty_out() {} void* connection_for_subscr(void) { abort(); return NULL; } struct tlv_definition nm_att_tlvdef; openbsc-0.15.0/openbsc/tests/channel/channel_test.ok000066400000000000000000000001111265565154000224350ustar00rootroot00000000000000Testing the gsm_subscriber chan logic Reached, didn't crash, test passed openbsc-0.15.0/openbsc/tests/ctrl_test_runner.py000066400000000000000000000563361265565154000220150ustar00rootroot00000000000000#!/usr/bin/env python # (C) 2013 by Jacob Erlbeck # (C) 2014 by Holger Hans Peter Freyther # based on vty_test_runner.py: # (C) 2013 by Katerina Barone-Adesi # (C) 2013 by Holger Hans Peter Freyther # based on bsc_control.py. # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU 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 General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . import os import time import unittest import socket import sys import struct import osmopy.obscvty as obscvty import osmopy.osmoutil as osmoutil confpath = '.' verbose = False class TestCtrlBase(unittest.TestCase): def ctrl_command(self): raise Exception("Needs to be implemented by a subclass") def ctrl_app(self): raise Exception("Needs to be implemented by a subclass") def setUp(self): osmo_ctrl_cmd = self.ctrl_command()[:] config_index = osmo_ctrl_cmd.index('-c') if config_index: cfi = config_index + 1 osmo_ctrl_cmd[cfi] = os.path.join(confpath, osmo_ctrl_cmd[cfi]) try: print "Launch: %s from %s" % (' '.join(osmo_ctrl_cmd), os.getcwd()) self.proc = osmoutil.popen_devnull(osmo_ctrl_cmd) except OSError: print >> sys.stderr, "Current directory: %s" % os.getcwd() print >> sys.stderr, "Consider setting -b" time.sleep(2) appstring = self.ctrl_app()[2] appport = self.ctrl_app()[0] self.connect("127.0.0.1", appport) self.next_id = 1000 def tearDown(self): self.disconnect() osmoutil.end_proc(self.proc) def prefix_ipa_ctrl_header(self, data): return struct.pack(">HBB", len(data)+1, 0xee, 0) + data def remove_ipa_ctrl_header(self, data): if (len(data) < 4): raise BaseException("Answer too short!") (plen, ipa_proto, osmo_proto) = struct.unpack(">HBB", data[:4]) if (plen + 3 > len(data)): print "Warning: Wrong payload length (expected %i, got %i)" % (plen, len(data) - 3) if (ipa_proto != 0xee or osmo_proto != 0): raise BaseException("Wrong protocol in answer!") return data[4:plen+3], data[plen+3:] def disconnect(self): if not (self.sock is None): self.sock.close() def connect(self, host, port): if verbose: print "Connecting to host %s:%i" % (host, port) sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sck.setblocking(1) sck.connect((host, port)) self.sock = sck return sck def send(self, data): if verbose: print "Sending \"%s\"" %(data) data = self.prefix_ipa_ctrl_header(data) return self.sock.send(data) == len(data) def send_set(self, var, value, id): setmsg = "SET %s %s %s" %(id, var, value) return self.send(setmsg) def send_get(self, var, id): getmsg = "GET %s %s" %(id, var) return self.send(getmsg) def do_set(self, var, value): id = self.next_id self.next_id += 1 self.send_set(var, value, id) return self.recv_msgs()[id] def do_get(self, var): id = self.next_id self.next_id += 1 self.send_get(var, id) return self.recv_msgs()[id] def recv_msgs(self): responses = {} data = self.sock.recv(4096) while (len(data)>0): (answer, data) = self.remove_ipa_ctrl_header(data) if verbose: print "Got message:", answer (mtype, id, msg) = answer.split(None, 2) id = int(id) rsp = {'mtype': mtype, 'id': id} if mtype == "ERROR": rsp['error'] = msg else: split = msg.split(None, 1) rsp['var'] = split[0] if len(split) > 1: rsp['value'] = split[1] else: rsp['value'] = None responses[id] = rsp if verbose: print "Decoded replies: ", responses return responses class TestCtrlBSC(TestCtrlBase): def tearDown(self): TestCtrlBase.tearDown(self) os.unlink("tmp_dummy_sock") def ctrl_command(self): return ["./src/osmo-bsc/osmo-bsc", "-r", "tmp_dummy_sock", "-c", "doc/examples/osmo-bsc/osmo-bsc.cfg"] def ctrl_app(self): return (4249, "./src/osmo-bsc/osmo-bsc", "OsmoBSC", "bsc") def testCtrlErrs(self): r = self.do_get('invalid') self.assertEquals(r['mtype'], 'ERROR') self.assertEquals(r['error'], 'Command not found') r = self.do_set('rf_locked', '999') self.assertEquals(r['mtype'], 'ERROR') self.assertEquals(r['error'], 'Value failed verification.') r = self.do_get('bts') self.assertEquals(r['mtype'], 'ERROR') self.assertEquals(r['error'], 'Error while parsing the index.') r = self.do_get('bts.999') self.assertEquals(r['mtype'], 'ERROR') self.assertEquals(r['error'], 'Error while resolving object') def testBtsLac(self): r = self.do_get('bts.0.location-area-code') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'bts.0.location-area-code') self.assertEquals(r['value'], '1') r = self.do_set('bts.0.location-area-code', '23') self.assertEquals(r['mtype'], 'SET_REPLY') self.assertEquals(r['var'], 'bts.0.location-area-code') self.assertEquals(r['value'], '23') r = self.do_get('bts.0.location-area-code') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'bts.0.location-area-code') self.assertEquals(r['value'], '23') r = self.do_set('bts.0.location-area-code', '-1') self.assertEquals(r['mtype'], 'ERROR') self.assertEquals(r['error'], 'Input not within the range') def testBtsCi(self): r = self.do_get('bts.0.cell-identity') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'bts.0.cell-identity') self.assertEquals(r['value'], '0') r = self.do_set('bts.0.cell-identity', '23') self.assertEquals(r['mtype'], 'SET_REPLY') self.assertEquals(r['var'], 'bts.0.cell-identity') self.assertEquals(r['value'], '23') r = self.do_get('bts.0.cell-identity') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'bts.0.cell-identity') self.assertEquals(r['value'], '23') r = self.do_set('bts.0.cell-identity', '-1') self.assertEquals(r['mtype'], 'ERROR') self.assertEquals(r['error'], 'Input not within the range') def testBtsGenerateSystemInformation(self): r = self.do_get('bts.0.send-new-system-informations') self.assertEquals(r['mtype'], 'ERROR') self.assertEquals(r['error'], 'Write only attribute') # No RSL links so it will fail r = self.do_set('bts.0.send-new-system-informations', '1') self.assertEquals(r['mtype'], 'ERROR') self.assertEquals(r['error'], 'Failed to generate SI') def testBtsChannelLoad(self): r = self.do_set('bts.0.channel-load', '1') self.assertEquals(r['mtype'], 'ERROR') self.assertEquals(r['error'], 'Read only attribute') # No RSL link so everything is 0 r = self.do_get('bts.0.channel-load') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['value'], 'CCCH+SDCCH4,0,0 TCH/F,0,0 TCH/H,0,0 SDCCH8,0,0 TCH/F_PDCH,0,0 CCCH+SDCCH4+CBCH,0,0 SDCCH8+CBCH,0,0') def testBtsOmlConnectionState(self): """Check OML state. It will not be connected""" r = self.do_set('bts.0.oml-connection-state', '1') self.assertEquals(r['mtype'], 'ERROR') self.assertEquals(r['error'], 'Read only attribute') # No RSL link so everything is 0 r = self.do_get('bts.0.oml-connection-state') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['value'], 'disconnected') def testTrxPowerRed(self): r = self.do_get('bts.0.trx.0.max-power-reduction') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'bts.0.trx.0.max-power-reduction') self.assertEquals(r['value'], '20') r = self.do_set('bts.0.trx.0.max-power-reduction', '22') self.assertEquals(r['mtype'], 'SET_REPLY') self.assertEquals(r['var'], 'bts.0.trx.0.max-power-reduction') self.assertEquals(r['value'], '22') r = self.do_get('bts.0.trx.0.max-power-reduction') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'bts.0.trx.0.max-power-reduction') self.assertEquals(r['value'], '22') r = self.do_set('bts.0.trx.0.max-power-reduction', '1') self.assertEquals(r['mtype'], 'ERROR') self.assertEquals(r['error'], 'Value must be even') def testTrxArfcn(self): r = self.do_get('bts.0.trx.0.arfcn') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'bts.0.trx.0.arfcn') self.assertEquals(r['value'], '871') r = self.do_set('bts.0.trx.0.arfcn', '873') self.assertEquals(r['mtype'], 'SET_REPLY') self.assertEquals(r['var'], 'bts.0.trx.0.arfcn') self.assertEquals(r['value'], '873') r = self.do_get('bts.0.trx.0.arfcn') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'bts.0.trx.0.arfcn') self.assertEquals(r['value'], '873') r = self.do_set('bts.0.trx.0.arfcn', '2000') self.assertEquals(r['mtype'], 'ERROR') self.assertEquals(r['error'], 'Input not within the range') def testRfLock(self): r = self.do_get('bts.0.rf_state') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'bts.0.rf_state') self.assertEquals(r['value'], 'inoperational,unlocked,on') r = self.do_set('rf_locked', '1') self.assertEquals(r['mtype'], 'SET_REPLY') self.assertEquals(r['var'], 'rf_locked') self.assertEquals(r['value'], '1') time.sleep(1.5) r = self.do_get('bts.0.rf_state') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'bts.0.rf_state') self.assertEquals(r['value'], 'inoperational,locked,off') r = self.do_get('rf_locked') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'rf_locked') self.assertEquals(r['value'], 'state=off,policy=off') r = self.do_set('rf_locked', '0') self.assertEquals(r['mtype'], 'SET_REPLY') self.assertEquals(r['var'], 'rf_locked') self.assertEquals(r['value'], '0') time.sleep(1.5) r = self.do_get('bts.0.rf_state') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'bts.0.rf_state') self.assertEquals(r['value'], 'inoperational,unlocked,on') r = self.do_get('rf_locked') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'rf_locked') self.assertEquals(r['value'], 'state=off,policy=on') def testTimezone(self): r = self.do_get('bts.0.timezone') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'bts.0.timezone') self.assertEquals(r['value'], 'off') r = self.do_set('bts.0.timezone', '-2,15,2') self.assertEquals(r['mtype'], 'SET_REPLY') self.assertEquals(r['var'], 'bts.0.timezone') self.assertEquals(r['value'], '-2,15,2') r = self.do_get('bts.0.timezone') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'bts.0.timezone') self.assertEquals(r['value'], '-2,15,2') # Test invalid input r = self.do_set('bts.0.timezone', '-2,15,2,5,6,7') self.assertEquals(r['mtype'], 'SET_REPLY') self.assertEquals(r['var'], 'bts.0.timezone') self.assertEquals(r['value'], '-2,15,2') r = self.do_set('bts.0.timezone', '-2,15') self.assertEquals(r['mtype'], 'ERROR') r = self.do_set('bts.0.timezone', '-2') self.assertEquals(r['mtype'], 'ERROR') r = self.do_set('bts.0.timezone', '1') r = self.do_set('bts.0.timezone', 'off') self.assertEquals(r['mtype'], 'SET_REPLY') self.assertEquals(r['var'], 'bts.0.timezone') self.assertEquals(r['value'], 'off') r = self.do_get('bts.0.timezone') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'bts.0.timezone') self.assertEquals(r['value'], 'off') def testMcc(self): r = self.do_set('mcc', '23') r = self.do_get('mcc') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'mcc') self.assertEquals(r['value'], '23') r = self.do_set('mcc', '023') r = self.do_get('mcc') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'mcc') self.assertEquals(r['value'], '23') def testMnc(self): r = self.do_set('mnc', '9') r = self.do_get('mnc') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'mnc') self.assertEquals(r['value'], '9') r = self.do_set('mnc', '09') r = self.do_get('mnc') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'mnc') self.assertEquals(r['value'], '9') def testMccMncApply(self): # Test some invalid input r = self.do_set('mcc-mnc-apply', 'WRONG') self.assertEquals(r['mtype'], 'ERROR') r = self.do_set('mcc-mnc-apply', '1,') self.assertEquals(r['mtype'], 'ERROR') r = self.do_set('mcc-mnc-apply', '200,3') self.assertEquals(r['mtype'], 'SET_REPLY') self.assertEquals(r['var'], 'mcc-mnc-apply') self.assertEquals(r['value'], 'Tried to drop the BTS') # Set it again r = self.do_set('mcc-mnc-apply', '200,3') self.assertEquals(r['mtype'], 'SET_REPLY') self.assertEquals(r['var'], 'mcc-mnc-apply') self.assertEquals(r['value'], 'Nothing changed') # Change it r = self.do_set('mcc-mnc-apply', '200,4') self.assertEquals(r['mtype'], 'SET_REPLY') self.assertEquals(r['var'], 'mcc-mnc-apply') self.assertEquals(r['value'], 'Tried to drop the BTS') # Change it r = self.do_set('mcc-mnc-apply', '201,4') self.assertEquals(r['mtype'], 'SET_REPLY') self.assertEquals(r['var'], 'mcc-mnc-apply') self.assertEquals(r['value'], 'Tried to drop the BTS') # Verify r = self.do_get('mnc') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'mnc') self.assertEquals(r['value'], '4') r = self.do_get('mcc') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'mcc') self.assertEquals(r['value'], '201') # Change it r = self.do_set('mcc-mnc-apply', '202,03') self.assertEquals(r['mtype'], 'SET_REPLY') self.assertEquals(r['var'], 'mcc-mnc-apply') self.assertEquals(r['value'], 'Tried to drop the BTS') r = self.do_get('mnc') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'mnc') self.assertEquals(r['value'], '3') r = self.do_get('mcc') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'mcc') self.assertEquals(r['value'], '202') class TestCtrlNITB(TestCtrlBase): def tearDown(self): TestCtrlBase.tearDown(self) os.unlink("test_hlr.sqlite3") def ctrl_command(self): return ["./src/osmo-nitb/osmo-nitb", "-c", "doc/examples/osmo-nitb/nanobts/openbsc.cfg", "-l", "test_hlr.sqlite3"] def ctrl_app(self): return (4249, "./src/osmo-nitb/osmo-nitb", "OsmoBSC", "nitb") def testNumberOfBTS(self): r = self.do_get('number-of-bts') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'number-of-bts') self.assertEquals(r['value'], '1') def testSubscriberAddRemove(self): r = self.do_set('subscriber-modify-v1', '2620345,445566') self.assertEquals(r['mtype'], 'SET_REPLY') self.assertEquals(r['var'], 'subscriber-modify-v1') self.assertEquals(r['value'], 'OK') r = self.do_set('subscriber-modify-v1', '2620345,445567') self.assertEquals(r['mtype'], 'SET_REPLY') self.assertEquals(r['var'], 'subscriber-modify-v1') self.assertEquals(r['value'], 'OK') # TODO. verify that the entry has been created and modified? Invoke # the sqlite3 CLI or do it through the DB libraries? r = self.do_set('subscriber-delete-v1', '2620345') self.assertEquals(r['mtype'], 'SET_REPLY') self.assertEquals(r['value'], 'Removed') r = self.do_set('subscriber-delete-v1', '2620345') self.assertEquals(r['mtype'], 'ERROR') self.assertEquals(r['error'], 'Failed to find subscriber') def testSubscriberList(self): # TODO. Add command to mark a subscriber as active r = self.do_get('subscriber-list-active-v1') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'subscriber-list-active-v1') self.assertEquals(r['value'], None) def testApplyConfiguration(self): r = self.do_get('bts.0.apply-configuration') self.assertEquals(r['mtype'], 'ERROR') self.assertEquals(r['error'], 'Write only attribute') r = self.do_set('bts.0.apply-configuration', '1') self.assertEquals(r['mtype'], 'SET_REPLY') self.assertEquals(r['value'], 'Tried to drop the BTS') def testGprsMode(self): r = self.do_get('bts.0.gprs-mode') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'bts.0.gprs-mode') self.assertEquals(r['value'], 'none') r = self.do_set('bts.0.gprs-mode', 'bla') self.assertEquals(r['mtype'], 'ERROR') self.assertEquals(r['error'], 'Mode is not known') r = self.do_set('bts.0.gprs-mode', 'egprs') self.assertEquals(r['mtype'], 'SET_REPLY') self.assertEquals(r['value'], 'egprs') r = self.do_get('bts.0.gprs-mode') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'bts.0.gprs-mode') self.assertEquals(r['value'], 'egprs') class TestCtrlNAT(TestCtrlBase): def ctrl_command(self): return ["./src/osmo-bsc_nat/osmo-bsc_nat", "-c", "doc/examples/osmo-bsc_nat/osmo-bsc_nat.cfg"] def ctrl_app(self): return (4250, "./src/osmo-bsc_nat/osmo-bsc_nat", "OsmoNAT", "nat") def testAccessList(self): r = self.do_get('net.0.bsc_cfg.0.access-list-name') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'net') self.assertEquals(r['value'], None) r = self.do_set('net.0.bsc_cfg.0.access-list-name', 'bla') self.assertEquals(r['mtype'], 'SET_REPLY') self.assertEquals(r['var'], 'net') self.assertEquals(r['value'], 'bla') r = self.do_get('net.0.bsc_cfg.0.access-list-name') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'net') self.assertEquals(r['value'], 'bla') r = self.do_set('net.0.bsc_cfg.0.no-access-list-name', '1') self.assertEquals(r['mtype'], 'SET_REPLY') self.assertEquals(r['var'], 'net') self.assertEquals(r['value'], None) r = self.do_get('net.0.bsc_cfg.0.access-list-name') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'net') self.assertEquals(r['value'], None) def testAccessListManagement(self): r = self.do_set("net.0.add.allow.access-list.404", "abc") self.assertEquals(r['mtype'], 'ERROR') r = self.do_set("net.0.add.allow.access-list.bla", "^234$") self.assertEquals(r['mtype'], 'SET_REPLY') self.assertEquals(r['var'], 'net.0.add.allow.access-list.bla') self.assertEquals(r['value'], 'IMSI allow added to access list') # TODO.. find a way to actually see if this rule has been # added. e.g. by implementing a get for the list. class TestCtrlSGSN(TestCtrlBase): def ctrl_command(self): return ["./src/gprs/osmo-sgsn", "-c", "doc/examples/osmo-sgsn/osmo-sgsn.cfg"] def ctrl_app(self): return (4251, "./src/gprs/osmo-sgsn", "OsmoSGSN", "sgsn") def testListSubscribers(self): # TODO. Add command to mark a subscriber as active r = self.do_get('subscriber-list-active-v1') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'subscriber-list-active-v1') self.assertEquals(r['value'], None) def add_bsc_test(suite, workdir): if not os.path.isfile(os.path.join(workdir, "src/osmo-bsc/osmo-bsc")): print("Skipping the BSC test") return test = unittest.TestLoader().loadTestsFromTestCase(TestCtrlBSC) suite.addTest(test) def add_nitb_test(suite, workdir): test = unittest.TestLoader().loadTestsFromTestCase(TestCtrlNITB) suite.addTest(test) def add_nat_test(suite, workdir): if not os.path.isfile(os.path.join(workdir, "src/osmo-bsc_nat/osmo-bsc_nat")): print("Skipping the NAT test") return test = unittest.TestLoader().loadTestsFromTestCase(TestCtrlNAT) suite.addTest(test) def add_sgsn_test(suite, workdir): if not os.path.isfile(os.path.join(workdir, "src/gprs/osmo-sgsn")): print("Skipping the SGSN test") return test = unittest.TestLoader().loadTestsFromTestCase(TestCtrlSGSN) suite.addTest(test) if __name__ == '__main__': import argparse import sys workdir = '.' parser = argparse.ArgumentParser() parser.add_argument("-v", "--verbose", dest="verbose", action="store_true", help="verbose mode") parser.add_argument("-p", "--pythonconfpath", dest="p", help="searchpath for config") parser.add_argument("-w", "--workdir", dest="w", help="Working directory") args = parser.parse_args() verbose_level = 1 if args.verbose: verbose_level = 2 verbose = True if args.w: workdir = args.w if args.p: confpath = args.p print "confpath %s, workdir %s" % (confpath, workdir) os.chdir(workdir) print "Running tests for specific control commands" suite = unittest.TestSuite() add_bsc_test(suite, workdir) add_nitb_test(suite, workdir) add_nat_test(suite, workdir) add_sgsn_test(suite, workdir) res = unittest.TextTestRunner(verbosity=verbose_level).run(suite) sys.exit(len(res.errors) + len(res.failures)) openbsc-0.15.0/openbsc/tests/db/000077500000000000000000000000001265565154000164175ustar00rootroot00000000000000openbsc-0.15.0/openbsc/tests/db/Makefile.am000066400000000000000000000012341265565154000204530ustar00rootroot00000000000000AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(LIBSMPP34_CFLAGS) $(COVERAGE_CFLAGS) AM_LDFLAGS = $(COVERAGE_LDFLAGS) EXTRA_DIST = db_test.ok db_test.err hlr.sqlite3 noinst_PROGRAMS = db_test db_test_SOURCES = db_test.c db_test_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ $(top_builddir)/src/libmsc/libmsc.a \ $(top_builddir)/src/libbsc/libbsc.a \ $(top_builddir)/src/libtrau/libtrau.a \ $(top_builddir)/src/libcommon/libcommon.a \ $(LIBOSMOCORE_LIBS) $(LIBOSMOABIS_LIBS) \ $(LIBOSMOGSM_LIBS) $(LIBSMPP34_LIBS) $(LIBOSMOVTY_LIBS) $(LIBCRYPTO_LIBS) -ldbi openbsc-0.15.0/openbsc/tests/db/db_test.c000066400000000000000000000176111265565154000202150ustar00rootroot00000000000000/* (C) 2008 by Jan Luebbe * (C) 2009 by Holger Hans Peter Freyther * (C) 2014 by Alexander Chemeris * 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 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 . * */ #include #include #include #include #include #include #include #include #include static struct gsm_network dummy_net; static struct gsm_subscriber_group dummy_sgrp; #define SUBSCR_PUT(sub) \ sub->group = &dummy_sgrp; \ subscr_put(sub); #define COMPARE(original, copy) \ if (original->id != copy->id) \ printf("Ids do not match in %s:%d %llu %llu\n", \ __FUNCTION__, __LINE__, original->id, copy->id); \ if (original->lac != copy->lac) \ printf("LAC do not match in %s:%d %d %d\n", \ __FUNCTION__, __LINE__, original->lac, copy->lac); \ if (original->authorized != copy->authorized) \ printf("Authorize do not match in %s:%d %d %d\n", \ __FUNCTION__, __LINE__, original->authorized, \ copy->authorized); \ if (strcmp(original->imsi, copy->imsi) != 0) \ printf("IMSIs do not match in %s:%d '%s' '%s'\n", \ __FUNCTION__, __LINE__, original->imsi, copy->imsi); \ if (original->tmsi != copy->tmsi) \ printf("TMSIs do not match in %s:%d '%u' '%u'\n", \ __FUNCTION__, __LINE__, original->tmsi, copy->tmsi); \ if (strcmp(original->name, copy->name) != 0) \ printf("names do not match in %s:%d '%s' '%s'\n", \ __FUNCTION__, __LINE__, original->name, copy->name); \ if (strcmp(original->extension, copy->extension) != 0) \ printf("Extensions do not match in %s:%d '%s' '%s'\n", \ __FUNCTION__, __LINE__, original->extension, copy->extension); \ /* * Create/Store a SMS and then try to load it. */ static void test_sms(void) { int rc; struct gsm_sms *sms; struct gsm_subscriber *subscr; subscr = db_get_subscriber(GSM_SUBSCRIBER_IMSI, "9993245423445"); OSMO_ASSERT(subscr); subscr->group = &dummy_sgrp; sms = sms_alloc(); sms->receiver = subscr_get(subscr); sms->src.ton = 0x23; sms->src.npi = 0x24; memcpy(sms->src.addr, "1234", strlen("1234") + 1); sms->dst.ton = 0x32; sms->dst.npi = 0x42; memcpy(sms->dst.addr, subscr->extension, sizeof(subscr->extension)); memcpy(sms->text, "Text123", strlen("Text123") + 1); memcpy(sms->user_data, "UserData123", strlen("UserData123") + 1); sms->user_data_len = strlen("UserData123"); /* random values */ sms->reply_path_req = 1; sms->status_rep_req = 2; sms->ud_hdr_ind = 3; sms->protocol_id = 4; sms->data_coding_scheme = 5; rc = db_sms_store(sms); sms_free(sms); OSMO_ASSERT(rc == 0); /* now query */ sms = db_sms_get_unsent_for_subscr(subscr); OSMO_ASSERT(sms); OSMO_ASSERT(sms->receiver == subscr); OSMO_ASSERT(sms->reply_path_req == 1); OSMO_ASSERT(sms->status_rep_req == 2); OSMO_ASSERT(sms->ud_hdr_ind == 3); OSMO_ASSERT(sms->protocol_id == 4); OSMO_ASSERT(sms->data_coding_scheme == 5); OSMO_ASSERT(sms->src.ton == 0x23); OSMO_ASSERT(sms->src.npi == 0x24); OSMO_ASSERT(sms->dst.ton == 0x32); OSMO_ASSERT(sms->dst.npi == 0x42); OSMO_ASSERT(strcmp((char *) sms->text, "Text123") == 0); OSMO_ASSERT(sms->user_data_len == strlen("UserData123")); OSMO_ASSERT(strcmp((char *) sms->user_data, "UserData123") == 0); /* Mark the SMS as delivered */ db_sms_mark_delivered(sms); sms_free(sms); sms = db_sms_get_unsent_for_subscr(subscr); OSMO_ASSERT(!sms); subscr_put(subscr); } static void test_sms_migrate(void) { struct gsm_subscriber *rcv_subscr; struct gsm_sms *sms; static const uint8_t user_data_1[] = { 0x41, 0xf1, 0xd8, 0x05, 0x22, 0x96, 0xcd, 0x2e, 0x90, 0xf1, 0xfd, 0x06, 0x00 }; static const uint8_t user_data_2[] = { 0x41, 0xf1, 0xd8, 0x05, 0x22, 0x96, 0xcd, 0x2e, 0xd0, 0xf1, 0xfd, 0x06, 0x00 }; rcv_subscr = db_get_subscriber(GSM_SUBSCRIBER_IMSI, "901010000001111"); rcv_subscr->group = &dummy_sgrp; sms = db_sms_get(&dummy_net, 1); OSMO_ASSERT(sms->id == 1); OSMO_ASSERT(sms->receiver == rcv_subscr); OSMO_ASSERT(strcmp(sms->text, "Abc. Def. Foo") == 0); OSMO_ASSERT(sms->user_data_len == ARRAY_SIZE(user_data_1)); OSMO_ASSERT(memcmp(sms->user_data, user_data_1, ARRAY_SIZE(user_data_1)) == 0); sms_free(sms); sms = db_sms_get(&dummy_net, 2); OSMO_ASSERT(sms->id == 2); OSMO_ASSERT(sms->receiver == rcv_subscr); OSMO_ASSERT(strcmp(sms->text, "Abc. Def. Goo") == 0); OSMO_ASSERT(sms->user_data_len == ARRAY_SIZE(user_data_2)); OSMO_ASSERT(memcmp(sms->user_data, user_data_2, ARRAY_SIZE(user_data_2)) == 0); sms_free(sms); subscr_put(rcv_subscr); } int main() { char scratch_str[256]; printf("Testing subscriber database code.\n"); osmo_init_logging(&log_info); log_set_print_filename(osmo_stderr_target, 0); dummy_net.subscr_group = &dummy_sgrp; dummy_sgrp.net = &dummy_net; if (db_init("hlr.sqlite3")) { printf("DB: Failed to init database. Please check the option settings.\n"); return 1; } printf("DB: Database initialized.\n"); if (db_prepare()) { printf("DB: Failed to prepare database.\n"); return 1; } printf("DB: Database prepared.\n"); struct gsm_subscriber *alice = NULL; struct gsm_subscriber *alice_db; char *alice_imsi = "3243245432345"; alice = db_create_subscriber(alice_imsi); db_sync_subscriber(alice); alice_db = db_get_subscriber(GSM_SUBSCRIBER_IMSI, alice->imsi); COMPARE(alice, alice_db); SUBSCR_PUT(alice_db); SUBSCR_PUT(alice); alice_imsi = "3693245423445"; alice = db_create_subscriber(alice_imsi); db_subscriber_assoc_imei(alice, "1234567890"); db_subscriber_alloc_tmsi(alice); alice->lac=42; db_sync_subscriber(alice); /* Get by TMSI */ snprintf(scratch_str, sizeof(scratch_str), "%"PRIu32, alice->tmsi); alice_db = db_get_subscriber(GSM_SUBSCRIBER_TMSI, scratch_str); COMPARE(alice, alice_db); SUBSCR_PUT(alice_db); /* Get by IMSI */ alice_db = db_get_subscriber(GSM_SUBSCRIBER_IMSI, alice_imsi); COMPARE(alice, alice_db); SUBSCR_PUT(alice_db); /* Get by id */ snprintf(scratch_str, sizeof(scratch_str), "%llu", alice->id); alice_db = db_get_subscriber(GSM_SUBSCRIBER_ID, scratch_str); COMPARE(alice, alice_db); SUBSCR_PUT(alice_db); /* Get by extension */ alice_db = db_get_subscriber(GSM_SUBSCRIBER_EXTENSION, alice->extension); COMPARE(alice, alice_db); SUBSCR_PUT(alice_db); SUBSCR_PUT(alice); alice_imsi = "9993245423445"; alice = db_create_subscriber(alice_imsi); db_subscriber_alloc_tmsi(alice); alice->lac=42; db_sync_subscriber(alice); db_subscriber_assoc_imei(alice, "1234567890"); db_subscriber_assoc_imei(alice, "6543560920"); /* Get by TMSI */ snprintf(scratch_str, sizeof(scratch_str), "%"PRIu32, alice->tmsi); alice_db = db_get_subscriber(GSM_SUBSCRIBER_TMSI, scratch_str); COMPARE(alice, alice_db); SUBSCR_PUT(alice_db); /* Get by IMSI */ alice_db = db_get_subscriber(GSM_SUBSCRIBER_IMSI, alice_imsi); COMPARE(alice, alice_db); SUBSCR_PUT(alice_db); /* Get by id */ snprintf(scratch_str, sizeof(scratch_str), "%llu", alice->id); alice_db = db_get_subscriber(GSM_SUBSCRIBER_ID, scratch_str); COMPARE(alice, alice_db); SUBSCR_PUT(alice_db); /* Get by extension */ alice_db = db_get_subscriber(GSM_SUBSCRIBER_EXTENSION, alice->extension); COMPARE(alice, alice_db); SUBSCR_PUT(alice_db); SUBSCR_PUT(alice); test_sms(); test_sms_migrate(); db_fini(); printf("Done\n"); return 0; } /* stubs */ void vty_out() {} openbsc-0.15.0/openbsc/tests/db/db_test.err000066400000000000000000000000461265565154000205550ustar00rootroot00000000000000Going to migrate from revision 3 openbsc-0.15.0/openbsc/tests/db/db_test.ok000066400000000000000000000001301265565154000203700ustar00rootroot00000000000000Testing subscriber database code. DB: Database initialized. DB: Database prepared. Done openbsc-0.15.0/openbsc/tests/db/hlr.sqlite3000066400000000000000000000720001265565154000205110ustar00rootroot00000000000000SQLite format 3@ : :-æñûöñ   ññ revision3 ôô  revision øŸÉ¨çÓ¸ŸSMS!Subscriber CountersöMeta)EquipmentWatch Equipment €À€> 33 2014-03-08 15:35:272014-03-08 15:35:283v¼ý8X48582> 33 2014-03-08 15:35:082014-03-08 15:35:083v¼ý8W28568 çôç 3v¼ý8X  3v¼ý8W í÷í 48582 28568 ÷ü÷    UUÓüN£Ø B|[tableMetaMetaCREATE TABLE Meta (id INTEGER PRIMARY KEY AUTOINCREMENT, key TEXT UNIQUE NOT NULL, value TEXT NOT NULL)';indexsqlite_autoindex_Meta_1MetaP++Ytablesqlite_sequencesqlite_sequenceCREATE TABLE sqlite_sequence(name,seq)‚R!!„otableSubscriberSubscriberCREATE TABLE Subscriber (id INTEGER PRIMARY KEY AUTOINCREMENT, created TIMESTAMP NOT NULL, updated TIMESTAMP NOT NULL, imsi NUMERIC UNIQUE NOT NULL, name TEXT, extension TEXT UNIQUE, authorized INTEGER NOT NULL DEFAULT 0, tmsi TEXT UNIQUE, lac INTEGER NOT NULL DEFAULT 0, expire_lu TIMESTAMP DEFAULT NULL)3G!indexsqlite_autoindex_Subscriber_1Subscriber3G!indexsqlite_autoindex_Subscriber_2Subscriber3G!indexsqlite_autoindex_Subscriber_3Subscriber;‚EtableAuthTokenAuthToken CREATE TABLE AuthToken (id INTEGER PRIMARY KEY AUTOINCREMENT, subscriber_id INTEGER UNIQUE NOT NULL, created TIMESTAMP NOT NULL, token TEXT UNIQUE NOT NULL) (([Ž„·Ã1 Eindexsqlite_autoindex_AuthToken_1AuthToken 1 Eindexsqlite_autoindex_AuthToken_2AuthToken s ƒ5tableEquipmentEquipmentCREATE TABLE Equipment (id INTEGER PRIMARY KEY AUTOINCREMENT, created TIMESTAMP NOT NULL, updated TIMESTAMP NOT NULL, name TEXT, classmark1 NUMERIC, classmark2 BLOB, classmark3 BLOB, imei NUMERIC UNIQUE NOT NULL)1 Eindexsqlite_autoindex_Equipment_1Equipment‚ ))ƒMtableEquipmentWatchEquipmentWatchCREATE TABLE EquipmentWatch (id INTEGER PRIMARY KEY AUTOINCREMENT, created TIMESTAMP NOT NULL, updated TIMESTAMP NOT NULL, subscriber_id NUMERIC NOT NULL, equipment_id NUMERIC NOT NULL, UNIQUE (subscriber_id, equipment_id) );O)indexsqlite_autoindex_EquipmentWatch_1EquipmentWatch ÆÆ8 33 2014-03-08 15:35:082014-03-08 15:35:283¯LJ€ªª ôô  ¯LJ€ªª £Ò£-33 2014-03-08 15:35:282014-03-08 15:35:28,33 2014-03-08 15:35:082014-03-08 15:35:08 ôûô  jµjI3 )'2014-03-08 15:38:47 ×Ü28568@ð×!•Ì-ÏðüÿAbc. Def. GooI3 )'2014-03-08 15:38:12 ×Ü28568@ð×!•Ì-ðüÿAbc. Def. Foo ff8ƒO‡tableSMSSMSCREATE TABLE SMS (id INTEGER PRIMARY KEY AUTOINCREMENT, created TIMESTAMP NOT NULL, sent TIMESTAMP, sender_id INTEGER NOT NULL, receiver_id INTEGER NOT NULL, deliver_attempts INTEGER NOT NULL DEFAULT 0, valid_until TIMESTAMP, reply_path_req INTEGER NOT NULL, status_rep_req INTEGER NOT NULL, protocol_id INTEGER NOT NULL, data_coding_scheme INTEGER NOT NULL, ud_hdr_ind INTEGER NOT NULL, dest_addr TEXT, user_data BLOB, header BLOB, text TEXT )E‚qtableVLRVLRCREATE TABLE VLR (id INTEGER PRIMARY KEY AUTOINCREMENT, created TIMESTAMP NOT NULL, updated TIMESTAMP NOT NULL, subscriber_id NUMERIC UNIQUE NOT NULL, last_bts NUMERIC NOT NULL )   ûû k’Y¢~))ƒ7tableAuthLastTuplesAuthLastTuplesCREATE TABLE AuthLastTuples (subscriber_id INTEGER PRIMARY KEY, issued TIMESTAMP NOT NULL, use_count INTEGER NOT NULL DEFAULT 0, key_seq INTEGER NOT NULL, rand BLOB NOT NULL, sres BLOB NOT NULL, kc BLOB NOT NULL )[tableAuthKeysAuthKeysCREATE TABLE AuthKeys (subscriber_id INTEGER PRIMARY KEY, algorithm_id INTEGER NOT NULL, a3a8_ki BLOB )F%%‚OtableRateCountersRateCountersCREATE TABLE RateCounters (id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp TIMESTAMP NOT NULL, value INTEGER NOT NULL, name TEXT NOT NULL, idx INTEGER NOT NULL )%9indexsqlite_autoindex_VLR_1VLRD‚WtableApduBlobsApduBlobsCREATE TABLE ApduBlobs (id INTEGER PRIMARY KEY AUTOINCREMENT, created TIMESTAMP NOT NULL, apdu_id_flags INTEGER NOT NULL, subscriber_id INTEGER NOT NULL, apdu BLOB )$‚tableCountersCountersCREATE TABLE Counters (id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp TIMESTAMP NOT NULL, value INTEGER NOT NULL, name TEXT NOT NULL )    3שyH꽌[(ùÈ—i<â·Š_3*312014-03-08 15:35:15net.sms.rp_err_mem)3/2014-03-08 15:35:15net.sms.delivered+332014-03-08 15:35:15net.sms.no_receiver)3/2014-03-08 15:35:15net.sms.submitted*312014-03-08 15:35:15net.paging.expired,352014-03-08 15:35:15net.paging.completed+332014-03-08 15:35:15net.paging.detached,352014-03-08 15:35:15net.paging.attempted/ 3;2014-03-08 15:35:15net.loc_upd_resp.accept/ 3 ;2014-03-08 15:35:15net.loc_upd_resp.reject- 372014-03-08 15:35:15net.imsi_detach.count1 3?2014-03-08 15:35:15net.loc_upd_type.periodic/ 3 ;2014-03-08 15:35:15net.loc_upd_type.normal/3;2014-03-08 15:35:15net.loc_upd_type.attach+332014-03-08 15:35:15net.handover.failed.392014-03-08 15:35:15net.handover.completed,352014-03-08 15:35:15net.handover.timeout/3;2014-03-08 15:35:15net.handover.no_channel.392014-03-08 15:35:15net.handover.attempted,352014-03-08 15:35:15net.chreq.no_channel'3 +2014-03-08 15:35:15net.chreq.total uÒ§vKôÊŸu(3 -2014-03-08 15:35:15net.bts.rsl_fail)3-2014-03-08 15:35:15net.bts.oml_fail(3-2014-03-08 15:35:15net.chan.rll_err(3-2014-03-08 15:35:15net.chan.rf_fail+332014-03-08 15:35:15net.call.mt_connect)3/2014-03-08 15:35:15net.call.mt_setup/3;2014-03-08 15:35:15net.call.mo_connect_ack)3/2014-03-08 15:35:15net.call.mo_setup,352014-03-08 15:35:15net.sms.rp_err_otheropenbsc-0.15.0/openbsc/tests/gbproxy/000077500000000000000000000000001265565154000175245ustar00rootroot00000000000000openbsc-0.15.0/openbsc/tests/gbproxy/Makefile.am000066400000000000000000000016261265565154000215650ustar00rootroot00000000000000AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOABIS_CFLAGS) AM_LDFLAGS = $(COVERAGE_LDFLAGS) EXTRA_DIST = gbproxy_test.ok noinst_PROGRAMS = gbproxy_test gbproxy_test_SOURCES = gbproxy_test.c gbproxy_test_LDADD = \ $(top_builddir)/src/gprs/gb_proxy.o \ $(top_builddir)/src/gprs/gb_proxy_patch.o \ $(top_builddir)/src/gprs/gb_proxy_peer.o \ $(top_builddir)/src/gprs/gb_proxy_tlli.o \ $(top_builddir)/src/gprs/gprs_gb_parse.o \ $(top_builddir)/src/gprs/gprs_llc_parse.o \ $(top_builddir)/src/gprs/crc24.o \ $(top_builddir)/src/gprs/gprs_utils.o \ $(top_builddir)/src/libcommon/libcommon.a \ $(top_builddir)/src/libbsc/libbsc.a \ $(top_builddir)/src/libtrau/libtrau.a \ $(LIBOSMOCORE_LIBS) $(LIBOSMOGB_LIBS) \ $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) \ $(LIBOSMOABIS_LIBS) $(LIBRARY_DL) \ -lrt openbsc-0.15.0/openbsc/tests/gbproxy/gbproxy_test.c000066400000000000000000004637041265565154000224370ustar00rootroot00000000000000/* test routines for gbproxy * send NS messages to the gbproxy and dumps what happens * (C) 2013 by sysmocom s.f.m.c. GmbH * Author: Jacob Erlbeck */ #undef _GNU_SOURCE #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define REMOTE_BSS_ADDR 0x01020304 #define REMOTE_SGSN_ADDR 0x05060708 #define SGSN_NSEI 0x0100 #define REMOTE_SGSN2_ADDR 0x15161718 #define SGSN2_NSEI 0x0102 #define MATCH_ANY (-1) struct gbproxy_config gbcfg = {0}; struct llist_head *received_messages = NULL; static int dump_global(FILE *stream, int indent) { unsigned int i; const struct rate_ctr_group_desc *desc; int rc; rc = fprintf(stream, "%*sGbproxy global:\n", indent, ""); if (rc < 0) return rc; desc = gbcfg.ctrg->desc; for (i = 0; i < desc->num_ctr; i++) { struct rate_ctr *ctr = &gbcfg.ctrg->ctr[i]; if (ctr->current) { rc = fprintf(stream, "%*s %s: %llu\n", indent, "", desc->ctr_desc[i].description, (long long)ctr->current); if (rc < 0) return rc; } } return 0; } static int dump_peers(FILE *stream, int indent, time_t now, struct gbproxy_config *cfg) { struct gbproxy_peer *peer; struct gprs_ra_id raid; unsigned int i; const struct rate_ctr_group_desc *desc; int rc; rc = fprintf(stream, "%*sPeers:\n", indent, ""); if (rc < 0) return rc; llist_for_each_entry(peer, &cfg->bts_peers, list) { struct gbproxy_link_info *link_info; struct gbproxy_patch_state *state = &peer->patch_state; gsm48_parse_ra(&raid, peer->ra); rc = fprintf(stream, "%*s NSEI %u, BVCI %u, %sblocked, " "RAI %u-%u-%u-%u\n", indent, "", peer->nsei, peer->bvci, peer->blocked ? "" : "not ", raid.mcc, raid.mnc, raid.lac, raid.rac); if (rc < 0) return rc; desc = peer->ctrg->desc; for (i = 0; i < desc->num_ctr; i++) { struct rate_ctr *ctr = &peer->ctrg->ctr[i]; if (ctr->current) { rc = fprintf(stream, "%*s %s: %llu\n", indent, "", desc->ctr_desc[i].description, (long long)ctr->current); if (rc < 0) return rc; } } fprintf(stream, "%*s TLLI-Cache: %d\n", indent, "", state->logical_link_count); llist_for_each_entry(link_info, &state->logical_links, list) { char mi_buf[200]; time_t age = now ? now - link_info->timestamp : 0; int stored_msgs = 0; struct llist_head *iter; enum gbproxy_match_id match_id; llist_for_each(iter, &link_info->stored_msgs) stored_msgs++; if (link_info->imsi_len > 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)"); } fprintf(stream, "%*s TLLI %08x", indent, "", link_info->tlli.current); if (link_info->tlli.assigned) fprintf(stream, "/%08x", link_info->tlli.assigned); if (link_info->sgsn_tlli.current) { fprintf(stream, " -> %08x", link_info->sgsn_tlli.current); if (link_info->sgsn_tlli.assigned) fprintf(stream, "/%08x", link_info->sgsn_tlli.assigned); } fprintf(stream, ", IMSI %s, AGE %d", mi_buf, (int)age); if (stored_msgs) fprintf(stream, ", STORED %d", stored_msgs); for (match_id = 0; match_id < ARRAY_SIZE(cfg->matches); ++match_id) { if (cfg->matches[match_id].enable && link_info->is_matching[match_id]) { fprintf(stream, ", IMSI matches"); break; } } if (link_info->imsi_acq_pending) fprintf(stream, ", IMSI acquisition in progress"); if (cfg->route_to_sgsn2) fprintf(stream, ", SGSN NSEI %d", link_info->sgsn_nsei); if (link_info->is_deregistered) fprintf(stream, ", DE-REGISTERED"); rc = fprintf(stream, "\n"); if (rc < 0) return rc; } } return 0; } const uint8_t *convert_ra(struct gprs_ra_id *raid) { static uint8_t buf[6]; gsm48_construct_ra(buf, raid); return buf; } /* DTAP - Attach Request */ static const unsigned char dtap_attach_req[] = { 0x08, 0x01, 0x02, 0xf5, 0xe0, 0x21, 0x08, 0x02, 0x05, 0xf4, 0xfb, 0xc5, 0x46, 0x79, 0x11, 0x22, 0x33, 0x40, 0x50, 0x60, 0x19, 0x18, 0xb3, 0x43, 0x2b, 0x25, 0x96, 0x62, 0x00, 0x60, 0x80, 0x9a, 0xc2, 0xc6, 0x62, 0x00, 0x60, 0x80, 0xba, 0xc8, 0xc6, 0x62, 0x00, 0x60, 0x80, 0x00, }; /* DTAP - Attach Request (invalid RAI) */ static const unsigned char dtap_attach_req2[] = { 0x08, 0x01, 0x02, 0xf5, 0xe0, 0x21, 0x08, 0x02, 0x05, 0xf4, 0xfb, 0x00, 0xbe, 0xef, 0x99, 0x99, 0x99, 0x40, 0x50, 0x60, 0x19, 0x18, 0xb3, 0x43, 0x2b, 0x25, 0x96, 0x62, 0x00, 0x60, 0x80, 0x9a, 0xc2, 0xc6, 0x62, 0x00, 0x60, 0x80, 0xba, 0xc8, 0xc6, 0x62, 0x00, 0x60, 0x80, 0x00, }; /* DTAP - Attach Request (P-TMSI 0x3f32b700) */ static const unsigned char dtap_attach_req3[] = { 0x08, 0x01, 0x02, 0xf5, 0xe0, 0x21, 0x08, 0x02, 0x05, 0xf4, 0xef, 0xe2, 0xb7, 0x00, 0x11, 0x22, 0x33, 0x40, 0x50, 0x60, 0x19, 0x18, 0xb3, 0x43, 0x2b, 0x25, 0x96, 0x62, 0x00, 0x60, 0x80, 0x9a, 0xc2, 0xc6, 0x62, 0x00, 0x60, 0x80, 0xba, 0xc8, 0xc6, 0x62, 0x00, 0x60, 0x80, 0x00, }; /* DTAP - Attach Request (IMSI 12131415161718) */ static const unsigned char dtap_attach_req4[] = { 0x08, 0x01, 0x02, 0xf5, 0xe0, 0x21, 0x08, 0x02, 0x08, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x11, 0x22, 0x33, 0x40, 0x50, 0x60, 0x19, 0x18, 0xb3, 0x43, 0x2b, 0x25, 0x96, 0x62, 0x00, 0x60, 0x80, 0x9a, 0xc2, 0xc6, 0x62, 0x00, 0x60, 0x80, 0xba, 0xc8, 0xc6, 0x62, 0x00, 0x60, 0x80, 0x00, }; /* DTAP - Identity Request */ static const unsigned char dtap_identity_req[] = { 0x08, 0x15, 0x01 }; /* DTAP - Identity Response */ static const unsigned char dtap_identity_resp[] = { 0x08, 0x16, 0x08, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18 }; /* DTAP - Identity Response, IMSI 2 */ static const unsigned char dtap_identity2_resp[] = { 0x08, 0x16, 0x08, 0x11, 0x12, 0x99, 0x99, 0x99, 0x16, 0x17, 0x18 }; /* DTAP - Identity Response, IMSI 3 */ static const unsigned char dtap_identity3_resp[] = { 0x08, 0x16, 0x08, 0x11, 0x12, 0x99, 0x99, 0x99, 0x26, 0x27, 0x28 }; /* DTAP - Attach Accept */ static const unsigned char dtap_attach_acc[] = { 0x08, 0x02, 0x01, 0x49, 0x04, 0x21, 0x63, 0x54, 0x40, 0x50, 0x60, 0x19, 0xcd, 0xd7, 0x08, 0x17, 0x16, 0x18, 0x05, 0xf4, 0xef, 0xe2, 0xb7, 0x00 }; /* DTAP - Attach Accept, P-TMSI 2 */ static const unsigned char dtap_attach_acc2[] = { 0x08, 0x02, 0x01, 0x49, 0x04, 0x21, 0x63, 0x54, 0x40, 0x50, 0x60, 0x19, 0xcd, 0xd7, 0x08, 0x17, 0x16, 0x18, 0x05, 0xf4, 0xe0, 0x98, 0x76, 0x54 }; /* DTAP - Attach Complete */ static const unsigned char dtap_attach_complete[] = { 0x08, 0x03 }; /* DTAP - Attach Reject (GPRS services not allowed) */ static const unsigned char dtap_attach_rej7[] = { 0x08, 0x04, 0x07 }; /* DTAP - GMM Information */ static const unsigned char dtap_gmm_information[] = { 0x08, 0x21 }; /* DTAP - Routing Area Update Request */ static const unsigned char dtap_ra_upd_req[] = { 0x08, 0x08, 0x10, 0x11, 0x22, 0x33, 0x40, 0x50, 0x60, 0x1d, 0x19, 0x13, 0x42, 0x33, 0x57, 0x2b, 0xf7, 0xc8, 0x48, 0x02, 0x13, 0x48, 0x50, 0xc8, 0x48, 0x02, 0x14, 0x48, 0x50, 0xc8, 0x48, 0x02, 0x17, 0x49, 0x10, 0xc8, 0x48, 0x02, 0x00, 0x19, 0x8b, 0xb2, 0x92, 0x17, 0x16, 0x27, 0x07, 0x04, 0x31, 0x02, 0xe5, 0xe0, 0x32, 0x02, 0x20, 0x00 }; /* DTAP - Routing Area Update Accept */ static const unsigned char dtap_ra_upd_acc[] = { 0x08, 0x09, 0x00, 0x49, 0x21, 0x63, 0x54, 0x40, 0x50, 0x60, 0x19, 0x54, 0xab, 0xb3, 0x18, 0x05, 0xf4, 0xef, 0xe2, 0xb7, 0x00, 0x17, 0x16, }; /* DTAP - Routing Area Update Accept, P-TMSI 2 */ static const unsigned char dtap_ra_upd_acc2[] = { 0x08, 0x09, 0x00, 0x49, 0x21, 0x63, 0x54, 0x40, 0x50, 0x60, 0x19, 0x54, 0xab, 0xb3, 0x18, 0x05, 0xf4, 0xe0, 0x98, 0x76, 0x54, 0x17, 0x16, }; /* DTAP - Routing Area Update Accept, P-TMSI 3 */ static const unsigned char dtap_ra_upd_acc3[] = { 0x08, 0x09, 0x00, 0x49, 0x21, 0x63, 0x54, 0x40, 0x50, 0x60, 0x19, 0x54, 0xab, 0xb3, 0x18, 0x05, 0xf4, 0xe0, 0x54, 0x32, 0x10, 0x17, 0x16, }; /* DTAP - Routing Area Update Complete */ static const unsigned char dtap_ra_upd_complete[] = { 0x08, 0x0a }; /* DTAP - Routing Area Update Reject */ /* cause = 10 ("Implicitly detached"), force_standby = 0 */ static const unsigned char dtap_ra_upd_rej[] = { 0x08, 0x0b, 0x0a, 0x00, }; /* DTAP - Activate PDP Context Request */ static const unsigned char dtap_act_pdp_ctx_req[] = { 0x0a, 0x41, 0x05, 0x03, 0x0c, 0x00, 0x00, 0x1f, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x21, 0x28, 0x03, 0x02, 0x61, 0x62, 0x27, 0x14, 0x80, 0x80, 0x21, 0x10, 0x01, 0x00, 0x00, 0x10, 0x81, 0x06, 0x00, 0x00, 0x00, 0x00, 0x83, 0x06, 0x00, 0x00, 0x00, 0x00 }; /* DTAP - Detach Request (MO) */ /* normal detach, power_off = 1 */ static const unsigned char dtap_detach_po_req[] = { 0x08, 0x05, 0x09, 0x18, 0x05, 0xf4, 0xef, 0xe2, 0xb7, 0x00, 0x19, 0x03, 0xb9, 0x97, 0xcb }; /* DTAP - Detach Request (MO) */ /* normal detach, power_off = 0 */ static const unsigned char dtap_detach_req[] = { 0x08, 0x05, 0x01, 0x18, 0x05, 0xf4, 0xef, 0xe2, 0xb7, 0x00, 0x19, 0x03, 0xb9, 0x97, 0xcb }; /* DTAP - Detach Accept (MO) */ static const unsigned char dtap_detach_acc[] = { 0x08, 0x06, 0x00 }; /* DTAP - Detach Request (MT) */ /* normal detach, reattach required, implicitly detached */ static const unsigned char dtap_mt_detach_rea_req[] = { 0x08, 0x05, 0x01, 0x25, 0x0a }; /* DTAP - Detach Request (MT) */ /* normal detach, reattach not required, implicitly detached */ static const unsigned char dtap_mt_detach_req[] = { 0x08, 0x05, 0x02, 0x25, 0x0a }; /* DTAP - Detach Accept (MT) */ static const unsigned char dtap_mt_detach_acc[] = { 0x08, 0x06 }; /* GPRS-LLC - SAPI: LLGMM, U, XID */ static const unsigned char llc_u_xid_ul[] = { 0x41, 0xfb, 0x01, 0x00, 0x0e, 0x00, 0x64, 0x11, 0x05, 0x16, 0x01, 0x90, 0x66, 0xb3, 0x28 }; /* GPRS-LLC - SAPI: LLGMM, U, XID */ static const unsigned char llc_u_xid_dl[] = { 0x41, 0xfb, 0x30, 0x84, 0x10, 0x61, 0xb6, 0x64, 0xe4, 0xa9, 0x1a, 0x9e }; /* GPRS-LLC - SAPI: LL11, UI, NSAPI 5, DNS query */ static const unsigned char llc_ui_ll11_dns_query_ul[] = { 0x0b, 0xc0, 0x01, 0x65, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x38, 0x95, 0x72, 0x00, 0x00, 0x45, 0x11, 0x20, 0x85, 0x0a, 0xc0, 0x07, 0xe4, 0xac, 0x10, 0x01, 0x0a, 0xad, 0xab, 0x00, 0x35, 0x00, 0x24, 0x0e, 0x1c, 0x3b, 0xe0, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x6d, 0x05, 0x68, 0x65, 0x69, 0x73, 0x65, 0x02, 0x64, 0x65, 0x00, 0x00, 0x01, 0x00, 0x01, 0x47, 0x8f, 0x07 }; /* GPRS-LLC - SAPI: LL11, UI, NSAPI 5, DNS query */ static const unsigned char llc_ui_ll11_dns_resp_dl[] = { 0x4b, 0xc0, 0x01, 0x65, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0xc6, 0x00, 0x00, 0x40, 0x00, 0x3e, 0x11, 0x7c, 0x69, 0xac, 0x10, 0x01, 0x0a, 0x0a, 0xc0, 0x07, 0xe4, 0x00, 0x35, 0xad, 0xab, 0x00, 0xb2, 0x74, 0x4e, 0x3b, 0xe0, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01, 0x00, 0x05, 0x00, 0x00, 0x01, 0x6d, 0x05, 0x68, 0x65, 0x69, 0x73, 0x65, 0x02, 0x64, 0x65, 0x00, 0x00, 0x01, 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0e, 0x10, 0x00, 0x04, 0xc1, 0x63, 0x90, 0x58, 0xc0, 0x0e, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x0e, 0x10, 0x00, 0x16, 0x03, 0x6e, 0x73, 0x32, 0x0c, 0x70, 0x6f, 0x70, 0x2d, 0x68, 0x61, 0x6e, 0x6e, 0x6f, 0x76, 0x65, 0x72, 0x03, 0x6e, 0x65, 0x74, 0x00, 0xc0, 0x0e, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x0e, 0x10, 0x00, 0x10, 0x02, 0x6e, 0x73, 0x01, 0x73, 0x08, 0x70, 0x6c, 0x75, 0x73, 0x6c, 0x69, 0x6e, 0x65, 0xc0, 0x14, 0xc0, 0x0e, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x0e, 0x10, 0x00, 0x05, 0x02, 0x6e, 0x73, 0xc0, 0x0e, 0xc0, 0x0e, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x0e, 0x10, 0x00, 0x05, 0x02, 0x6e, 0x73, 0xc0, 0x5f, 0xc0, 0x0e, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x0e, 0x10, 0x00, 0x12, 0x02, 0x6e, 0x73, 0x0c, 0x70, 0x6f, 0x70, 0x2d, 0x68, 0x61, 0x6e, 0x6e, 0x6f, 0x76, 0x65, 0x72, 0xc0, 0x14, 0xaa, 0xdf, 0x31 }; static int gprs_process_message(struct gprs_ns_inst *nsi, const char *text, struct sockaddr_in *peer, const unsigned char* data, size_t data_len); static void send_ns_reset(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr, enum ns_cause cause, uint16_t nsvci, uint16_t nsei) { /* GPRS Network Service, PDU type: NS_RESET, */ unsigned char msg[12] = { 0x02, 0x00, 0x81, 0x01, 0x01, 0x82, 0x11, 0x22, 0x04, 0x82, 0x11, 0x22 }; msg[3] = cause; msg[6] = nsvci / 256; msg[7] = nsvci % 256; msg[10] = nsei / 256; msg[11] = nsei % 256; gprs_process_message(nsi, "RESET", src_addr, msg, sizeof(msg)); } static void send_ns_reset_ack(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr, uint16_t nsvci, uint16_t nsei) { /* GPRS Network Service, PDU type: NS_RESET_ACK, */ unsigned char msg[9] = { 0x03, 0x01, 0x82, 0x11, 0x22, 0x04, 0x82, 0x11, 0x22 }; msg[3] = nsvci / 256; msg[4] = nsvci % 256; msg[7] = nsei / 256; msg[8] = nsei % 256; gprs_process_message(nsi, "RESET_ACK", src_addr, msg, sizeof(msg)); } static void send_ns_alive(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr) { /* GPRS Network Service, PDU type: NS_ALIVE */ unsigned char msg[1] = { 0x0a }; gprs_process_message(nsi, "ALIVE", src_addr, msg, sizeof(msg)); } static void send_ns_alive_ack(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr) { /* GPRS Network Service, PDU type: NS_ALIVE_ACK */ unsigned char msg[1] = { 0x0b }; gprs_process_message(nsi, "ALIVE_ACK", src_addr, msg, sizeof(msg)); } static void send_ns_unblock(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr) { /* GPRS Network Service, PDU type: NS_UNBLOCK */ unsigned char msg[1] = { 0x06 }; gprs_process_message(nsi, "UNBLOCK", src_addr, msg, sizeof(msg)); } static void send_ns_unblock_ack(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr) { /* GPRS Network Service, PDU type: NS_UNBLOCK_ACK */ unsigned char msg[1] = { 0x07 }; gprs_process_message(nsi, "UNBLOCK_ACK", src_addr, msg, sizeof(msg)); } static void send_ns_unitdata(struct gprs_ns_inst *nsi, const char *text, struct sockaddr_in *src_addr, uint16_t nsbvci, const unsigned char *bssgp_msg, size_t bssgp_msg_size) { /* GPRS Network Service, PDU type: NS_UNITDATA */ unsigned char msg[4096] = { 0x00, 0x00, 0x00, 0x00 }; OSMO_ASSERT(bssgp_msg_size <= sizeof(msg) - 4); msg[2] = nsbvci / 256; msg[3] = nsbvci % 256; memcpy(msg + 4, bssgp_msg, bssgp_msg_size); gprs_process_message(nsi, text ? text : "UNITDATA", src_addr, msg, bssgp_msg_size + 4); } static void send_bssgp_ul_unitdata( struct gprs_ns_inst *nsi, const char *text, struct sockaddr_in *src_addr, uint16_t nsbvci, uint32_t tlli, struct gprs_ra_id *raid, uint16_t cell_id, const uint8_t *llc_msg, size_t llc_msg_size) { /* GPRS Network Service, PDU type: NS_UNITDATA */ /* Base Station Subsystem GPRS Protocol: UL_UNITDATA */ unsigned char msg[4096] = { 0x01, /* TLLI */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x08, 0x88, /* RAI */ 0x11, 0x22, 0x33, 0x40, 0x50, 0x60, /* CELL ID */ 0x00, 0x00, 0x00, 0x80, 0x0e, /* LLC LEN */ 0x00, 0x00, }; size_t bssgp_msg_size = 23 + llc_msg_size; OSMO_ASSERT(bssgp_msg_size <= sizeof(msg)); gsm48_construct_ra(msg + 10, raid); msg[1] = (uint8_t)(tlli >> 24); msg[2] = (uint8_t)(tlli >> 16); msg[3] = (uint8_t)(tlli >> 8); msg[4] = (uint8_t)(tlli >> 0); msg[16] = cell_id / 256; msg[17] = cell_id % 256; msg[21] = llc_msg_size / 256; msg[22] = llc_msg_size % 256; memcpy(msg + 23, llc_msg, llc_msg_size); send_ns_unitdata(nsi, text ? text : "BSSGP UL UNITDATA", src_addr, nsbvci, msg, bssgp_msg_size); } static void send_bssgp_dl_unitdata( struct gprs_ns_inst *nsi, const char *text, struct sockaddr_in *src_addr, uint16_t nsbvci, uint32_t tlli, int with_racap_drx, const uint8_t *imsi, size_t imsi_size, const uint8_t *llc_msg, size_t llc_msg_size) { /* Base Station Subsystem GPRS Protocol: DL_UNITDATA */ unsigned char msg[4096] = { 0x00, /* TLLI */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x20, 0x16, 0x82, 0x02, 0x58, }; unsigned char racap_drx[] = { 0x13, 0x99, 0x18, 0xb3, 0x43, 0x2b, 0x25, 0x96, 0x62, 0x00, 0x60, 0x80, 0x9a, 0xc2, 0xc6, 0x62, 0x00, 0x60, 0x80, 0xba, 0xc8, 0xc6, 0x62, 0x00, 0x60, 0x80, 0x00, 0x0a, 0x82, 0x08, 0x02 }; size_t bssgp_msg_size = 0; OSMO_ASSERT(51 + imsi_size + llc_msg_size <= sizeof(msg)); msg[1] = (uint8_t)(tlli >> 24); msg[2] = (uint8_t)(tlli >> 16); msg[3] = (uint8_t)(tlli >> 8); msg[4] = (uint8_t)(tlli >> 0); bssgp_msg_size = 12; if (with_racap_drx) { memcpy(msg + bssgp_msg_size, racap_drx, sizeof(racap_drx)); bssgp_msg_size += sizeof(racap_drx); } if (imsi) { OSMO_ASSERT(imsi_size <= 127); msg[bssgp_msg_size] = BSSGP_IE_IMSI; msg[bssgp_msg_size + 1] = 0x80 | imsi_size; memcpy(msg + bssgp_msg_size + 2, imsi, imsi_size); bssgp_msg_size += 2 + imsi_size; } if ((bssgp_msg_size % 4) != 0) { size_t abytes = (4 - (bssgp_msg_size + 2) % 4) % 4; msg[bssgp_msg_size] = BSSGP_IE_ALIGNMENT; msg[bssgp_msg_size + 1] = 0x80 | abytes; memset(msg + bssgp_msg_size + 2, 0, abytes); bssgp_msg_size += 2 + abytes; } msg[bssgp_msg_size] = BSSGP_IE_LLC_PDU; if (llc_msg_size < 128) { msg[bssgp_msg_size + 1] = 0x80 | llc_msg_size; bssgp_msg_size += 2; } else { msg[bssgp_msg_size + 1] = llc_msg_size / 256; msg[bssgp_msg_size + 2] = llc_msg_size % 256; bssgp_msg_size += 3; } memcpy(msg + bssgp_msg_size, llc_msg, llc_msg_size); bssgp_msg_size += llc_msg_size; send_ns_unitdata(nsi, text ? text : "BSSGP DL UNITDATA", src_addr, nsbvci, msg, bssgp_msg_size); } static void send_bssgp_reset(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr, uint16_t bvci) { /* GPRS Network Service, PDU type: NS_UNITDATA, BVCI 0 * BSSGP RESET */ unsigned char msg[18] = { 0x22, 0x04, 0x82, 0x4a, 0x2e, 0x07, 0x81, 0x08, 0x08, 0x88, 0x11, 0x22, 0x33, 0x40, 0x50, 0x60, 0x10, 0x00 }; msg[3] = bvci / 256; msg[4] = bvci % 256; send_ns_unitdata(nsi, "BVC_RESET", src_addr, 0, msg, sizeof(msg)); } static void send_bssgp_reset_ack(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr, uint16_t bvci) { /* GPRS Network Service, PDU type: NS_UNITDATA, BVCI 0 * BSSGP RESET_ACK */ static unsigned char msg[5] = { 0x23, 0x04, 0x82, 0x00, 0x00 }; msg[3] = bvci / 256; msg[4] = bvci % 256; send_ns_unitdata(nsi, "BVC_RESET_ACK", src_addr, 0, msg, sizeof(msg)); } static void send_bssgp_suspend(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr, uint32_t tlli, struct gprs_ra_id *raid) { /* Base Station Subsystem GPRS Protocol, BSSGP SUSPEND */ unsigned char msg[15] = { 0x0b, 0x1f, 0x84, /* TLLI */ 0xff, 0xff, 0xff, 0xff, 0x1b, 0x86, /* RAI */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; msg[3] = (uint8_t)(tlli >> 24); msg[4] = (uint8_t)(tlli >> 16); msg[5] = (uint8_t)(tlli >> 8); msg[6] = (uint8_t)(tlli >> 0); gsm48_construct_ra(msg + 9, raid); send_ns_unitdata(nsi, "BVC_SUSPEND", src_addr, 0, msg, sizeof(msg)); } static void send_bssgp_suspend_ack(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr, uint32_t tlli, struct gprs_ra_id *raid) { /* Base Station Subsystem GPRS Protocol, BSSGP SUSPEND ACK */ unsigned char msg[18] = { 0x0c, 0x1f, 0x84, /* TLLI */ 0xff, 0xff, 0xff, 0xff, 0x1b, 0x86, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1d, 0x81, 0x01 }; msg[3] = (uint8_t)(tlli >> 24); msg[4] = (uint8_t)(tlli >> 16); msg[5] = (uint8_t)(tlli >> 8); msg[6] = (uint8_t)(tlli >> 0); gsm48_construct_ra(msg + 9, raid); send_ns_unitdata(nsi, "BVC_SUSPEND_ACK", src_addr, 0, msg, sizeof(msg)); } static void send_bssgp_llc_discarded(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr, uint16_t bvci, uint32_t tlli, unsigned n_frames, unsigned n_octets) { /* Base Station Subsystem GPRS Protocol: LLC-DISCARDED (0x2c) */ unsigned char msg[] = { 0x2c, 0x1f, 0x84, /* TLLI */ 0xff, 0xff, 0xff, 0xff, 0x0f, 0x81, /* n frames */ 0xff, 0x04, 0x82, /* BVCI */ 0xff, 0xff, 0x25, 0x83, /* n octets */ 0xff, 0xff, 0xff }; msg[3] = (uint8_t)(tlli >> 24); msg[4] = (uint8_t)(tlli >> 16); msg[5] = (uint8_t)(tlli >> 8); msg[6] = (uint8_t)(tlli >> 0); msg[9] = (uint8_t)(n_frames); msg[12] = (uint8_t)(bvci >> 8); msg[13] = (uint8_t)(bvci >> 0); msg[16] = (uint8_t)(n_octets >> 16); msg[17] = (uint8_t)(n_octets >> 8); msg[18] = (uint8_t)(n_octets >> 0); send_ns_unitdata(nsi, "LLC_DISCARDED", src_addr, 0, msg, sizeof(msg)); } static void send_bssgp_paging(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr, const uint8_t *imsi, size_t imsi_size, struct gprs_ra_id *raid, uint32_t ptmsi) { /* Base Station Subsystem GPRS Protocol, BSSGP SUSPEND */ unsigned char msg[100] = { 0x06, }; const unsigned char drx_ie[] = {0x0a, 0x82, 0x07, 0x04}; const unsigned char qos_ie[] = {0x18, 0x83, 0x00, 0x00, 0x00}; size_t bssgp_msg_size = 1; if (imsi) { OSMO_ASSERT(imsi_size <= 127); msg[bssgp_msg_size] = BSSGP_IE_IMSI; msg[bssgp_msg_size + 1] = 0x80 | imsi_size; memcpy(msg + bssgp_msg_size + 2, imsi, imsi_size); bssgp_msg_size += 2 + imsi_size; } memcpy(msg + bssgp_msg_size, drx_ie, sizeof(drx_ie)); bssgp_msg_size += sizeof(drx_ie); if (raid) { msg[bssgp_msg_size] = BSSGP_IE_ROUTEING_AREA; msg[bssgp_msg_size+1] = 0x86; gsm48_construct_ra(msg + bssgp_msg_size + 2, raid); bssgp_msg_size += 8; } memcpy(msg + bssgp_msg_size, qos_ie, sizeof(qos_ie)); bssgp_msg_size += sizeof(qos_ie); if (ptmsi != GSM_RESERVED_TMSI) { const uint32_t ptmsi_be = htonl(ptmsi); msg[bssgp_msg_size] = BSSGP_IE_TMSI; msg[bssgp_msg_size+1] = 0x84; memcpy(msg + bssgp_msg_size + 2, &ptmsi_be, 4); bssgp_msg_size += 6; } send_ns_unitdata(nsi, "PAGING_PS", src_addr, 0, msg, bssgp_msg_size); } static void send_bssgp_flow_control_bvc(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr, uint16_t bvci, uint8_t tag) { /* GPRS Network Service, PDU type: NS_UNITDATA, * BSSGP FLOW_CONTROL_BVC */ unsigned char msg[] = { 0x26, 0x1e, 0x81, /* Tag */ 0xff, 0x05, 0x82, 0x01, 0xdc, 0x03, 0x82, 0x02, 0x76, 0x01, 0x82, 0x00, 0x50, 0x1c, 0x82, 0x02, 0x58, 0x06, 0x82, 0x00, 0x03 }; msg[3] = tag; send_ns_unitdata(nsi, "FLOW_CONTROL_BVC", src_addr, bvci, msg, sizeof(msg)); } static void send_bssgp_flow_control_bvc_ack(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr, uint16_t bvci, uint8_t tag) { /* GPRS Network Service, PDU type: NS_UNITDATA, * BSSGP FLOW_CONTROL_BVC_ACK */ unsigned char msg[] = { 0x27, 0x1e, 0x81, /* Tag */ 0xce }; msg[3] = tag; send_ns_unitdata(nsi, "FLOW_CONTROL_BVC_ACK", src_addr, bvci, msg, sizeof(msg)); } static void send_llc_ul_ui( struct gprs_ns_inst *nsi, const char *text, struct sockaddr_in *src_addr, uint16_t nsbvci, uint32_t tlli, struct gprs_ra_id *raid, uint16_t cell_id, unsigned sapi, unsigned nu, const uint8_t *msg, size_t msg_size) { unsigned char llc_msg[4096] = { 0x00, 0xc0, 0x01 }; size_t llc_msg_size = 3 + msg_size + 3; uint8_t e_bit = 0; uint8_t pm_bit = 1; unsigned fcs; nu &= 0x01ff; OSMO_ASSERT(llc_msg_size <= sizeof(llc_msg)); llc_msg[0] = (sapi & 0x0f); llc_msg[1] = 0xc0 | (nu >> 6); /* UI frame */ llc_msg[2] = (nu << 2) | ((e_bit & 1) << 1) | (pm_bit & 1); memcpy(llc_msg + 3, msg, msg_size); fcs = gprs_llc_fcs(llc_msg, msg_size + 3); llc_msg[3 + msg_size + 0] = (uint8_t)(fcs >> 0); llc_msg[3 + msg_size + 1] = (uint8_t)(fcs >> 8); llc_msg[3 + msg_size + 2] = (uint8_t)(fcs >> 16); send_bssgp_ul_unitdata(nsi, text ? text : "LLC UI", src_addr, nsbvci, tlli, raid, cell_id, llc_msg, llc_msg_size); } static void send_llc_dl_ui( struct gprs_ns_inst *nsi, const char *text, struct sockaddr_in *src_addr, uint16_t nsbvci, uint32_t tlli, int with_racap_drx, const uint8_t *imsi, size_t imsi_size, unsigned sapi, unsigned nu, const uint8_t *msg, size_t msg_size) { /* GPRS Network Service, PDU type: NS_UNITDATA */ /* Base Station Subsystem GPRS Protocol: UL_UNITDATA */ unsigned char llc_msg[4096] = { 0x00, 0x00, 0x01 }; size_t llc_msg_size = 3 + msg_size + 3; uint8_t e_bit = 0; uint8_t pm_bit = 1; unsigned fcs; nu &= 0x01ff; OSMO_ASSERT(llc_msg_size <= sizeof(llc_msg)); llc_msg[0] = 0x40 | (sapi & 0x0f); llc_msg[1] = 0xc0 | (nu >> 6); /* UI frame */ llc_msg[2] = (nu << 2) | ((e_bit & 1) << 1) | (pm_bit & 1); memcpy(llc_msg + 3, msg, msg_size); fcs = gprs_llc_fcs(llc_msg, msg_size + 3); llc_msg[3 + msg_size + 0] = (uint8_t)(fcs >> 0); llc_msg[3 + msg_size + 1] = (uint8_t)(fcs >> 8); llc_msg[3 + msg_size + 2] = (uint8_t)(fcs >> 16); send_bssgp_dl_unitdata(nsi, text ? text : "LLC UI", src_addr, nsbvci, tlli, with_racap_drx, imsi, imsi_size, llc_msg, llc_msg_size); } static void setup_ns(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr, uint16_t nsvci, uint16_t nsei) { printf("Setup NS-VC: remote 0x%08x:%d, " "NSVCI 0x%04x(%d), NSEI 0x%04x(%d)\n\n", ntohl(src_addr->sin_addr.s_addr), ntohs(src_addr->sin_port), nsvci, nsvci, nsei, nsei); send_ns_reset(nsi, src_addr, NS_CAUSE_OM_INTERVENTION, nsvci, nsei); send_ns_alive(nsi, src_addr); send_ns_unblock(nsi, src_addr); send_ns_alive_ack(nsi, src_addr); } static void setup_bssgp(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr, uint16_t bvci) { printf("Setup BSSGP: remote 0x%08x:%d, " "BVCI 0x%04x(%d)\n\n", ntohl(src_addr->sin_addr.s_addr), ntohs(src_addr->sin_port), bvci, bvci); send_bssgp_reset(nsi, src_addr, bvci); } static void connect_sgsn(struct gprs_ns_inst *nsi, struct sockaddr_in *sgsn_peer, uint32_t sgsn_nsei) { gprs_ns_nsip_connect(nsi, sgsn_peer, sgsn_nsei, sgsn_nsei+1); send_ns_reset_ack(nsi, sgsn_peer, sgsn_nsei+1, sgsn_nsei); send_ns_alive_ack(nsi, sgsn_peer); send_ns_unblock_ack(nsi, sgsn_peer); send_ns_alive(nsi, sgsn_peer); } static void configure_sgsn_peer(struct sockaddr_in *sgsn_peer) { sgsn_peer->sin_family = AF_INET; sgsn_peer->sin_port = htons(32000); sgsn_peer->sin_addr.s_addr = htonl(REMOTE_SGSN_ADDR); } static void configure_sgsn2_peer(struct sockaddr_in *sgsn_peer) { sgsn_peer->sin_family = AF_INET; sgsn_peer->sin_port = htons(32001); sgsn_peer->sin_addr.s_addr = htonl(REMOTE_SGSN2_ADDR); } static void configure_bss_peers(struct sockaddr_in *bss_peers, size_t size) { size_t i; for (i = 0; i < size; ++i) { bss_peers[i].sin_family = AF_INET; bss_peers[i].sin_port = htons((i + 1) * 1111); bss_peers[i].sin_addr.s_addr = htonl(REMOTE_BSS_ADDR); } } int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg, struct sockaddr_in *saddr, enum gprs_ns_ll ll); /* override */ int gprs_ns_callback(enum gprs_ns_evt event, struct gprs_nsvc *nsvc, struct msgb *msg, uint16_t bvci) { printf("CALLBACK, event %d, msg length %zu, bvci 0x%04x\n%s\n\n", event, msgb_bssgp_len(msg), bvci, osmo_hexdump(msgb_l2(msg), msgb_l2len(msg))); switch (event) { case GPRS_NS_EVT_UNIT_DATA: return gbprox_rcvmsg(&gbcfg, msg, nsvc->nsei, bvci, nsvc->nsvci); default: break; } return 0; } /* override */ ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen) { typedef ssize_t (*sendto_t)(int, const void *, size_t, int, const struct sockaddr *, socklen_t); static sendto_t real_sendto = NULL; uint32_t dest_host = htonl(((struct sockaddr_in *)dest_addr)->sin_addr.s_addr); int dest_port = htons(((struct sockaddr_in *)dest_addr)->sin_port); if (!real_sendto) real_sendto = dlsym(RTLD_NEXT, "sendto"); if (dest_host == REMOTE_BSS_ADDR) printf("MESSAGE to BSS at 0x%08x:%d, msg length %zu\n%s\n\n", dest_host, dest_port, len, osmo_hexdump(buf, len)); else if (dest_host == REMOTE_SGSN_ADDR) printf("MESSAGE to SGSN at 0x%08x:%d, msg length %zu\n%s\n\n", dest_host, dest_port, len, osmo_hexdump(buf, len)); else if (dest_host == REMOTE_SGSN2_ADDR) printf("MESSAGE to SGSN 2 at 0x%08x:%d, msg length %zu\n%s\n\n", dest_host, dest_port, len, osmo_hexdump(buf, len)); else return real_sendto(sockfd, buf, len, flags, dest_addr, addrlen); return len; } /* override */ int gprs_ns_sendmsg(struct gprs_ns_inst *nsi, struct msgb *msg) { typedef int (*gprs_ns_sendmsg_t)(struct gprs_ns_inst *nsi, struct msgb *msg); static gprs_ns_sendmsg_t real_gprs_ns_sendmsg = NULL; uint16_t bvci = msgb_bvci(msg); uint16_t nsei = msgb_nsei(msg); size_t len = msgb_length(msg); if (!real_gprs_ns_sendmsg) real_gprs_ns_sendmsg = dlsym(RTLD_NEXT, "gprs_ns_sendmsg"); if (nsei == SGSN_NSEI) printf("NS UNITDATA MESSAGE to SGSN, BVCI 0x%04x, " "msg length %zu (%s)\n", bvci, len, __func__); else if (nsei == SGSN2_NSEI) printf("NS UNITDATA MESSAGE to SGSN 2, BVCI 0x%04x, " "msg length %zu (%s)\n", bvci, len, __func__); else printf("NS UNITDATA MESSAGE to BSS, BVCI 0x%04x, " "msg length %zu (%s)\n", bvci, len, __func__); if (received_messages) { struct msgb *msg_copy; msg_copy = gprs_msgb_copy(msg, "received_messages"); llist_add_tail(&msg_copy->list, received_messages); } return real_gprs_ns_sendmsg(nsi, msg); } /* Get the next message from the receive FIFO * * \returns a pointer to the message which will be invalidated at the next call * to expect_msg. Returns NULL, if there is no message left. */ static struct msgb *expect_msg(void) { static struct msgb *msg = NULL; msgb_free(msg); msg = NULL; if (!received_messages) return NULL; if (llist_empty(received_messages)) return NULL; msg = llist_entry(received_messages->next, struct msgb, list); llist_del(&msg->list); return msg; } struct expect_result { struct msgb *msg; struct gprs_gb_parse_context parse_ctx; }; static struct expect_result *expect_bssgp_msg( int match_nsei, int match_bvci, int match_pdu_type) { static struct expect_result result; static const struct expect_result empty_result = {0,}; static struct msgb *msg; uint16_t nsei; int rc; memcpy(&result, &empty_result, sizeof(result)); msg = expect_msg(); if (!msg) return NULL; nsei = msgb_nsei(msg); if (match_nsei != MATCH_ANY && match_nsei != nsei) { fprintf(stderr, "%s: NSEI mismatch (expected %u, got %u)\n", __func__, match_nsei, nsei); return NULL; } if (match_bvci != MATCH_ANY && match_bvci != msgb_bvci(msg)) { fprintf(stderr, "%s: BVCI mismatch (expected %u, got %u)\n", __func__, match_bvci, msgb_bvci(msg)); return NULL; } result.msg = msg; result.parse_ctx.to_bss = nsei != SGSN_NSEI && nsei != SGSN2_NSEI; result.parse_ctx.peer_nsei = nsei; if (!msgb_bssgph(msg)) { fprintf(stderr, "%s: Expected BSSGP\n", __func__); return NULL; } rc = gprs_gb_parse_bssgp(msgb_bssgph(msg), msgb_bssgp_len(msg), &result.parse_ctx); if (!rc) { fprintf(stderr, "%s: Failed to parse message\n", __func__); return NULL; } if (match_pdu_type != MATCH_ANY && match_pdu_type != result.parse_ctx.pdu_type) { fprintf(stderr, "%s: PDU type mismatch (expected %u, got %u)\n", __func__, match_pdu_type, result.parse_ctx.pdu_type); return NULL; } return &result; } static struct expect_result *expect_llc_msg( int match_nsei, int match_bvci, int match_sapi, int match_type) { static struct expect_result *result; result = expect_bssgp_msg(match_nsei, match_bvci, MATCH_ANY); if (!result) return NULL; if (!result->parse_ctx.llc) { fprintf(stderr, "%s: Expected LLC message\n", __func__); return NULL; } if (match_sapi != MATCH_ANY && match_sapi != result->parse_ctx.llc_hdr_parsed.sapi) { fprintf(stderr, "%s: LLC SAPI mismatch (expected %u, got %u)\n", __func__, match_sapi, result->parse_ctx.llc_hdr_parsed.sapi); return NULL; } if (match_type != MATCH_ANY && match_type != result->parse_ctx.llc_hdr_parsed.cmd) { fprintf(stderr, "%s: LLC command/type mismatch (expected %u, got %u)\n", __func__, match_type, result->parse_ctx.llc_hdr_parsed.cmd); return NULL; } return result; } static struct expect_result *expect_gmm_msg(int match_nsei, int match_bvci, int match_type) { static struct expect_result *result; result = expect_llc_msg(match_nsei, match_bvci, GPRS_SAPI_GMM, GPRS_LLC_UI); if (!result) return NULL; if (!result->parse_ctx.g48_hdr) { fprintf(stderr, "%s: Expected GSM 04.08 message\n", __func__); return NULL; } if (match_type != MATCH_ANY && match_type != result->parse_ctx.g48_hdr->msg_type) { fprintf(stderr, "%s: GSM 04.08 message type mismatch (expected %u, got %u)\n", __func__, match_type, result->parse_ctx.g48_hdr->msg_type); return NULL; } return result; } static void dump_rate_ctr_group(FILE *stream, const char *prefix, struct rate_ctr_group *ctrg) { unsigned int i; for (i = 0; i < ctrg->desc->num_ctr; i++) { struct rate_ctr *ctr = &ctrg->ctr[i]; if (ctr->current && !strchr(ctrg->desc->ctr_desc[i].name, '.')) fprintf(stream, " %s%s: %llu%s", prefix, ctrg->desc->ctr_desc[i].description, (long long)ctr->current, "\n"); }; } /* Signal handler for signals from NS layer */ static int test_signal(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct ns_signal_data *nssd = signal_data; int rc; if (subsys != SS_L_NS) return 0; switch (signal) { case S_NS_RESET: printf("==> got signal NS_RESET, NS-VC 0x%04x/%s\n", nssd->nsvc->nsvci, gprs_ns_ll_str(nssd->nsvc)); break; case S_NS_ALIVE_EXP: printf("==> got signal NS_ALIVE_EXP, NS-VC 0x%04x/%s\n", nssd->nsvc->nsvci, gprs_ns_ll_str(nssd->nsvc)); break; case S_NS_BLOCK: printf("==> got signal NS_BLOCK, NS-VC 0x%04x/%s\n", nssd->nsvc->nsvci, gprs_ns_ll_str(nssd->nsvc)); break; case S_NS_UNBLOCK: printf("==> got signal NS_UNBLOCK, NS-VC 0x%04x/%s\n", nssd->nsvc->nsvci, gprs_ns_ll_str(nssd->nsvc)); break; case S_NS_REPLACED: printf("==> got signal NS_REPLACED: 0x%04x/%s", nssd->nsvc->nsvci, gprs_ns_ll_str(nssd->nsvc)); printf(" -> 0x%04x/%s\n", nssd->old_nsvc->nsvci, gprs_ns_ll_str(nssd->old_nsvc)); break; default: printf("==> got signal %d, NS-VC 0x%04x/%s\n", signal, nssd->nsvc->nsvci, gprs_ns_ll_str(nssd->nsvc)); break; } printf("\n"); rc = gbprox_signal(subsys, signal, handler_data, signal_data); return rc; } static int gprs_process_message(struct gprs_ns_inst *nsi, const char *text, struct sockaddr_in *peer, const unsigned char* data, size_t data_len) { struct msgb *msg; int ret; if (data_len > NS_ALLOC_SIZE - NS_ALLOC_HEADROOM) { fprintf(stderr, "message too long: %zu\n", data_len); return -1; } msg = gprs_ns_msgb_alloc(); memmove(msg->data, data, data_len); msg->l2h = msg->data; msgb_put(msg, data_len); printf("PROCESSING %s from 0x%08x:%d\n%s\n\n", text, ntohl(peer->sin_addr.s_addr), ntohs(peer->sin_port), osmo_hexdump(data, data_len)); ret = gprs_ns_rcvmsg(nsi, msg, peer, GPRS_NS_LL_UDP); printf("result (%s) = %d\n\n", text, ret); msgb_free(msg); return ret; } static void gprs_dump_nsi(struct gprs_ns_inst *nsi) { struct gprs_nsvc *nsvc; printf("Current NS-VCIs:\n"); llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) { struct sockaddr_in *peer = &(nsvc->ip.bts_addr); printf(" VCI 0x%04x, NSEI 0x%04x, peer 0x%08x:%d%s%s\n", nsvc->nsvci, nsvc->nsei, ntohl(peer->sin_addr.s_addr), ntohs(peer->sin_port), nsvc->state & NSE_S_BLOCKED ? ", blocked" : "", nsvc->state & NSE_S_ALIVE ? "" : ", dead" ); dump_rate_ctr_group(stdout, " ", nsvc->ctrg); } printf("\n"); } static void test_gbproxy() { struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, NULL); struct sockaddr_in bss_peer[4] = {{0},}; struct sockaddr_in sgsn_peer= {0}; bssgp_nsi = nsi; gbcfg.nsi = bssgp_nsi; gbcfg.nsip_sgsn_nsei = SGSN_NSEI; configure_sgsn_peer(&sgsn_peer); configure_bss_peers(bss_peer, ARRAY_SIZE(bss_peer)); printf("=== %s ===\n", __func__); printf("--- Initialise SGSN ---\n\n"); connect_sgsn(nsi, &sgsn_peer, SGSN_NSEI); gprs_dump_nsi(nsi); printf("--- Initialise BSS 1 ---\n\n"); setup_ns(nsi, &bss_peer[0], 0x1001, 0x1000); setup_bssgp(nsi, &bss_peer[0], 0x1002); gprs_dump_nsi(nsi); dump_peers(stdout, 0, 0, &gbcfg); send_bssgp_reset_ack(nsi, &sgsn_peer, 0x1002); printf("--- Initialise BSS 2 ---\n\n"); setup_ns(nsi, &bss_peer[1], 0x2001, 0x2000); setup_bssgp(nsi, &bss_peer[1], 0x2002); gprs_dump_nsi(nsi); dump_peers(stdout, 0, 0, &gbcfg); send_bssgp_reset_ack(nsi, &sgsn_peer, 0x2002); printf("--- Move BSS 1 to new port ---\n\n"); setup_ns(nsi, &bss_peer[2], 0x1001, 0x1000); gprs_dump_nsi(nsi); dump_peers(stdout, 0, 0, &gbcfg); printf("--- Move BSS 2 to former BSS 1 port ---\n\n"); setup_ns(nsi, &bss_peer[0], 0x2001, 0x2000); gprs_dump_nsi(nsi); dump_peers(stdout, 0, 0, &gbcfg); printf("--- Move BSS 1 to current BSS 2 port ---\n\n"); setup_ns(nsi, &bss_peer[0], 0x2001, 0x2000); gprs_dump_nsi(nsi); dump_peers(stdout, 0, 0, &gbcfg); printf("--- Move BSS 2 to new port ---\n\n"); setup_ns(nsi, &bss_peer[3], 0x2001, 0x2000); gprs_dump_nsi(nsi); dump_peers(stdout, 0, 0, &gbcfg); printf("--- Move BSS 2 to former BSS 1 port ---\n\n"); setup_ns(nsi, &bss_peer[2], 0x2001, 0x2000); gprs_dump_nsi(nsi); dump_peers(stdout, 0, 0, &gbcfg); printf("--- Move BSS 1 to original BSS 1 port ---\n\n"); setup_ns(nsi, &bss_peer[0], 0x1001, 0x1000); gprs_dump_nsi(nsi); dump_peers(stdout, 0, 0, &gbcfg); printf("--- Reset BSS 1 with a new BVCI ---\n\n"); setup_bssgp(nsi, &bss_peer[0], 0x1012); gprs_dump_nsi(nsi); dump_peers(stdout, 0, 0, &gbcfg); send_bssgp_reset_ack(nsi, &sgsn_peer, 0x1012); printf("--- Reset BSS 1 with the old BVCI ---\n\n"); setup_bssgp(nsi, &bss_peer[0], 0x1002); gprs_dump_nsi(nsi); dump_peers(stdout, 0, 0, &gbcfg); send_bssgp_reset_ack(nsi, &sgsn_peer, 0x1002); printf("--- Reset BSS 1 with the old BVCI again ---\n\n"); setup_bssgp(nsi, &bss_peer[0], 0x1002); gprs_dump_nsi(nsi); dump_peers(stdout, 0, 0, &gbcfg); send_bssgp_reset_ack(nsi, &sgsn_peer, 0x1002); printf("--- Send message from BSS 1 to SGSN, BVCI 0x1012 ---\n\n"); send_ns_unitdata(nsi, NULL, &bss_peer[0], 0x1012, (uint8_t *)"", 0); printf("--- Send message from SGSN to BSS 1, BVCI 0x1012 ---\n\n"); send_ns_unitdata(nsi, NULL, &sgsn_peer, 0x1012, (uint8_t *)"", 0); printf("--- Send message from BSS 1 to SGSN, BVCI 0x1002 ---\n\n"); send_ns_unitdata(nsi, NULL, &bss_peer[0], 0x1012, (uint8_t *)"", 0); printf("--- Send message from SGSN to BSS 1, BVCI 0x1002 ---\n\n"); send_ns_unitdata(nsi, NULL, &sgsn_peer, 0x1012, (uint8_t *)"", 0); printf("--- Send message from BSS 2 to SGSN, BVCI 0x2002 ---\n\n"); send_ns_unitdata(nsi, NULL, &bss_peer[0], 0x2002, (uint8_t *)"", 0); printf("--- Send message from SGSN to BSS 2, BVCI 0x2002 ---\n\n"); send_ns_unitdata(nsi, NULL, &sgsn_peer, 0x2002, (uint8_t *)"", 0); printf("--- Reset BSS 1 with the old BVCI on BSS2's link ---\n\n"); setup_bssgp(nsi, &bss_peer[2], 0x1002); gprs_dump_nsi(nsi); dump_peers(stdout, 0, 0, &gbcfg); dump_global(stdout, 0); send_bssgp_reset_ack(nsi, &sgsn_peer, 0x1002); printf("--- Send message from BSS 1 to SGSN, BVCI 0x1002 ---\n\n"); send_ns_unitdata(nsi, NULL, &bss_peer[0], 0x1012, (uint8_t *)"", 0); printf("--- Send message from SGSN to BSS 1, BVCI 0x1002 ---\n\n"); send_ns_unitdata(nsi, NULL, &sgsn_peer, 0x1012, (uint8_t *)"", 0); printf("--- Send message from SGSN to BSS 1, BVCI 0x10ff (invalid) ---\n\n"); send_ns_unitdata(nsi, NULL, &sgsn_peer, 0x10ff, (uint8_t *)"", 0); /* Find peer */ OSMO_ASSERT(gbproxy_peer_by_bvci(&gbcfg, 0xeeee) == NULL); OSMO_ASSERT(gbproxy_peer_by_bvci(&gbcfg, 0x1000) == NULL); OSMO_ASSERT(gbproxy_peer_by_bvci(&gbcfg, 0x1012) != NULL); OSMO_ASSERT(gbproxy_peer_by_nsei(&gbcfg, 0xeeee) == NULL); OSMO_ASSERT(gbproxy_peer_by_nsei(&gbcfg, 0x1012) == NULL); OSMO_ASSERT(gbproxy_peer_by_nsei(&gbcfg, 0x1000) != NULL); /* Cleanup */ OSMO_ASSERT(gbproxy_cleanup_peers(&gbcfg, 0, 0) == 0); OSMO_ASSERT(gbproxy_cleanup_peers(&gbcfg, 0x1000, 0xeeee) == 0); OSMO_ASSERT(gbproxy_cleanup_peers(&gbcfg, 0, 0x1002) == 0); OSMO_ASSERT(gbproxy_cleanup_peers(&gbcfg, 0x1000, 0x1012) == 1); OSMO_ASSERT(gbproxy_cleanup_peers(&gbcfg, 0x1000, 0x1012) == 0); dump_peers(stdout, 0, 0, &gbcfg); dump_global(stdout, 0); gbprox_reset(&gbcfg); gprs_ns_destroy(nsi); nsi = NULL; } static void test_gbproxy_ident_changes() { struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, NULL); struct sockaddr_in bss_peer[1] = {{0},}; struct sockaddr_in sgsn_peer= {0}; uint16_t nsei[2] = {0x1000, 0x2000}; uint16_t nsvci[2] = {0x1001, 0x2001}; uint16_t bvci[4] = {0x1002, 0x2002, 0x3002, 0x4002}; bssgp_nsi = nsi; gbcfg.nsi = bssgp_nsi; gbcfg.nsip_sgsn_nsei = SGSN_NSEI; configure_sgsn_peer(&sgsn_peer); configure_bss_peers(bss_peer, ARRAY_SIZE(bss_peer)); printf("=== %s ===\n", __func__); printf("--- Initialise SGSN ---\n\n"); connect_sgsn(nsi, &sgsn_peer, SGSN_NSEI); gprs_dump_nsi(nsi); printf("--- Initialise BSS 1 ---\n\n"); setup_ns(nsi, &bss_peer[0], nsvci[0], nsei[0]); gprs_dump_nsi(nsi); printf("--- Setup BVCI 1 ---\n\n"); setup_bssgp(nsi, &bss_peer[0], bvci[0]); send_bssgp_reset_ack(nsi, &sgsn_peer, bvci[0]); dump_peers(stdout, 0, 0, &gbcfg); printf("--- Setup BVCI 2 ---\n\n"); setup_bssgp(nsi, &bss_peer[0], bvci[1]); send_bssgp_reset_ack(nsi, &sgsn_peer, bvci[1]); dump_peers(stdout, 0, 0, &gbcfg); printf("--- Send message from BSS 1 to SGSN and back, BVCI 1 ---\n\n"); send_ns_unitdata(nsi, NULL, &bss_peer[0], bvci[0], (uint8_t *)"", 0); send_ns_unitdata(nsi, NULL, &sgsn_peer, bvci[0], (uint8_t *)"", 0); printf("--- Send message from BSS 1 to SGSN and back, BVCI 2 ---\n\n"); send_ns_unitdata(nsi, NULL, &bss_peer[0], bvci[1], (uint8_t *)"", 0); send_ns_unitdata(nsi, NULL, &sgsn_peer, bvci[1], (uint8_t *)"", 0); printf("--- Change NSEI ---\n\n"); setup_ns(nsi, &bss_peer[0], nsvci[0], nsei[1]); gprs_dump_nsi(nsi); printf("--- Setup BVCI 1 ---\n\n"); setup_bssgp(nsi, &bss_peer[0], bvci[0]); send_bssgp_reset_ack(nsi, &sgsn_peer, bvci[0]); dump_peers(stdout, 0, 0, &gbcfg); printf("--- Setup BVCI 3 ---\n\n"); setup_bssgp(nsi, &bss_peer[0], bvci[2]); send_bssgp_reset_ack(nsi, &sgsn_peer, bvci[2]); dump_peers(stdout, 0, 0, &gbcfg); printf("--- Send message from BSS 1 to SGSN and back, BVCI 1 ---\n\n"); send_ns_unitdata(nsi, NULL, &bss_peer[0], bvci[0], (uint8_t *)"", 0); send_ns_unitdata(nsi, NULL, &sgsn_peer, bvci[0], (uint8_t *)"", 0); printf("--- Send message from BSS 1 to SGSN and back, BVCI 2 " " (should fail) ---\n\n"); send_ns_unitdata(nsi, NULL, &bss_peer[0], bvci[1], (uint8_t *)"", 0); dump_peers(stdout, 0, 0, &gbcfg); send_ns_unitdata(nsi, NULL, &sgsn_peer, bvci[1], (uint8_t *)"", 0); dump_peers(stdout, 0, 0, &gbcfg); printf("--- Send message from BSS 1 to SGSN and back, BVCI 3 ---\n\n"); send_ns_unitdata(nsi, NULL, &bss_peer[0], bvci[2], (uint8_t *)"", 0); send_ns_unitdata(nsi, NULL, &sgsn_peer, bvci[2], (uint8_t *)"", 0); printf("--- Change NSVCI ---\n\n"); setup_ns(nsi, &bss_peer[0], nsvci[1], nsei[1]); gprs_dump_nsi(nsi); printf("--- Setup BVCI 1 ---\n\n"); setup_bssgp(nsi, &bss_peer[0], bvci[0]); send_bssgp_reset_ack(nsi, &sgsn_peer, bvci[0]); dump_peers(stdout, 0, 0, &gbcfg); printf("--- Setup BVCI 4 ---\n\n"); setup_bssgp(nsi, &bss_peer[0], bvci[3]); send_bssgp_reset_ack(nsi, &sgsn_peer, bvci[3]); dump_peers(stdout, 0, 0, &gbcfg); printf("--- Send message from BSS 1 to SGSN and back, BVCI 1 ---\n\n"); send_ns_unitdata(nsi, NULL, &bss_peer[0], bvci[0], (uint8_t *)"", 0); send_ns_unitdata(nsi, NULL, &sgsn_peer, bvci[0], (uint8_t *)"", 0); printf("--- Send message from BSS 1 to SGSN and back, BVCI 2 " " (should fail) ---\n\n"); send_ns_unitdata(nsi, NULL, &bss_peer[0], bvci[1], (uint8_t *)"", 0); dump_peers(stdout, 0, 0, &gbcfg); send_ns_unitdata(nsi, NULL, &sgsn_peer, bvci[1], (uint8_t *)"", 0); dump_peers(stdout, 0, 0, &gbcfg); printf("--- Send message from BSS 1 to SGSN and back, BVCI 3 ---\n\n"); send_ns_unitdata(nsi, NULL, &bss_peer[0], bvci[2], (uint8_t *)"", 0); send_ns_unitdata(nsi, NULL, &sgsn_peer, bvci[2], (uint8_t *)"", 0); printf("--- Send message from BSS 1 to SGSN and back, BVCI 4 ---\n\n"); send_ns_unitdata(nsi, NULL, &bss_peer[0], bvci[3], (uint8_t *)"", 0); send_ns_unitdata(nsi, NULL, &sgsn_peer, bvci[3], (uint8_t *)"", 0); dump_global(stdout, 0); dump_peers(stdout, 0, 0, &gbcfg); gbprox_reset(&gbcfg); gprs_ns_destroy(nsi); nsi = NULL; } static void test_gbproxy_ra_patching() { struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, NULL); struct sockaddr_in bss_peer[1] = {{0},}; struct sockaddr_in sgsn_peer= {0}; struct gprs_ra_id rai_bss = {.mcc = 112, .mnc = 332, .lac = 16464, .rac = 96}; struct gprs_ra_id rai_sgsn = {.mcc = 123, .mnc = 456, .lac = 16464, .rac = 96}; struct gprs_ra_id rai_unknown = {.mcc = 1, .mnc = 99, .lac = 99, .rac = 96}; uint16_t cell_id = 0x7530; const char *err_msg = NULL; const uint32_t ptmsi = 0xefe2b700; const uint32_t local_tlli = 0xefe2b700; const uint32_t foreign_tlli = 0xbbc54679; const uint32_t foreign_tlli2 = 0xbb00beef; const uint8_t imsi[] = {0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18}; const char *patch_re = "^9898|^121314"; struct gbproxy_link_info *link_info; struct gbproxy_peer *peer; LLIST_HEAD(rcv_list); struct expect_result *expect_res; OSMO_ASSERT(local_tlli == gprs_tmsi2tlli(ptmsi, TLLI_LOCAL)); bssgp_nsi = nsi; gbcfg.nsi = bssgp_nsi; gbcfg.nsip_sgsn_nsei = SGSN_NSEI; gbcfg.core_mcc = 123; gbcfg.core_mnc = 456; gbcfg.core_apn = talloc_zero_size(NULL, 100); gbcfg.core_apn_size = gprs_str_to_apn(gbcfg.core_apn, 100, "foo.bar"); gbcfg.patch_ptmsi = 0; configure_sgsn_peer(&sgsn_peer); configure_bss_peers(bss_peer, ARRAY_SIZE(bss_peer)); if (gbproxy_set_patch_filter(&gbcfg.matches[GBPROX_MATCH_PATCHING], patch_re, &err_msg) != 0) { fprintf(stderr, "Failed to compile RE '%s': %s\n", patch_re, err_msg); exit(1); } printf("=== %s ===\n", __func__); printf("--- Initialise SGSN ---\n\n"); connect_sgsn(nsi, &sgsn_peer, SGSN_NSEI); gprs_dump_nsi(nsi); printf("--- Initialise BSS 1 ---\n\n"); setup_ns(nsi, &bss_peer[0], 0x1001, 0x1000); received_messages = &rcv_list; setup_bssgp(nsi, &bss_peer[0], 0x1002); gprs_dump_nsi(nsi); dump_peers(stdout, 0, 0, &gbcfg); peer = gbproxy_peer_by_nsei(&gbcfg, 0x1000); OSMO_ASSERT(peer != NULL); OSMO_ASSERT(expect_bssgp_msg(SGSN_NSEI, 0, BSSGP_PDUT_BVC_RESET)); send_bssgp_reset_ack(nsi, &sgsn_peer, 0x1002); OSMO_ASSERT(expect_bssgp_msg(0x1000, 0, BSSGP_PDUT_BVC_RESET_ACK)); send_bssgp_suspend(nsi, &bss_peer[0], 0xccd1758b, &rai_bss); OSMO_ASSERT(expect_bssgp_msg(SGSN_NSEI, 0, BSSGP_PDUT_SUSPEND)); send_bssgp_suspend_ack(nsi, &sgsn_peer, 0xccd1758b, &rai_sgsn); OSMO_ASSERT(expect_bssgp_msg(0x1000, 0, BSSGP_PDUT_SUSPEND_ACK)); dump_global(stdout, 0); dump_peers(stdout, 0, 0, &gbcfg); OSMO_ASSERT(2 == peer->ctrg->ctr[GBPROX_PEER_CTR_RAID_PATCHED_BSS].current); OSMO_ASSERT(1 == peer->ctrg->ctr[GBPROX_PEER_CTR_RAID_PATCHED_SGSN].current); printf("--- Send message from BSS 1 to SGSN, BVCI 0x1002 ---\n\n"); send_llc_ul_ui(nsi, "ATTACH REQUEST", &bss_peer[0], 0x1002, foreign_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, 0, dtap_attach_req, sizeof(dtap_attach_req)); OSMO_ASSERT(4 == peer->ctrg->ctr[GBPROX_PEER_CTR_RAID_PATCHED_BSS].current); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_REQ)); send_llc_dl_ui(nsi, "IDENT REQUEST", &sgsn_peer, 0x1002, foreign_tlli, 0, NULL, 0, GPRS_SAPI_GMM, 0, dtap_identity_req, sizeof(dtap_identity_req)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_ID_REQ)); send_llc_ul_ui(nsi, "IDENT RESPONSE", &bss_peer[0], 0x1002, foreign_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, 3, dtap_identity_resp, sizeof(dtap_identity_resp)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ID_RESP)); OSMO_ASSERT(5 == peer->ctrg->ctr[GBPROX_PEER_CTR_RAID_PATCHED_BSS].current); OSMO_ASSERT(1 == peer->ctrg->ctr[GBPROX_PEER_CTR_RAID_PATCHED_SGSN].current); send_llc_dl_ui(nsi, "ATTACH ACCEPT", &sgsn_peer, 0x1002, foreign_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, 1, dtap_attach_acc, sizeof(dtap_attach_acc)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_ATTACH_ACK)); OSMO_ASSERT(2 == peer->ctrg->ctr[GBPROX_PEER_CTR_RAID_PATCHED_SGSN].current); OSMO_ASSERT(gbproxy_peer_by_rai(&gbcfg, convert_ra(&rai_bss)) != NULL); OSMO_ASSERT(gbproxy_peer_by_rai(&gbcfg, convert_ra(&rai_sgsn)) == NULL); OSMO_ASSERT(gbproxy_peer_by_rai(&gbcfg, convert_ra(&rai_unknown)) == NULL); OSMO_ASSERT(gbproxy_peer_by_lai(&gbcfg, convert_ra(&rai_bss)) != NULL); OSMO_ASSERT(gbproxy_peer_by_lai(&gbcfg, convert_ra(&rai_sgsn)) == NULL); OSMO_ASSERT(gbproxy_peer_by_lai(&gbcfg, convert_ra(&rai_unknown)) == NULL); OSMO_ASSERT(gbproxy_peer_by_lac(&gbcfg, convert_ra(&rai_bss)) != NULL); OSMO_ASSERT(gbproxy_peer_by_lac(&gbcfg, convert_ra(&rai_sgsn)) != NULL); OSMO_ASSERT(gbproxy_peer_by_lac(&gbcfg, convert_ra(&rai_unknown)) == NULL); link_info = gbproxy_link_info_by_sgsn_tlli(peer, local_tlli, SGSN_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.assigned == local_tlli); OSMO_ASSERT(link_info->tlli.current != local_tlli); OSMO_ASSERT(!link_info->tlli.bss_validated); OSMO_ASSERT(!link_info->tlli.net_validated); OSMO_ASSERT(link_info->sgsn_tlli.assigned == local_tlli); OSMO_ASSERT(link_info->sgsn_tlli.current != local_tlli); OSMO_ASSERT(!link_info->sgsn_tlli.bss_validated); OSMO_ASSERT(!link_info->sgsn_tlli.net_validated); send_llc_ul_ui(nsi, "ATTACH COMPLETE", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, 4, dtap_attach_complete, sizeof(dtap_attach_complete)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_COMPL)); OSMO_ASSERT(6 == peer->ctrg->ctr[GBPROX_PEER_CTR_RAID_PATCHED_BSS].current); link_info = gbproxy_link_info_by_sgsn_tlli(peer, local_tlli, SGSN_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.assigned == local_tlli); OSMO_ASSERT(link_info->tlli.current != local_tlli); OSMO_ASSERT(link_info->tlli.bss_validated); OSMO_ASSERT(!link_info->tlli.net_validated); OSMO_ASSERT(link_info->sgsn_tlli.assigned == local_tlli); OSMO_ASSERT(link_info->sgsn_tlli.current != local_tlli); OSMO_ASSERT(link_info->sgsn_tlli.bss_validated); OSMO_ASSERT(!link_info->sgsn_tlli.net_validated); /* Replace APN (1) */ send_llc_ul_ui(nsi, "ACT PDP CTX REQ (REPLACE APN)", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, 3, dtap_act_pdp_ctx_req, sizeof(dtap_act_pdp_ctx_req)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GSM_ACT_PDP_REQ)); OSMO_ASSERT(7 == peer->ctrg->ctr[GBPROX_PEER_CTR_RAID_PATCHED_BSS].current); link_info = gbproxy_link_info_by_sgsn_tlli(peer, local_tlli, SGSN_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.assigned == local_tlli); OSMO_ASSERT(link_info->tlli.current != local_tlli); OSMO_ASSERT(link_info->tlli.bss_validated); OSMO_ASSERT(!link_info->tlli.net_validated); OSMO_ASSERT(link_info->sgsn_tlli.assigned == local_tlli); OSMO_ASSERT(link_info->sgsn_tlli.current != local_tlli); OSMO_ASSERT(link_info->sgsn_tlli.bss_validated); OSMO_ASSERT(!link_info->sgsn_tlli.net_validated); send_llc_dl_ui(nsi, "GMM INFO", &sgsn_peer, 0x1002, local_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, 2, dtap_gmm_information, sizeof(dtap_gmm_information)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_INFO)); OSMO_ASSERT(2 == peer->ctrg->ctr[GBPROX_PEER_CTR_RAID_PATCHED_SGSN].current); link_info = gbproxy_link_info_by_sgsn_tlli(peer, local_tlli, SGSN_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.assigned == 0); OSMO_ASSERT(link_info->tlli.current == local_tlli); OSMO_ASSERT(link_info->sgsn_tlli.assigned == 0); OSMO_ASSERT(link_info->sgsn_tlli.current == local_tlli); /* Replace APN (2) */ send_llc_ul_ui(nsi, "ACT PDP CTX REQ (REPLACE APN)", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, 3, dtap_act_pdp_ctx_req, sizeof(dtap_act_pdp_ctx_req)); expect_res = expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GSM_ACT_PDP_REQ); OSMO_ASSERT(expect_res != NULL); OSMO_ASSERT(expect_res->parse_ctx.apn_ie_len == gbcfg.core_apn_size + 2); OSMO_ASSERT(8 == peer->ctrg->ctr[GBPROX_PEER_CTR_RAID_PATCHED_BSS].current); gbcfg.core_apn[0] = 0; gbcfg.core_apn_size = 0; /* Remove APN */ send_llc_ul_ui(nsi, "ACT PDP CTX REQ (REMOVE APN)", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, 3, dtap_act_pdp_ctx_req, sizeof(dtap_act_pdp_ctx_req)); expect_res = expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GSM_ACT_PDP_REQ); OSMO_ASSERT(expect_res != NULL); OSMO_ASSERT(expect_res->parse_ctx.apn_ie_len == 0); OSMO_ASSERT(9 == peer->ctrg->ctr[GBPROX_PEER_CTR_RAID_PATCHED_BSS].current); dump_peers(stdout, 0, 0, &gbcfg); /* Detach */ send_llc_ul_ui(nsi, "DETACH REQ", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, 6, dtap_detach_req, sizeof(dtap_detach_req)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_DETACH_REQ)); OSMO_ASSERT(10 == peer->ctrg->ctr[GBPROX_PEER_CTR_RAID_PATCHED_BSS].current); OSMO_ASSERT(2 == peer->ctrg->ctr[GBPROX_PEER_CTR_RAID_PATCHED_SGSN].current); send_llc_dl_ui(nsi, "DETACH ACC", &sgsn_peer, 0x1002, local_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, 5, dtap_detach_acc, sizeof(dtap_detach_acc)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_DETACH_ACK)); dump_peers(stdout, 0, 0, &gbcfg); printf("--- RA update ---\n\n"); send_llc_ul_ui(nsi, "RA UPD REQ", &bss_peer[0], 0x1002, foreign_tlli, &rai_bss, 0x7080, GPRS_SAPI_GMM, 5, dtap_ra_upd_req, sizeof(dtap_ra_upd_req)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_RA_UPD_REQ)); OSMO_ASSERT(12 == peer->ctrg->ctr[GBPROX_PEER_CTR_RAID_PATCHED_BSS].current); send_llc_dl_ui(nsi, "RA UPD ACC", &sgsn_peer, 0x1002, foreign_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, 6, dtap_ra_upd_acc, sizeof(dtap_ra_upd_acc)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_RA_UPD_ACK)); OSMO_ASSERT(3 == peer->ctrg->ctr[GBPROX_PEER_CTR_RAID_PATCHED_SGSN].current); /* Remove APN */ send_llc_ul_ui(nsi, "ACT PDP CTX REQ (REMOVE APN)", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, 3, dtap_act_pdp_ctx_req, sizeof(dtap_act_pdp_ctx_req)); expect_res = expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GSM_ACT_PDP_REQ); OSMO_ASSERT(expect_res != NULL); OSMO_ASSERT(expect_res->parse_ctx.apn_ie_len == 0); OSMO_ASSERT(13 == peer->ctrg->ctr[GBPROX_PEER_CTR_RAID_PATCHED_BSS].current); dump_peers(stdout, 0, 0, &gbcfg); /* Detach (power off -> no Detach Accept) */ send_llc_ul_ui(nsi, "DETACH REQ (PWR OFF)", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, 6, dtap_detach_po_req, sizeof(dtap_detach_po_req)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_DETACH_REQ)); OSMO_ASSERT(14 == peer->ctrg->ctr[GBPROX_PEER_CTR_RAID_PATCHED_BSS].current); dump_global(stdout, 0); dump_peers(stdout, 0, 0, &gbcfg); printf("--- Bad cases ---\n\n"); /* The RAI in the Attach Request message differs from the RAI in the * BSSGP message, only patch the latter */ send_llc_ul_ui(nsi, "ATTACH REQUEST (foreign RAI)", &bss_peer[0], 0x1002, foreign_tlli2, &rai_bss, cell_id, GPRS_SAPI_GMM, 0, dtap_attach_req2, sizeof(dtap_attach_req2)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_REQ)); OSMO_ASSERT(15 == peer->ctrg->ctr[GBPROX_PEER_CTR_RAID_PATCHED_BSS].current); printf("TLLI is already detached, shouldn't patch\n"); send_llc_ul_ui(nsi, "ACT PDP CTX REQ", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, 3, dtap_act_pdp_ctx_req, sizeof(dtap_act_pdp_ctx_req)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GSM_ACT_PDP_REQ)); printf("Invalid RAI, shouldn't patch\n"); send_bssgp_suspend_ack(nsi, &sgsn_peer, 0xccd1758b, &rai_unknown); /* TODO: The following breaks with the current libosmocore, enable it * again (and remove the plain expect_msg), when the msgb_bssgph patch * is integrated */ /* OSMO_ASSERT(expect_bssgp_msg(SGSN_NSEI, 0, BSSGP_PDUT_STATUS)); */ OSMO_ASSERT(expect_msg()); dump_global(stdout, 0); dump_peers(stdout, 0, 0, &gbcfg); OSMO_ASSERT(!expect_msg()); received_messages = NULL; gbproxy_clear_patch_filter(&gbcfg.matches[GBPROX_MATCH_PATCHING]); gbprox_reset(&gbcfg); gprs_ns_destroy(nsi); nsi = NULL; } static void test_gbproxy_ptmsi_assignment() { struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, NULL); struct sockaddr_in bss_peer[1] = {{0},}; struct sockaddr_in sgsn_peer= {0}; struct gprs_ra_id rai_bss = {.mcc = 112, .mnc = 332, .lac = 16464, .rac = 96}; struct gprs_ra_id rai_unknown = {.mcc = 1, .mnc = 99, .lac = 99, .rac = 96}; uint16_t cell_id = 0x1234; const uint32_t ptmsi = 0xefe2b700; const uint32_t local_tlli = 0xefe2b700; const uint32_t foreign_tlli1 = 0x8000dead; const uint32_t foreign_tlli2 = 0x8000beef; const uint8_t imsi1[] = {0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18}; const uint8_t imsi2[] = {0x11, 0x12, 0x99, 0x99, 0x99, 0x16, 0x17, 0x18}; struct gbproxy_link_info *link_info, *link_info2; struct gbproxy_peer *peer; unsigned bss_nu = 0; unsigned sgsn_nu = 0; OSMO_ASSERT(local_tlli == gprs_tmsi2tlli(ptmsi, TLLI_LOCAL)); bssgp_nsi = nsi; gbcfg.nsi = bssgp_nsi; gbcfg.nsip_sgsn_nsei = SGSN_NSEI; gbcfg.core_mcc = 0; gbcfg.core_mnc = 0; gbcfg.core_apn = talloc_zero_size(NULL, 100); gbcfg.core_apn_size = gprs_str_to_apn(gbcfg.core_apn, 100, "foo.bar"); gbcfg.patch_ptmsi = 0; gbcfg.bss_ptmsi_state = 0; gbcfg.sgsn_tlli_state = 1; configure_sgsn_peer(&sgsn_peer); configure_bss_peers(bss_peer, ARRAY_SIZE(bss_peer)); printf("=== %s ===\n", __func__); printf("--- Initialise SGSN ---\n\n"); connect_sgsn(nsi, &sgsn_peer, SGSN_NSEI); printf("--- Initialise BSS 1 ---\n\n"); setup_ns(nsi, &bss_peer[0], 0x1001, 0x1000); setup_bssgp(nsi, &bss_peer[0], 0x1002); peer = gbproxy_peer_by_nsei(&gbcfg, 0x1000); OSMO_ASSERT(peer != NULL); send_bssgp_reset_ack(nsi, &sgsn_peer, 0x1002); gprs_dump_nsi(nsi); dump_global(stdout, 0); dump_peers(stdout, 0, 0, &gbcfg); printf("--- Establish first LLC connection ---\n\n"); send_llc_ul_ui(nsi, "ATTACH REQUEST", &bss_peer[0], 0x1002, foreign_tlli1, &rai_unknown, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_req, sizeof(dtap_attach_req)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "IDENT REQUEST", &sgsn_peer, 0x1002, foreign_tlli1, 0, NULL, 0, GPRS_SAPI_GMM, sgsn_nu++, dtap_identity_req, sizeof(dtap_identity_req)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "IDENT RESPONSE", &bss_peer[0], 0x1002, foreign_tlli1, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_identity_resp, sizeof(dtap_identity_resp)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "ATTACH ACCEPT", &sgsn_peer, 0x1002, foreign_tlli1, 1, imsi1, sizeof(imsi1), GPRS_SAPI_GMM, sgsn_nu++, dtap_attach_acc, sizeof(dtap_attach_acc)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_tlli(peer, foreign_tlli1); link_info2 = gbproxy_link_info_by_tlli(peer, local_tlli); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info == link_info2); OSMO_ASSERT(link_info->tlli.assigned == local_tlli); OSMO_ASSERT(link_info->tlli.current == foreign_tlli1); OSMO_ASSERT(!link_info->tlli.bss_validated); OSMO_ASSERT(!link_info->tlli.net_validated); OSMO_ASSERT(link_info->tlli.ptmsi == ptmsi); send_llc_ul_ui(nsi, "ATTACH COMPLETE", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_complete, sizeof(dtap_attach_complete)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_tlli(peer, local_tlli); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.assigned == local_tlli); OSMO_ASSERT(link_info->tlli.current == foreign_tlli1); OSMO_ASSERT(link_info->tlli.bss_validated); OSMO_ASSERT(!link_info->tlli.net_validated); OSMO_ASSERT(link_info->tlli.ptmsi == ptmsi); send_llc_dl_ui(nsi, "GMM INFO", &sgsn_peer, 0x1002, local_tlli, 1, imsi1, sizeof(imsi1), GPRS_SAPI_GMM, sgsn_nu++, dtap_gmm_information, sizeof(dtap_gmm_information)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_imsi(peer, imsi1, ARRAY_SIZE(imsi1)); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.ptmsi == ptmsi); OSMO_ASSERT(!gbproxy_link_info_by_imsi(peer, imsi2, ARRAY_SIZE(imsi2))); link_info2 = gbproxy_link_info_by_tlli(peer, local_tlli); OSMO_ASSERT(link_info == link_info2); OSMO_ASSERT(link_info->tlli.assigned == 0); OSMO_ASSERT(link_info->tlli.current == local_tlli); OSMO_ASSERT(link_info->tlli.ptmsi == ptmsi); printf("--- Establish second LLC connection with the same P-TMSI ---\n\n"); send_llc_ul_ui(nsi, "ATTACH REQUEST", &bss_peer[0], 0x1002, foreign_tlli2, &rai_unknown, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_req, sizeof(dtap_attach_req)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "IDENT REQUEST", &sgsn_peer, 0x1002, foreign_tlli2, 0, NULL, 0, GPRS_SAPI_GMM, sgsn_nu++, dtap_identity_req, sizeof(dtap_identity_req)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "IDENT RESPONSE", &bss_peer[0], 0x1002, foreign_tlli2, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_identity2_resp, sizeof(dtap_identity2_resp)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "ATTACH ACCEPT", &sgsn_peer, 0x1002, foreign_tlli2, 1, imsi2, sizeof(imsi2), GPRS_SAPI_GMM, sgsn_nu++, dtap_attach_acc, sizeof(dtap_attach_acc)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_tlli(peer, foreign_tlli2); link_info2 = gbproxy_link_info_by_tlli(peer, local_tlli); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info == link_info2); OSMO_ASSERT(link_info->tlli.assigned == local_tlli); OSMO_ASSERT(link_info->tlli.current == foreign_tlli2); OSMO_ASSERT(!link_info->tlli.bss_validated); OSMO_ASSERT(!link_info->tlli.net_validated); OSMO_ASSERT(link_info->tlli.ptmsi == ptmsi); send_llc_ul_ui(nsi, "ATTACH COMPLETE", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_complete, sizeof(dtap_attach_complete)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_tlli(peer, local_tlli); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.assigned == local_tlli); OSMO_ASSERT(link_info->tlli.current == foreign_tlli2); OSMO_ASSERT(link_info->tlli.bss_validated); OSMO_ASSERT(!link_info->tlli.net_validated); OSMO_ASSERT(link_info->tlli.ptmsi == ptmsi); send_llc_dl_ui(nsi, "GMM INFO", &sgsn_peer, 0x1002, local_tlli, 1, imsi2, sizeof(imsi2), GPRS_SAPI_GMM, sgsn_nu++, dtap_gmm_information, sizeof(dtap_gmm_information)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_imsi(peer, imsi2, ARRAY_SIZE(imsi2)); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.ptmsi == ptmsi); OSMO_ASSERT(!gbproxy_link_info_by_imsi(peer, imsi1, ARRAY_SIZE(imsi1))); link_info2 = gbproxy_link_info_by_tlli(peer, local_tlli); OSMO_ASSERT(link_info == link_info2); OSMO_ASSERT(link_info->tlli.assigned == 0); OSMO_ASSERT(link_info->tlli.current == local_tlli); OSMO_ASSERT(link_info->tlli.ptmsi == ptmsi); dump_global(stdout, 0); gbprox_reset(&gbcfg); gprs_ns_destroy(nsi); nsi = NULL; } static void test_gbproxy_ptmsi_patching() { struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, NULL); struct sockaddr_in bss_peer[1] = {{0},}; struct sockaddr_in sgsn_peer= {0}; struct gprs_ra_id rai_bss = {.mcc = 112, .mnc = 332, .lac = 16464, .rac = 96}; struct gprs_ra_id rai_sgsn = {.mcc = 123, .mnc = 456, .lac = 16464, .rac = 96}; struct gprs_ra_id rai_wrong_mcc_sgsn = {.mcc = 999, .mnc = 456, .lac = 16464, .rac = 96}; struct gprs_ra_id rai_unknown = {.mcc = 1, .mnc = 99, .lac = 99, .rac = 96}; uint16_t cell_id = 0x1234; const uint32_t sgsn_ptmsi = 0xefe2b700; const uint32_t sgsn_ptmsi2 = 0xe0987654; const uint32_t sgsn_ptmsi3 = 0xe0543210; const uint32_t local_sgsn_tlli = 0xefe2b700; const uint32_t local_sgsn_tlli2 = 0xe0987654; const uint32_t local_sgsn_tlli3 = 0xe0543210; const uint32_t random_sgsn_tlli = 0x7c69fb81; const uint32_t unknown_sgsn_tlli = 0xeebadbad; const uint32_t bss_ptmsi = 0xc00f7304; const uint32_t bss_ptmsi2 = 0xe656aa1f; const uint32_t bss_ptmsi3 = 0xead4775a; const uint32_t local_bss_tlli = 0xc00f7304; const uint32_t local_bss_tlli2 = 0xe656aa1f; const uint32_t local_bss_tlli3 = 0xead4775a; const uint32_t foreign_bss_tlli = 0x8000dead; const uint8_t imsi[] = {0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18}; struct gbproxy_link_info *link_info; struct gbproxy_peer *peer; unsigned bss_nu = 0; unsigned sgsn_nu = 0; int old_ctr; OSMO_ASSERT(local_sgsn_tlli == gprs_tmsi2tlli(sgsn_ptmsi, TLLI_LOCAL)); OSMO_ASSERT(local_sgsn_tlli2 == gprs_tmsi2tlli(sgsn_ptmsi2, TLLI_LOCAL)); OSMO_ASSERT(local_sgsn_tlli3 == gprs_tmsi2tlli(sgsn_ptmsi3, TLLI_LOCAL)); OSMO_ASSERT(local_bss_tlli == gprs_tmsi2tlli(bss_ptmsi, TLLI_LOCAL)); OSMO_ASSERT(local_bss_tlli2 == gprs_tmsi2tlli(bss_ptmsi2, TLLI_LOCAL)); OSMO_ASSERT(local_bss_tlli3 == gprs_tmsi2tlli(bss_ptmsi3, TLLI_LOCAL)); bssgp_nsi = nsi; gbcfg.nsi = bssgp_nsi; gbcfg.nsip_sgsn_nsei = SGSN_NSEI; gbcfg.core_mcc = 123; gbcfg.core_mnc = 456; gbcfg.core_apn = talloc_zero_size(NULL, 100); gbcfg.core_apn_size = gprs_str_to_apn(gbcfg.core_apn, 100, "foo.bar"); gbcfg.patch_ptmsi = 1; gbcfg.bss_ptmsi_state = 0; gbcfg.sgsn_tlli_state = 1; configure_sgsn_peer(&sgsn_peer); configure_bss_peers(bss_peer, ARRAY_SIZE(bss_peer)); printf("=== %s ===\n", __func__); printf("--- Initialise SGSN ---\n\n"); connect_sgsn(nsi, &sgsn_peer, SGSN_NSEI); printf("--- Initialise BSS 1 ---\n\n"); setup_ns(nsi, &bss_peer[0], 0x1001, 0x1000); setup_bssgp(nsi, &bss_peer[0], 0x1002); peer = gbproxy_peer_by_nsei(&gbcfg, 0x1000); OSMO_ASSERT(peer != NULL); send_bssgp_reset_ack(nsi, &sgsn_peer, 0x1002); gprs_dump_nsi(nsi); dump_global(stdout, 0); dump_peers(stdout, 0, 0, &gbcfg); printf("--- Send message from BSS 1 to SGSN, BVCI 0x1002 ---\n\n"); send_llc_ul_ui(nsi, "ATTACH REQUEST", &bss_peer[0], 0x1002, foreign_bss_tlli, &rai_unknown, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_req, sizeof(dtap_attach_req)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "IDENT REQUEST", &sgsn_peer, 0x1002, random_sgsn_tlli, 0, NULL, 0, GPRS_SAPI_GMM, sgsn_nu++, dtap_identity_req, sizeof(dtap_identity_req)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "IDENT RESPONSE", &bss_peer[0], 0x1002, foreign_bss_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_identity_resp, sizeof(dtap_identity_resp)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "ATTACH ACCEPT", &sgsn_peer, 0x1002, random_sgsn_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_attach_acc, sizeof(dtap_attach_acc)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_sgsn_tlli(peer, random_sgsn_tlli, SGSN_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.assigned == local_bss_tlli); OSMO_ASSERT(link_info->tlli.current == foreign_bss_tlli); OSMO_ASSERT(!link_info->tlli.bss_validated); OSMO_ASSERT(!link_info->tlli.net_validated); OSMO_ASSERT(link_info->tlli.ptmsi == bss_ptmsi); OSMO_ASSERT(link_info->sgsn_tlli.assigned == local_sgsn_tlli); OSMO_ASSERT(link_info->sgsn_tlli.current == random_sgsn_tlli); OSMO_ASSERT(!link_info->sgsn_tlli.bss_validated); OSMO_ASSERT(!link_info->sgsn_tlli.net_validated); OSMO_ASSERT(link_info->sgsn_tlli.ptmsi == sgsn_ptmsi); send_llc_ul_ui(nsi, "ATTACH COMPLETE", &bss_peer[0], 0x1002, local_bss_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_complete, sizeof(dtap_attach_complete)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_sgsn_tlli(peer, local_sgsn_tlli, SGSN_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.assigned == local_bss_tlli); OSMO_ASSERT(link_info->tlli.current == foreign_bss_tlli); OSMO_ASSERT(link_info->tlli.bss_validated); OSMO_ASSERT(!link_info->tlli.net_validated); OSMO_ASSERT(link_info->sgsn_tlli.assigned == local_sgsn_tlli); OSMO_ASSERT(link_info->sgsn_tlli.current == random_sgsn_tlli); OSMO_ASSERT(link_info->sgsn_tlli.bss_validated); OSMO_ASSERT(!link_info->sgsn_tlli.net_validated); send_llc_dl_ui(nsi, "GMM INFO", &sgsn_peer, 0x1002, local_sgsn_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_gmm_information, sizeof(dtap_gmm_information)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_sgsn_tlli(peer, local_sgsn_tlli, SGSN_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.current == local_bss_tlli); OSMO_ASSERT(link_info->tlli.assigned == 0); OSMO_ASSERT(link_info->sgsn_tlli.current == local_sgsn_tlli); OSMO_ASSERT(link_info->sgsn_tlli.assigned == 0); send_llc_ul_ui(nsi, "ACT PDP CTX REQ (REPLACE APN)", &bss_peer[0], 0x1002, local_bss_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_act_pdp_ctx_req, sizeof(dtap_act_pdp_ctx_req)); dump_peers(stdout, 0, 0, &gbcfg); /* Non-DTAP */ send_bssgp_ul_unitdata(nsi, "XID (UL)", &bss_peer[0], 0x1002, local_bss_tlli, &rai_bss, cell_id, llc_u_xid_ul, sizeof(llc_u_xid_ul)); send_bssgp_dl_unitdata(nsi, "XID (DL)", &sgsn_peer, 0x1002, local_sgsn_tlli, 1, imsi, sizeof(imsi), llc_u_xid_dl, sizeof(llc_u_xid_dl)); send_bssgp_ul_unitdata(nsi, "LL11 DNS QUERY (UL)", &bss_peer[0], 0x1002, local_bss_tlli, &rai_bss, cell_id, llc_ui_ll11_dns_query_ul, sizeof(llc_ui_ll11_dns_query_ul)); send_bssgp_dl_unitdata(nsi, "LL11 DNS RESP (DL)", &sgsn_peer, 0x1002, local_sgsn_tlli, 1, imsi, sizeof(imsi), llc_ui_ll11_dns_resp_dl, sizeof(llc_ui_ll11_dns_resp_dl)); dump_peers(stdout, 0, 0, &gbcfg); /* Repeated RA Update Requests */ send_llc_ul_ui(nsi, "RA UPD REQ (P-TMSI 2)", &bss_peer[0], 0x1002, local_bss_tlli, &rai_bss, 0x7080, GPRS_SAPI_GMM, bss_nu++, dtap_ra_upd_req, sizeof(dtap_ra_upd_req)); send_llc_dl_ui(nsi, "RA UDP ACC (P-TMSI 2)", &sgsn_peer, 0x1002, local_sgsn_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_ra_upd_acc2, sizeof(dtap_ra_upd_acc2)); dump_peers(stdout, 0, 0, &gbcfg); OSMO_ASSERT(gbproxy_link_info_by_sgsn_tlli(peer, local_sgsn_tlli2, SGSN_NSEI) != NULL); link_info = gbproxy_link_info_by_sgsn_tlli(peer, local_sgsn_tlli, SGSN_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.assigned == local_bss_tlli2); OSMO_ASSERT(link_info->tlli.current == local_bss_tlli); OSMO_ASSERT(!link_info->tlli.bss_validated); OSMO_ASSERT(!link_info->tlli.net_validated); OSMO_ASSERT(link_info->tlli.ptmsi == bss_ptmsi2); OSMO_ASSERT(link_info->sgsn_tlli.assigned == local_sgsn_tlli2); OSMO_ASSERT(link_info->sgsn_tlli.current == local_sgsn_tlli); OSMO_ASSERT(!link_info->sgsn_tlli.bss_validated); OSMO_ASSERT(!link_info->sgsn_tlli.net_validated); OSMO_ASSERT(link_info->sgsn_tlli.ptmsi == sgsn_ptmsi2); send_llc_ul_ui(nsi, "RA UPD REQ (P-TMSI 3)", &bss_peer[0], 0x1002, local_bss_tlli2, &rai_bss, 0x7080, GPRS_SAPI_GMM, bss_nu++, dtap_ra_upd_req, sizeof(dtap_ra_upd_req)); send_llc_dl_ui(nsi, "RA UDP ACC (P-TMSI 3)", &sgsn_peer, 0x1002, local_sgsn_tlli2, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_ra_upd_acc3, sizeof(dtap_ra_upd_acc3)); dump_peers(stdout, 0, 0, &gbcfg); OSMO_ASSERT(gbproxy_link_info_by_sgsn_tlli(peer, local_sgsn_tlli2, SGSN_NSEI) == NULL); OSMO_ASSERT(gbproxy_link_info_by_sgsn_tlli(peer, local_sgsn_tlli3, SGSN_NSEI) != NULL); link_info = gbproxy_link_info_by_sgsn_tlli(peer, local_sgsn_tlli, SGSN_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.assigned == local_bss_tlli3); OSMO_ASSERT(link_info->tlli.current == local_bss_tlli); OSMO_ASSERT(!link_info->tlli.bss_validated); OSMO_ASSERT(!link_info->tlli.net_validated); OSMO_ASSERT(link_info->tlli.ptmsi == bss_ptmsi3); OSMO_ASSERT(link_info->sgsn_tlli.assigned == local_sgsn_tlli3); OSMO_ASSERT(link_info->sgsn_tlli.current == local_sgsn_tlli); OSMO_ASSERT(!link_info->sgsn_tlli.bss_validated); OSMO_ASSERT(!link_info->sgsn_tlli.net_validated); OSMO_ASSERT(link_info->sgsn_tlli.ptmsi == sgsn_ptmsi3); send_llc_ul_ui(nsi, "RA UPD COMPLETE", &bss_peer[0], 0x1002, local_bss_tlli3, &rai_bss, 0x7080, GPRS_SAPI_GMM, bss_nu++, dtap_ra_upd_complete, sizeof(dtap_ra_upd_complete)); link_info = gbproxy_link_info_by_tlli(peer, local_bss_tlli3); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.bss_validated); OSMO_ASSERT(!link_info->tlli.net_validated); OSMO_ASSERT(link_info->sgsn_tlli.bss_validated); OSMO_ASSERT(!link_info->sgsn_tlli.net_validated); send_llc_dl_ui(nsi, "GMM INFO", &sgsn_peer, 0x1002, local_sgsn_tlli3, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_gmm_information, sizeof(dtap_gmm_information)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_sgsn_tlli(peer, local_sgsn_tlli3, SGSN_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.current == local_bss_tlli3); OSMO_ASSERT(link_info->tlli.assigned == 0); OSMO_ASSERT(link_info->sgsn_tlli.current == local_sgsn_tlli3); OSMO_ASSERT(link_info->sgsn_tlli.assigned == 0); /* Other messages */ send_bssgp_llc_discarded(nsi, &bss_peer[0], 0x1002, local_bss_tlli3, 1, 12); dump_peers(stdout, 0, 0, &gbcfg); send_bssgp_suspend(nsi, &bss_peer[0], local_bss_tlli3, &rai_bss); dump_peers(stdout, 0, 0, &gbcfg); send_bssgp_suspend_ack(nsi, &sgsn_peer, local_sgsn_tlli3, &rai_sgsn); dump_peers(stdout, 0, 0, &gbcfg); old_ctr = peer->ctrg->ctr[GBPROX_PEER_CTR_PTMSI_PATCHED_SGSN].current; send_bssgp_paging(nsi, &sgsn_peer, imsi, sizeof(imsi), &rai_bss, sgsn_ptmsi3); dump_peers(stdout, 0, 0, &gbcfg); OSMO_ASSERT(old_ctr + 1 == peer->ctrg->ctr[GBPROX_PEER_CTR_PTMSI_PATCHED_SGSN].current); /* Bad case: Invalid BVCI */ send_bssgp_llc_discarded(nsi, &bss_peer[0], 0xeee1, local_bss_tlli3, 1, 12); dump_global(stdout, 0); /* Bad case: Invalid RAI */ send_bssgp_suspend_ack(nsi, &sgsn_peer, local_sgsn_tlli3, &rai_unknown); dump_global(stdout, 0); /* Bad case: Invalid MCC (LAC ok) */ send_bssgp_suspend_ack(nsi, &sgsn_peer, local_sgsn_tlli3, &rai_wrong_mcc_sgsn); dump_global(stdout, 0); /* Bad case: Invalid TLLI from SGSN (IMSI unknown) */ send_llc_dl_ui(nsi, "GMM INFO", &sgsn_peer, 0x1002, unknown_sgsn_tlli, 1, NULL, 0, GPRS_SAPI_GMM, 2, dtap_gmm_information, sizeof(dtap_gmm_information)); /* Bad case: Invalid TLLI from SGSN (IMSI known) */ send_llc_dl_ui(nsi, "GMM INFO", &sgsn_peer, 0x1002, unknown_sgsn_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, 3, dtap_gmm_information, sizeof(dtap_gmm_information)); /* Detach */ send_llc_ul_ui(nsi, "DETACH REQ", &bss_peer[0], 0x1002, local_bss_tlli3, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_detach_req, sizeof(dtap_detach_req)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "DETACH ACC", &sgsn_peer, 0x1002, local_sgsn_tlli3, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_detach_acc, sizeof(dtap_detach_acc)); dump_peers(stdout, 0, 0, &gbcfg); dump_global(stdout, 0); gbprox_reset(&gbcfg); gprs_ns_destroy(nsi); nsi = NULL; } static void test_gbproxy_ptmsi_patching_bad_cases() { struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, NULL); struct sockaddr_in bss_peer[1] = {{0},}; struct sockaddr_in sgsn_peer= {0}; struct gprs_ra_id rai_bss = {.mcc = 112, .mnc = 332, .lac = 16464, .rac = 96}; struct gprs_ra_id rai_unknown = {.mcc = 1, .mnc = 99, .lac = 99, .rac = 96}; uint16_t cell_id = 0x1234; const uint32_t sgsn_ptmsi = 0xefe2b700; const uint32_t local_sgsn_tlli = 0xefe2b700; const uint32_t random_sgsn_tlli = 0x7c69fb81; const uint32_t bss_ptmsi = 0xc00f7304; const uint32_t local_bss_tlli = 0xc00f7304; const uint32_t foreign_bss_tlli = 0x8000dead; const uint8_t imsi[] = {0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18}; struct gbproxy_link_info *link_info; struct gbproxy_peer *peer; unsigned bss_nu = 0; unsigned sgsn_nu = 0; OSMO_ASSERT(local_sgsn_tlli == gprs_tmsi2tlli(sgsn_ptmsi, TLLI_LOCAL)); OSMO_ASSERT(local_bss_tlli == gprs_tmsi2tlli(bss_ptmsi, TLLI_LOCAL)); bssgp_nsi = nsi; gbcfg.nsi = bssgp_nsi; gbcfg.nsip_sgsn_nsei = SGSN_NSEI; gbcfg.core_mcc = 123; gbcfg.core_mnc = 456; gbcfg.core_apn = talloc_zero_size(NULL, 100); gbcfg.core_apn_size = gprs_str_to_apn(gbcfg.core_apn, 100, "foo.bar"); gbcfg.patch_ptmsi = 1; gbcfg.bss_ptmsi_state = 0; gbcfg.sgsn_tlli_state = 1; configure_sgsn_peer(&sgsn_peer); configure_bss_peers(bss_peer, ARRAY_SIZE(bss_peer)); printf("=== %s ===\n", __func__); printf("--- Initialise SGSN ---\n\n"); connect_sgsn(nsi, &sgsn_peer, SGSN_NSEI); printf("--- Initialise BSS 1 ---\n\n"); setup_ns(nsi, &bss_peer[0], 0x1001, 0x1000); setup_bssgp(nsi, &bss_peer[0], 0x1002); peer = gbproxy_peer_by_nsei(&gbcfg, 0x1000); OSMO_ASSERT(peer != NULL); send_bssgp_reset_ack(nsi, &sgsn_peer, 0x1002); gprs_dump_nsi(nsi); dump_global(stdout, 0); dump_peers(stdout, 0, 0, &gbcfg); printf("--- Send message from BSS 1 to SGSN, BVCI 0x1002 ---\n\n"); send_llc_ul_ui(nsi, "ATTACH REQUEST", &bss_peer[0], 0x1002, foreign_bss_tlli, &rai_unknown, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_req, sizeof(dtap_attach_req)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "IDENT REQUEST", &sgsn_peer, 0x1002, random_sgsn_tlli, 0, NULL, 0, GPRS_SAPI_GMM, sgsn_nu++, dtap_identity_req, sizeof(dtap_identity_req)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "IDENT RESPONSE", &bss_peer[0], 0x1002, foreign_bss_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_identity_resp, sizeof(dtap_identity_resp)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "ATTACH ACCEPT", &sgsn_peer, 0x1002, random_sgsn_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_attach_acc, sizeof(dtap_attach_acc)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_sgsn_tlli(peer, random_sgsn_tlli, SGSN_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.assigned == local_bss_tlli); OSMO_ASSERT(link_info->tlli.current == foreign_bss_tlli); OSMO_ASSERT(!link_info->tlli.bss_validated); OSMO_ASSERT(!link_info->tlli.net_validated); OSMO_ASSERT(link_info->tlli.ptmsi == bss_ptmsi); OSMO_ASSERT(link_info->sgsn_tlli.assigned == local_sgsn_tlli); OSMO_ASSERT(link_info->sgsn_tlli.current == random_sgsn_tlli); OSMO_ASSERT(!link_info->sgsn_tlli.bss_validated); OSMO_ASSERT(!link_info->sgsn_tlli.net_validated); OSMO_ASSERT(link_info->sgsn_tlli.ptmsi == sgsn_ptmsi); send_llc_dl_ui(nsi, "ATTACH ACCEPT (duplicated)", &sgsn_peer, 0x1002, random_sgsn_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_attach_acc, sizeof(dtap_attach_acc)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_sgsn_tlli(peer, random_sgsn_tlli, SGSN_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.assigned == local_bss_tlli); OSMO_ASSERT(link_info->tlli.current == foreign_bss_tlli); OSMO_ASSERT(!link_info->tlli.bss_validated); OSMO_ASSERT(!link_info->tlli.net_validated); OSMO_ASSERT(link_info->tlli.ptmsi == bss_ptmsi); OSMO_ASSERT(link_info->sgsn_tlli.assigned == local_sgsn_tlli); OSMO_ASSERT(link_info->sgsn_tlli.current == random_sgsn_tlli); OSMO_ASSERT(!link_info->sgsn_tlli.bss_validated); OSMO_ASSERT(!link_info->sgsn_tlli.net_validated); OSMO_ASSERT(link_info->sgsn_tlli.ptmsi == sgsn_ptmsi); send_llc_ul_ui(nsi, "ATTACH COMPLETE", &bss_peer[0], 0x1002, local_bss_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_complete, sizeof(dtap_attach_complete)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_sgsn_tlli(peer, local_sgsn_tlli, SGSN_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.assigned == local_bss_tlli); OSMO_ASSERT(link_info->tlli.current == foreign_bss_tlli); OSMO_ASSERT(link_info->tlli.bss_validated); OSMO_ASSERT(!link_info->tlli.net_validated); OSMO_ASSERT(link_info->sgsn_tlli.assigned == local_sgsn_tlli); OSMO_ASSERT(link_info->sgsn_tlli.current == random_sgsn_tlli); OSMO_ASSERT(link_info->sgsn_tlli.bss_validated); OSMO_ASSERT(!link_info->sgsn_tlli.net_validated); send_llc_dl_ui(nsi, "GMM INFO", &sgsn_peer, 0x1002, local_sgsn_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_gmm_information, sizeof(dtap_gmm_information)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_sgsn_tlli(peer, local_sgsn_tlli, SGSN_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.current == local_bss_tlli); OSMO_ASSERT(link_info->tlli.assigned == 0); OSMO_ASSERT(link_info->sgsn_tlli.current == local_sgsn_tlli); OSMO_ASSERT(link_info->sgsn_tlli.assigned == 0); /* Detach */ send_llc_ul_ui(nsi, "DETACH REQ", &bss_peer[0], 0x1002, local_bss_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_detach_req, sizeof(dtap_detach_req)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "DETACH ACC", &sgsn_peer, 0x1002, local_sgsn_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_detach_acc, sizeof(dtap_detach_acc)); dump_peers(stdout, 0, 0, &gbcfg); dump_global(stdout, 0); gbprox_reset(&gbcfg); gprs_ns_destroy(nsi); nsi = NULL; } static void test_gbproxy_imsi_acquisition() { struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, NULL); struct sockaddr_in bss_peer[1] = {{0},}; struct sockaddr_in sgsn_peer= {0}; struct gprs_ra_id rai_bss = {.mcc = 112, .mnc = 332, .lac = 16464, .rac = 96}; struct gprs_ra_id rai_sgsn = {.mcc = 123, .mnc = 456, .lac = 16464, .rac = 96}; struct gprs_ra_id rai_wrong_mcc_sgsn = {.mcc = 999, .mnc = 456, .lac = 16464, .rac = 96}; struct gprs_ra_id rai_unknown = {.mcc = 1, .mnc = 99, .lac = 99, .rac = 96}; uint16_t cell_id = 0x1234; const uint32_t sgsn_ptmsi = 0xefe2b700; const uint32_t local_sgsn_tlli = 0xefe2b700; const uint32_t random_sgsn_tlli = 0x7c69fb81; const uint32_t random_sgsn_tlli2 = 0x7eb52dfb; const uint32_t bss_ptmsi = 0xc00f7304; const uint32_t local_bss_tlli = 0xc00f7304; const uint32_t foreign_bss_tlli = 0x8000dead; const uint32_t other_bss_tlli = 0x8000beef; const uint8_t imsi[] = {0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18}; struct gbproxy_link_info *link_info; struct gbproxy_peer *peer; unsigned bss_nu = 0; unsigned sgsn_nu = 0; OSMO_ASSERT(local_sgsn_tlli == gprs_tmsi2tlli(sgsn_ptmsi, TLLI_LOCAL)); bssgp_nsi = nsi; gbcfg.nsi = bssgp_nsi; gbcfg.nsip_sgsn_nsei = SGSN_NSEI; gbcfg.core_mcc = 123; gbcfg.core_mnc = 456; gbcfg.core_apn = talloc_zero_size(NULL, 100); gbcfg.core_apn_size = gprs_str_to_apn(gbcfg.core_apn, 100, "foo.bar"); gbcfg.patch_ptmsi = 1; gbcfg.acquire_imsi = 1; gbcfg.bss_ptmsi_state = 0; gbcfg.sgsn_tlli_state = 1; configure_sgsn_peer(&sgsn_peer); configure_bss_peers(bss_peer, ARRAY_SIZE(bss_peer)); printf("=== %s ===\n", __func__); printf("--- Initialise SGSN ---\n\n"); connect_sgsn(nsi, &sgsn_peer, SGSN_NSEI); printf("--- Initialise BSS 1 ---\n\n"); setup_ns(nsi, &bss_peer[0], 0x1001, 0x1000); setup_bssgp(nsi, &bss_peer[0], 0x1002); peer = gbproxy_peer_by_nsei(&gbcfg, 0x1000); OSMO_ASSERT(peer != NULL); send_bssgp_reset_ack(nsi, &sgsn_peer, 0x1002); gprs_dump_nsi(nsi); dump_global(stdout, 0); dump_peers(stdout, 0, 0, &gbcfg); printf("--- Send message from BSS 1 to SGSN, BVCI 0x1002 ---\n\n"); send_llc_ul_ui(nsi, "ATTACH REQUEST", &bss_peer[0], 0x1002, foreign_bss_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_req, sizeof(dtap_attach_req)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "IDENT RESPONSE", &bss_peer[0], 0x1002, foreign_bss_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_identity_resp, sizeof(dtap_identity_resp)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "IDENT REQUEST", &sgsn_peer, 0x1002, random_sgsn_tlli, 0, NULL, 0, GPRS_SAPI_GMM, sgsn_nu++, dtap_identity_req, sizeof(dtap_identity_req)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "IDENT RESPONSE", &bss_peer[0], 0x1002, foreign_bss_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_identity_resp, sizeof(dtap_identity_resp)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "ATTACH ACCEPT", &sgsn_peer, 0x1002, random_sgsn_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_attach_acc, sizeof(dtap_attach_acc)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_sgsn_tlli(peer, random_sgsn_tlli, SGSN_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.assigned == local_bss_tlli); OSMO_ASSERT(link_info->tlli.current == foreign_bss_tlli); OSMO_ASSERT(!link_info->tlli.bss_validated); OSMO_ASSERT(!link_info->tlli.net_validated); OSMO_ASSERT(link_info->tlli.ptmsi == bss_ptmsi); OSMO_ASSERT(link_info->sgsn_tlli.assigned == local_sgsn_tlli); OSMO_ASSERT(link_info->sgsn_tlli.current == random_sgsn_tlli); OSMO_ASSERT(!link_info->sgsn_tlli.bss_validated); OSMO_ASSERT(!link_info->sgsn_tlli.net_validated); OSMO_ASSERT(link_info->sgsn_tlli.ptmsi == sgsn_ptmsi); send_llc_ul_ui(nsi, "ATTACH COMPLETE", &bss_peer[0], 0x1002, local_bss_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_complete, sizeof(dtap_attach_complete)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_sgsn_tlli(peer, local_sgsn_tlli, SGSN_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.assigned == local_bss_tlli); OSMO_ASSERT(link_info->tlli.current == foreign_bss_tlli); OSMO_ASSERT(link_info->tlli.bss_validated); OSMO_ASSERT(!link_info->tlli.net_validated); OSMO_ASSERT(link_info->sgsn_tlli.assigned == local_sgsn_tlli); OSMO_ASSERT(link_info->sgsn_tlli.current == random_sgsn_tlli); OSMO_ASSERT(link_info->sgsn_tlli.bss_validated); OSMO_ASSERT(!link_info->sgsn_tlli.net_validated); send_llc_dl_ui(nsi, "GMM INFO", &sgsn_peer, 0x1002, local_sgsn_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_gmm_information, sizeof(dtap_gmm_information)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_sgsn_tlli(peer, local_sgsn_tlli, SGSN_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.current == local_bss_tlli); OSMO_ASSERT(link_info->tlli.assigned == 0); OSMO_ASSERT(link_info->sgsn_tlli.current == local_sgsn_tlli); OSMO_ASSERT(link_info->sgsn_tlli.assigned == 0); /* Non-DTAP */ send_bssgp_ul_unitdata(nsi, "XID (UL)", &bss_peer[0], 0x1002, local_bss_tlli, &rai_bss, cell_id, llc_u_xid_ul, sizeof(llc_u_xid_ul)); send_bssgp_dl_unitdata(nsi, "XID (DL)", &sgsn_peer, 0x1002, local_sgsn_tlli, 1, imsi, sizeof(imsi), llc_u_xid_dl, sizeof(llc_u_xid_dl)); send_bssgp_ul_unitdata(nsi, "LL11 DNS QUERY (UL)", &bss_peer[0], 0x1002, local_bss_tlli, &rai_bss, cell_id, llc_ui_ll11_dns_query_ul, sizeof(llc_ui_ll11_dns_query_ul)); send_bssgp_dl_unitdata(nsi, "LL11 DNS RESP (DL)", &sgsn_peer, 0x1002, local_sgsn_tlli, 1, imsi, sizeof(imsi), llc_ui_ll11_dns_resp_dl, sizeof(llc_ui_ll11_dns_resp_dl)); dump_peers(stdout, 0, 0, &gbcfg); /* Other messages */ send_bssgp_llc_discarded(nsi, &bss_peer[0], 0x1002, local_bss_tlli, 1, 12); dump_peers(stdout, 0, 0, &gbcfg); send_bssgp_llc_discarded(nsi, &sgsn_peer, 0x1002, local_sgsn_tlli, 1, 12); dump_peers(stdout, 0, 0, &gbcfg); send_bssgp_suspend(nsi, &bss_peer[0], local_bss_tlli, &rai_bss); dump_peers(stdout, 0, 0, &gbcfg); send_bssgp_suspend_ack(nsi, &sgsn_peer, local_sgsn_tlli, &rai_sgsn); dump_peers(stdout, 0, 0, &gbcfg); /* Bad case: Invalid BVCI */ send_bssgp_llc_discarded(nsi, &bss_peer[0], 0xeee1, local_bss_tlli, 1, 12); dump_global(stdout, 0); /* Bad case: Invalid RAI */ send_bssgp_suspend_ack(nsi, &sgsn_peer, local_sgsn_tlli, &rai_unknown); dump_global(stdout, 0); /* Bad case: Invalid MCC (LAC ok) */ send_bssgp_suspend_ack(nsi, &sgsn_peer, local_sgsn_tlli, &rai_wrong_mcc_sgsn); dump_global(stdout, 0); /* Detach */ send_llc_ul_ui(nsi, "DETACH REQ", &bss_peer[0], 0x1002, local_bss_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_detach_req, sizeof(dtap_detach_req)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "DETACH ACC", &sgsn_peer, 0x1002, local_sgsn_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_detach_acc, sizeof(dtap_detach_acc)); dump_peers(stdout, 0, 0, &gbcfg); /* RA Update request */ send_llc_ul_ui(nsi, "RA UPD REQ", &bss_peer[0], 0x1002, foreign_bss_tlli, &rai_unknown, 0x7080, GPRS_SAPI_GMM, bss_nu++, dtap_ra_upd_req, sizeof(dtap_ra_upd_req)); send_llc_ul_ui(nsi, "IDENT RESPONSE", &bss_peer[0], 0x1002, foreign_bss_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_identity_resp, sizeof(dtap_identity_resp)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "RA UDP ACC", &sgsn_peer, 0x1002, random_sgsn_tlli2, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_ra_upd_acc, sizeof(dtap_ra_upd_acc)); dump_peers(stdout, 0, 0, &gbcfg); /* Detach */ send_llc_ul_ui(nsi, "DETACH REQ", &bss_peer[0], 0x1002, local_bss_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_detach_req, sizeof(dtap_detach_req)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "DETACH ACC", &sgsn_peer, 0x1002, local_sgsn_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_detach_acc, sizeof(dtap_detach_acc)); dump_peers(stdout, 0, 0, &gbcfg); /* Special case: Repeated Attach Requests */ send_llc_ul_ui(nsi, "ATTACH REQUEST", &bss_peer[0], 0x1002, foreign_bss_tlli, &rai_unknown, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_req, sizeof(dtap_attach_req)); send_llc_ul_ui(nsi, "ATTACH REQUEST", &bss_peer[0], 0x1002, foreign_bss_tlli, &rai_unknown, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_req, sizeof(dtap_attach_req)); send_llc_ul_ui(nsi, "DETACH REQ", &bss_peer[0], 0x1002, foreign_bss_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_detach_req, sizeof(dtap_detach_req)); dump_peers(stdout, 0, 0, &gbcfg); /* Special case: Detach from an unknown TLLI */ send_llc_ul_ui(nsi, "DETACH REQ (unknown TLLI)", &bss_peer[0], 0x1002, other_bss_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_detach_req, sizeof(dtap_detach_req)); dump_peers(stdout, 0, 0, &gbcfg); /* Special case: Repeated RA Update Requests */ send_llc_ul_ui(nsi, "RA UPD REQ", &bss_peer[0], 0x1002, foreign_bss_tlli, &rai_unknown, 0x7080, GPRS_SAPI_GMM, bss_nu++, dtap_ra_upd_req, sizeof(dtap_ra_upd_req)); send_llc_ul_ui(nsi, "RA UPD REQ", &bss_peer[0], 0x1002, foreign_bss_tlli, &rai_unknown, 0x7080, GPRS_SAPI_GMM, bss_nu++, dtap_ra_upd_req, sizeof(dtap_ra_upd_req)); send_llc_ul_ui(nsi, "DETACH REQ", &bss_peer[0], 0x1002, foreign_bss_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_detach_req, sizeof(dtap_detach_req)); dump_peers(stdout, 0, 0, &gbcfg); dump_global(stdout, 0); gbprox_reset(&gbcfg); gprs_ns_destroy(nsi); nsi = NULL; } static void test_gbproxy_secondary_sgsn() { struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, NULL); struct sockaddr_in bss_peer[1] = {{0},}; struct sockaddr_in sgsn_peer[2]= {{0},}; struct gprs_ra_id rai_bss = {.mcc = 112, .mnc = 332, .lac = 16464, .rac = 96}; struct gprs_ra_id rai_sgsn = {.mcc = 123, .mnc = 456, .lac = 16464, .rac = 96}; struct gprs_ra_id rai_unknown = {.mcc = 1, .mnc = 99, .lac = 99, .rac = 96}; uint16_t cell_id = 0x1234; const uint32_t sgsn_ptmsi = 0xefe2b700; const uint32_t local_sgsn_tlli = 0xefe2b700; const uint32_t random_sgsn_tlli = 0x7c69fb81; const uint32_t bss_ptmsi = 0xc00f7304; const uint32_t local_bss_tlli = 0xc00f7304; const uint32_t foreign_bss_tlli = 0x8000dead; const uint32_t sgsn_ptmsi2 = 0xe0987654; const uint32_t local_sgsn_tlli2 = 0xe0987654; const uint32_t random_sgsn_tlli2 = 0x7eb52dfb; const uint32_t bss_ptmsi2 = 0xe656aa1f; const uint32_t local_bss_tlli2 = 0xe656aa1f; const uint32_t foreign_bss_tlli2 = 0x8000beef; const uint32_t random_sgsn_tlli3 = 0x7e23ef54; const uint32_t bss_ptmsi3 = 0xead4775a; const uint32_t local_bss_tlli3 = 0xead4775a; const uint32_t foreign_bss_tlli3 = 0x8000feed; const uint8_t imsi1[] = {0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18}; const uint8_t imsi2[] = {0x11, 0x12, 0x99, 0x99, 0x99, 0x16, 0x17, 0x18}; const uint8_t imsi3[] = {0x11, 0x12, 0x99, 0x99, 0x99, 0x26, 0x27, 0x28}; struct gbproxy_link_info *link_info; struct gbproxy_link_info *other_info; struct gbproxy_peer *peer; unsigned bss_nu = 0; unsigned sgsn_nu = 0; const char *err_msg = NULL; const char *filter_re = "999999"; OSMO_ASSERT(local_sgsn_tlli == gprs_tmsi2tlli(sgsn_ptmsi, TLLI_LOCAL)); OSMO_ASSERT(local_sgsn_tlli2 == gprs_tmsi2tlli(sgsn_ptmsi2, TLLI_LOCAL)); bssgp_nsi = nsi; gbcfg.nsi = bssgp_nsi; gbcfg.nsip_sgsn_nsei = SGSN_NSEI; gbcfg.core_mcc = 123; gbcfg.core_mnc = 456; gbcfg.core_apn = talloc_zero_size(NULL, 100); gbcfg.core_apn_size = gprs_str_to_apn(gbcfg.core_apn, 100, "foo.bar"); gbcfg.patch_ptmsi = 1; gbcfg.acquire_imsi = 1; gbcfg.bss_ptmsi_state = 0; gbcfg.sgsn_tlli_state = 1; gbcfg.route_to_sgsn2 = 1; gbcfg.nsip_sgsn2_nsei = SGSN2_NSEI; if (gbproxy_set_patch_filter(&gbcfg.matches[GBPROX_MATCH_ROUTING], filter_re, &err_msg) != 0) { fprintf(stderr, "gbprox_set_patch_filter: got error: %s\n", err_msg); OSMO_ASSERT(err_msg == NULL); } configure_sgsn_peer(&sgsn_peer[0]); configure_sgsn2_peer(&sgsn_peer[1]); configure_bss_peers(bss_peer, ARRAY_SIZE(bss_peer)); printf("=== %s ===\n", __func__); printf("--- Initialise SGSN 1 ---\n\n"); connect_sgsn(nsi, &sgsn_peer[0], SGSN_NSEI); printf("--- Initialise SGSN 2 ---\n\n"); connect_sgsn(nsi, &sgsn_peer[1], SGSN2_NSEI); printf("--- Initialise BSS 1 ---\n\n"); setup_ns(nsi, &bss_peer[0], 0x1001, 0x1000); setup_bssgp(nsi, &bss_peer[0], 0x0); send_bssgp_reset_ack(nsi, &sgsn_peer[0], 0x0); setup_bssgp(nsi, &bss_peer[0], 0x1002); send_bssgp_reset_ack(nsi, &sgsn_peer[0], 0x1002); send_bssgp_reset_ack(nsi, &sgsn_peer[1], 0x1002); peer = gbproxy_peer_by_nsei(&gbcfg, 0x1000); OSMO_ASSERT(peer != NULL); gprs_dump_nsi(nsi); dump_global(stdout, 0); dump_peers(stdout, 0, 0, &gbcfg); printf("--- Flow control ---\n\n"); send_bssgp_flow_control_bvc(nsi, &bss_peer[0], 0x1002, 1); send_bssgp_flow_control_bvc_ack(nsi, &sgsn_peer[0], 0x1002, 1); send_bssgp_flow_control_bvc_ack(nsi, &sgsn_peer[1], 0x1002, 1); printf("--- Establish GPRS connection (SGSN 1) ---\n\n"); send_llc_ul_ui(nsi, "ATTACH REQUEST", &bss_peer[0], 0x1002, foreign_bss_tlli, &rai_unknown, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_req, sizeof(dtap_attach_req)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "IDENT RESPONSE", &bss_peer[0], 0x1002, foreign_bss_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_identity_resp, sizeof(dtap_identity_resp)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "IDENT REQUEST", &sgsn_peer[0], 0x1002, random_sgsn_tlli, 0, NULL, 0, GPRS_SAPI_GMM, sgsn_nu++, dtap_identity_req, sizeof(dtap_identity_req)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "IDENT RESPONSE", &bss_peer[0], 0x1002, foreign_bss_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_identity_resp, sizeof(dtap_identity_resp)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "ATTACH ACCEPT", &sgsn_peer[0], 0x1002, random_sgsn_tlli, 1, imsi1, sizeof(imsi1), GPRS_SAPI_GMM, sgsn_nu++, dtap_attach_acc, sizeof(dtap_attach_acc)); dump_peers(stdout, 0, 0, &gbcfg); OSMO_ASSERT(!gbproxy_link_info_by_sgsn_tlli(peer, local_sgsn_tlli, SGSN2_NSEI)); link_info = gbproxy_link_info_by_sgsn_tlli(peer, random_sgsn_tlli, SGSN_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.assigned == local_bss_tlli); OSMO_ASSERT(link_info->tlli.current == foreign_bss_tlli); OSMO_ASSERT(!link_info->tlli.bss_validated); OSMO_ASSERT(!link_info->tlli.net_validated); OSMO_ASSERT(link_info->tlli.ptmsi == bss_ptmsi); OSMO_ASSERT(link_info->sgsn_tlli.assigned == local_sgsn_tlli); OSMO_ASSERT(link_info->sgsn_tlli.current == random_sgsn_tlli); OSMO_ASSERT(!link_info->sgsn_tlli.bss_validated); OSMO_ASSERT(!link_info->sgsn_tlli.net_validated); OSMO_ASSERT(link_info->sgsn_tlli.ptmsi == sgsn_ptmsi); send_llc_ul_ui(nsi, "ATTACH COMPLETE", &bss_peer[0], 0x1002, local_bss_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_complete, sizeof(dtap_attach_complete)); dump_peers(stdout, 0, 0, &gbcfg); OSMO_ASSERT(!gbproxy_link_info_by_sgsn_tlli(peer, local_sgsn_tlli, SGSN2_NSEI)); link_info = gbproxy_link_info_by_sgsn_tlli(peer, local_sgsn_tlli, SGSN_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.assigned == local_bss_tlli); OSMO_ASSERT(link_info->tlli.current == foreign_bss_tlli); OSMO_ASSERT(link_info->tlli.bss_validated); OSMO_ASSERT(!link_info->tlli.net_validated); OSMO_ASSERT(link_info->sgsn_tlli.assigned == local_sgsn_tlli); OSMO_ASSERT(link_info->sgsn_tlli.current == random_sgsn_tlli); OSMO_ASSERT(link_info->sgsn_tlli.bss_validated); OSMO_ASSERT(!link_info->sgsn_tlli.net_validated); send_llc_dl_ui(nsi, "GMM INFO", &sgsn_peer[0], 0x1002, local_sgsn_tlli, 1, imsi1, sizeof(imsi1), GPRS_SAPI_GMM, sgsn_nu++, dtap_gmm_information, sizeof(dtap_gmm_information)); dump_peers(stdout, 0, 0, &gbcfg); OSMO_ASSERT(!gbproxy_link_info_by_sgsn_tlli(peer, local_sgsn_tlli, SGSN2_NSEI)); link_info = gbproxy_link_info_by_sgsn_tlli(peer, local_sgsn_tlli, SGSN_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.current == local_bss_tlli); OSMO_ASSERT(link_info->tlli.assigned == 0); OSMO_ASSERT(link_info->sgsn_tlli.current == local_sgsn_tlli); OSMO_ASSERT(link_info->sgsn_tlli.assigned == 0); /* Non-DTAP */ send_bssgp_ul_unitdata(nsi, "XID (UL)", &bss_peer[0], 0x1002, local_bss_tlli, &rai_bss, cell_id, llc_u_xid_ul, sizeof(llc_u_xid_ul)); send_bssgp_dl_unitdata(nsi, "XID (DL)", &sgsn_peer[0], 0x1002, local_sgsn_tlli, 1, imsi1, sizeof(imsi1), llc_u_xid_dl, sizeof(llc_u_xid_dl)); send_bssgp_ul_unitdata(nsi, "LL11 DNS QUERY (UL)", &bss_peer[0], 0x1002, local_bss_tlli, &rai_bss, cell_id, llc_ui_ll11_dns_query_ul, sizeof(llc_ui_ll11_dns_query_ul)); send_bssgp_dl_unitdata(nsi, "LL11 DNS RESP (DL)", &sgsn_peer[0], 0x1002, local_sgsn_tlli, 1, imsi1, sizeof(imsi1), llc_ui_ll11_dns_resp_dl, sizeof(llc_ui_ll11_dns_resp_dl)); dump_peers(stdout, 0, 0, &gbcfg); /* Other messages */ send_bssgp_llc_discarded(nsi, &bss_peer[0], 0x1002, local_bss_tlli, 1, 12); dump_peers(stdout, 0, 0, &gbcfg); send_bssgp_llc_discarded(nsi, &sgsn_peer[0], 0x1002, local_sgsn_tlli, 1, 12); dump_peers(stdout, 0, 0, &gbcfg); send_bssgp_suspend(nsi, &bss_peer[0], local_bss_tlli, &rai_bss); dump_peers(stdout, 0, 0, &gbcfg); send_bssgp_suspend_ack(nsi, &sgsn_peer[0], local_sgsn_tlli, &rai_sgsn); dump_peers(stdout, 0, 0, &gbcfg); printf("--- Establish GPRS connection (SGSN 2) ---\n\n"); send_llc_ul_ui(nsi, "ATTACH REQUEST", &bss_peer[0], 0x1002, foreign_bss_tlli2, &rai_unknown, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_req, sizeof(dtap_attach_req)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "IDENT RESPONSE", &bss_peer[0], 0x1002, foreign_bss_tlli2, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_identity2_resp, sizeof(dtap_identity2_resp)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "IDENT REQUEST", &sgsn_peer[1], 0x1002, random_sgsn_tlli2, 0, NULL, 0, GPRS_SAPI_GMM, sgsn_nu++, dtap_identity_req, sizeof(dtap_identity_req)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "IDENT RESPONSE", &bss_peer[0], 0x1002, foreign_bss_tlli2, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_identity2_resp, sizeof(dtap_identity2_resp)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "ATTACH ACCEPT", &sgsn_peer[1], 0x1002, random_sgsn_tlli2, 1, imsi2, sizeof(imsi2), GPRS_SAPI_GMM, sgsn_nu++, dtap_attach_acc2, sizeof(dtap_attach_acc2)); dump_peers(stdout, 0, 0, &gbcfg); OSMO_ASSERT(!gbproxy_link_info_by_sgsn_tlli(peer, random_sgsn_tlli2, SGSN_NSEI)); link_info = gbproxy_link_info_by_sgsn_tlli(peer, random_sgsn_tlli2, SGSN2_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.assigned == local_bss_tlli2); OSMO_ASSERT(link_info->tlli.current == foreign_bss_tlli2); OSMO_ASSERT(!link_info->tlli.bss_validated); OSMO_ASSERT(!link_info->tlli.net_validated); OSMO_ASSERT(link_info->tlli.ptmsi == bss_ptmsi2); OSMO_ASSERT(link_info->sgsn_tlli.assigned == local_sgsn_tlli2); OSMO_ASSERT(link_info->sgsn_tlli.current == random_sgsn_tlli2); OSMO_ASSERT(!link_info->sgsn_tlli.bss_validated); OSMO_ASSERT(!link_info->sgsn_tlli.net_validated); OSMO_ASSERT(link_info->sgsn_tlli.ptmsi == sgsn_ptmsi2); send_llc_ul_ui(nsi, "ATTACH COMPLETE", &bss_peer[0], 0x1002, local_bss_tlli2, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_complete, sizeof(dtap_attach_complete)); dump_peers(stdout, 0, 0, &gbcfg); OSMO_ASSERT(!gbproxy_link_info_by_sgsn_tlli(peer, local_sgsn_tlli2, SGSN_NSEI)); link_info = gbproxy_link_info_by_sgsn_tlli(peer, local_sgsn_tlli2, SGSN2_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.assigned == local_bss_tlli2); OSMO_ASSERT(link_info->tlli.current == foreign_bss_tlli2); OSMO_ASSERT(link_info->tlli.bss_validated); OSMO_ASSERT(!link_info->tlli.net_validated); OSMO_ASSERT(link_info->sgsn_tlli.assigned == local_sgsn_tlli2); OSMO_ASSERT(link_info->sgsn_tlli.current == random_sgsn_tlli2); OSMO_ASSERT(link_info->sgsn_tlli.bss_validated); OSMO_ASSERT(!link_info->sgsn_tlli.net_validated); send_llc_dl_ui(nsi, "GMM INFO", &sgsn_peer[1], 0x1002, local_sgsn_tlli2, 1, imsi2, sizeof(imsi2), GPRS_SAPI_GMM, sgsn_nu++, dtap_gmm_information, sizeof(dtap_gmm_information)); dump_peers(stdout, 0, 0, &gbcfg); OSMO_ASSERT(!gbproxy_link_info_by_sgsn_tlli(peer, local_sgsn_tlli2, SGSN_NSEI)); link_info = gbproxy_link_info_by_sgsn_tlli(peer, local_sgsn_tlli2, SGSN2_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.current == local_bss_tlli2); OSMO_ASSERT(link_info->tlli.assigned == 0); OSMO_ASSERT(link_info->sgsn_tlli.current == local_sgsn_tlli2); OSMO_ASSERT(link_info->sgsn_tlli.assigned == 0); /* Non-DTAP */ send_bssgp_ul_unitdata(nsi, "XID (UL)", &bss_peer[0], 0x1002, local_bss_tlli2, &rai_bss, cell_id, llc_u_xid_ul, sizeof(llc_u_xid_ul)); send_bssgp_dl_unitdata(nsi, "XID (DL)", &sgsn_peer[1], 0x1002, local_sgsn_tlli2, 1, imsi2, sizeof(imsi2), llc_u_xid_dl, sizeof(llc_u_xid_dl)); send_bssgp_ul_unitdata(nsi, "LL11 DNS QUERY (UL)", &bss_peer[0], 0x1002, local_bss_tlli2, &rai_bss, cell_id, llc_ui_ll11_dns_query_ul, sizeof(llc_ui_ll11_dns_query_ul)); send_bssgp_dl_unitdata(nsi, "LL11 DNS RESP (DL)", &sgsn_peer[1], 0x1002, local_sgsn_tlli2, 1, imsi2, sizeof(imsi2), llc_ui_ll11_dns_resp_dl, sizeof(llc_ui_ll11_dns_resp_dl)); dump_peers(stdout, 0, 0, &gbcfg); /* Other messages */ send_bssgp_llc_discarded(nsi, &bss_peer[0], 0x1002, local_bss_tlli2, 1, 12); dump_peers(stdout, 0, 0, &gbcfg); send_bssgp_llc_discarded(nsi, &sgsn_peer[1], 0x1002, local_sgsn_tlli2, 1, 12); dump_peers(stdout, 0, 0, &gbcfg); send_bssgp_suspend(nsi, &bss_peer[0], local_bss_tlli2, &rai_bss); dump_peers(stdout, 0, 0, &gbcfg); send_bssgp_suspend_ack(nsi, &sgsn_peer[1], local_sgsn_tlli2, &rai_sgsn); dump_peers(stdout, 0, 0, &gbcfg); printf("--- Establish GPRS connection (SGSN 2, P-TMSI collision) ---\n\n"); send_llc_ul_ui(nsi, "ATTACH REQUEST", &bss_peer[0], 0x1002, foreign_bss_tlli3, &rai_unknown, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_req, sizeof(dtap_attach_req)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "IDENT RESPONSE", &bss_peer[0], 0x1002, foreign_bss_tlli3, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_identity3_resp, sizeof(dtap_identity3_resp)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "IDENT REQUEST", &sgsn_peer[1], 0x1002, random_sgsn_tlli3, 0, NULL, 0, GPRS_SAPI_GMM, sgsn_nu++, dtap_identity_req, sizeof(dtap_identity_req)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "IDENT RESPONSE", &bss_peer[0], 0x1002, foreign_bss_tlli3, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_identity3_resp, sizeof(dtap_identity3_resp)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "ATTACH ACCEPT (P-TMSI 1)", &sgsn_peer[1], 0x1002, random_sgsn_tlli3, 1, imsi3, sizeof(imsi3), GPRS_SAPI_GMM, sgsn_nu++, dtap_attach_acc, sizeof(dtap_attach_acc)); dump_peers(stdout, 0, 0, &gbcfg); OSMO_ASSERT(!gbproxy_link_info_by_sgsn_tlli(peer, random_sgsn_tlli3, SGSN_NSEI)); link_info = gbproxy_link_info_by_sgsn_tlli(peer, random_sgsn_tlli3, SGSN2_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.assigned == local_bss_tlli3); OSMO_ASSERT(link_info->tlli.current == foreign_bss_tlli3); OSMO_ASSERT(!link_info->tlli.bss_validated); OSMO_ASSERT(!link_info->tlli.net_validated); OSMO_ASSERT(link_info->tlli.ptmsi == bss_ptmsi3); OSMO_ASSERT(link_info->sgsn_tlli.assigned == local_sgsn_tlli); OSMO_ASSERT(link_info->sgsn_tlli.current == random_sgsn_tlli3); OSMO_ASSERT(!link_info->sgsn_tlli.bss_validated); OSMO_ASSERT(!link_info->sgsn_tlli.net_validated); OSMO_ASSERT(link_info->sgsn_tlli.ptmsi == sgsn_ptmsi); send_llc_ul_ui(nsi, "ATTACH COMPLETE", &bss_peer[0], 0x1002, local_bss_tlli3, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_complete, sizeof(dtap_attach_complete)); dump_peers(stdout, 0, 0, &gbcfg); other_info = gbproxy_link_info_by_sgsn_tlli(peer, local_sgsn_tlli, SGSN_NSEI); OSMO_ASSERT(other_info); link_info = gbproxy_link_info_by_sgsn_tlli(peer, local_sgsn_tlli, SGSN2_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info != other_info); OSMO_ASSERT(link_info->tlli.assigned == local_bss_tlli3); OSMO_ASSERT(link_info->tlli.current == foreign_bss_tlli3); OSMO_ASSERT(link_info->tlli.bss_validated); OSMO_ASSERT(!link_info->tlli.net_validated); OSMO_ASSERT(link_info->sgsn_tlli.assigned == local_sgsn_tlli); OSMO_ASSERT(link_info->sgsn_tlli.current == random_sgsn_tlli3); OSMO_ASSERT(link_info->sgsn_tlli.bss_validated); OSMO_ASSERT(!link_info->sgsn_tlli.net_validated); send_llc_dl_ui(nsi, "GMM INFO", &sgsn_peer[1], 0x1002, local_sgsn_tlli, 1, imsi3, sizeof(imsi3), GPRS_SAPI_GMM, sgsn_nu++, dtap_gmm_information, sizeof(dtap_gmm_information)); dump_peers(stdout, 0, 0, &gbcfg); other_info = gbproxy_link_info_by_sgsn_tlli(peer, local_sgsn_tlli, SGSN_NSEI); OSMO_ASSERT(other_info); link_info = gbproxy_link_info_by_sgsn_tlli(peer, local_sgsn_tlli, SGSN2_NSEI); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info != other_info); OSMO_ASSERT(link_info->tlli.current == local_bss_tlli3); OSMO_ASSERT(link_info->tlli.assigned == 0); OSMO_ASSERT(link_info->sgsn_tlli.current == local_sgsn_tlli); OSMO_ASSERT(link_info->sgsn_tlli.assigned == 0); printf("--- Shutdown GPRS connection (SGSN 1) ---\n\n"); /* Detach */ send_llc_ul_ui(nsi, "DETACH REQ", &bss_peer[0], 0x1002, local_bss_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_detach_req, sizeof(dtap_detach_req)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "DETACH ACC", &sgsn_peer[0], 0x1002, local_sgsn_tlli, 1, imsi1, sizeof(imsi1), GPRS_SAPI_GMM, sgsn_nu++, dtap_detach_acc, sizeof(dtap_detach_acc)); dump_peers(stdout, 0, 0, &gbcfg); printf("--- Shutdown GPRS connection (SGSN 2) ---\n\n"); send_llc_ul_ui(nsi, "DETACH REQ", &bss_peer[0], 0x1002, local_bss_tlli2, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_detach_req, sizeof(dtap_detach_req)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "DETACH ACC", &sgsn_peer[1], 0x1002, local_sgsn_tlli2, 1, imsi2, sizeof(imsi2), GPRS_SAPI_GMM, sgsn_nu++, dtap_detach_acc, sizeof(dtap_detach_acc)); dump_peers(stdout, 0, 0, &gbcfg); printf("--- Shutdown GPRS connection (SGSN 2, P-TMSI 1) ---\n\n"); send_llc_ul_ui(nsi, "DETACH REQ", &bss_peer[0], 0x1002, local_bss_tlli3, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_detach_req, sizeof(dtap_detach_req)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "DETACH ACC", &sgsn_peer[1], 0x1002, local_sgsn_tlli, 1, imsi3, sizeof(imsi3), GPRS_SAPI_GMM, sgsn_nu++, dtap_detach_acc, sizeof(dtap_detach_acc)); dump_peers(stdout, 0, 0, &gbcfg); dump_global(stdout, 0); gbproxy_clear_patch_filter(&gbcfg.matches[GBPROX_MATCH_ROUTING]); gbprox_reset(&gbcfg); gprs_ns_destroy(nsi); nsi = NULL; } static void test_gbproxy_keep_info() { struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, NULL); struct sockaddr_in bss_peer[1] = {{0},}; struct sockaddr_in sgsn_peer= {0}; struct gprs_ra_id rai_bss = {.mcc = 112, .mnc = 332, .lac = 16464, .rac = 96}; uint16_t cell_id = 0x1234; const uint32_t ptmsi = 0xefe2b700; const uint32_t local_tlli = 0xefe2b700; const uint32_t foreign_tlli = 0xafe2b700; const uint8_t imsi[] = {0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18}; struct gbproxy_link_info *link_info, *link_info2; struct gbproxy_peer *peer; unsigned bss_nu = 0; unsigned sgsn_nu = 0; LLIST_HEAD(rcv_list); OSMO_ASSERT(local_tlli == gprs_tmsi2tlli(ptmsi, TLLI_LOCAL)); bssgp_nsi = nsi; gbcfg.nsi = bssgp_nsi; gbcfg.nsip_sgsn_nsei = SGSN_NSEI; gbcfg.patch_ptmsi = 0; gbcfg.acquire_imsi = 1; gbcfg.bss_ptmsi_state = 0; gbcfg.sgsn_tlli_state = 1; gbcfg.core_mcc = 0; gbcfg.core_mnc = 0; gbcfg.core_apn = NULL; gbcfg.core_apn_size = 0; gbcfg.route_to_sgsn2 = 0; gbcfg.nsip_sgsn2_nsei = 0xffff; gbcfg.keep_link_infos = GBPROX_KEEP_ALWAYS; configure_sgsn_peer(&sgsn_peer); configure_bss_peers(bss_peer, ARRAY_SIZE(bss_peer)); printf("=== %s ===\n", __func__); printf("--- Initialise SGSN ---\n\n"); connect_sgsn(nsi, &sgsn_peer, SGSN_NSEI); printf("--- Initialise BSS 1 ---\n\n"); setup_ns(nsi, &bss_peer[0], 0x1001, 0x1000); setup_bssgp(nsi, &bss_peer[0], 0x1002); peer = gbproxy_peer_by_nsei(&gbcfg, 0x1000); OSMO_ASSERT(peer != NULL); send_bssgp_reset_ack(nsi, &sgsn_peer, 0x1002); gprs_dump_nsi(nsi); dump_global(stdout, 0); dump_peers(stdout, 0, 0, &gbcfg); printf("--- Send message from BSS 1 to SGSN, BVCI 0x1002 ---\n\n"); received_messages = &rcv_list; send_llc_ul_ui(nsi, "ATTACH REQUEST", &bss_peer[0], 0x1002, foreign_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_req, sizeof(dtap_attach_req)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_ID_REQ)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_tlli(peer, foreign_tlli); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->imsi_len == 0); OSMO_ASSERT(!link_info->is_deregistered); OSMO_ASSERT(link_info->imsi_acq_pending); send_llc_ul_ui(nsi, "IDENT RESPONSE", &bss_peer[0], 0x1002, foreign_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_identity_resp, sizeof(dtap_identity_resp)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_REQ)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_tlli(peer, foreign_tlli); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->imsi_len > 0); OSMO_ASSERT(!link_info->imsi_acq_pending); OSMO_ASSERT(gprs_tlli_type(link_info->sgsn_tlli.current) == TLLI_FOREIGN); send_llc_dl_ui(nsi, "IDENT REQUEST", &sgsn_peer, 0x1002, foreign_tlli, 0, NULL, 0, GPRS_SAPI_GMM, sgsn_nu++, dtap_identity_req, sizeof(dtap_identity_req)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_ID_REQ)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "IDENT RESPONSE", &bss_peer[0], 0x1002, foreign_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_identity_resp, sizeof(dtap_identity_resp)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ID_RESP)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_tlli(peer, foreign_tlli); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->imsi_len > 0); OSMO_ASSERT(gbproxy_link_info_by_imsi(peer, imsi, sizeof(imsi))); send_llc_dl_ui(nsi, "ATTACH ACCEPT", &sgsn_peer, 0x1002, foreign_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_attach_acc, sizeof(dtap_attach_acc)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_ATTACH_ACK)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "ATTACH COMPLETE", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_complete, sizeof(dtap_attach_complete)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_COMPL)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "GMM INFO", &sgsn_peer, 0x1002, local_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_gmm_information, sizeof(dtap_gmm_information)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_INFO)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_tlli(peer, local_tlli); OSMO_ASSERT(link_info); /* Detach (MO) */ send_llc_ul_ui(nsi, "DETACH REQ", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_detach_req, sizeof(dtap_detach_req)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_DETACH_REQ)); link_info = gbproxy_link_info_by_tlli(peer, local_tlli); OSMO_ASSERT(link_info); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "DETACH ACC", &sgsn_peer, 0x1002, local_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_detach_acc, sizeof(dtap_detach_acc)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_DETACH_ACK)); dump_peers(stdout, 0, 0, &gbcfg); OSMO_ASSERT(!gbproxy_link_info_by_tlli(peer, local_tlli)); link_info = gbproxy_link_info_by_imsi(peer, imsi, sizeof(imsi)); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->is_deregistered); OSMO_ASSERT(!expect_msg()); /* Re-Attach */ send_llc_ul_ui(nsi, "ATTACH REQUEST", &bss_peer[0], 0x1002, foreign_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_req3, sizeof(dtap_attach_req3)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_REQ)); dump_peers(stdout, 0, 0, &gbcfg); link_info2 = gbproxy_link_info_by_imsi(peer, imsi, sizeof(imsi)); link_info = gbproxy_link_info_by_tlli(peer, foreign_tlli); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info == link_info2); OSMO_ASSERT(link_info->imsi_len != 0); OSMO_ASSERT(!link_info->is_deregistered); OSMO_ASSERT(!link_info->imsi_acq_pending); OSMO_ASSERT(gprs_tlli_type(link_info->sgsn_tlli.current) == TLLI_FOREIGN); send_llc_dl_ui(nsi, "ATTACH ACCEPT", &sgsn_peer, 0x1002, foreign_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_attach_acc, sizeof(dtap_attach_acc)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_ATTACH_ACK)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "ATTACH COMPLETE", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_complete, sizeof(dtap_attach_complete)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_COMPL)); dump_peers(stdout, 0, 0, &gbcfg); /* Detach (MT) */ send_llc_dl_ui(nsi, "DETACH REQ (re-attach)", &sgsn_peer, 0x1002, local_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_mt_detach_rea_req, sizeof(dtap_mt_detach_rea_req)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_DETACH_REQ)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_tlli(peer, local_tlli); OSMO_ASSERT(link_info); send_llc_ul_ui(nsi, "DETACH ACC", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_mt_detach_acc, sizeof(dtap_mt_detach_acc)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_DETACH_ACK)); OSMO_ASSERT(!expect_msg()); dump_peers(stdout, 0, 0, &gbcfg); OSMO_ASSERT(!gbproxy_link_info_by_tlli(peer, local_tlli)); link_info = gbproxy_link_info_by_imsi(peer, imsi, sizeof(imsi)); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->is_deregistered); /* Re-Attach */ send_llc_ul_ui(nsi, "ATTACH REQUEST", &bss_peer[0], 0x1002, foreign_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_req3, sizeof(dtap_attach_req3)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_REQ)); dump_peers(stdout, 0, 0, &gbcfg); link_info2 = gbproxy_link_info_by_imsi(peer, imsi, sizeof(imsi)); link_info = gbproxy_link_info_by_tlli(peer, foreign_tlli); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info == link_info2); OSMO_ASSERT(link_info->imsi_len != 0); OSMO_ASSERT(!link_info->is_deregistered); OSMO_ASSERT(!link_info->imsi_acq_pending); send_llc_dl_ui(nsi, "ATTACH ACCEPT", &sgsn_peer, 0x1002, foreign_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_attach_acc, sizeof(dtap_attach_acc)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_ATTACH_ACK)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "ATTACH COMPLETE", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_complete, sizeof(dtap_attach_complete)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_COMPL)); dump_peers(stdout, 0, 0, &gbcfg); /* Detach (MT) */ send_llc_dl_ui(nsi, "DETACH REQ", &sgsn_peer, 0x1002, local_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_mt_detach_req, sizeof(dtap_mt_detach_req)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_DETACH_REQ)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_tlli(peer, local_tlli); OSMO_ASSERT(link_info); send_llc_ul_ui(nsi, "DETACH ACC", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_mt_detach_acc, sizeof(dtap_mt_detach_acc)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_DETACH_ACK)); OSMO_ASSERT(!expect_msg()); dump_peers(stdout, 0, 0, &gbcfg); OSMO_ASSERT(!gbproxy_link_info_by_tlli(peer, local_tlli)); link_info = gbproxy_link_info_by_imsi(peer, imsi, sizeof(imsi)); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->is_deregistered); /* Re-Attach with IMSI */ send_llc_ul_ui(nsi, "ATTACH REQUEST (IMSI)", &bss_peer[0], 0x1002, foreign_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_req4, sizeof(dtap_attach_req4)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_REQ)); dump_peers(stdout, 0, 0, &gbcfg); link_info2 = gbproxy_link_info_by_imsi(peer, imsi, sizeof(imsi)); link_info = gbproxy_link_info_by_tlli(peer, foreign_tlli); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info == link_info2); OSMO_ASSERT(link_info->imsi_len != 0); OSMO_ASSERT(!link_info->is_deregistered); OSMO_ASSERT(!link_info->imsi_acq_pending); OSMO_ASSERT(link_info->sgsn_tlli.current == foreign_tlli); send_llc_dl_ui(nsi, "ATTACH ACCEPT", &sgsn_peer, 0x1002, foreign_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_attach_acc, sizeof(dtap_attach_acc)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_ATTACH_ACK)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "ATTACH COMPLETE", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_complete, sizeof(dtap_attach_complete)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_COMPL)); dump_peers(stdout, 0, 0, &gbcfg); /* Detach (MT) */ send_llc_dl_ui(nsi, "DETACH REQ", &sgsn_peer, 0x1002, local_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_mt_detach_req, sizeof(dtap_mt_detach_req)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_DETACH_REQ)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_tlli(peer, local_tlli); OSMO_ASSERT(link_info); send_llc_ul_ui(nsi, "DETACH ACC", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_mt_detach_acc, sizeof(dtap_mt_detach_acc)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_DETACH_ACK)); OSMO_ASSERT(!expect_msg()); dump_peers(stdout, 0, 0, &gbcfg); OSMO_ASSERT(!gbproxy_link_info_by_tlli(peer, local_tlli)); link_info = gbproxy_link_info_by_imsi(peer, imsi, sizeof(imsi)); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->is_deregistered); /* Re-Attach */ send_llc_ul_ui(nsi, "ATTACH REQUEST", &bss_peer[0], 0x1002, foreign_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_req3, sizeof(dtap_attach_req3)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_REQ)); dump_peers(stdout, 0, 0, &gbcfg); link_info2 = gbproxy_link_info_by_imsi(peer, imsi, sizeof(imsi)); link_info = gbproxy_link_info_by_tlli(peer, foreign_tlli); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info == link_info2); OSMO_ASSERT(link_info->imsi_len != 0); OSMO_ASSERT(!link_info->is_deregistered); OSMO_ASSERT(!link_info->imsi_acq_pending); send_llc_dl_ui(nsi, "ATTACH ACCEPT", &sgsn_peer, 0x1002, foreign_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_attach_acc, sizeof(dtap_attach_acc)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_ATTACH_ACK)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "ATTACH COMPLETE", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_complete, sizeof(dtap_attach_complete)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_COMPL)); dump_peers(stdout, 0, 0, &gbcfg); /* RA update procedure (reject -> Detach) */ send_llc_ul_ui(nsi, "RA UPD REQ", &bss_peer[0], 0x1002, local_tlli, &rai_bss, 0x7080, GPRS_SAPI_GMM, bss_nu++, dtap_ra_upd_req, sizeof(dtap_ra_upd_req)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_RA_UPD_REQ)); send_llc_dl_ui(nsi, "RA UDP REJ", &sgsn_peer, 0x1002, local_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_ra_upd_rej, sizeof(dtap_ra_upd_rej)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_RA_UPD_REJ)); OSMO_ASSERT(!expect_msg()); dump_peers(stdout, 0, 0, &gbcfg); OSMO_ASSERT(!gbproxy_link_info_by_tlli(peer, local_tlli)); link_info = gbproxy_link_info_by_imsi(peer, imsi, sizeof(imsi)); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->is_deregistered); /* Bad case: Re-Attach with wrong (initial) P-TMSI */ send_llc_ul_ui(nsi, "ATTACH REQUEST", &bss_peer[0], 0x1002, foreign_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_req, sizeof(dtap_attach_req)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_ID_REQ)); dump_peers(stdout, 0, 0, &gbcfg); link_info2 = gbproxy_link_info_by_imsi(peer, imsi, sizeof(imsi)); link_info = gbproxy_link_info_by_tlli(peer, foreign_tlli); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info != link_info2); OSMO_ASSERT(link_info->imsi_len == 0); OSMO_ASSERT(!link_info->is_deregistered); OSMO_ASSERT(link_info->imsi_acq_pending); send_llc_ul_ui(nsi, "IDENT RESPONSE", &bss_peer[0], 0x1002, foreign_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_identity_resp, sizeof(dtap_identity_resp)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_REQ)); dump_peers(stdout, 0, 0, &gbcfg); link_info2 = gbproxy_link_info_by_imsi(peer, imsi, sizeof(imsi)); link_info = gbproxy_link_info_by_tlli(peer, foreign_tlli); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info == link_info2); OSMO_ASSERT(link_info->imsi_len != 0); OSMO_ASSERT(!link_info->is_deregistered); OSMO_ASSERT(!link_info->imsi_acq_pending); send_llc_dl_ui(nsi, "ATTACH ACCEPT", &sgsn_peer, 0x1002, foreign_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_attach_acc, sizeof(dtap_attach_acc)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_ATTACH_ACK)); dump_peers(stdout, 0, 0, &gbcfg); link_info2 = gbproxy_link_info_by_imsi(peer, imsi, sizeof(imsi)); link_info = gbproxy_link_info_by_tlli(peer, foreign_tlli); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info == link_info2); OSMO_ASSERT(link_info->imsi_len > 0); send_llc_ul_ui(nsi, "ATTACH COMPLETE", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_complete, sizeof(dtap_attach_complete)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_COMPL)); dump_peers(stdout, 0, 0, &gbcfg); /* Detach (MT) */ send_llc_dl_ui(nsi, "DETACH REQ", &sgsn_peer, 0x1002, local_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_mt_detach_req, sizeof(dtap_mt_detach_req)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_DETACH_REQ)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_tlli(peer, local_tlli); OSMO_ASSERT(link_info); send_llc_ul_ui(nsi, "DETACH ACC", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_mt_detach_acc, sizeof(dtap_mt_detach_acc)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_DETACH_ACK)); dump_peers(stdout, 0, 0, &gbcfg); OSMO_ASSERT(!gbproxy_link_info_by_tlli(peer, local_tlli)); link_info = gbproxy_link_info_by_imsi(peer, imsi, sizeof(imsi)); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->is_deregistered); OSMO_ASSERT(!expect_msg()); /* Bad case: Re-Attach with local TLLI */ send_llc_ul_ui(nsi, "ATTACH REQUEST (local TLLI)", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_req3, sizeof(dtap_attach_req3)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_REQ)); dump_peers(stdout, 0, 0, &gbcfg); link_info2 = gbproxy_link_info_by_imsi(peer, imsi, sizeof(imsi)); link_info = gbproxy_link_info_by_tlli(peer, local_tlli); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info == link_info2); OSMO_ASSERT(link_info->imsi_len != 0); OSMO_ASSERT(!link_info->is_deregistered); OSMO_ASSERT(!link_info->imsi_acq_pending); OSMO_ASSERT(link_info->sgsn_tlli.current == local_tlli); send_llc_dl_ui(nsi, "ATTACH ACCEPT", &sgsn_peer, 0x1002, local_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_attach_acc, sizeof(dtap_attach_acc)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_ATTACH_ACK)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "ATTACH COMPLETE", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_complete, sizeof(dtap_attach_complete)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_COMPL)); dump_peers(stdout, 0, 0, &gbcfg); /* Detach (MT) */ send_llc_dl_ui(nsi, "DETACH REQ (re-attach)", &sgsn_peer, 0x1002, local_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_mt_detach_rea_req, sizeof(dtap_mt_detach_rea_req)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_DETACH_REQ)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_tlli(peer, local_tlli); OSMO_ASSERT(link_info); send_llc_ul_ui(nsi, "DETACH ACC", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_mt_detach_acc, sizeof(dtap_mt_detach_acc)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_DETACH_ACK)); OSMO_ASSERT(!expect_msg()); dump_peers(stdout, 0, 0, &gbcfg); OSMO_ASSERT(!gbproxy_link_info_by_tlli(peer, local_tlli)); link_info = gbproxy_link_info_by_imsi(peer, imsi, sizeof(imsi)); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->is_deregistered); /* Bad case: Unexpected Re-Attach with IMSI after completed attachment * procedure */ send_llc_ul_ui(nsi, "ATTACH REQUEST", &bss_peer[0], 0x1002, foreign_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_req3, sizeof(dtap_attach_req3)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_REQ)); dump_peers(stdout, 0, 0, &gbcfg); link_info2 = gbproxy_link_info_by_imsi(peer, imsi, sizeof(imsi)); link_info = gbproxy_link_info_by_tlli(peer, foreign_tlli); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info == link_info2); OSMO_ASSERT(link_info->imsi_len != 0); OSMO_ASSERT(!link_info->is_deregistered); OSMO_ASSERT(!link_info->imsi_acq_pending); send_llc_dl_ui(nsi, "ATTACH ACCEPT", &sgsn_peer, 0x1002, foreign_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_attach_acc, sizeof(dtap_attach_acc)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_ATTACH_ACK)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "ATTACH COMPLETE", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_complete, sizeof(dtap_attach_complete)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_COMPL)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "GMM INFO", &sgsn_peer, 0x1002, local_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_gmm_information, sizeof(dtap_gmm_information)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_INFO)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "ATTACH REQUEST (unexpected, IMSI)", &bss_peer[0], 0x1002, foreign_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_req4, sizeof(dtap_attach_req4)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_REQ)); dump_peers(stdout, 0, 0, &gbcfg); link_info2 = gbproxy_link_info_by_imsi(peer, imsi, sizeof(imsi)); link_info = gbproxy_link_info_by_tlli(peer, foreign_tlli); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info == link_info2); OSMO_ASSERT(link_info->imsi_len != 0); OSMO_ASSERT(!link_info->is_deregistered); OSMO_ASSERT(!link_info->imsi_acq_pending); OSMO_ASSERT(link_info->sgsn_tlli.current == foreign_tlli); OSMO_ASSERT(link_info->sgsn_tlli.assigned == 0); send_llc_dl_ui(nsi, "ATTACH ACCEPT", &sgsn_peer, 0x1002, foreign_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_attach_acc, sizeof(dtap_attach_acc)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_ATTACH_ACK)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "ATTACH COMPLETE", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_complete, sizeof(dtap_attach_complete)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_COMPL)); dump_peers(stdout, 0, 0, &gbcfg); /* Detach (MT) */ send_llc_dl_ui(nsi, "DETACH REQ", &sgsn_peer, 0x1002, local_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_mt_detach_req, sizeof(dtap_mt_detach_req)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_DETACH_REQ)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_tlli(peer, local_tlli); OSMO_ASSERT(link_info); send_llc_ul_ui(nsi, "DETACH ACC", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_mt_detach_acc, sizeof(dtap_mt_detach_acc)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_DETACH_ACK)); OSMO_ASSERT(!expect_msg()); dump_peers(stdout, 0, 0, &gbcfg); OSMO_ASSERT(!gbproxy_link_info_by_tlli(peer, local_tlli)); link_info = gbproxy_link_info_by_imsi(peer, imsi, sizeof(imsi)); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->is_deregistered); /* Bad case: Unexpected Re-Attach with P-TMSI after completed attachment * procedure */ send_llc_ul_ui(nsi, "ATTACH REQUEST", &bss_peer[0], 0x1002, foreign_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_req3, sizeof(dtap_attach_req3)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_REQ)); dump_peers(stdout, 0, 0, &gbcfg); link_info2 = gbproxy_link_info_by_imsi(peer, imsi, sizeof(imsi)); link_info = gbproxy_link_info_by_tlli(peer, foreign_tlli); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info == link_info2); OSMO_ASSERT(link_info->imsi_len != 0); OSMO_ASSERT(!link_info->is_deregistered); OSMO_ASSERT(!link_info->imsi_acq_pending); send_llc_dl_ui(nsi, "ATTACH ACCEPT", &sgsn_peer, 0x1002, foreign_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_attach_acc, sizeof(dtap_attach_acc)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_ATTACH_ACK)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "ATTACH COMPLETE", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_complete, sizeof(dtap_attach_complete)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_COMPL)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_dl_ui(nsi, "GMM INFO", &sgsn_peer, 0x1002, local_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_gmm_information, sizeof(dtap_gmm_information)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_INFO)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "ATTACH REQUEST (unexpected)", &bss_peer[0], 0x1002, foreign_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_req3, sizeof(dtap_attach_req3)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_REQ)); dump_peers(stdout, 0, 0, &gbcfg); link_info2 = gbproxy_link_info_by_imsi(peer, imsi, sizeof(imsi)); link_info = gbproxy_link_info_by_tlli(peer, foreign_tlli); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info == link_info2); OSMO_ASSERT(link_info->imsi_len != 0); OSMO_ASSERT(!link_info->is_deregistered); OSMO_ASSERT(!link_info->imsi_acq_pending); OSMO_ASSERT(link_info->sgsn_tlli.current == foreign_tlli); OSMO_ASSERT(link_info->sgsn_tlli.assigned == 0); send_llc_dl_ui(nsi, "ATTACH ACCEPT", &sgsn_peer, 0x1002, foreign_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_attach_acc, sizeof(dtap_attach_acc)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_ATTACH_ACK)); dump_peers(stdout, 0, 0, &gbcfg); send_llc_ul_ui(nsi, "ATTACH COMPLETE", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_complete, sizeof(dtap_attach_complete)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_COMPL)); dump_peers(stdout, 0, 0, &gbcfg); /* Detach (MT) */ send_llc_dl_ui(nsi, "DETACH REQ", &sgsn_peer, 0x1002, local_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_mt_detach_req, sizeof(dtap_mt_detach_req)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_DETACH_REQ)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_tlli(peer, local_tlli); OSMO_ASSERT(link_info); send_llc_ul_ui(nsi, "DETACH ACC", &bss_peer[0], 0x1002, local_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_mt_detach_acc, sizeof(dtap_mt_detach_acc)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_DETACH_ACK)); OSMO_ASSERT(!expect_msg()); dump_peers(stdout, 0, 0, &gbcfg); OSMO_ASSERT(!gbproxy_link_info_by_tlli(peer, local_tlli)); link_info = gbproxy_link_info_by_imsi(peer, imsi, sizeof(imsi)); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->is_deregistered); /* Attach rejected */ gbproxy_delete_link_infos(peer); send_llc_ul_ui(nsi, "ATTACH REQUEST", &bss_peer[0], 0x1002, foreign_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_req, sizeof(dtap_attach_req)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_ID_REQ)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_tlli(peer, foreign_tlli); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->imsi_len == 0); OSMO_ASSERT(!link_info->is_deregistered); OSMO_ASSERT(link_info->imsi_acq_pending); send_llc_ul_ui(nsi, "IDENT RESPONSE", &bss_peer[0], 0x1002, foreign_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_identity_resp, sizeof(dtap_identity_resp)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_REQ)); dump_peers(stdout, 0, 0, &gbcfg); link_info2 = gbproxy_link_info_by_imsi(peer, imsi, sizeof(imsi)); link_info = gbproxy_link_info_by_tlli(peer, foreign_tlli); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info == link_info2); OSMO_ASSERT(link_info->imsi_len != 0); OSMO_ASSERT(!link_info->is_deregistered); OSMO_ASSERT(!link_info->imsi_acq_pending); send_llc_dl_ui(nsi, "ATTACH REJECT", &sgsn_peer, 0x1002, foreign_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_attach_rej7, sizeof(dtap_attach_rej7)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_ATTACH_REJ)); dump_peers(stdout, 0, 0, &gbcfg); OSMO_ASSERT(!gbproxy_link_info_by_tlli(peer, foreign_tlli)); OSMO_ASSERT(!expect_msg()); /* Attach (incomplete) and Detach (MO) */ gbproxy_delete_link_infos(peer); send_llc_ul_ui(nsi, "ATTACH REQUEST", &bss_peer[0], 0x1002, foreign_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_req, sizeof(dtap_attach_req)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_ID_REQ)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_tlli(peer, foreign_tlli); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->imsi_len == 0); OSMO_ASSERT(!link_info->is_deregistered); OSMO_ASSERT(link_info->imsi_acq_pending); send_llc_ul_ui(nsi, "DETACH REQ (MO)", &bss_peer[0], 0x1002, foreign_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_detach_req, sizeof(dtap_detach_req)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_DETACH_ACK)); dump_peers(stdout, 0, 0, &gbcfg); OSMO_ASSERT(!expect_msg()); /* Attach (incomplete) and Detach (MT) */ gbproxy_delete_link_infos(peer); send_llc_ul_ui(nsi, "ATTACH REQUEST", &bss_peer[0], 0x1002, foreign_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_attach_req, sizeof(dtap_attach_req)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_ID_REQ)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_tlli(peer, foreign_tlli); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->imsi_len == 0); OSMO_ASSERT(!link_info->is_deregistered); OSMO_ASSERT(link_info->imsi_acq_pending); send_llc_dl_ui(nsi, "DETACH REQ (MT)", &sgsn_peer, 0x1002, foreign_tlli, 1, imsi, sizeof(imsi), GPRS_SAPI_GMM, sgsn_nu++, dtap_mt_detach_req, sizeof(dtap_mt_detach_req)); OSMO_ASSERT(expect_gmm_msg(0x1000, 0x1002, GSM48_MT_GMM_DETACH_REQ)); dump_peers(stdout, 0, 0, &gbcfg); link_info = gbproxy_link_info_by_tlli(peer, foreign_tlli); OSMO_ASSERT(link_info); send_llc_ul_ui(nsi, "DETACH ACC", &bss_peer[0], 0x1002, foreign_tlli, &rai_bss, cell_id, GPRS_SAPI_GMM, bss_nu++, dtap_mt_detach_acc, sizeof(dtap_mt_detach_acc)); /* TODO: The stored messaged should be cleaned when receiving a Detach * Ack. Remove the first OSMO_ASSERT when this is fixed. */ OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_ATTACH_REQ)); OSMO_ASSERT(expect_gmm_msg(SGSN_NSEI, 0x1002, GSM48_MT_GMM_DETACH_ACK)); dump_peers(stdout, 0, 0, &gbcfg); OSMO_ASSERT(!gbproxy_link_info_by_tlli(peer, foreign_tlli)); link_info = gbproxy_link_info_by_imsi(peer, imsi, sizeof(imsi)); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->is_deregistered); OSMO_ASSERT(!expect_msg()); received_messages = NULL; dump_global(stdout, 0); gbprox_reset(&gbcfg); gprs_ns_destroy(nsi); nsi = NULL; } struct gbproxy_link_info *register_tlli( struct gbproxy_peer *peer, uint32_t tlli, const uint8_t *imsi, size_t imsi_len, time_t now) { struct gbproxy_link_info *link_info; int imsi_matches = -1; int tlli_already_known = 0; struct gbproxy_config *cfg = peer->cfg; /* Check, whether the IMSI matches */ if (gprs_is_mi_imsi(imsi, imsi_len)) { imsi_matches = gbproxy_check_imsi( &cfg->matches[GBPROX_MATCH_PATCHING], imsi, imsi_len); if (imsi_matches < 0) return NULL; } link_info = gbproxy_link_info_by_tlli(peer, tlli); if (!link_info) { link_info = gbproxy_link_info_by_imsi(peer, imsi, imsi_len); if (link_info) { /* TLLI has changed somehow, adjust it */ LOGP(DGPRS, LOGL_INFO, "The TLLI has changed from %08x to %08x\n", link_info->tlli.current, tlli); link_info->tlli.current = tlli; } } if (!link_info) { link_info = gbproxy_link_info_alloc(peer); link_info->tlli.current = tlli; } else { gbproxy_detach_link_info(peer, link_info); tlli_already_known = 1; } OSMO_ASSERT(link_info != NULL); if (!tlli_already_known) LOGP(DGPRS, LOGL_INFO, "Adding TLLI %08x to list\n", tlli); gbproxy_attach_link_info(peer, now, link_info); gbproxy_update_link_info(link_info, imsi, imsi_len); if (imsi_matches >= 0) link_info->is_matching[GBPROX_MATCH_PATCHING] = imsi_matches; return link_info; } static void test_gbproxy_tlli_expire(void) { struct gbproxy_config cfg = {0}; struct gbproxy_peer *peer; const char *err_msg = NULL; const uint8_t imsi1[] = { GSM_MI_TYPE_IMSI, 0x23, 0x24, 0x25, 0x26 }; const uint8_t imsi2[] = { GSM_MI_TYPE_IMSI, 0x26, 0x27, 0x28, 0x29 }; const uint8_t imsi3[] = { GSM_MI_TYPE_IMSI | 0x10, 0x32, 0x54, 0x76, 0xf8 }; const uint32_t tlli1 = 1234 | 0xc0000000; const uint32_t tlli2 = 5678 | 0xc0000000; const uint32_t tlli3 = 3456 | 0xc0000000; const char *filter_re = ".*"; time_t now = 1407479214; printf("Test TLLI info expiry\n\n"); gbproxy_init_config(&cfg); if (gbproxy_set_patch_filter(&cfg.matches[GBPROX_MATCH_PATCHING], filter_re, &err_msg) != 0) { fprintf(stderr, "gbprox_set_patch_filter: got error: %s\n", err_msg); OSMO_ASSERT(err_msg == NULL); } { struct gbproxy_link_info *link_info; printf("Test TLLI replacement:\n"); cfg.tlli_max_len = 0; cfg.tlli_max_age = 0; peer = gbproxy_peer_alloc(&cfg, 20); OSMO_ASSERT(peer->patch_state.logical_link_count == 0); printf(" Add TLLI 1, IMSI 1\n"); link_info = register_tlli(peer, tlli1, imsi1, ARRAY_SIZE(imsi1), now); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.current == tlli1); OSMO_ASSERT(peer->patch_state.logical_link_count == 1); /* replace the old entry */ printf(" Add TLLI 2, IMSI 1 (should replace TLLI 1)\n"); link_info = register_tlli(peer, tlli2, imsi1, ARRAY_SIZE(imsi1), now); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.current == tlli2); OSMO_ASSERT(peer->patch_state.logical_link_count == 1); dump_peers(stdout, 2, now, &cfg); /* verify that 5678 has survived */ link_info = gbproxy_link_info_by_imsi(peer, imsi1, ARRAY_SIZE(imsi1)); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.current == tlli2); link_info = gbproxy_link_info_by_imsi(peer, imsi2, ARRAY_SIZE(imsi2)); OSMO_ASSERT(!link_info); printf("\n"); gbproxy_peer_free(peer); } { struct gbproxy_link_info *link_info; printf("Test IMSI replacement:\n"); cfg.tlli_max_len = 0; cfg.tlli_max_age = 0; peer = gbproxy_peer_alloc(&cfg, 20); OSMO_ASSERT(peer->patch_state.logical_link_count == 0); printf(" Add TLLI 1, IMSI 1\n"); link_info = register_tlli(peer, tlli1, imsi1, ARRAY_SIZE(imsi1), now); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.current == tlli1); OSMO_ASSERT(peer->patch_state.logical_link_count == 1); /* try to replace the old entry */ printf(" Add TLLI 1, IMSI 2 (should replace IMSI 1)\n"); link_info = register_tlli(peer, tlli1, imsi2, ARRAY_SIZE(imsi2), now); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.current == tlli1); OSMO_ASSERT(peer->patch_state.logical_link_count == 1); dump_peers(stdout, 2, now, &cfg); /* verify that 5678 has survived */ link_info = gbproxy_link_info_by_imsi(peer, imsi1, ARRAY_SIZE(imsi1)); OSMO_ASSERT(!link_info); link_info = gbproxy_link_info_by_imsi(peer, imsi2, ARRAY_SIZE(imsi2)); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.current == tlli1); printf("\n"); gbproxy_peer_free(peer); } { struct gbproxy_link_info *link_info; int num_removed; printf("Test TLLI expiry, max_len == 1:\n"); cfg.tlli_max_len = 1; cfg.tlli_max_age = 0; peer = gbproxy_peer_alloc(&cfg, 20); OSMO_ASSERT(peer->patch_state.logical_link_count == 0); printf(" Add TLLI 1, IMSI 1\n"); register_tlli(peer, tlli1, imsi1, ARRAY_SIZE(imsi1), now); OSMO_ASSERT(peer->patch_state.logical_link_count == 1); /* replace the old entry */ printf(" Add TLLI 2, IMSI 2 (should replace IMSI 1)\n"); register_tlli(peer, tlli2, imsi2, ARRAY_SIZE(imsi2), now); OSMO_ASSERT(peer->patch_state.logical_link_count == 2); num_removed = gbproxy_remove_stale_link_infos(peer, now + 2); OSMO_ASSERT(num_removed == 1); OSMO_ASSERT(peer->patch_state.logical_link_count == 1); dump_peers(stdout, 2, now, &cfg); /* verify that 5678 has survived */ link_info = gbproxy_link_info_by_imsi(peer, imsi1, ARRAY_SIZE(imsi1)); OSMO_ASSERT(!link_info); link_info = gbproxy_link_info_by_imsi(peer, imsi2, ARRAY_SIZE(imsi2)); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.current == tlli2); printf("\n"); gbproxy_peer_free(peer); } { struct gbproxy_link_info *link_info; int num_removed; printf("Test TLLI expiry, max_age == 1:\n"); cfg.tlli_max_len = 0; cfg.tlli_max_age = 1; peer = gbproxy_peer_alloc(&cfg, 20); OSMO_ASSERT(peer->patch_state.logical_link_count == 0); printf(" Add TLLI 1, IMSI 1 (should expire after timeout)\n"); register_tlli(peer, tlli1, imsi1, ARRAY_SIZE(imsi1), now); OSMO_ASSERT(peer->patch_state.logical_link_count == 1); printf(" Add TLLI 2, IMSI 2 (should not expire after timeout)\n"); register_tlli(peer, tlli2, imsi2, ARRAY_SIZE(imsi2), now + 1); OSMO_ASSERT(peer->patch_state.logical_link_count == 2); num_removed = gbproxy_remove_stale_link_infos(peer, now + 2); OSMO_ASSERT(num_removed == 1); OSMO_ASSERT(peer->patch_state.logical_link_count == 1); dump_peers(stdout, 2, now + 2, &cfg); /* verify that 5678 has survived */ link_info = gbproxy_link_info_by_imsi(peer, imsi1, ARRAY_SIZE(imsi1)); OSMO_ASSERT(!link_info); link_info = gbproxy_link_info_by_imsi(peer, imsi2, ARRAY_SIZE(imsi2)); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.current == tlli2); printf("\n"); gbproxy_peer_free(peer); } { struct gbproxy_link_info *link_info; int num_removed; printf("Test TLLI expiry, max_len == 2, max_age == 1:\n"); cfg.tlli_max_len = 0; cfg.tlli_max_age = 1; peer = gbproxy_peer_alloc(&cfg, 20); OSMO_ASSERT(peer->patch_state.logical_link_count == 0); printf(" Add TLLI 1, IMSI 1 (should expire)\n"); register_tlli(peer, tlli1, imsi1, ARRAY_SIZE(imsi1), now); OSMO_ASSERT(peer->patch_state.logical_link_count == 1); printf(" Add TLLI 2, IMSI 2 (should expire after timeout)\n"); register_tlli(peer, tlli2, imsi2, ARRAY_SIZE(imsi2), now + 1); OSMO_ASSERT(peer->patch_state.logical_link_count == 2); printf(" Add TLLI 3, IMSI 3 (should not expire after timeout)\n"); register_tlli(peer, tlli3, imsi3, ARRAY_SIZE(imsi3), now + 2); OSMO_ASSERT(peer->patch_state.logical_link_count == 3); dump_peers(stdout, 2, now + 2, &cfg); printf(" Remove stale TLLIs\n"); num_removed = gbproxy_remove_stale_link_infos(peer, now + 3); OSMO_ASSERT(num_removed == 2); OSMO_ASSERT(peer->patch_state.logical_link_count == 1); dump_peers(stdout, 2, now + 2, &cfg); /* verify that tlli3 has survived */ link_info = gbproxy_link_info_by_imsi(peer, imsi1, ARRAY_SIZE(imsi1)); OSMO_ASSERT(!link_info); link_info = gbproxy_link_info_by_imsi(peer, imsi2, ARRAY_SIZE(imsi2)); OSMO_ASSERT(!link_info); link_info = gbproxy_link_info_by_imsi(peer, imsi3, ARRAY_SIZE(imsi3)); OSMO_ASSERT(link_info); OSMO_ASSERT(link_info->tlli.current == tlli3); printf("\n"); gbproxy_peer_free(peer); } gbproxy_clear_patch_filter(&cfg.matches[GBPROX_MATCH_PATCHING]); gbprox_reset(&cfg); } static void test_gbproxy_imsi_matching(void) { const char *err_msg = NULL; const uint8_t imsi1[] = { GSM_MI_TYPE_IMSI | 0x10, 0x32, 0x54, 0xf6 }; const uint8_t imsi2[] = { GSM_MI_TYPE_IMSI | GSM_MI_ODD | 0x10, 0x32, 0x54, 0x76 }; const uint8_t imsi3_bad[] = { GSM_MI_TYPE_IMSI | 0x10, 0xee, 0x54, 0xff }; const uint8_t tmsi1[] = { GSM_MI_TYPE_TMSI | 0xf0, 0x11, 0x22, 0x33, 0x44 }; const uint8_t tmsi2_bad[] = { GSM_MI_TYPE_TMSI | 0xf0, 0x11, 0x22 }; const uint8_t imei1[] = { GSM_MI_TYPE_IMEI | 0x10, 0x32, 0x54, 0xf6 }; const uint8_t imei2[] = { GSM_MI_TYPE_IMEI | GSM_MI_ODD | 0x10, 0x32, 0x54, 0x76 }; const char *filter_re1 = ".*"; const char *filter_re2 = "^1234"; const char *filter_re3 = "^4321"; const char *filter_re4_bad = "^12["; struct gbproxy_match match = {0,}; printf("=== Test IMSI/TMSI matching ===\n\n"); OSMO_ASSERT(match.enable == 0); OSMO_ASSERT(gbproxy_set_patch_filter(&match, filter_re1, &err_msg) == 0); OSMO_ASSERT(match.enable == 1); OSMO_ASSERT(gbproxy_set_patch_filter(&match, filter_re2, &err_msg) == 0); OSMO_ASSERT(match.enable == 1); err_msg = NULL; OSMO_ASSERT(gbproxy_set_patch_filter(&match, filter_re4_bad, &err_msg) == -1); OSMO_ASSERT(err_msg != NULL); OSMO_ASSERT(match.enable == 0); OSMO_ASSERT(gbproxy_set_patch_filter(&match, filter_re2, &err_msg) == 0); OSMO_ASSERT(match.enable == 1); OSMO_ASSERT(gbproxy_set_patch_filter(&match, NULL, &err_msg) == 0); OSMO_ASSERT(match.enable == 0); OSMO_ASSERT(gbproxy_set_patch_filter(&match, filter_re2, &err_msg) == 0); OSMO_ASSERT(match.enable == 1); gbproxy_clear_patch_filter(&match); OSMO_ASSERT(match.enable == 0); OSMO_ASSERT(gbproxy_set_patch_filter(&match, filter_re2, &err_msg) == 0); OSMO_ASSERT(match.enable == 1); OSMO_ASSERT(gbproxy_check_imsi(&match, imsi1, ARRAY_SIZE(imsi1)) == 1); OSMO_ASSERT(gbproxy_check_imsi(&match, imsi2, ARRAY_SIZE(imsi2)) == 1); /* imsi3_bad contains 0xE and 0xF digits, but the conversion function * doesn't complain, so gbproxy_check_imsi() doesn't return -1 in this * case. */ OSMO_ASSERT(gbproxy_check_imsi(&match, imsi3_bad, ARRAY_SIZE(imsi3_bad)) == 0); OSMO_ASSERT(gbproxy_check_imsi(&match, tmsi1, ARRAY_SIZE(tmsi1)) == -1); OSMO_ASSERT(gbproxy_check_imsi(&match, tmsi2_bad, ARRAY_SIZE(tmsi2_bad)) == -1); OSMO_ASSERT(gbproxy_check_imsi(&match, imei1, ARRAY_SIZE(imei1)) == -1); OSMO_ASSERT(gbproxy_check_imsi(&match, imei2, ARRAY_SIZE(imei2)) == -1); OSMO_ASSERT(gbproxy_set_patch_filter(&match, filter_re3, &err_msg) == 0); OSMO_ASSERT(match.enable == 1); OSMO_ASSERT(gbproxy_check_imsi(&match, imsi1, ARRAY_SIZE(imsi1)) == 0); OSMO_ASSERT(gbproxy_check_imsi(&match, imsi2, ARRAY_SIZE(imsi2)) == 0); OSMO_ASSERT(gbproxy_check_imsi(&match, imsi3_bad, ARRAY_SIZE(imsi3_bad)) == 0); OSMO_ASSERT(gbproxy_check_imsi(&match, tmsi1, ARRAY_SIZE(tmsi1)) == -1); OSMO_ASSERT(gbproxy_check_imsi(&match, tmsi2_bad, ARRAY_SIZE(tmsi2_bad)) == -1); OSMO_ASSERT(gbproxy_check_imsi(&match, imei1, ARRAY_SIZE(imei1)) == -1); OSMO_ASSERT(gbproxy_check_imsi(&match, imei2, ARRAY_SIZE(imei2)) == -1); /* TODO: Check correct length but wrong type with is_mi_tmsi */ gbproxy_clear_patch_filter(&match); OSMO_ASSERT(match.enable == 0); } 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 struct log_info info = { .cat = gprs_categories, .num_cat = ARRAY_SIZE(gprs_categories), }; int main(int argc, char **argv) { osmo_init_logging(&info); log_set_use_color(osmo_stderr_target, 0); log_set_print_filename(osmo_stderr_target, 0); osmo_signal_register_handler(SS_L_NS, &test_signal, &gbcfg); log_set_print_filename(osmo_stderr_target, 0); log_set_log_level(osmo_stderr_target, LOGL_DEBUG); log_set_all_filter(osmo_stderr_target, 1); rate_ctr_init(NULL); setlinebuf(stdout); printf("===== GbProxy test START\n"); gbproxy_init_config(&gbcfg); test_gbproxy(); test_gbproxy_ident_changes(); test_gbproxy_imsi_matching(); test_gbproxy_ptmsi_assignment(); test_gbproxy_ra_patching(); test_gbproxy_ptmsi_patching(); test_gbproxy_ptmsi_patching_bad_cases(); test_gbproxy_imsi_acquisition(); test_gbproxy_secondary_sgsn(); test_gbproxy_keep_info(); test_gbproxy_tlli_expire(); printf("===== GbProxy test END\n\n"); exit(EXIT_SUCCESS); } openbsc-0.15.0/openbsc/tests/gbproxy/gbproxy_test.ok000066400000000000000000010665521265565154000226270ustar00rootroot00000000000000===== GbProxy test START === test_gbproxy === --- Initialise SGSN --- MESSAGE to SGSN at 0x05060708:32000, msg length 12 02 00 81 01 01 82 01 01 04 82 01 00 PROCESSING RESET_ACK from 0x05060708:32000 03 01 82 01 01 04 82 01 00 MESSAGE to SGSN at 0x05060708:32000, msg length 1 0a result (RESET_ACK) = 1 PROCESSING ALIVE_ACK from 0x05060708:32000 0b MESSAGE to SGSN at 0x05060708:32000, msg length 1 06 result (ALIVE_ACK) = 1 PROCESSING UNBLOCK_ACK from 0x05060708:32000 07 ==> got signal NS_UNBLOCK, NS-VC 0x0101/5.6.7.8:32000 result (UNBLOCK_ACK) = 0 PROCESSING ALIVE from 0x05060708:32000 0a MESSAGE to SGSN at 0x05060708:32000, msg length 1 0b result (ALIVE) = 1 Current NS-VCIs: VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 NS-VC Block count : 1 --- Initialise BSS 1 --- Setup NS-VC: remote 0x01020304:1111, NSVCI 0x1001(4097), NSEI 0x1000(4096) PROCESSING RESET from 0x01020304:1111 02 00 81 01 01 82 10 01 04 82 10 00 ==> got signal NS_RESET, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 9 03 01 82 10 01 04 82 10 00 MESSAGE to BSS at 0x01020304:1111, msg length 1 0a result (RESET) = 9 PROCESSING ALIVE from 0x01020304:1111 0a MESSAGE to BSS at 0x01020304:1111, msg length 1 0b result (ALIVE) = 1 PROCESSING UNBLOCK from 0x01020304:1111 06 ==> got signal NS_UNBLOCK, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 1 07 result (UNBLOCK) = 1 PROCESSING ALIVE_ACK from 0x01020304:1111 0b result (ALIVE_ACK) = 0 Setup BSSGP: remote 0x01020304:1111, BVCI 0x1002(4098) PROCESSING BVC_RESET from 0x01020304:1111 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 22 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 result (BVC_RESET) = 22 Current NS-VCIs: VCI 0x1001, NSEI 0x1000, peer 0x01020304:1111 VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 NS-VC Block count : 1 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 PROCESSING BVC_RESET_ACK from 0x05060708:32000 00 00 00 00 23 04 82 10 02 CALLBACK, event 0, msg length 5, bvci 0x0000 00 00 00 00 23 04 82 10 02 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 5 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 9 00 00 00 00 23 04 82 10 02 result (BVC_RESET_ACK) = 9 --- Initialise BSS 2 --- Setup NS-VC: remote 0x01020304:2222, NSVCI 0x2001(8193), NSEI 0x2000(8192) PROCESSING RESET from 0x01020304:2222 02 00 81 01 01 82 20 01 04 82 20 00 ==> got signal NS_RESET, NS-VC 0x2001/1.2.3.4:2222 MESSAGE to BSS at 0x01020304:2222, msg length 9 03 01 82 20 01 04 82 20 00 MESSAGE to BSS at 0x01020304:2222, msg length 1 0a result (RESET) = 9 PROCESSING ALIVE from 0x01020304:2222 0a MESSAGE to BSS at 0x01020304:2222, msg length 1 0b result (ALIVE) = 1 PROCESSING UNBLOCK from 0x01020304:2222 06 ==> got signal NS_UNBLOCK, NS-VC 0x2001/1.2.3.4:2222 MESSAGE to BSS at 0x01020304:2222, msg length 1 07 result (UNBLOCK) = 1 PROCESSING ALIVE_ACK from 0x01020304:2222 0b result (ALIVE_ACK) = 0 Setup BSSGP: remote 0x01020304:2222, BVCI 0x2002(8194) PROCESSING BVC_RESET from 0x01020304:2222 00 00 00 00 22 04 82 20 02 07 81 08 08 88 11 22 33 40 50 60 10 00 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 22 04 82 20 02 07 81 08 08 88 11 22 33 40 50 60 10 00 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 22 00 00 00 00 22 04 82 20 02 07 81 08 08 88 11 22 33 40 50 60 10 00 result (BVC_RESET) = 22 Current NS-VCIs: VCI 0x2001, NSEI 0x2000, peer 0x01020304:2222 VCI 0x1001, NSEI 0x1000, peer 0x01020304:1111 VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 NS-VC Block count : 1 Peers: NSEI 8192, BVCI 8194, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 PROCESSING BVC_RESET_ACK from 0x05060708:32000 00 00 00 00 23 04 82 20 02 CALLBACK, event 0, msg length 5, bvci 0x0000 00 00 00 00 23 04 82 20 02 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 5 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:2222, msg length 9 00 00 00 00 23 04 82 20 02 result (BVC_RESET_ACK) = 9 --- Move BSS 1 to new port --- Setup NS-VC: remote 0x01020304:3333, NSVCI 0x1001(4097), NSEI 0x1000(4096) PROCESSING RESET from 0x01020304:3333 02 00 81 01 01 82 10 01 04 82 10 00 ==> got signal NS_RESET, NS-VC 0x1001/1.2.3.4:3333 MESSAGE to BSS at 0x01020304:3333, msg length 9 03 01 82 10 01 04 82 10 00 MESSAGE to BSS at 0x01020304:3333, msg length 1 0a result (RESET) = 9 PROCESSING ALIVE from 0x01020304:3333 0a MESSAGE to BSS at 0x01020304:3333, msg length 1 0b result (ALIVE) = 1 PROCESSING UNBLOCK from 0x01020304:3333 06 ==> got signal NS_UNBLOCK, NS-VC 0x1001/1.2.3.4:3333 MESSAGE to BSS at 0x01020304:3333, msg length 1 07 result (UNBLOCK) = 1 PROCESSING ALIVE_ACK from 0x01020304:3333 0b result (ALIVE_ACK) = 0 Current NS-VCIs: VCI 0x2001, NSEI 0x2000, peer 0x01020304:2222 VCI 0x1001, NSEI 0x1000, peer 0x01020304:3333 VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 NS-VC Block count : 1 Peers: NSEI 8192, BVCI 8194, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 --- Move BSS 2 to former BSS 1 port --- Setup NS-VC: remote 0x01020304:1111, NSVCI 0x2001(8193), NSEI 0x2000(8192) PROCESSING RESET from 0x01020304:1111 02 00 81 01 01 82 20 01 04 82 20 00 ==> got signal NS_RESET, NS-VC 0x2001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 9 03 01 82 20 01 04 82 20 00 MESSAGE to BSS at 0x01020304:1111, msg length 1 0a result (RESET) = 9 PROCESSING ALIVE from 0x01020304:1111 0a MESSAGE to BSS at 0x01020304:1111, msg length 1 0b result (ALIVE) = 1 PROCESSING UNBLOCK from 0x01020304:1111 06 ==> got signal NS_UNBLOCK, NS-VC 0x2001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 1 07 result (UNBLOCK) = 1 PROCESSING ALIVE_ACK from 0x01020304:1111 0b result (ALIVE_ACK) = 0 Current NS-VCIs: VCI 0x2001, NSEI 0x2000, peer 0x01020304:1111 VCI 0x1001, NSEI 0x1000, peer 0x01020304:3333 VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 NS-VC Block count : 1 Peers: NSEI 8192, BVCI 8194, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 --- Move BSS 1 to current BSS 2 port --- Setup NS-VC: remote 0x01020304:1111, NSVCI 0x2001(8193), NSEI 0x2000(8192) PROCESSING RESET from 0x01020304:1111 02 00 81 01 01 82 20 01 04 82 20 00 ==> got signal NS_RESET, NS-VC 0x2001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 9 03 01 82 20 01 04 82 20 00 MESSAGE to BSS at 0x01020304:1111, msg length 1 0a result (RESET) = 9 PROCESSING ALIVE from 0x01020304:1111 0a MESSAGE to BSS at 0x01020304:1111, msg length 1 0b result (ALIVE) = 1 PROCESSING UNBLOCK from 0x01020304:1111 06 ==> got signal NS_UNBLOCK, NS-VC 0x2001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 1 07 result (UNBLOCK) = 1 PROCESSING ALIVE_ACK from 0x01020304:1111 0b result (ALIVE_ACK) = 0 Current NS-VCIs: VCI 0x2001, NSEI 0x2000, peer 0x01020304:1111 VCI 0x1001, NSEI 0x1000, peer 0x01020304:3333 VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 NS-VC Block count : 1 Peers: NSEI 8192, BVCI 8194, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 --- Move BSS 2 to new port --- Setup NS-VC: remote 0x01020304:4444, NSVCI 0x2001(8193), NSEI 0x2000(8192) PROCESSING RESET from 0x01020304:4444 02 00 81 01 01 82 20 01 04 82 20 00 ==> got signal NS_RESET, NS-VC 0x2001/1.2.3.4:4444 MESSAGE to BSS at 0x01020304:4444, msg length 9 03 01 82 20 01 04 82 20 00 MESSAGE to BSS at 0x01020304:4444, msg length 1 0a result (RESET) = 9 PROCESSING ALIVE from 0x01020304:4444 0a MESSAGE to BSS at 0x01020304:4444, msg length 1 0b result (ALIVE) = 1 PROCESSING UNBLOCK from 0x01020304:4444 06 ==> got signal NS_UNBLOCK, NS-VC 0x2001/1.2.3.4:4444 MESSAGE to BSS at 0x01020304:4444, msg length 1 07 result (UNBLOCK) = 1 PROCESSING ALIVE_ACK from 0x01020304:4444 0b result (ALIVE_ACK) = 0 Current NS-VCIs: VCI 0x2001, NSEI 0x2000, peer 0x01020304:4444 VCI 0x1001, NSEI 0x1000, peer 0x01020304:3333 VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 NS-VC Block count : 1 Peers: NSEI 8192, BVCI 8194, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 --- Move BSS 2 to former BSS 1 port --- Setup NS-VC: remote 0x01020304:3333, NSVCI 0x2001(8193), NSEI 0x2000(8192) PROCESSING RESET from 0x01020304:3333 02 00 81 01 01 82 20 01 04 82 20 00 ==> got signal NS_REPLACED: 0x2001/1.2.3.4:4444 -> 0x1001/1.2.3.4:3333 ==> got signal NS_RESET, NS-VC 0x2001/1.2.3.4:3333 MESSAGE to BSS at 0x01020304:3333, msg length 9 03 01 82 20 01 04 82 20 00 MESSAGE to BSS at 0x01020304:3333, msg length 1 0a result (RESET) = 9 PROCESSING ALIVE from 0x01020304:3333 0a MESSAGE to BSS at 0x01020304:3333, msg length 1 0b result (ALIVE) = 1 PROCESSING UNBLOCK from 0x01020304:3333 06 ==> got signal NS_UNBLOCK, NS-VC 0x2001/1.2.3.4:3333 MESSAGE to BSS at 0x01020304:3333, msg length 1 07 result (UNBLOCK) = 1 PROCESSING ALIVE_ACK from 0x01020304:3333 0b result (ALIVE_ACK) = 0 Current NS-VCIs: VCI 0x2001, NSEI 0x2000, peer 0x01020304:3333 NS-VC replaced other count: 1 VCI 0x1001, NSEI 0x1000, peer 0x00000000:0 VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 NS-VC Block count : 1 Peers: NSEI 8192, BVCI 8194, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 --- Move BSS 1 to original BSS 1 port --- Setup NS-VC: remote 0x01020304:1111, NSVCI 0x1001(4097), NSEI 0x1000(4096) PROCESSING RESET from 0x01020304:1111 02 00 81 01 01 82 10 01 04 82 10 00 ==> got signal NS_RESET, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 9 03 01 82 10 01 04 82 10 00 MESSAGE to BSS at 0x01020304:1111, msg length 1 0a result (RESET) = 9 PROCESSING ALIVE from 0x01020304:1111 0a MESSAGE to BSS at 0x01020304:1111, msg length 1 0b result (ALIVE) = 1 PROCESSING UNBLOCK from 0x01020304:1111 06 ==> got signal NS_UNBLOCK, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 1 07 result (UNBLOCK) = 1 PROCESSING ALIVE_ACK from 0x01020304:1111 0b result (ALIVE_ACK) = 0 Current NS-VCIs: VCI 0x2001, NSEI 0x2000, peer 0x01020304:3333 NS-VC replaced other count: 1 VCI 0x1001, NSEI 0x1000, peer 0x01020304:1111 VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 NS-VC Block count : 1 Peers: NSEI 8192, BVCI 8194, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 --- Reset BSS 1 with a new BVCI --- Setup BSSGP: remote 0x01020304:1111, BVCI 0x1012(4114) PROCESSING BVC_RESET from 0x01020304:1111 00 00 00 00 22 04 82 10 12 07 81 08 08 88 11 22 33 40 50 60 10 00 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 22 04 82 10 12 07 81 08 08 88 11 22 33 40 50 60 10 00 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 22 00 00 00 00 22 04 82 10 12 07 81 08 08 88 11 22 33 40 50 60 10 00 result (BVC_RESET) = 22 Current NS-VCIs: VCI 0x2001, NSEI 0x2000, peer 0x01020304:3333 NS-VC replaced other count: 1 VCI 0x1001, NSEI 0x1000, peer 0x01020304:1111 VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 NS-VC Block count : 1 Peers: NSEI 4096, BVCI 4114, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 8192, BVCI 8194, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 PROCESSING BVC_RESET_ACK from 0x05060708:32000 00 00 00 00 23 04 82 10 12 CALLBACK, event 0, msg length 5, bvci 0x0000 00 00 00 00 23 04 82 10 12 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 5 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 9 00 00 00 00 23 04 82 10 12 result (BVC_RESET_ACK) = 9 --- Reset BSS 1 with the old BVCI --- Setup BSSGP: remote 0x01020304:1111, BVCI 0x1002(4098) PROCESSING BVC_RESET from 0x01020304:1111 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 22 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 result (BVC_RESET) = 22 Current NS-VCIs: VCI 0x2001, NSEI 0x2000, peer 0x01020304:3333 NS-VC replaced other count: 1 VCI 0x1001, NSEI 0x1000, peer 0x01020304:1111 VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 NS-VC Block count : 1 Peers: NSEI 4096, BVCI 4114, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 8192, BVCI 8194, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 PROCESSING BVC_RESET_ACK from 0x05060708:32000 00 00 00 00 23 04 82 10 02 CALLBACK, event 0, msg length 5, bvci 0x0000 00 00 00 00 23 04 82 10 02 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 5 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 9 00 00 00 00 23 04 82 10 02 result (BVC_RESET_ACK) = 9 --- Reset BSS 1 with the old BVCI again --- Setup BSSGP: remote 0x01020304:1111, BVCI 0x1002(4098) PROCESSING BVC_RESET from 0x01020304:1111 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 22 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 result (BVC_RESET) = 22 Current NS-VCIs: VCI 0x2001, NSEI 0x2000, peer 0x01020304:3333 NS-VC replaced other count: 1 VCI 0x1001, NSEI 0x1000, peer 0x01020304:1111 VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 NS-VC Block count : 1 Peers: NSEI 4096, BVCI 4114, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 8192, BVCI 8194, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 PROCESSING BVC_RESET_ACK from 0x05060708:32000 00 00 00 00 23 04 82 10 02 CALLBACK, event 0, msg length 5, bvci 0x0000 00 00 00 00 23 04 82 10 02 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 5 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 9 00 00 00 00 23 04 82 10 02 result (BVC_RESET_ACK) = 9 --- Send message from BSS 1 to SGSN, BVCI 0x1012 --- PROCESSING UNITDATA from 0x01020304:1111 00 00 10 12 CALLBACK, event 0, msg length 0, bvci 0x1012 00 00 10 12 NS UNITDATA MESSAGE to SGSN, BVCI 0x1012, msg length 0 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 4 00 00 10 12 result (UNITDATA) = 4 --- Send message from SGSN to BSS 1, BVCI 0x1012 --- PROCESSING UNITDATA from 0x05060708:32000 00 00 10 12 CALLBACK, event 0, msg length 0, bvci 0x1012 00 00 10 12 NS UNITDATA MESSAGE to BSS, BVCI 0x1012, msg length 0 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 4 00 00 10 12 result (UNITDATA) = 4 --- Send message from BSS 1 to SGSN, BVCI 0x1002 --- PROCESSING UNITDATA from 0x01020304:1111 00 00 10 12 CALLBACK, event 0, msg length 0, bvci 0x1012 00 00 10 12 NS UNITDATA MESSAGE to SGSN, BVCI 0x1012, msg length 0 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 4 00 00 10 12 result (UNITDATA) = 4 --- Send message from SGSN to BSS 1, BVCI 0x1002 --- PROCESSING UNITDATA from 0x05060708:32000 00 00 10 12 CALLBACK, event 0, msg length 0, bvci 0x1012 00 00 10 12 NS UNITDATA MESSAGE to BSS, BVCI 0x1012, msg length 0 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 4 00 00 10 12 result (UNITDATA) = 4 --- Send message from BSS 2 to SGSN, BVCI 0x2002 --- PROCESSING UNITDATA from 0x01020304:1111 00 00 20 02 CALLBACK, event 0, msg length 0, bvci 0x2002 00 00 20 02 NS UNITDATA MESSAGE to SGSN, BVCI 0x2002, msg length 0 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 4 00 00 20 02 result (UNITDATA) = 4 --- Send message from SGSN to BSS 2, BVCI 0x2002 --- PROCESSING UNITDATA from 0x05060708:32000 00 00 20 02 CALLBACK, event 0, msg length 0, bvci 0x2002 00 00 20 02 NS UNITDATA MESSAGE to BSS, BVCI 0x2002, msg length 0 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:3333, msg length 4 00 00 20 02 result (UNITDATA) = 4 --- Reset BSS 1 with the old BVCI on BSS2's link --- Setup BSSGP: remote 0x01020304:3333, BVCI 0x1002(4098) PROCESSING BVC_RESET from 0x01020304:3333 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 22 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 result (BVC_RESET) = 22 Current NS-VCIs: VCI 0x2001, NSEI 0x2000, peer 0x01020304:3333 NS-VC replaced other count: 1 VCI 0x1001, NSEI 0x1000, peer 0x01020304:1111 VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 NS-VC Block count : 1 Peers: NSEI 4096, BVCI 4114, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 8192, BVCI 8194, not blocked, RAI 112-332-16464-96 NSEI mismatch : 1 TLLI-Cache: 0 NSEI 8192, BVCI 4098, not blocked, RAI 112-332-16464-96 NSEI mismatch : 1 TLLI-Cache: 0 Gbproxy global: PROCESSING BVC_RESET_ACK from 0x05060708:32000 00 00 00 00 23 04 82 10 02 CALLBACK, event 0, msg length 5, bvci 0x0000 00 00 00 00 23 04 82 10 02 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 5 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:3333, msg length 9 00 00 00 00 23 04 82 10 02 result (BVC_RESET_ACK) = 9 --- Send message from BSS 1 to SGSN, BVCI 0x1002 --- PROCESSING UNITDATA from 0x01020304:1111 00 00 10 12 CALLBACK, event 0, msg length 0, bvci 0x1012 00 00 10 12 NS UNITDATA MESSAGE to SGSN, BVCI 0x1012, msg length 0 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 4 00 00 10 12 result (UNITDATA) = 4 --- Send message from SGSN to BSS 1, BVCI 0x1002 --- PROCESSING UNITDATA from 0x05060708:32000 00 00 10 12 CALLBACK, event 0, msg length 0, bvci 0x1012 00 00 10 12 NS UNITDATA MESSAGE to BSS, BVCI 0x1012, msg length 0 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 4 00 00 10 12 result (UNITDATA) = 4 --- Send message from SGSN to BSS 1, BVCI 0x10ff (invalid) --- PROCESSING UNITDATA from 0x05060708:32000 00 00 10 ff CALLBACK, event 0, msg length 0, bvci 0x10ff 00 00 10 ff NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 10 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 14 00 00 00 00 41 07 81 05 04 82 10 ff 15 80 result (UNITDATA) = 14 Peers: NSEI 8192, BVCI 8194, not blocked, RAI 112-332-16464-96 NSEI mismatch : 1 TLLI-Cache: 0 NSEI 8192, BVCI 4098, not blocked, RAI 112-332-16464-96 NSEI mismatch : 1 TLLI-Cache: 0 Gbproxy global: Invalid BVC Identifier : 1 === test_gbproxy_ident_changes === --- Initialise SGSN --- MESSAGE to SGSN at 0x05060708:32000, msg length 12 02 00 81 01 01 82 01 01 04 82 01 00 PROCESSING RESET_ACK from 0x05060708:32000 03 01 82 01 01 04 82 01 00 MESSAGE to SGSN at 0x05060708:32000, msg length 1 0a result (RESET_ACK) = 1 PROCESSING ALIVE_ACK from 0x05060708:32000 0b MESSAGE to SGSN at 0x05060708:32000, msg length 1 06 result (ALIVE_ACK) = 1 PROCESSING UNBLOCK_ACK from 0x05060708:32000 07 ==> got signal NS_UNBLOCK, NS-VC 0x0101/5.6.7.8:32000 result (UNBLOCK_ACK) = 0 PROCESSING ALIVE from 0x05060708:32000 0a MESSAGE to SGSN at 0x05060708:32000, msg length 1 0b result (ALIVE) = 1 Current NS-VCIs: VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 NS-VC Block count : 1 --- Initialise BSS 1 --- Setup NS-VC: remote 0x01020304:1111, NSVCI 0x1001(4097), NSEI 0x1000(4096) PROCESSING RESET from 0x01020304:1111 02 00 81 01 01 82 10 01 04 82 10 00 ==> got signal NS_RESET, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 9 03 01 82 10 01 04 82 10 00 MESSAGE to BSS at 0x01020304:1111, msg length 1 0a result (RESET) = 9 PROCESSING ALIVE from 0x01020304:1111 0a MESSAGE to BSS at 0x01020304:1111, msg length 1 0b result (ALIVE) = 1 PROCESSING UNBLOCK from 0x01020304:1111 06 ==> got signal NS_UNBLOCK, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 1 07 result (UNBLOCK) = 1 PROCESSING ALIVE_ACK from 0x01020304:1111 0b result (ALIVE_ACK) = 0 Current NS-VCIs: VCI 0x1001, NSEI 0x1000, peer 0x01020304:1111 VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 NS-VC Block count : 1 --- Setup BVCI 1 --- Setup BSSGP: remote 0x01020304:1111, BVCI 0x1002(4098) PROCESSING BVC_RESET from 0x01020304:1111 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 22 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 result (BVC_RESET) = 22 PROCESSING BVC_RESET_ACK from 0x05060708:32000 00 00 00 00 23 04 82 10 02 CALLBACK, event 0, msg length 5, bvci 0x0000 00 00 00 00 23 04 82 10 02 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 5 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 9 00 00 00 00 23 04 82 10 02 result (BVC_RESET_ACK) = 9 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 --- Setup BVCI 2 --- Setup BSSGP: remote 0x01020304:1111, BVCI 0x2002(8194) PROCESSING BVC_RESET from 0x01020304:1111 00 00 00 00 22 04 82 20 02 07 81 08 08 88 11 22 33 40 50 60 10 00 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 22 04 82 20 02 07 81 08 08 88 11 22 33 40 50 60 10 00 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 22 00 00 00 00 22 04 82 20 02 07 81 08 08 88 11 22 33 40 50 60 10 00 result (BVC_RESET) = 22 PROCESSING BVC_RESET_ACK from 0x05060708:32000 00 00 00 00 23 04 82 20 02 CALLBACK, event 0, msg length 5, bvci 0x0000 00 00 00 00 23 04 82 20 02 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 5 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 9 00 00 00 00 23 04 82 20 02 result (BVC_RESET_ACK) = 9 Peers: NSEI 4096, BVCI 8194, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 --- Send message from BSS 1 to SGSN and back, BVCI 1 --- PROCESSING UNITDATA from 0x01020304:1111 00 00 10 02 CALLBACK, event 0, msg length 0, bvci 0x1002 00 00 10 02 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 0 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 4 00 00 10 02 result (UNITDATA) = 4 PROCESSING UNITDATA from 0x05060708:32000 00 00 10 02 CALLBACK, event 0, msg length 0, bvci 0x1002 00 00 10 02 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 0 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 4 00 00 10 02 result (UNITDATA) = 4 --- Send message from BSS 1 to SGSN and back, BVCI 2 --- PROCESSING UNITDATA from 0x01020304:1111 00 00 20 02 CALLBACK, event 0, msg length 0, bvci 0x2002 00 00 20 02 NS UNITDATA MESSAGE to SGSN, BVCI 0x2002, msg length 0 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 4 00 00 20 02 result (UNITDATA) = 4 PROCESSING UNITDATA from 0x05060708:32000 00 00 20 02 CALLBACK, event 0, msg length 0, bvci 0x2002 00 00 20 02 NS UNITDATA MESSAGE to BSS, BVCI 0x2002, msg length 0 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 4 00 00 20 02 result (UNITDATA) = 4 --- Change NSEI --- Setup NS-VC: remote 0x01020304:1111, NSVCI 0x1001(4097), NSEI 0x2000(8192) PROCESSING RESET from 0x01020304:1111 02 00 81 01 01 82 10 01 04 82 20 00 ==> got signal NS_RESET, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 9 03 01 82 10 01 04 82 20 00 MESSAGE to BSS at 0x01020304:1111, msg length 1 0a result (RESET) = 9 PROCESSING ALIVE from 0x01020304:1111 0a MESSAGE to BSS at 0x01020304:1111, msg length 1 0b result (ALIVE) = 1 PROCESSING UNBLOCK from 0x01020304:1111 06 ==> got signal NS_UNBLOCK, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 1 07 result (UNBLOCK) = 1 PROCESSING ALIVE_ACK from 0x01020304:1111 0b result (ALIVE_ACK) = 0 Current NS-VCIs: VCI 0x1001, NSEI 0x2000, peer 0x01020304:1111 NS-VC changed NSEI count : 1 VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 NS-VC Block count : 1 --- Setup BVCI 1 --- Setup BSSGP: remote 0x01020304:1111, BVCI 0x1002(4098) PROCESSING BVC_RESET from 0x01020304:1111 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 22 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 result (BVC_RESET) = 22 PROCESSING BVC_RESET_ACK from 0x05060708:32000 00 00 00 00 23 04 82 10 02 CALLBACK, event 0, msg length 5, bvci 0x0000 00 00 00 00 23 04 82 10 02 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 5 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 9 00 00 00 00 23 04 82 10 02 result (BVC_RESET_ACK) = 9 Peers: NSEI 4096, BVCI 8194, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 8192, BVCI 4098, not blocked, RAI 112-332-16464-96 NSEI mismatch : 1 TLLI-Cache: 0 --- Setup BVCI 3 --- Setup BSSGP: remote 0x01020304:1111, BVCI 0x3002(12290) PROCESSING BVC_RESET from 0x01020304:1111 00 00 00 00 22 04 82 30 02 07 81 08 08 88 11 22 33 40 50 60 10 00 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 22 04 82 30 02 07 81 08 08 88 11 22 33 40 50 60 10 00 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 22 00 00 00 00 22 04 82 30 02 07 81 08 08 88 11 22 33 40 50 60 10 00 result (BVC_RESET) = 22 PROCESSING BVC_RESET_ACK from 0x05060708:32000 00 00 00 00 23 04 82 30 02 CALLBACK, event 0, msg length 5, bvci 0x0000 00 00 00 00 23 04 82 30 02 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 5 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 9 00 00 00 00 23 04 82 30 02 result (BVC_RESET_ACK) = 9 Peers: NSEI 8192, BVCI 12290, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 4096, BVCI 8194, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 8192, BVCI 4098, not blocked, RAI 112-332-16464-96 NSEI mismatch : 1 TLLI-Cache: 0 --- Send message from BSS 1 to SGSN and back, BVCI 1 --- PROCESSING UNITDATA from 0x01020304:1111 00 00 10 02 CALLBACK, event 0, msg length 0, bvci 0x1002 00 00 10 02 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 0 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 4 00 00 10 02 result (UNITDATA) = 4 PROCESSING UNITDATA from 0x05060708:32000 00 00 10 02 CALLBACK, event 0, msg length 0, bvci 0x1002 00 00 10 02 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 0 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 4 00 00 10 02 result (UNITDATA) = 4 --- Send message from BSS 1 to SGSN and back, BVCI 2 (should fail) --- PROCESSING UNITDATA from 0x01020304:1111 00 00 20 02 CALLBACK, event 0, msg length 0, bvci 0x2002 00 00 20 02 NS UNITDATA MESSAGE to SGSN, BVCI 0x2002, msg length 0 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 4 00 00 20 02 result (UNITDATA) = 4 Peers: NSEI 8192, BVCI 12290, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 4096, BVCI 8194, not blocked, RAI 112-332-16464-96 NSEI mismatch : 1 TLLI-Cache: 0 NSEI 8192, BVCI 4098, not blocked, RAI 112-332-16464-96 NSEI mismatch : 1 TLLI-Cache: 0 PROCESSING UNITDATA from 0x05060708:32000 00 00 20 02 CALLBACK, event 0, msg length 0, bvci 0x2002 00 00 20 02 NS UNITDATA MESSAGE to BSS, BVCI 0x2002, msg length 0 (gprs_ns_sendmsg) result (UNITDATA) = -22 Peers: NSEI 8192, BVCI 12290, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 4096, BVCI 8194, not blocked, RAI 112-332-16464-96 NSEI mismatch : 1 NS Transmission error : 1 TLLI-Cache: 0 NSEI 8192, BVCI 4098, not blocked, RAI 112-332-16464-96 NSEI mismatch : 1 TLLI-Cache: 0 --- Send message from BSS 1 to SGSN and back, BVCI 3 --- PROCESSING UNITDATA from 0x01020304:1111 00 00 30 02 CALLBACK, event 0, msg length 0, bvci 0x3002 00 00 30 02 NS UNITDATA MESSAGE to SGSN, BVCI 0x3002, msg length 0 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 4 00 00 30 02 result (UNITDATA) = 4 PROCESSING UNITDATA from 0x05060708:32000 00 00 30 02 CALLBACK, event 0, msg length 0, bvci 0x3002 00 00 30 02 NS UNITDATA MESSAGE to BSS, BVCI 0x3002, msg length 0 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 4 00 00 30 02 result (UNITDATA) = 4 --- Change NSVCI --- Setup NS-VC: remote 0x01020304:1111, NSVCI 0x2001(8193), NSEI 0x2000(8192) PROCESSING RESET from 0x01020304:1111 02 00 81 01 01 82 20 01 04 82 20 00 ==> got signal NS_REPLACED: 0x2001/0.0.0.0:0 -> 0x1001/1.2.3.4:1111 ==> got signal NS_RESET, NS-VC 0x2001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 9 03 01 82 20 01 04 82 20 00 MESSAGE to BSS at 0x01020304:1111, msg length 1 0a result (RESET) = 9 PROCESSING ALIVE from 0x01020304:1111 0a MESSAGE to BSS at 0x01020304:1111, msg length 1 0b result (ALIVE) = 1 PROCESSING UNBLOCK from 0x01020304:1111 06 ==> got signal NS_UNBLOCK, NS-VC 0x2001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 1 07 result (UNBLOCK) = 1 PROCESSING ALIVE_ACK from 0x01020304:1111 0b result (ALIVE_ACK) = 0 Current NS-VCIs: VCI 0x2001, NSEI 0x2000, peer 0x01020304:1111 NS-VC replaced other count: 1 VCI 0x1001, NSEI 0x2000, peer 0x00000000:0 NS-VC changed NSEI count : 1 VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 NS-VC Block count : 1 --- Setup BVCI 1 --- Setup BSSGP: remote 0x01020304:1111, BVCI 0x1002(4098) PROCESSING BVC_RESET from 0x01020304:1111 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 22 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 result (BVC_RESET) = 22 PROCESSING BVC_RESET_ACK from 0x05060708:32000 00 00 00 00 23 04 82 10 02 CALLBACK, event 0, msg length 5, bvci 0x0000 00 00 00 00 23 04 82 10 02 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 5 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 9 00 00 00 00 23 04 82 10 02 result (BVC_RESET_ACK) = 9 Peers: NSEI 8192, BVCI 12290, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 4096, BVCI 8194, not blocked, RAI 112-332-16464-96 NSEI mismatch : 1 NS Transmission error : 1 TLLI-Cache: 0 NSEI 8192, BVCI 4098, not blocked, RAI 112-332-16464-96 NSEI mismatch : 1 TLLI-Cache: 0 --- Setup BVCI 4 --- Setup BSSGP: remote 0x01020304:1111, BVCI 0x4002(16386) PROCESSING BVC_RESET from 0x01020304:1111 00 00 00 00 22 04 82 40 02 07 81 08 08 88 11 22 33 40 50 60 10 00 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 22 04 82 40 02 07 81 08 08 88 11 22 33 40 50 60 10 00 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 22 00 00 00 00 22 04 82 40 02 07 81 08 08 88 11 22 33 40 50 60 10 00 result (BVC_RESET) = 22 PROCESSING BVC_RESET_ACK from 0x05060708:32000 00 00 00 00 23 04 82 40 02 CALLBACK, event 0, msg length 5, bvci 0x0000 00 00 00 00 23 04 82 40 02 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 5 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 9 00 00 00 00 23 04 82 40 02 result (BVC_RESET_ACK) = 9 Peers: NSEI 8192, BVCI 16386, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 8192, BVCI 12290, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 4096, BVCI 8194, not blocked, RAI 112-332-16464-96 NSEI mismatch : 1 NS Transmission error : 1 TLLI-Cache: 0 NSEI 8192, BVCI 4098, not blocked, RAI 112-332-16464-96 NSEI mismatch : 1 TLLI-Cache: 0 --- Send message from BSS 1 to SGSN and back, BVCI 1 --- PROCESSING UNITDATA from 0x01020304:1111 00 00 10 02 CALLBACK, event 0, msg length 0, bvci 0x1002 00 00 10 02 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 0 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 4 00 00 10 02 result (UNITDATA) = 4 PROCESSING UNITDATA from 0x05060708:32000 00 00 10 02 CALLBACK, event 0, msg length 0, bvci 0x1002 00 00 10 02 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 0 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 4 00 00 10 02 result (UNITDATA) = 4 --- Send message from BSS 1 to SGSN and back, BVCI 2 (should fail) --- PROCESSING UNITDATA from 0x01020304:1111 00 00 20 02 CALLBACK, event 0, msg length 0, bvci 0x2002 00 00 20 02 NS UNITDATA MESSAGE to SGSN, BVCI 0x2002, msg length 0 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 4 00 00 20 02 result (UNITDATA) = 4 Peers: NSEI 8192, BVCI 16386, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 8192, BVCI 12290, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 4096, BVCI 8194, not blocked, RAI 112-332-16464-96 NSEI mismatch : 2 NS Transmission error : 1 TLLI-Cache: 0 NSEI 8192, BVCI 4098, not blocked, RAI 112-332-16464-96 NSEI mismatch : 1 TLLI-Cache: 0 PROCESSING UNITDATA from 0x05060708:32000 00 00 20 02 CALLBACK, event 0, msg length 0, bvci 0x2002 00 00 20 02 NS UNITDATA MESSAGE to BSS, BVCI 0x2002, msg length 0 (gprs_ns_sendmsg) result (UNITDATA) = -22 Peers: NSEI 8192, BVCI 16386, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 8192, BVCI 12290, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 4096, BVCI 8194, not blocked, RAI 112-332-16464-96 NSEI mismatch : 2 NS Transmission error : 2 TLLI-Cache: 0 NSEI 8192, BVCI 4098, not blocked, RAI 112-332-16464-96 NSEI mismatch : 1 TLLI-Cache: 0 --- Send message from BSS 1 to SGSN and back, BVCI 3 --- PROCESSING UNITDATA from 0x01020304:1111 00 00 30 02 CALLBACK, event 0, msg length 0, bvci 0x3002 00 00 30 02 NS UNITDATA MESSAGE to SGSN, BVCI 0x3002, msg length 0 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 4 00 00 30 02 result (UNITDATA) = 4 PROCESSING UNITDATA from 0x05060708:32000 00 00 30 02 CALLBACK, event 0, msg length 0, bvci 0x3002 00 00 30 02 NS UNITDATA MESSAGE to BSS, BVCI 0x3002, msg length 0 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 4 00 00 30 02 result (UNITDATA) = 4 --- Send message from BSS 1 to SGSN and back, BVCI 4 --- PROCESSING UNITDATA from 0x01020304:1111 00 00 40 02 CALLBACK, event 0, msg length 0, bvci 0x4002 00 00 40 02 NS UNITDATA MESSAGE to SGSN, BVCI 0x4002, msg length 0 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 4 00 00 40 02 result (UNITDATA) = 4 PROCESSING UNITDATA from 0x05060708:32000 00 00 40 02 CALLBACK, event 0, msg length 0, bvci 0x4002 00 00 40 02 NS UNITDATA MESSAGE to BSS, BVCI 0x4002, msg length 0 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 4 00 00 40 02 result (UNITDATA) = 4 Gbproxy global: Peers: NSEI 8192, BVCI 16386, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 8192, BVCI 12290, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 NSEI 4096, BVCI 8194, not blocked, RAI 112-332-16464-96 NSEI mismatch : 2 NS Transmission error : 2 TLLI-Cache: 0 NSEI 8192, BVCI 4098, not blocked, RAI 112-332-16464-96 NSEI mismatch : 1 TLLI-Cache: 0 === Test IMSI/TMSI matching === === test_gbproxy_ptmsi_assignment === --- Initialise SGSN --- MESSAGE to SGSN at 0x05060708:32000, msg length 12 02 00 81 01 01 82 01 01 04 82 01 00 PROCESSING RESET_ACK from 0x05060708:32000 03 01 82 01 01 04 82 01 00 MESSAGE to SGSN at 0x05060708:32000, msg length 1 0a result (RESET_ACK) = 1 PROCESSING ALIVE_ACK from 0x05060708:32000 0b MESSAGE to SGSN at 0x05060708:32000, msg length 1 06 result (ALIVE_ACK) = 1 PROCESSING UNBLOCK_ACK from 0x05060708:32000 07 ==> got signal NS_UNBLOCK, NS-VC 0x0101/5.6.7.8:32000 result (UNBLOCK_ACK) = 0 PROCESSING ALIVE from 0x05060708:32000 0a MESSAGE to SGSN at 0x05060708:32000, msg length 1 0b result (ALIVE) = 1 --- Initialise BSS 1 --- Setup NS-VC: remote 0x01020304:1111, NSVCI 0x1001(4097), NSEI 0x1000(4096) PROCESSING RESET from 0x01020304:1111 02 00 81 01 01 82 10 01 04 82 10 00 ==> got signal NS_RESET, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 9 03 01 82 10 01 04 82 10 00 MESSAGE to BSS at 0x01020304:1111, msg length 1 0a result (RESET) = 9 PROCESSING ALIVE from 0x01020304:1111 0a MESSAGE to BSS at 0x01020304:1111, msg length 1 0b result (ALIVE) = 1 PROCESSING UNBLOCK from 0x01020304:1111 06 ==> got signal NS_UNBLOCK, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 1 07 result (UNBLOCK) = 1 PROCESSING ALIVE_ACK from 0x01020304:1111 0b result (ALIVE_ACK) = 0 Setup BSSGP: remote 0x01020304:1111, BVCI 0x1002(4098) PROCESSING BVC_RESET from 0x01020304:1111 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 22 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 result (BVC_RESET) = 22 PROCESSING BVC_RESET_ACK from 0x05060708:32000 00 00 00 00 23 04 82 10 02 CALLBACK, event 0, msg length 5, bvci 0x0000 00 00 00 00 23 04 82 10 02 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 5 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 9 00 00 00 00 23 04 82 10 02 result (BVC_RESET_ACK) = 9 Current NS-VCIs: VCI 0x1001, NSEI 0x1000, peer 0x01020304:1111 VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 NS-VC Block count : 1 Gbproxy global: Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 --- Establish first LLC connection --- PROCESSING ATTACH REQUEST from 0x01020304:1111 00 00 10 02 01 80 00 de ad 00 00 04 08 88 00 f1 99 00 63 60 12 34 00 80 0e 00 34 01 c0 01 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 16 6d 01 CALLBACK, event 0, msg length 75, bvci 0x1002 00 00 10 02 01 80 00 de ad 00 00 04 08 88 00 f1 99 00 63 60 12 34 00 80 0e 00 34 01 c0 01 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 16 6d 01 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 75 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 79 00 00 10 02 01 80 00 de ad 00 00 04 08 88 00 f1 99 00 63 60 12 34 00 80 0e 00 34 01 c0 01 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 16 6d 01 result (ATTACH REQUEST) = 79 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead -> 8000dead, IMSI (none), AGE 0 PROCESSING IDENT REQUEST from 0x05060708:32000 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 0e 89 41 c0 01 08 15 01 ff 6c ba CALLBACK, event 0, msg length 23, bvci 0x1002 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 0e 89 41 c0 01 08 15 01 ff 6c ba NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 23 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 27 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 0e 89 41 c0 01 08 15 01 ff 6c ba result (IDENT REQUEST) = 27 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead -> 8000dead, IMSI (none), AGE 0 PROCESSING IDENT RESPONSE from 0x01020304:1111 00 00 10 02 01 80 00 de ad 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 05 08 16 08 11 12 13 14 15 16 17 18 ad 05 28 CALLBACK, event 0, msg length 40, bvci 0x1002 00 00 10 02 01 80 00 de ad 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 05 08 16 08 11 12 13 14 15 16 17 18 ad 05 28 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 40 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 44 00 00 10 02 01 80 00 de ad 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 05 08 16 08 11 12 13 14 15 16 17 18 ad 05 28 result (IDENT RESPONSE) = 44 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead -> 8000dead, IMSI 12131415161718, AGE 0 PROCESSING ATTACH ACCEPT from 0x05060708:32000 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 05 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 53 62 f1 CALLBACK, event 0, msg length 88, bvci 0x1002 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 05 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 53 62 f1 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 88 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 92 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 05 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 53 62 f1 result (ATTACH ACCEPT) = 92 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead/efe2b700 -> 8000dead/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH COMPLETE from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 09 08 03 39 d7 bc CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 09 08 03 39 d7 bc NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 09 08 03 39 d7 bc result (ATTACH COMPLETE) = 35 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead/efe2b700 -> 8000dead/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING GMM INFO from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 09 08 21 04 ba 3d CALLBACK, event 0, msg length 66, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 09 08 21 04 ba 3d NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 66 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 70 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 09 08 21 04 ba 3d result (GMM INFO) = 70 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI efe2b700 -> efe2b700, IMSI 12131415161718, AGE 0 --- Establish second LLC connection with the same P-TMSI --- PROCESSING ATTACH REQUEST from 0x01020304:1111 00 00 10 02 01 80 00 be ef 00 00 04 08 88 00 f1 99 00 63 60 12 34 00 80 0e 00 34 01 c0 0d 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 46 42 6e CALLBACK, event 0, msg length 75, bvci 0x1002 00 00 10 02 01 80 00 be ef 00 00 04 08 88 00 f1 99 00 63 60 12 34 00 80 0e 00 34 01 c0 0d 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 46 42 6e NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 75 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 79 00 00 10 02 01 80 00 be ef 00 00 04 08 88 00 f1 99 00 63 60 12 34 00 80 0e 00 34 01 c0 0d 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 46 42 6e result (ATTACH REQUEST) = 79 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 2 TLLI cache size : 2 TLLI-Cache: 2 TLLI 8000beef -> 8000beef, IMSI (none), AGE 0 TLLI efe2b700 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING IDENT REQUEST from 0x05060708:32000 00 00 10 02 00 80 00 be ef 00 50 20 16 82 02 58 0e 89 41 c0 0d 08 15 01 0c a6 18 CALLBACK, event 0, msg length 23, bvci 0x1002 00 00 10 02 00 80 00 be ef 00 50 20 16 82 02 58 0e 89 41 c0 0d 08 15 01 0c a6 18 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 23 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 27 00 00 10 02 00 80 00 be ef 00 50 20 16 82 02 58 0e 89 41 c0 0d 08 15 01 0c a6 18 result (IDENT REQUEST) = 27 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 2 TLLI cache size : 2 TLLI-Cache: 2 TLLI 8000beef -> 8000beef, IMSI (none), AGE 0 TLLI efe2b700 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING IDENT RESPONSE from 0x01020304:1111 00 00 10 02 01 80 00 be ef 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 11 08 16 08 11 12 99 99 99 16 17 18 bf d2 01 CALLBACK, event 0, msg length 40, bvci 0x1002 00 00 10 02 01 80 00 be ef 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 11 08 16 08 11 12 99 99 99 16 17 18 bf d2 01 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 40 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 44 00 00 10 02 01 80 00 be ef 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 11 08 16 08 11 12 99 99 99 16 17 18 bf d2 01 result (IDENT RESPONSE) = 44 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 2 TLLI cache size : 2 TLLI-Cache: 2 TLLI 8000beef -> 8000beef, IMSI 12199999961718, AGE 0 TLLI efe2b700 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH ACCEPT from 0x05060708:32000 00 00 10 02 00 80 00 be ef 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 16 17 18 00 81 00 0e 9e 41 c0 11 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 3a 6d d4 CALLBACK, event 0, msg length 88, bvci 0x1002 00 00 10 02 00 80 00 be ef 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 16 17 18 00 81 00 0e 9e 41 c0 11 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 3a 6d d4 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 88 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 92 00 00 10 02 00 80 00 be ef 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 16 17 18 00 81 00 0e 9e 41 c0 11 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 3a 6d d4 result (ATTACH ACCEPT) = 92 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 2 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000beef/efe2b700 -> 8000beef/efe2b700, IMSI 12199999961718, AGE 0 PROCESSING ATTACH COMPLETE from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 15 08 03 86 ac 47 CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 15 08 03 86 ac 47 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 15 08 03 86 ac 47 result (ATTACH COMPLETE) = 35 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 2 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000beef/efe2b700 -> 8000beef/efe2b700, IMSI 12199999961718, AGE 0 PROCESSING GMM INFO from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 16 17 18 00 81 00 0e 88 41 c0 15 08 21 bb c1 c6 CALLBACK, event 0, msg length 66, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 16 17 18 00 81 00 0e 88 41 c0 15 08 21 bb c1 c6 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 66 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 70 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 16 17 18 00 81 00 0e 88 41 c0 15 08 21 bb c1 c6 result (GMM INFO) = 70 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 2 TLLI cache size : 1 TLLI-Cache: 1 TLLI efe2b700 -> efe2b700, IMSI 12199999961718, AGE 0 Gbproxy global: === test_gbproxy_ra_patching === --- Initialise SGSN --- MESSAGE to SGSN at 0x05060708:32000, msg length 12 02 00 81 01 01 82 01 01 04 82 01 00 PROCESSING RESET_ACK from 0x05060708:32000 03 01 82 01 01 04 82 01 00 MESSAGE to SGSN at 0x05060708:32000, msg length 1 0a result (RESET_ACK) = 1 PROCESSING ALIVE_ACK from 0x05060708:32000 0b MESSAGE to SGSN at 0x05060708:32000, msg length 1 06 result (ALIVE_ACK) = 1 PROCESSING UNBLOCK_ACK from 0x05060708:32000 07 ==> got signal NS_UNBLOCK, NS-VC 0x0101/5.6.7.8:32000 result (UNBLOCK_ACK) = 0 PROCESSING ALIVE from 0x05060708:32000 0a MESSAGE to SGSN at 0x05060708:32000, msg length 1 0b result (ALIVE) = 1 Current NS-VCIs: VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 NS-VC Block count : 1 --- Initialise BSS 1 --- Setup NS-VC: remote 0x01020304:1111, NSVCI 0x1001(4097), NSEI 0x1000(4096) PROCESSING RESET from 0x01020304:1111 02 00 81 01 01 82 10 01 04 82 10 00 ==> got signal NS_RESET, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 9 03 01 82 10 01 04 82 10 00 MESSAGE to BSS at 0x01020304:1111, msg length 1 0a result (RESET) = 9 PROCESSING ALIVE from 0x01020304:1111 0a MESSAGE to BSS at 0x01020304:1111, msg length 1 0b result (ALIVE) = 1 PROCESSING UNBLOCK from 0x01020304:1111 06 ==> got signal NS_UNBLOCK, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 1 07 result (UNBLOCK) = 1 PROCESSING ALIVE_ACK from 0x01020304:1111 0b result (ALIVE_ACK) = 0 Setup BSSGP: remote 0x01020304:1111, BVCI 0x1002(4098) PROCESSING BVC_RESET from 0x01020304:1111 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 22 00 00 00 00 22 04 82 10 02 07 81 08 08 88 21 63 54 40 50 60 10 00 result (BVC_RESET) = 22 Current NS-VCIs: VCI 0x1001, NSEI 0x1000, peer 0x01020304:1111 VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 NS-VC Block count : 1 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 1 TLLI-Cache: 0 PROCESSING BVC_RESET_ACK from 0x05060708:32000 00 00 00 00 23 04 82 10 02 CALLBACK, event 0, msg length 5, bvci 0x0000 00 00 00 00 23 04 82 10 02 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 5 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 9 00 00 00 00 23 04 82 10 02 result (BVC_RESET_ACK) = 9 PROCESSING BVC_SUSPEND from 0x01020304:1111 00 00 00 00 0b 1f 84 cc d1 75 8b 1b 86 11 22 33 40 50 60 CALLBACK, event 0, msg length 15, bvci 0x0000 00 00 00 00 0b 1f 84 cc d1 75 8b 1b 86 11 22 33 40 50 60 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 15 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 19 00 00 00 00 0b 1f 84 cc d1 75 8b 1b 86 21 63 54 40 50 60 result (BVC_SUSPEND) = 19 PROCESSING BVC_SUSPEND_ACK from 0x05060708:32000 00 00 00 00 0c 1f 84 cc d1 75 8b 1b 86 21 63 54 40 50 60 1d 81 01 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 0c 1f 84 cc d1 75 8b 1b 86 21 63 54 40 50 60 1d 81 01 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 22 00 00 00 00 0c 1f 84 cc d1 75 8b 1b 86 11 22 33 40 50 60 1d 81 01 result (BVC_SUSPEND_ACK) = 22 Gbproxy global: Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 2 RAID patched (SGSN): 1 TLLI from SGSN unknown : 1 TLLI-Cache: 0 --- Send message from BSS 1 to SGSN, BVCI 0x1002 --- PROCESSING ATTACH REQUEST from 0x01020304:1111 00 00 10 02 01 bb c5 46 79 00 00 04 08 88 11 22 33 40 50 60 75 30 00 80 0e 00 34 01 c0 01 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 16 6d 01 CALLBACK, event 0, msg length 75, bvci 0x1002 00 00 10 02 01 bb c5 46 79 00 00 04 08 88 11 22 33 40 50 60 75 30 00 80 0e 00 34 01 c0 01 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 16 6d 01 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 75 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 79 00 00 10 02 01 bb c5 46 79 00 00 04 08 88 21 63 54 40 50 60 75 30 00 80 0e 00 34 01 c0 01 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 21 63 54 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 8e cd 32 result (ATTACH REQUEST) = 79 PROCESSING IDENT REQUEST from 0x05060708:32000 00 00 10 02 00 bb c5 46 79 00 50 20 16 82 02 58 0e 89 41 c0 01 08 15 01 ff 6c ba CALLBACK, event 0, msg length 23, bvci 0x1002 00 00 10 02 00 bb c5 46 79 00 50 20 16 82 02 58 0e 89 41 c0 01 08 15 01 ff 6c ba NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 23 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 27 00 00 10 02 00 bb c5 46 79 00 50 20 16 82 02 58 0e 89 41 c0 01 08 15 01 ff 6c ba result (IDENT REQUEST) = 27 PROCESSING IDENT RESPONSE from 0x01020304:1111 00 00 10 02 01 bb c5 46 79 00 00 04 08 88 11 22 33 40 50 60 75 30 00 80 0e 00 11 01 c0 0d 08 16 08 11 12 13 14 15 16 17 18 b7 1b 9a CALLBACK, event 0, msg length 40, bvci 0x1002 00 00 10 02 01 bb c5 46 79 00 00 04 08 88 11 22 33 40 50 60 75 30 00 80 0e 00 11 01 c0 0d 08 16 08 11 12 13 14 15 16 17 18 b7 1b 9a NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 40 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 44 00 00 10 02 01 bb c5 46 79 00 00 04 08 88 21 63 54 40 50 60 75 30 00 80 0e 00 11 01 c0 0d 08 16 08 11 12 13 14 15 16 17 18 b7 1b 9a result (IDENT RESPONSE) = 44 PROCESSING ATTACH ACCEPT from 0x05060708:32000 00 00 10 02 00 bb c5 46 79 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 05 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 53 62 f1 CALLBACK, event 0, msg length 88, bvci 0x1002 00 00 10 02 00 bb c5 46 79 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 05 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 53 62 f1 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 88 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 92 00 00 10 02 00 bb c5 46 79 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 05 08 02 01 49 04 11 22 33 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 be 38 c0 result (ATTACH ACCEPT) = 92 PROCESSING ATTACH COMPLETE from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 75 30 00 80 0e 00 08 01 c0 11 08 03 ea 67 11 CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 75 30 00 80 0e 00 08 01 c0 11 08 03 ea 67 11 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 75 30 00 80 0e 00 08 01 c0 11 08 03 ea 67 11 result (ATTACH COMPLETE) = 35 PROCESSING ACT PDP CTX REQ (REPLACE APN) from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 75 30 00 80 0e 00 35 01 c0 0d 0a 41 05 03 0c 00 00 1f 10 00 00 00 00 00 00 00 00 02 01 21 28 03 02 61 62 27 14 80 80 21 10 01 00 00 10 81 06 00 00 00 00 83 06 00 00 00 00 5a ff 02 CALLBACK, event 0, msg length 76, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 75 30 00 80 0e 00 35 01 c0 0d 0a 41 05 03 0c 00 00 1f 10 00 00 00 00 00 00 00 00 02 01 21 28 03 02 61 62 27 14 80 80 21 10 01 00 00 10 81 06 00 00 00 00 83 06 00 00 00 00 5a ff 02 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 81 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 85 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 75 30 00 80 0e 00 3a 01 c0 0d 0a 41 05 03 0c 00 00 1f 10 00 00 00 00 00 00 00 00 02 01 21 28 08 03 66 6f 6f 03 62 61 72 27 14 80 80 21 10 01 00 00 10 81 06 00 00 00 00 83 06 00 00 00 00 24 9d 75 result (ACT PDP CTX REQ (REPLACE APN)) = 85 PROCESSING GMM INFO from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 09 08 21 04 ba 3d CALLBACK, event 0, msg length 66, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 09 08 21 04 ba 3d NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 66 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 70 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 09 08 21 04 ba 3d result (GMM INFO) = 70 PROCESSING ACT PDP CTX REQ (REPLACE APN) from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 75 30 00 80 0e 00 35 01 c0 0d 0a 41 05 03 0c 00 00 1f 10 00 00 00 00 00 00 00 00 02 01 21 28 03 02 61 62 27 14 80 80 21 10 01 00 00 10 81 06 00 00 00 00 83 06 00 00 00 00 5a ff 02 CALLBACK, event 0, msg length 76, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 75 30 00 80 0e 00 35 01 c0 0d 0a 41 05 03 0c 00 00 1f 10 00 00 00 00 00 00 00 00 02 01 21 28 03 02 61 62 27 14 80 80 21 10 01 00 00 10 81 06 00 00 00 00 83 06 00 00 00 00 5a ff 02 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 81 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 85 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 75 30 00 80 0e 00 3a 01 c0 0d 0a 41 05 03 0c 00 00 1f 10 00 00 00 00 00 00 00 00 02 01 21 28 08 03 66 6f 6f 03 62 61 72 27 14 80 80 21 10 01 00 00 10 81 06 00 00 00 00 83 06 00 00 00 00 24 9d 75 result (ACT PDP CTX REQ (REPLACE APN)) = 85 PROCESSING ACT PDP CTX REQ (REMOVE APN) from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 75 30 00 80 0e 00 35 01 c0 0d 0a 41 05 03 0c 00 00 1f 10 00 00 00 00 00 00 00 00 02 01 21 28 03 02 61 62 27 14 80 80 21 10 01 00 00 10 81 06 00 00 00 00 83 06 00 00 00 00 5a ff 02 CALLBACK, event 0, msg length 76, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 75 30 00 80 0e 00 35 01 c0 0d 0a 41 05 03 0c 00 00 1f 10 00 00 00 00 00 00 00 00 02 01 21 28 03 02 61 62 27 14 80 80 21 10 01 00 00 10 81 06 00 00 00 00 83 06 00 00 00 00 5a ff 02 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 71 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 75 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 75 30 00 80 0e 00 30 01 c0 0d 0a 41 05 03 0c 00 00 1f 10 00 00 00 00 00 00 00 00 02 01 21 27 14 80 80 21 10 01 00 00 10 81 06 00 00 00 00 83 06 00 00 00 00 85 fa 60 result (ACT PDP CTX REQ (REMOVE APN)) = 75 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 9 RAID patched (SGSN): 2 APN patched : 3 Attach Request count : 1 TLLI from SGSN unknown : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI efe2b700 -> efe2b700, IMSI 12131415161718, AGE 0, IMSI matches PROCESSING DETACH REQ from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 75 30 00 80 0e 00 15 01 c0 19 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 7e e1 41 CALLBACK, event 0, msg length 44, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 75 30 00 80 0e 00 15 01 c0 19 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 7e e1 41 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 44 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 48 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 75 30 00 80 0e 00 15 01 c0 19 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 7e e1 41 result (DETACH REQ) = 48 PROCESSING DETACH ACC from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 89 41 c0 15 08 06 00 f7 35 f0 CALLBACK, event 0, msg length 67, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 89 41 c0 15 08 06 00 f7 35 f0 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 67 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 71 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 89 41 c0 15 08 06 00 f7 35 f0 result (DETACH ACC) = 71 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 10 RAID patched (SGSN): 2 APN patched : 3 Attach Request count : 1 TLLI from SGSN unknown : 1 TLLI-Cache: 0 --- RA update --- PROCESSING RA UPD REQ from 0x01020304:1111 00 00 10 02 01 bb c5 46 79 00 00 04 08 88 11 22 33 40 50 60 70 80 00 80 0e 00 3e 01 c0 15 08 08 10 11 22 33 40 50 60 1d 19 13 42 33 57 2b f7 c8 48 02 13 48 50 c8 48 02 14 48 50 c8 48 02 17 49 10 c8 48 02 00 19 8b b2 92 17 16 27 07 04 31 02 e5 e0 32 02 20 00 96 3e 97 CALLBACK, event 0, msg length 85, bvci 0x1002 00 00 10 02 01 bb c5 46 79 00 00 04 08 88 11 22 33 40 50 60 70 80 00 80 0e 00 3e 01 c0 15 08 08 10 11 22 33 40 50 60 1d 19 13 42 33 57 2b f7 c8 48 02 13 48 50 c8 48 02 14 48 50 c8 48 02 17 49 10 c8 48 02 00 19 8b b2 92 17 16 27 07 04 31 02 e5 e0 32 02 20 00 96 3e 97 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 85 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 89 00 00 10 02 01 bb c5 46 79 00 00 04 08 88 21 63 54 40 50 60 70 80 00 80 0e 00 3e 01 c0 15 08 08 10 21 63 54 40 50 60 1d 19 13 42 33 57 2b f7 c8 48 02 13 48 50 c8 48 02 14 48 50 c8 48 02 17 49 10 c8 48 02 00 19 8b b2 92 17 16 27 07 04 31 02 e5 e0 32 02 20 00 1d f0 41 result (RA UPD REQ) = 89 PROCESSING RA UPD ACC from 0x05060708:32000 00 00 10 02 00 bb c5 46 79 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9d 41 c0 19 08 09 00 49 21 63 54 40 50 60 19 54 ab b3 18 05 f4 ef e2 b7 00 17 16 d7 59 65 CALLBACK, event 0, msg length 87, bvci 0x1002 00 00 10 02 00 bb c5 46 79 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9d 41 c0 19 08 09 00 49 21 63 54 40 50 60 19 54 ab b3 18 05 f4 ef e2 b7 00 17 16 d7 59 65 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 87 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 91 00 00 10 02 00 bb c5 46 79 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9d 41 c0 19 08 09 00 49 11 22 33 40 50 60 19 54 ab b3 18 05 f4 ef e2 b7 00 17 16 3a 03 54 result (RA UPD ACC) = 91 PROCESSING ACT PDP CTX REQ (REMOVE APN) from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 75 30 00 80 0e 00 35 01 c0 0d 0a 41 05 03 0c 00 00 1f 10 00 00 00 00 00 00 00 00 02 01 21 28 03 02 61 62 27 14 80 80 21 10 01 00 00 10 81 06 00 00 00 00 83 06 00 00 00 00 5a ff 02 CALLBACK, event 0, msg length 76, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 75 30 00 80 0e 00 35 01 c0 0d 0a 41 05 03 0c 00 00 1f 10 00 00 00 00 00 00 00 00 02 01 21 28 03 02 61 62 27 14 80 80 21 10 01 00 00 10 81 06 00 00 00 00 83 06 00 00 00 00 5a ff 02 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 71 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 75 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 75 30 00 80 0e 00 30 01 c0 0d 0a 41 05 03 0c 00 00 1f 10 00 00 00 00 00 00 00 00 02 01 21 27 14 80 80 21 10 01 00 00 10 81 06 00 00 00 00 83 06 00 00 00 00 85 fa 60 result (ACT PDP CTX REQ (REMOVE APN)) = 75 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 13 RAID patched (SGSN): 3 APN patched : 4 Attach Request count : 1 TLLI from SGSN unknown : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI bbc54679/efe2b700 -> bbc54679/efe2b700, IMSI 12131415161718, AGE 0, IMSI matches PROCESSING DETACH REQ (PWR OFF) from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 75 30 00 80 0e 00 15 01 c0 19 08 05 09 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 84 0c eb CALLBACK, event 0, msg length 44, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 75 30 00 80 0e 00 15 01 c0 19 08 05 09 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 84 0c eb NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 44 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 48 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 75 30 00 80 0e 00 15 01 c0 19 08 05 09 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 84 0c eb result (DETACH REQ (PWR OFF)) = 48 Gbproxy global: Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 14 RAID patched (SGSN): 3 APN patched : 4 Attach Request count : 1 TLLI from SGSN unknown : 1 TLLI-Cache: 0 --- Bad cases --- PROCESSING ATTACH REQUEST (foreign RAI) from 0x01020304:1111 00 00 10 02 01 bb 00 be ef 00 00 04 08 88 11 22 33 40 50 60 75 30 00 80 0e 00 34 01 c0 01 08 01 02 f5 e0 21 08 02 05 f4 fb 00 be ef 99 99 99 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 2d c7 df CALLBACK, event 0, msg length 75, bvci 0x1002 00 00 10 02 01 bb 00 be ef 00 00 04 08 88 11 22 33 40 50 60 75 30 00 80 0e 00 34 01 c0 01 08 01 02 f5 e0 21 08 02 05 f4 fb 00 be ef 99 99 99 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 2d c7 df NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 75 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 79 00 00 10 02 01 bb 00 be ef 00 00 04 08 88 21 63 54 40 50 60 75 30 00 80 0e 00 34 01 c0 01 08 01 02 f5 e0 21 08 02 05 f4 fb 00 be ef 99 99 99 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 2d c7 df result (ATTACH REQUEST (foreign RAI)) = 79 TLLI is already detached, shouldn't patch PROCESSING ACT PDP CTX REQ from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 75 30 00 80 0e 00 35 01 c0 0d 0a 41 05 03 0c 00 00 1f 10 00 00 00 00 00 00 00 00 02 01 21 28 03 02 61 62 27 14 80 80 21 10 01 00 00 10 81 06 00 00 00 00 83 06 00 00 00 00 5a ff 02 CALLBACK, event 0, msg length 76, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 75 30 00 80 0e 00 35 01 c0 0d 0a 41 05 03 0c 00 00 1f 10 00 00 00 00 00 00 00 00 02 01 21 28 03 02 61 62 27 14 80 80 21 10 01 00 00 10 81 06 00 00 00 00 83 06 00 00 00 00 5a ff 02 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 76 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 80 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 75 30 00 80 0e 00 35 01 c0 0d 0a 41 05 03 0c 00 00 1f 10 00 00 00 00 00 00 00 00 02 01 21 28 03 02 61 62 27 14 80 80 21 10 01 00 00 10 81 06 00 00 00 00 83 06 00 00 00 00 5a ff 02 result (ACT PDP CTX REQ) = 80 Invalid RAI, shouldn't patch PROCESSING BVC_SUSPEND_ACK from 0x05060708:32000 00 00 00 00 0c 1f 84 cc d1 75 8b 1b 86 00 f1 99 00 63 60 1d 81 01 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 0c 1f 84 cc d1 75 8b 1b 86 00 f1 99 00 63 60 1d 81 01 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 24 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 28 00 00 00 00 41 07 81 21 15 92 0c 1f 84 cc d1 75 8b 1b 86 00 f1 99 00 63 60 1d 81 01 result (BVC_SUSPEND_ACK) = 28 Gbproxy global: Invalid Routing Area Identifier : 1 Patch error: no peer : 1 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 16 RAID patched (SGSN): 3 APN patched : 4 Attach Request count : 2 TLLI from SGSN unknown : 1 TLLI cache size : 2 TLLI-Cache: 2 TLLI efe2b700 -> efe2b700, IMSI (none), AGE 0 TLLI bb00beef -> bb00beef, IMSI (none), AGE 0 === test_gbproxy_ptmsi_patching === --- Initialise SGSN --- MESSAGE to SGSN at 0x05060708:32000, msg length 12 02 00 81 01 01 82 01 01 04 82 01 00 PROCESSING RESET_ACK from 0x05060708:32000 03 01 82 01 01 04 82 01 00 MESSAGE to SGSN at 0x05060708:32000, msg length 1 0a result (RESET_ACK) = 1 PROCESSING ALIVE_ACK from 0x05060708:32000 0b MESSAGE to SGSN at 0x05060708:32000, msg length 1 06 result (ALIVE_ACK) = 1 PROCESSING UNBLOCK_ACK from 0x05060708:32000 07 ==> got signal NS_UNBLOCK, NS-VC 0x0101/5.6.7.8:32000 result (UNBLOCK_ACK) = 0 PROCESSING ALIVE from 0x05060708:32000 0a MESSAGE to SGSN at 0x05060708:32000, msg length 1 0b result (ALIVE) = 1 --- Initialise BSS 1 --- Setup NS-VC: remote 0x01020304:1111, NSVCI 0x1001(4097), NSEI 0x1000(4096) PROCESSING RESET from 0x01020304:1111 02 00 81 01 01 82 10 01 04 82 10 00 ==> got signal NS_RESET, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 9 03 01 82 10 01 04 82 10 00 MESSAGE to BSS at 0x01020304:1111, msg length 1 0a result (RESET) = 9 PROCESSING ALIVE from 0x01020304:1111 0a MESSAGE to BSS at 0x01020304:1111, msg length 1 0b result (ALIVE) = 1 PROCESSING UNBLOCK from 0x01020304:1111 06 ==> got signal NS_UNBLOCK, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 1 07 result (UNBLOCK) = 1 PROCESSING ALIVE_ACK from 0x01020304:1111 0b result (ALIVE_ACK) = 0 Setup BSSGP: remote 0x01020304:1111, BVCI 0x1002(4098) PROCESSING BVC_RESET from 0x01020304:1111 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 22 00 00 00 00 22 04 82 10 02 07 81 08 08 88 21 63 54 40 50 60 10 00 result (BVC_RESET) = 22 PROCESSING BVC_RESET_ACK from 0x05060708:32000 00 00 00 00 23 04 82 10 02 CALLBACK, event 0, msg length 5, bvci 0x0000 00 00 00 00 23 04 82 10 02 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 5 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 9 00 00 00 00 23 04 82 10 02 result (BVC_RESET_ACK) = 9 Current NS-VCIs: VCI 0x1001, NSEI 0x1000, peer 0x01020304:1111 VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 NS-VC Block count : 1 Gbproxy global: Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 1 TLLI-Cache: 0 --- Send message from BSS 1 to SGSN, BVCI 0x1002 --- PROCESSING ATTACH REQUEST from 0x01020304:1111 00 00 10 02 01 80 00 de ad 00 00 04 08 88 00 f1 99 00 63 60 12 34 00 80 0e 00 34 01 c0 01 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 16 6d 01 CALLBACK, event 0, msg length 75, bvci 0x1002 00 00 10 02 01 80 00 de ad 00 00 04 08 88 00 f1 99 00 63 60 12 34 00 80 0e 00 34 01 c0 01 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 16 6d 01 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 75 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 79 00 00 10 02 01 7c 69 fb 81 00 00 04 08 88 21 63 54 00 63 60 12 34 00 80 0e 00 34 01 c0 01 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 16 6d 01 result (ATTACH REQUEST) = 79 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 2 TLLI patched (BSS ): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead -> 7c69fb81, IMSI (none), AGE 0 PROCESSING IDENT REQUEST from 0x05060708:32000 00 00 10 02 00 7c 69 fb 81 00 50 20 16 82 02 58 0e 89 41 c0 01 08 15 01 ff 6c ba CALLBACK, event 0, msg length 23, bvci 0x1002 00 00 10 02 00 7c 69 fb 81 00 50 20 16 82 02 58 0e 89 41 c0 01 08 15 01 ff 6c ba NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 23 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 27 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 0e 89 41 c0 01 08 15 01 ff 6c ba result (IDENT REQUEST) = 27 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 2 TLLI patched (BSS ): 1 TLLI patched (SGSN): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead -> 7c69fb81, IMSI (none), AGE 0 PROCESSING IDENT RESPONSE from 0x01020304:1111 00 00 10 02 01 80 00 de ad 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 05 08 16 08 11 12 13 14 15 16 17 18 ad 05 28 CALLBACK, event 0, msg length 40, bvci 0x1002 00 00 10 02 01 80 00 de ad 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 05 08 16 08 11 12 13 14 15 16 17 18 ad 05 28 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 40 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 44 00 00 10 02 01 7c 69 fb 81 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 11 01 c0 05 08 16 08 11 12 13 14 15 16 17 18 ad 05 28 result (IDENT RESPONSE) = 44 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 3 TLLI patched (BSS ): 2 TLLI patched (SGSN): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead -> 7c69fb81, IMSI 12131415161718, AGE 0 PROCESSING ATTACH ACCEPT from 0x05060708:32000 00 00 10 02 00 7c 69 fb 81 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 05 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 53 62 f1 CALLBACK, event 0, msg length 88, bvci 0x1002 00 00 10 02 00 7c 69 fb 81 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 05 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 53 62 f1 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 88 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 92 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 05 08 02 01 49 04 11 22 33 40 50 60 19 cd d7 08 17 16 18 05 f4 c0 0f 73 04 50 22 97 result (ATTACH ACCEPT) = 92 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 3 RAID patched (SGSN): 1 TLLI patched (BSS ): 2 TLLI patched (SGSN): 2 P-TMSI patched (SGSN): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead/c00f7304 -> 7c69fb81/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH COMPLETE from 0x01020304:1111 00 00 10 02 01 c0 0f 73 04 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 09 08 03 39 d7 bc CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 c0 0f 73 04 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 09 08 03 39 d7 bc NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 08 01 c0 09 08 03 39 d7 bc result (ATTACH COMPLETE) = 35 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 4 RAID patched (SGSN): 1 TLLI patched (BSS ): 3 TLLI patched (SGSN): 2 P-TMSI patched (SGSN): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead/c00f7304 -> 7c69fb81/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING GMM INFO from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 09 08 21 04 ba 3d CALLBACK, event 0, msg length 66, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 09 08 21 04 ba 3d NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 66 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 70 00 00 10 02 00 c0 0f 73 04 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 09 08 21 04 ba 3d result (GMM INFO) = 70 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 4 RAID patched (SGSN): 1 TLLI patched (BSS ): 3 TLLI patched (SGSN): 3 P-TMSI patched (SGSN): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI c00f7304 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING ACT PDP CTX REQ (REPLACE APN) from 0x01020304:1111 00 00 10 02 01 c0 0f 73 04 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 35 01 c0 0d 0a 41 05 03 0c 00 00 1f 10 00 00 00 00 00 00 00 00 02 01 21 28 03 02 61 62 27 14 80 80 21 10 01 00 00 10 81 06 00 00 00 00 83 06 00 00 00 00 5a ff 02 CALLBACK, event 0, msg length 76, bvci 0x1002 00 00 10 02 01 c0 0f 73 04 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 35 01 c0 0d 0a 41 05 03 0c 00 00 1f 10 00 00 00 00 00 00 00 00 02 01 21 28 03 02 61 62 27 14 80 80 21 10 01 00 00 10 81 06 00 00 00 00 83 06 00 00 00 00 5a ff 02 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 81 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 85 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 3a 01 c0 0d 0a 41 05 03 0c 00 00 1f 10 00 00 00 00 00 00 00 00 02 01 21 28 08 03 66 6f 6f 03 62 61 72 27 14 80 80 21 10 01 00 00 10 81 06 00 00 00 00 83 06 00 00 00 00 24 9d 75 result (ACT PDP CTX REQ (REPLACE APN)) = 85 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 5 RAID patched (SGSN): 1 APN patched : 1 TLLI patched (BSS ): 4 TLLI patched (SGSN): 3 P-TMSI patched (SGSN): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI c00f7304 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING XID (UL) from 0x01020304:1111 00 00 10 02 01 c0 0f 73 04 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 0f 41 fb 01 00 0e 00 64 11 05 16 01 90 66 b3 28 CALLBACK, event 0, msg length 38, bvci 0x1002 00 00 10 02 01 c0 0f 73 04 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 0f 41 fb 01 00 0e 00 64 11 05 16 01 90 66 b3 28 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 38 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 42 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 0f 41 fb 01 00 0e 00 64 11 05 16 01 90 66 b3 28 result (XID (UL)) = 42 PROCESSING XID (DL) from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8c 41 fb 30 84 10 61 b6 64 e4 a9 1a 9e CALLBACK, event 0, msg length 70, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8c 41 fb 30 84 10 61 b6 64 e4 a9 1a 9e NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 70 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 74 00 00 10 02 00 c0 0f 73 04 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8c 41 fb 30 84 10 61 b6 64 e4 a9 1a 9e result (XID (DL)) = 74 PROCESSING LL11 DNS QUERY (UL) from 0x01020304:1111 00 00 10 02 01 c0 0f 73 04 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 42 0b c0 01 65 00 00 00 45 00 00 38 95 72 00 00 45 11 20 85 0a c0 07 e4 ac 10 01 0a ad ab 00 35 00 24 0e 1c 3b e0 01 00 00 01 00 00 00 00 00 00 01 6d 05 68 65 69 73 65 02 64 65 00 00 01 00 01 47 8f 07 CALLBACK, event 0, msg length 89, bvci 0x1002 00 00 10 02 01 c0 0f 73 04 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 42 0b c0 01 65 00 00 00 45 00 00 38 95 72 00 00 45 11 20 85 0a c0 07 e4 ac 10 01 0a ad ab 00 35 00 24 0e 1c 3b e0 01 00 00 01 00 00 00 00 00 00 01 6d 05 68 65 69 73 65 02 64 65 00 00 01 00 01 47 8f 07 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 89 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 93 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 42 0b c0 01 65 00 00 00 45 00 00 38 95 72 00 00 45 11 20 85 0a c0 07 e4 ac 10 01 0a ad ab 00 35 00 24 0e 1c 3b e0 01 00 00 01 00 00 00 00 00 00 01 6d 05 68 65 69 73 65 02 64 65 00 00 01 00 01 47 8f 07 result (LL11 DNS QUERY (UL)) = 93 PROCESSING LL11 DNS RESP (DL) from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 00 d0 4b c0 01 65 00 00 00 45 00 00 c6 00 00 40 00 3e 11 7c 69 ac 10 01 0a 0a c0 07 e4 00 35 ad ab 00 b2 74 4e 3b e0 81 80 00 01 00 01 00 05 00 00 01 6d 05 68 65 69 73 65 02 64 65 00 00 01 00 01 c0 0c 00 01 00 01 00 00 0e 10 00 04 c1 63 90 58 c0 0e 00 02 00 01 00 00 0e 10 00 16 03 6e 73 32 0c 70 6f 70 2d 68 61 6e 6e 6f 76 65 72 03 6e 65 74 00 c0 0e 00 02 00 01 00 00 0e 10 00 10 02 6e 73 01 73 08 70 6c 75 73 6c 69 6e 65 c0 14 c0 0e 00 02 00 01 00 00 0e 10 00 05 02 6e 73 c0 0e c0 0e 00 02 00 01 00 00 0e 10 00 05 02 6e 73 c0 5f c0 0e 00 02 00 01 00 00 0e 10 00 12 02 6e 73 0c 70 6f 70 2d 68 61 6e 6e 6f 76 65 72 c0 14 aa df 31 CALLBACK, event 0, msg length 267, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 00 d0 4b c0 01 65 00 00 00 45 00 00 c6 00 00 40 00 3e 11 7c 69 ac 10 01 0a 0a c0 07 e4 00 35 ad ab 00 b2 74 4e 3b e0 81 80 00 01 00 01 00 05 00 00 01 6d 05 68 65 69 73 65 02 64 65 00 00 01 00 01 c0 0c 00 01 00 01 00 00 0e 10 00 04 c1 63 90 58 c0 0e 00 02 00 01 00 00 0e 10 00 16 03 6e 73 32 0c 70 6f 70 2d 68 61 6e 6e 6f 76 65 72 03 6e 65 74 00 c0 0e 00 02 00 01 00 00 0e 10 00 10 02 6e 73 01 73 08 70 6c 75 73 6c 69 6e 65 c0 14 c0 0e 00 02 00 01 00 00 0e 10 00 05 02 6e 73 c0 0e c0 0e 00 02 00 01 00 00 0e 10 00 05 02 6e 73 c0 5f c0 0e 00 02 00 01 00 00 0e 10 00 12 02 6e 73 0c 70 6f 70 2d 68 61 6e 6e 6f 76 65 72 c0 14 aa df 31 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 267 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 271 00 00 10 02 00 c0 0f 73 04 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 00 d0 4b c0 01 65 00 00 00 45 00 00 c6 00 00 40 00 3e 11 7c 69 ac 10 01 0a 0a c0 07 e4 00 35 ad ab 00 b2 74 4e 3b e0 81 80 00 01 00 01 00 05 00 00 01 6d 05 68 65 69 73 65 02 64 65 00 00 01 00 01 c0 0c 00 01 00 01 00 00 0e 10 00 04 c1 63 90 58 c0 0e 00 02 00 01 00 00 0e 10 00 16 03 6e 73 32 0c 70 6f 70 2d 68 61 6e 6e 6f 76 65 72 03 6e 65 74 00 c0 0e 00 02 00 01 00 00 0e 10 00 10 02 6e 73 01 73 08 70 6c 75 73 6c 69 6e 65 c0 14 c0 0e 00 02 00 01 00 00 0e 10 00 05 02 6e 73 c0 0e c0 0e 00 02 00 01 00 00 0e 10 00 05 02 6e 73 c0 5f c0 0e 00 02 00 01 00 00 0e 10 00 12 02 6e 73 0c 70 6f 70 2d 68 61 6e 6e 6f 76 65 72 c0 14 aa df 31 result (LL11 DNS RESP (DL)) = 271 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 7 RAID patched (SGSN): 1 APN patched : 1 TLLI patched (BSS ): 6 TLLI patched (SGSN): 5 P-TMSI patched (SGSN): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI c00f7304 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING RA UPD REQ (P-TMSI 2) from 0x01020304:1111 00 00 10 02 01 c0 0f 73 04 00 00 04 08 88 11 22 33 40 50 60 70 80 00 80 0e 00 3e 01 c0 11 08 08 10 11 22 33 40 50 60 1d 19 13 42 33 57 2b f7 c8 48 02 13 48 50 c8 48 02 14 48 50 c8 48 02 17 49 10 c8 48 02 00 19 8b b2 92 17 16 27 07 04 31 02 e5 e0 32 02 20 00 e2 6d 78 CALLBACK, event 0, msg length 85, bvci 0x1002 00 00 10 02 01 c0 0f 73 04 00 00 04 08 88 11 22 33 40 50 60 70 80 00 80 0e 00 3e 01 c0 11 08 08 10 11 22 33 40 50 60 1d 19 13 42 33 57 2b f7 c8 48 02 13 48 50 c8 48 02 14 48 50 c8 48 02 17 49 10 c8 48 02 00 19 8b b2 92 17 16 27 07 04 31 02 e5 e0 32 02 20 00 e2 6d 78 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 85 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 89 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 70 80 00 80 0e 00 3e 01 c0 11 08 08 10 21 63 54 40 50 60 1d 19 13 42 33 57 2b f7 c8 48 02 13 48 50 c8 48 02 14 48 50 c8 48 02 17 49 10 c8 48 02 00 19 8b b2 92 17 16 27 07 04 31 02 e5 e0 32 02 20 00 69 a3 ae result (RA UPD REQ (P-TMSI 2)) = 89 PROCESSING RA UDP ACC (P-TMSI 2) from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9d 41 c0 0d 08 09 00 49 21 63 54 40 50 60 19 54 ab b3 18 05 f4 e0 98 76 54 17 16 9f e8 ea CALLBACK, event 0, msg length 87, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9d 41 c0 0d 08 09 00 49 21 63 54 40 50 60 19 54 ab b3 18 05 f4 e0 98 76 54 17 16 9f e8 ea NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 87 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 91 00 00 10 02 00 c0 0f 73 04 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9d 41 c0 0d 08 09 00 49 11 22 33 40 50 60 19 54 ab b3 18 05 f4 e6 56 aa 1f 17 16 c5 7f 98 result (RA UDP ACC (P-TMSI 2)) = 91 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 9 RAID patched (SGSN): 2 APN patched : 1 TLLI patched (BSS ): 7 TLLI patched (SGSN): 6 P-TMSI patched (SGSN): 2 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI c00f7304/e656aa1f -> efe2b700/e0987654, IMSI 12131415161718, AGE 0 PROCESSING RA UPD REQ (P-TMSI 3) from 0x01020304:1111 00 00 10 02 01 e6 56 aa 1f 00 00 04 08 88 11 22 33 40 50 60 70 80 00 80 0e 00 3e 01 c0 15 08 08 10 11 22 33 40 50 60 1d 19 13 42 33 57 2b f7 c8 48 02 13 48 50 c8 48 02 14 48 50 c8 48 02 17 49 10 c8 48 02 00 19 8b b2 92 17 16 27 07 04 31 02 e5 e0 32 02 20 00 96 3e 97 CALLBACK, event 0, msg length 85, bvci 0x1002 00 00 10 02 01 e6 56 aa 1f 00 00 04 08 88 11 22 33 40 50 60 70 80 00 80 0e 00 3e 01 c0 15 08 08 10 11 22 33 40 50 60 1d 19 13 42 33 57 2b f7 c8 48 02 13 48 50 c8 48 02 14 48 50 c8 48 02 17 49 10 c8 48 02 00 19 8b b2 92 17 16 27 07 04 31 02 e5 e0 32 02 20 00 96 3e 97 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 85 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 89 00 00 10 02 01 e0 98 76 54 00 00 04 08 88 21 63 54 40 50 60 70 80 00 80 0e 00 3e 01 c0 15 08 08 10 21 63 54 40 50 60 1d 19 13 42 33 57 2b f7 c8 48 02 13 48 50 c8 48 02 14 48 50 c8 48 02 17 49 10 c8 48 02 00 19 8b b2 92 17 16 27 07 04 31 02 e5 e0 32 02 20 00 1d f0 41 result (RA UPD REQ (P-TMSI 3)) = 89 PROCESSING RA UDP ACC (P-TMSI 3) from 0x05060708:32000 00 00 10 02 00 e0 98 76 54 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9d 41 c0 11 08 09 00 49 21 63 54 40 50 60 19 54 ab b3 18 05 f4 e0 54 32 10 17 16 1b a3 a8 CALLBACK, event 0, msg length 87, bvci 0x1002 00 00 10 02 00 e0 98 76 54 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9d 41 c0 11 08 09 00 49 21 63 54 40 50 60 19 54 ab b3 18 05 f4 e0 54 32 10 17 16 1b a3 a8 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 87 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 91 00 00 10 02 00 e6 56 aa 1f 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9d 41 c0 11 08 09 00 49 11 22 33 40 50 60 19 54 ab b3 18 05 f4 ea d4 77 5a 17 16 31 d5 78 result (RA UDP ACC (P-TMSI 3)) = 91 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 11 RAID patched (SGSN): 3 APN patched : 1 TLLI patched (BSS ): 8 TLLI patched (SGSN): 7 P-TMSI patched (SGSN): 3 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI c00f7304/ead4775a -> efe2b700/e0543210, IMSI 12131415161718, AGE 0 PROCESSING RA UPD COMPLETE from 0x01020304:1111 00 00 10 02 01 ea d4 77 5a 00 00 04 08 88 11 22 33 40 50 60 70 80 00 80 0e 00 08 01 c0 19 08 0a d5 5f 5e CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 ea d4 77 5a 00 00 04 08 88 11 22 33 40 50 60 70 80 00 80 0e 00 08 01 c0 19 08 0a d5 5f 5e NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 e0 54 32 10 00 00 04 08 88 21 63 54 40 50 60 70 80 00 80 0e 00 08 01 c0 19 08 0a d5 5f 5e result (RA UPD COMPLETE) = 35 PROCESSING GMM INFO from 0x05060708:32000 00 00 10 02 00 e0 54 32 10 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 15 08 21 bb c1 c6 CALLBACK, event 0, msg length 66, bvci 0x1002 00 00 10 02 00 e0 54 32 10 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 15 08 21 bb c1 c6 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 66 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 70 00 00 10 02 00 ea d4 77 5a 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 15 08 21 bb c1 c6 result (GMM INFO) = 70 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 12 RAID patched (SGSN): 3 APN patched : 1 TLLI patched (BSS ): 9 TLLI patched (SGSN): 8 P-TMSI patched (SGSN): 3 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI ead4775a -> e0543210, IMSI 12131415161718, AGE 0 PROCESSING LLC_DISCARDED from 0x01020304:1111 00 00 00 00 2c 1f 84 ea d4 77 5a 0f 81 01 04 82 10 02 25 83 00 00 0c CALLBACK, event 0, msg length 19, bvci 0x0000 00 00 00 00 2c 1f 84 ea d4 77 5a 0f 81 01 04 82 10 02 25 83 00 00 0c NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 19 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 23 00 00 00 00 2c 1f 84 e0 54 32 10 0f 81 01 04 82 10 02 25 83 00 00 0c result (LLC_DISCARDED) = 23 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 12 RAID patched (SGSN): 3 APN patched : 1 TLLI patched (BSS ): 10 TLLI patched (SGSN): 8 P-TMSI patched (SGSN): 3 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI ead4775a -> e0543210, IMSI 12131415161718, AGE 0 PROCESSING BVC_SUSPEND from 0x01020304:1111 00 00 00 00 0b 1f 84 ea d4 77 5a 1b 86 11 22 33 40 50 60 CALLBACK, event 0, msg length 15, bvci 0x0000 00 00 00 00 0b 1f 84 ea d4 77 5a 1b 86 11 22 33 40 50 60 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 15 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 19 00 00 00 00 0b 1f 84 e0 54 32 10 1b 86 21 63 54 40 50 60 result (BVC_SUSPEND) = 19 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 13 RAID patched (SGSN): 3 APN patched : 1 TLLI patched (BSS ): 11 TLLI patched (SGSN): 8 P-TMSI patched (SGSN): 3 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI ead4775a -> e0543210, IMSI 12131415161718, AGE 0 PROCESSING BVC_SUSPEND_ACK from 0x05060708:32000 00 00 00 00 0c 1f 84 e0 54 32 10 1b 86 21 63 54 40 50 60 1d 81 01 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 0c 1f 84 e0 54 32 10 1b 86 21 63 54 40 50 60 1d 81 01 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 22 00 00 00 00 0c 1f 84 ea d4 77 5a 1b 86 11 22 33 40 50 60 1d 81 01 result (BVC_SUSPEND_ACK) = 22 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 13 RAID patched (SGSN): 4 APN patched : 1 TLLI patched (BSS ): 11 TLLI patched (SGSN): 9 P-TMSI patched (SGSN): 3 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI ead4775a -> e0543210, IMSI 12131415161718, AGE 0 PROCESSING PAGING_PS from 0x05060708:32000 00 00 00 00 06 0d 88 11 12 13 14 15 16 17 18 0a 82 07 04 1b 86 11 22 33 40 50 60 18 83 00 00 00 20 84 e0 54 32 10 CALLBACK, event 0, msg length 34, bvci 0x0000 00 00 00 00 06 0d 88 11 12 13 14 15 16 17 18 0a 82 07 04 1b 86 11 22 33 40 50 60 18 83 00 00 00 20 84 e0 54 32 10 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 34 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 38 00 00 00 00 06 0d 88 11 12 13 14 15 16 17 18 0a 82 07 04 1b 86 11 22 33 40 50 60 18 83 00 00 00 20 84 ea d4 77 5a result (PAGING_PS) = 38 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 13 RAID patched (SGSN): 5 APN patched : 1 TLLI patched (BSS ): 11 TLLI patched (SGSN): 9 P-TMSI patched (SGSN): 4 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI ead4775a -> e0543210, IMSI 12131415161718, AGE 0 PROCESSING LLC_DISCARDED from 0x01020304:1111 00 00 00 00 2c 1f 84 ea d4 77 5a 0f 81 01 04 82 ee e1 25 83 00 00 0c CALLBACK, event 0, msg length 19, bvci 0x0000 00 00 00 00 2c 1f 84 ea d4 77 5a 0f 81 01 04 82 ee e1 25 83 00 00 0c NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 19 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 23 00 00 00 00 2c 1f 84 e0 54 32 10 0f 81 01 04 82 ee e1 25 83 00 00 0c result (LLC_DISCARDED) = 23 Gbproxy global: PROCESSING BVC_SUSPEND_ACK from 0x05060708:32000 00 00 00 00 0c 1f 84 e0 54 32 10 1b 86 00 f1 99 00 63 60 1d 81 01 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 0c 1f 84 e0 54 32 10 1b 86 00 f1 99 00 63 60 1d 81 01 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 24 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 28 00 00 00 00 41 07 81 21 15 92 0c 1f 84 e0 54 32 10 1b 86 00 f1 99 00 63 60 1d 81 01 result (BVC_SUSPEND_ACK) = 28 Gbproxy global: Invalid Routing Area Identifier : 1 Patch error: no peer : 1 PROCESSING BVC_SUSPEND_ACK from 0x05060708:32000 00 00 00 00 0c 1f 84 e0 54 32 10 1b 86 99 69 54 40 50 60 1d 81 01 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 0c 1f 84 e0 54 32 10 1b 86 99 69 54 40 50 60 1d 81 01 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 22 00 00 00 00 0c 1f 84 ea d4 77 5a 1b 86 11 22 33 40 50 60 1d 81 01 result (BVC_SUSPEND_ACK) = 22 Gbproxy global: Invalid Routing Area Identifier : 1 Patch error: no peer : 1 PROCESSING GMM INFO from 0x05060708:32000 00 00 10 02 00 ee ba db ad 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 00 83 00 00 00 0e 88 41 c0 09 08 21 04 ba 3d CALLBACK, event 0, msg length 58, bvci 0x1002 00 00 10 02 00 ee ba db ad 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 00 83 00 00 00 0e 88 41 c0 09 08 21 04 ba 3d NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 58 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 62 00 00 10 02 00 ee ba db ad 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 00 83 00 00 00 0e 88 41 c0 09 08 21 04 ba 3d result (GMM INFO) = 62 PROCESSING GMM INFO from 0x05060708:32000 00 00 10 02 00 ee ba db ad 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 0d 08 21 68 71 6b CALLBACK, event 0, msg length 66, bvci 0x1002 00 00 10 02 00 ee ba db ad 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 0d 08 21 68 71 6b NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 66 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 70 00 00 10 02 00 ee ba db ad 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 0d 08 21 68 71 6b result (GMM INFO) = 70 PROCESSING DETACH REQ from 0x01020304:1111 00 00 10 02 01 ea d4 77 5a 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 15 01 c0 1d 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb aa cc a3 CALLBACK, event 0, msg length 44, bvci 0x1002 00 00 10 02 01 ea d4 77 5a 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 15 01 c0 1d 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb aa cc a3 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 44 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 48 00 00 10 02 01 e0 54 32 10 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 15 01 c0 1d 08 05 01 18 05 f4 e0 54 32 10 19 03 b9 97 cb ea 6d af result (DETACH REQ) = 48 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 14 RAID patched (SGSN): 6 APN patched : 1 TLLI patched (BSS ): 13 TLLI patched (SGSN): 10 P-TMSI patched (BSS ): 1 P-TMSI patched (SGSN): 4 Attach Request count : 1 TLLI from SGSN unknown : 2 TLLI cache size : 1 TLLI-Cache: 1 TLLI ead4775a -> e0543210, IMSI 12131415161718, AGE 0 PROCESSING DETACH ACC from 0x05060708:32000 00 00 10 02 00 e0 54 32 10 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 89 41 c0 19 08 06 00 04 ff 52 CALLBACK, event 0, msg length 67, bvci 0x1002 00 00 10 02 00 e0 54 32 10 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 89 41 c0 19 08 06 00 04 ff 52 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 67 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 71 00 00 10 02 00 ea d4 77 5a 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 89 41 c0 19 08 06 00 04 ff 52 result (DETACH ACC) = 71 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 14 RAID patched (SGSN): 6 APN patched : 1 TLLI patched (BSS ): 13 TLLI patched (SGSN): 11 P-TMSI patched (BSS ): 1 P-TMSI patched (SGSN): 4 Attach Request count : 1 TLLI from SGSN unknown : 2 TLLI-Cache: 0 Gbproxy global: Invalid Routing Area Identifier : 1 Patch error: no peer : 1 === test_gbproxy_ptmsi_patching_bad_cases === --- Initialise SGSN --- MESSAGE to SGSN at 0x05060708:32000, msg length 12 02 00 81 01 01 82 01 01 04 82 01 00 PROCESSING RESET_ACK from 0x05060708:32000 03 01 82 01 01 04 82 01 00 MESSAGE to SGSN at 0x05060708:32000, msg length 1 0a result (RESET_ACK) = 1 PROCESSING ALIVE_ACK from 0x05060708:32000 0b MESSAGE to SGSN at 0x05060708:32000, msg length 1 06 result (ALIVE_ACK) = 1 PROCESSING UNBLOCK_ACK from 0x05060708:32000 07 ==> got signal NS_UNBLOCK, NS-VC 0x0101/5.6.7.8:32000 result (UNBLOCK_ACK) = 0 PROCESSING ALIVE from 0x05060708:32000 0a MESSAGE to SGSN at 0x05060708:32000, msg length 1 0b result (ALIVE) = 1 --- Initialise BSS 1 --- Setup NS-VC: remote 0x01020304:1111, NSVCI 0x1001(4097), NSEI 0x1000(4096) PROCESSING RESET from 0x01020304:1111 02 00 81 01 01 82 10 01 04 82 10 00 ==> got signal NS_RESET, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 9 03 01 82 10 01 04 82 10 00 MESSAGE to BSS at 0x01020304:1111, msg length 1 0a result (RESET) = 9 PROCESSING ALIVE from 0x01020304:1111 0a MESSAGE to BSS at 0x01020304:1111, msg length 1 0b result (ALIVE) = 1 PROCESSING UNBLOCK from 0x01020304:1111 06 ==> got signal NS_UNBLOCK, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 1 07 result (UNBLOCK) = 1 PROCESSING ALIVE_ACK from 0x01020304:1111 0b result (ALIVE_ACK) = 0 Setup BSSGP: remote 0x01020304:1111, BVCI 0x1002(4098) PROCESSING BVC_RESET from 0x01020304:1111 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 22 00 00 00 00 22 04 82 10 02 07 81 08 08 88 21 63 54 40 50 60 10 00 result (BVC_RESET) = 22 PROCESSING BVC_RESET_ACK from 0x05060708:32000 00 00 00 00 23 04 82 10 02 CALLBACK, event 0, msg length 5, bvci 0x0000 00 00 00 00 23 04 82 10 02 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 5 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 9 00 00 00 00 23 04 82 10 02 result (BVC_RESET_ACK) = 9 Current NS-VCIs: VCI 0x1001, NSEI 0x1000, peer 0x01020304:1111 VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 NS-VC Block count : 1 Gbproxy global: Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 1 TLLI-Cache: 0 --- Send message from BSS 1 to SGSN, BVCI 0x1002 --- PROCESSING ATTACH REQUEST from 0x01020304:1111 00 00 10 02 01 80 00 de ad 00 00 04 08 88 00 f1 99 00 63 60 12 34 00 80 0e 00 34 01 c0 01 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 16 6d 01 CALLBACK, event 0, msg length 75, bvci 0x1002 00 00 10 02 01 80 00 de ad 00 00 04 08 88 00 f1 99 00 63 60 12 34 00 80 0e 00 34 01 c0 01 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 16 6d 01 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 75 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 79 00 00 10 02 01 7c 69 fb 81 00 00 04 08 88 21 63 54 00 63 60 12 34 00 80 0e 00 34 01 c0 01 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 16 6d 01 result (ATTACH REQUEST) = 79 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 2 TLLI patched (BSS ): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead -> 7c69fb81, IMSI (none), AGE 0 PROCESSING IDENT REQUEST from 0x05060708:32000 00 00 10 02 00 7c 69 fb 81 00 50 20 16 82 02 58 0e 89 41 c0 01 08 15 01 ff 6c ba CALLBACK, event 0, msg length 23, bvci 0x1002 00 00 10 02 00 7c 69 fb 81 00 50 20 16 82 02 58 0e 89 41 c0 01 08 15 01 ff 6c ba NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 23 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 27 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 0e 89 41 c0 01 08 15 01 ff 6c ba result (IDENT REQUEST) = 27 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 2 TLLI patched (BSS ): 1 TLLI patched (SGSN): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead -> 7c69fb81, IMSI (none), AGE 0 PROCESSING IDENT RESPONSE from 0x01020304:1111 00 00 10 02 01 80 00 de ad 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 05 08 16 08 11 12 13 14 15 16 17 18 ad 05 28 CALLBACK, event 0, msg length 40, bvci 0x1002 00 00 10 02 01 80 00 de ad 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 05 08 16 08 11 12 13 14 15 16 17 18 ad 05 28 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 40 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 44 00 00 10 02 01 7c 69 fb 81 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 11 01 c0 05 08 16 08 11 12 13 14 15 16 17 18 ad 05 28 result (IDENT RESPONSE) = 44 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 3 TLLI patched (BSS ): 2 TLLI patched (SGSN): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead -> 7c69fb81, IMSI 12131415161718, AGE 0 PROCESSING ATTACH ACCEPT from 0x05060708:32000 00 00 10 02 00 7c 69 fb 81 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 05 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 53 62 f1 CALLBACK, event 0, msg length 88, bvci 0x1002 00 00 10 02 00 7c 69 fb 81 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 05 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 53 62 f1 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 88 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 92 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 05 08 02 01 49 04 11 22 33 40 50 60 19 cd d7 08 17 16 18 05 f4 c0 0f 73 04 50 22 97 result (ATTACH ACCEPT) = 92 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 3 RAID patched (SGSN): 1 TLLI patched (BSS ): 2 TLLI patched (SGSN): 2 P-TMSI patched (SGSN): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead/c00f7304 -> 7c69fb81/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH ACCEPT (duplicated) from 0x05060708:32000 00 00 10 02 00 7c 69 fb 81 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 09 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 1d 9e 24 CALLBACK, event 0, msg length 88, bvci 0x1002 00 00 10 02 00 7c 69 fb 81 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 09 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 1d 9e 24 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 88 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 92 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 09 08 02 01 49 04 11 22 33 40 50 60 19 cd d7 08 17 16 18 05 f4 c0 0f 73 04 1e de 42 result (ATTACH ACCEPT (duplicated)) = 92 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 3 RAID patched (SGSN): 2 TLLI patched (BSS ): 2 TLLI patched (SGSN): 3 P-TMSI patched (SGSN): 2 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead/c00f7304 -> 7c69fb81/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH COMPLETE from 0x01020304:1111 00 00 10 02 01 c0 0f 73 04 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 09 08 03 39 d7 bc CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 c0 0f 73 04 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 09 08 03 39 d7 bc NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 08 01 c0 09 08 03 39 d7 bc result (ATTACH COMPLETE) = 35 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 4 RAID patched (SGSN): 2 TLLI patched (BSS ): 3 TLLI patched (SGSN): 3 P-TMSI patched (SGSN): 2 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead/c00f7304 -> 7c69fb81/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING GMM INFO from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 0d 08 21 68 71 6b CALLBACK, event 0, msg length 66, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 0d 08 21 68 71 6b NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 66 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 70 00 00 10 02 00 c0 0f 73 04 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 0d 08 21 68 71 6b result (GMM INFO) = 70 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 4 RAID patched (SGSN): 2 TLLI patched (BSS ): 3 TLLI patched (SGSN): 4 P-TMSI patched (SGSN): 2 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI c00f7304 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING DETACH REQ from 0x01020304:1111 00 00 10 02 01 c0 0f 73 04 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 15 01 c0 0d 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 37 67 c6 CALLBACK, event 0, msg length 44, bvci 0x1002 00 00 10 02 01 c0 0f 73 04 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 15 01 c0 0d 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 37 67 c6 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 44 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 48 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 15 01 c0 0d 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 37 67 c6 result (DETACH REQ) = 48 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 5 RAID patched (SGSN): 2 TLLI patched (BSS ): 4 TLLI patched (SGSN): 4 P-TMSI patched (SGSN): 2 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI c00f7304 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING DETACH ACC from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 89 41 c0 11 08 06 00 cf 8a 58 CALLBACK, event 0, msg length 67, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 89 41 c0 11 08 06 00 cf 8a 58 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 67 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 71 00 00 10 02 00 c0 0f 73 04 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 89 41 c0 11 08 06 00 cf 8a 58 result (DETACH ACC) = 71 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 5 RAID patched (SGSN): 2 TLLI patched (BSS ): 4 TLLI patched (SGSN): 5 P-TMSI patched (SGSN): 2 Attach Request count : 1 TLLI-Cache: 0 Gbproxy global: === test_gbproxy_imsi_acquisition === --- Initialise SGSN --- MESSAGE to SGSN at 0x05060708:32000, msg length 12 02 00 81 01 01 82 01 01 04 82 01 00 PROCESSING RESET_ACK from 0x05060708:32000 03 01 82 01 01 04 82 01 00 MESSAGE to SGSN at 0x05060708:32000, msg length 1 0a result (RESET_ACK) = 1 PROCESSING ALIVE_ACK from 0x05060708:32000 0b MESSAGE to SGSN at 0x05060708:32000, msg length 1 06 result (ALIVE_ACK) = 1 PROCESSING UNBLOCK_ACK from 0x05060708:32000 07 ==> got signal NS_UNBLOCK, NS-VC 0x0101/5.6.7.8:32000 result (UNBLOCK_ACK) = 0 PROCESSING ALIVE from 0x05060708:32000 0a MESSAGE to SGSN at 0x05060708:32000, msg length 1 0b result (ALIVE) = 1 --- Initialise BSS 1 --- Setup NS-VC: remote 0x01020304:1111, NSVCI 0x1001(4097), NSEI 0x1000(4096) PROCESSING RESET from 0x01020304:1111 02 00 81 01 01 82 10 01 04 82 10 00 ==> got signal NS_RESET, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 9 03 01 82 10 01 04 82 10 00 MESSAGE to BSS at 0x01020304:1111, msg length 1 0a result (RESET) = 9 PROCESSING ALIVE from 0x01020304:1111 0a MESSAGE to BSS at 0x01020304:1111, msg length 1 0b result (ALIVE) = 1 PROCESSING UNBLOCK from 0x01020304:1111 06 ==> got signal NS_UNBLOCK, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 1 07 result (UNBLOCK) = 1 PROCESSING ALIVE_ACK from 0x01020304:1111 0b result (ALIVE_ACK) = 0 Setup BSSGP: remote 0x01020304:1111, BVCI 0x1002(4098) PROCESSING BVC_RESET from 0x01020304:1111 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 22 00 00 00 00 22 04 82 10 02 07 81 08 08 88 21 63 54 40 50 60 10 00 result (BVC_RESET) = 22 PROCESSING BVC_RESET_ACK from 0x05060708:32000 00 00 00 00 23 04 82 10 02 CALLBACK, event 0, msg length 5, bvci 0x0000 00 00 00 00 23 04 82 10 02 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 5 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 9 00 00 00 00 23 04 82 10 02 result (BVC_RESET_ACK) = 9 Current NS-VCIs: VCI 0x1001, NSEI 0x1000, peer 0x01020304:1111 VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 NS-VC Block count : 1 Gbproxy global: Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 1 TLLI-Cache: 0 --- Send message from BSS 1 to SGSN, BVCI 0x1002 --- PROCESSING ATTACH REQUEST from 0x01020304:1111 00 00 10 02 01 80 00 de ad 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 01 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 16 6d 01 CALLBACK, event 0, msg length 75, bvci 0x1002 00 00 10 02 01 80 00 de ad 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 01 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 16 6d 01 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 24 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 28 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 0e 00 09 41 c4 01 08 15 01 b7 f8 36 result (ATTACH REQUEST) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead -> 7c69fb81, IMSI (none), AGE 0, STORED 1, IMSI acquisition in progress PROCESSING IDENT RESPONSE from 0x01020304:1111 00 00 10 02 01 80 00 de ad 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 05 08 16 08 11 12 13 14 15 16 17 18 ad 05 28 CALLBACK, event 0, msg length 40, bvci 0x1002 00 00 10 02 01 80 00 de ad 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 05 08 16 08 11 12 13 14 15 16 17 18 ad 05 28 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 75 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 79 00 00 10 02 01 7c 69 fb 81 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 34 01 c0 01 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 21 63 54 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 8e cd 32 result (IDENT RESPONSE) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 3 TLLI patched (BSS ): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead -> 7c69fb81, IMSI 12131415161718, AGE 0 PROCESSING IDENT REQUEST from 0x05060708:32000 00 00 10 02 00 7c 69 fb 81 00 50 20 16 82 02 58 0e 89 41 c0 01 08 15 01 ff 6c ba CALLBACK, event 0, msg length 23, bvci 0x1002 00 00 10 02 00 7c 69 fb 81 00 50 20 16 82 02 58 0e 89 41 c0 01 08 15 01 ff 6c ba NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 23 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 27 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 0e 89 41 c0 01 08 15 01 ff 6c ba result (IDENT REQUEST) = 27 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 3 TLLI patched (BSS ): 1 TLLI patched (SGSN): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead -> 7c69fb81, IMSI 12131415161718, AGE 0 PROCESSING IDENT RESPONSE from 0x01020304:1111 00 00 10 02 01 80 00 de ad 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 09 08 16 08 11 12 13 14 15 16 17 18 ba 14 c3 CALLBACK, event 0, msg length 40, bvci 0x1002 00 00 10 02 01 80 00 de ad 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 09 08 16 08 11 12 13 14 15 16 17 18 ba 14 c3 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 40 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 44 00 00 10 02 01 7c 69 fb 81 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 11 01 c0 09 08 16 08 11 12 13 14 15 16 17 18 ba 14 c3 result (IDENT RESPONSE) = 44 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 4 TLLI patched (BSS ): 2 TLLI patched (SGSN): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead -> 7c69fb81, IMSI 12131415161718, AGE 0 PROCESSING ATTACH ACCEPT from 0x05060708:32000 00 00 10 02 00 7c 69 fb 81 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 05 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 53 62 f1 CALLBACK, event 0, msg length 88, bvci 0x1002 00 00 10 02 00 7c 69 fb 81 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 05 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 53 62 f1 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 88 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 92 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 05 08 02 01 49 04 11 22 33 40 50 60 19 cd d7 08 17 16 18 05 f4 c0 0f 73 04 50 22 97 result (ATTACH ACCEPT) = 92 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 4 RAID patched (SGSN): 1 TLLI patched (BSS ): 2 TLLI patched (SGSN): 2 P-TMSI patched (SGSN): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead/c00f7304 -> 7c69fb81/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH COMPLETE from 0x01020304:1111 00 00 10 02 01 c0 0f 73 04 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 0d 08 03 55 1c ea CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 c0 0f 73 04 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 0d 08 03 55 1c ea NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 08 01 c0 0d 08 03 55 1c ea result (ATTACH COMPLETE) = 35 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 5 RAID patched (SGSN): 1 TLLI patched (BSS ): 3 TLLI patched (SGSN): 2 P-TMSI patched (SGSN): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead/c00f7304 -> 7c69fb81/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING GMM INFO from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 09 08 21 04 ba 3d CALLBACK, event 0, msg length 66, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 09 08 21 04 ba 3d NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 66 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 70 00 00 10 02 00 c0 0f 73 04 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 09 08 21 04 ba 3d result (GMM INFO) = 70 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 5 RAID patched (SGSN): 1 TLLI patched (BSS ): 3 TLLI patched (SGSN): 3 P-TMSI patched (SGSN): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI c00f7304 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING XID (UL) from 0x01020304:1111 00 00 10 02 01 c0 0f 73 04 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 0f 41 fb 01 00 0e 00 64 11 05 16 01 90 66 b3 28 CALLBACK, event 0, msg length 38, bvci 0x1002 00 00 10 02 01 c0 0f 73 04 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 0f 41 fb 01 00 0e 00 64 11 05 16 01 90 66 b3 28 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 38 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 42 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 0f 41 fb 01 00 0e 00 64 11 05 16 01 90 66 b3 28 result (XID (UL)) = 42 PROCESSING XID (DL) from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8c 41 fb 30 84 10 61 b6 64 e4 a9 1a 9e CALLBACK, event 0, msg length 70, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8c 41 fb 30 84 10 61 b6 64 e4 a9 1a 9e NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 70 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 74 00 00 10 02 00 c0 0f 73 04 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8c 41 fb 30 84 10 61 b6 64 e4 a9 1a 9e result (XID (DL)) = 74 PROCESSING LL11 DNS QUERY (UL) from 0x01020304:1111 00 00 10 02 01 c0 0f 73 04 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 42 0b c0 01 65 00 00 00 45 00 00 38 95 72 00 00 45 11 20 85 0a c0 07 e4 ac 10 01 0a ad ab 00 35 00 24 0e 1c 3b e0 01 00 00 01 00 00 00 00 00 00 01 6d 05 68 65 69 73 65 02 64 65 00 00 01 00 01 47 8f 07 CALLBACK, event 0, msg length 89, bvci 0x1002 00 00 10 02 01 c0 0f 73 04 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 42 0b c0 01 65 00 00 00 45 00 00 38 95 72 00 00 45 11 20 85 0a c0 07 e4 ac 10 01 0a ad ab 00 35 00 24 0e 1c 3b e0 01 00 00 01 00 00 00 00 00 00 01 6d 05 68 65 69 73 65 02 64 65 00 00 01 00 01 47 8f 07 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 89 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 93 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 42 0b c0 01 65 00 00 00 45 00 00 38 95 72 00 00 45 11 20 85 0a c0 07 e4 ac 10 01 0a ad ab 00 35 00 24 0e 1c 3b e0 01 00 00 01 00 00 00 00 00 00 01 6d 05 68 65 69 73 65 02 64 65 00 00 01 00 01 47 8f 07 result (LL11 DNS QUERY (UL)) = 93 PROCESSING LL11 DNS RESP (DL) from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 00 d0 4b c0 01 65 00 00 00 45 00 00 c6 00 00 40 00 3e 11 7c 69 ac 10 01 0a 0a c0 07 e4 00 35 ad ab 00 b2 74 4e 3b e0 81 80 00 01 00 01 00 05 00 00 01 6d 05 68 65 69 73 65 02 64 65 00 00 01 00 01 c0 0c 00 01 00 01 00 00 0e 10 00 04 c1 63 90 58 c0 0e 00 02 00 01 00 00 0e 10 00 16 03 6e 73 32 0c 70 6f 70 2d 68 61 6e 6e 6f 76 65 72 03 6e 65 74 00 c0 0e 00 02 00 01 00 00 0e 10 00 10 02 6e 73 01 73 08 70 6c 75 73 6c 69 6e 65 c0 14 c0 0e 00 02 00 01 00 00 0e 10 00 05 02 6e 73 c0 0e c0 0e 00 02 00 01 00 00 0e 10 00 05 02 6e 73 c0 5f c0 0e 00 02 00 01 00 00 0e 10 00 12 02 6e 73 0c 70 6f 70 2d 68 61 6e 6e 6f 76 65 72 c0 14 aa df 31 CALLBACK, event 0, msg length 267, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 00 d0 4b c0 01 65 00 00 00 45 00 00 c6 00 00 40 00 3e 11 7c 69 ac 10 01 0a 0a c0 07 e4 00 35 ad ab 00 b2 74 4e 3b e0 81 80 00 01 00 01 00 05 00 00 01 6d 05 68 65 69 73 65 02 64 65 00 00 01 00 01 c0 0c 00 01 00 01 00 00 0e 10 00 04 c1 63 90 58 c0 0e 00 02 00 01 00 00 0e 10 00 16 03 6e 73 32 0c 70 6f 70 2d 68 61 6e 6e 6f 76 65 72 03 6e 65 74 00 c0 0e 00 02 00 01 00 00 0e 10 00 10 02 6e 73 01 73 08 70 6c 75 73 6c 69 6e 65 c0 14 c0 0e 00 02 00 01 00 00 0e 10 00 05 02 6e 73 c0 0e c0 0e 00 02 00 01 00 00 0e 10 00 05 02 6e 73 c0 5f c0 0e 00 02 00 01 00 00 0e 10 00 12 02 6e 73 0c 70 6f 70 2d 68 61 6e 6e 6f 76 65 72 c0 14 aa df 31 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 267 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 271 00 00 10 02 00 c0 0f 73 04 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 00 d0 4b c0 01 65 00 00 00 45 00 00 c6 00 00 40 00 3e 11 7c 69 ac 10 01 0a 0a c0 07 e4 00 35 ad ab 00 b2 74 4e 3b e0 81 80 00 01 00 01 00 05 00 00 01 6d 05 68 65 69 73 65 02 64 65 00 00 01 00 01 c0 0c 00 01 00 01 00 00 0e 10 00 04 c1 63 90 58 c0 0e 00 02 00 01 00 00 0e 10 00 16 03 6e 73 32 0c 70 6f 70 2d 68 61 6e 6e 6f 76 65 72 03 6e 65 74 00 c0 0e 00 02 00 01 00 00 0e 10 00 10 02 6e 73 01 73 08 70 6c 75 73 6c 69 6e 65 c0 14 c0 0e 00 02 00 01 00 00 0e 10 00 05 02 6e 73 c0 0e c0 0e 00 02 00 01 00 00 0e 10 00 05 02 6e 73 c0 5f c0 0e 00 02 00 01 00 00 0e 10 00 12 02 6e 73 0c 70 6f 70 2d 68 61 6e 6e 6f 76 65 72 c0 14 aa df 31 result (LL11 DNS RESP (DL)) = 271 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 7 RAID patched (SGSN): 1 TLLI patched (BSS ): 5 TLLI patched (SGSN): 5 P-TMSI patched (SGSN): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI c00f7304 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING LLC_DISCARDED from 0x01020304:1111 00 00 00 00 2c 1f 84 c0 0f 73 04 0f 81 01 04 82 10 02 25 83 00 00 0c CALLBACK, event 0, msg length 19, bvci 0x0000 00 00 00 00 2c 1f 84 c0 0f 73 04 0f 81 01 04 82 10 02 25 83 00 00 0c NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 19 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 23 00 00 00 00 2c 1f 84 ef e2 b7 00 0f 81 01 04 82 10 02 25 83 00 00 0c result (LLC_DISCARDED) = 23 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 7 RAID patched (SGSN): 1 TLLI patched (BSS ): 6 TLLI patched (SGSN): 5 P-TMSI patched (SGSN): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI c00f7304 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING LLC_DISCARDED from 0x05060708:32000 00 00 00 00 2c 1f 84 ef e2 b7 00 0f 81 01 04 82 10 02 25 83 00 00 0c CALLBACK, event 0, msg length 19, bvci 0x0000 00 00 00 00 2c 1f 84 ef e2 b7 00 0f 81 01 04 82 10 02 25 83 00 00 0c NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 25 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 29 00 00 00 00 41 07 81 27 15 93 2c 1f 84 ef e2 b7 00 0f 81 01 04 82 10 02 25 83 00 00 0c result (LLC_DISCARDED) = 29 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 7 RAID patched (SGSN): 1 TLLI patched (BSS ): 6 TLLI patched (SGSN): 6 P-TMSI patched (SGSN): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI c00f7304 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING BVC_SUSPEND from 0x01020304:1111 00 00 00 00 0b 1f 84 c0 0f 73 04 1b 86 11 22 33 40 50 60 CALLBACK, event 0, msg length 15, bvci 0x0000 00 00 00 00 0b 1f 84 c0 0f 73 04 1b 86 11 22 33 40 50 60 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 15 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 19 00 00 00 00 0b 1f 84 ef e2 b7 00 1b 86 21 63 54 40 50 60 result (BVC_SUSPEND) = 19 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 8 RAID patched (SGSN): 1 TLLI patched (BSS ): 7 TLLI patched (SGSN): 6 P-TMSI patched (SGSN): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI c00f7304 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING BVC_SUSPEND_ACK from 0x05060708:32000 00 00 00 00 0c 1f 84 ef e2 b7 00 1b 86 21 63 54 40 50 60 1d 81 01 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 0c 1f 84 ef e2 b7 00 1b 86 21 63 54 40 50 60 1d 81 01 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 22 00 00 00 00 0c 1f 84 c0 0f 73 04 1b 86 11 22 33 40 50 60 1d 81 01 result (BVC_SUSPEND_ACK) = 22 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 8 RAID patched (SGSN): 2 TLLI patched (BSS ): 7 TLLI patched (SGSN): 7 P-TMSI patched (SGSN): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI c00f7304 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING LLC_DISCARDED from 0x01020304:1111 00 00 00 00 2c 1f 84 c0 0f 73 04 0f 81 01 04 82 ee e1 25 83 00 00 0c CALLBACK, event 0, msg length 19, bvci 0x0000 00 00 00 00 2c 1f 84 c0 0f 73 04 0f 81 01 04 82 ee e1 25 83 00 00 0c NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 19 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 23 00 00 00 00 2c 1f 84 ef e2 b7 00 0f 81 01 04 82 ee e1 25 83 00 00 0c result (LLC_DISCARDED) = 23 Gbproxy global: BSSGP protocol error (SGSN): 1 PROCESSING BVC_SUSPEND_ACK from 0x05060708:32000 00 00 00 00 0c 1f 84 ef e2 b7 00 1b 86 00 f1 99 00 63 60 1d 81 01 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 0c 1f 84 ef e2 b7 00 1b 86 00 f1 99 00 63 60 1d 81 01 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 24 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 28 00 00 00 00 41 07 81 21 15 92 0c 1f 84 ef e2 b7 00 1b 86 00 f1 99 00 63 60 1d 81 01 result (BVC_SUSPEND_ACK) = 28 Gbproxy global: Invalid Routing Area Identifier : 1 BSSGP protocol error (SGSN): 1 Patch error: no peer : 1 PROCESSING BVC_SUSPEND_ACK from 0x05060708:32000 00 00 00 00 0c 1f 84 ef e2 b7 00 1b 86 99 69 54 40 50 60 1d 81 01 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 0c 1f 84 ef e2 b7 00 1b 86 99 69 54 40 50 60 1d 81 01 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 22 00 00 00 00 0c 1f 84 c0 0f 73 04 1b 86 11 22 33 40 50 60 1d 81 01 result (BVC_SUSPEND_ACK) = 22 Gbproxy global: Invalid Routing Area Identifier : 1 BSSGP protocol error (SGSN): 1 Patch error: no peer : 1 PROCESSING DETACH REQ from 0x01020304:1111 00 00 10 02 01 c0 0f 73 04 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 15 01 c0 11 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 6d b1 de CALLBACK, event 0, msg length 44, bvci 0x1002 00 00 10 02 01 c0 0f 73 04 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 15 01 c0 11 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 6d b1 de NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 44 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 48 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 15 01 c0 11 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 6d b1 de result (DETACH REQ) = 48 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 9 RAID patched (SGSN): 3 TLLI patched (BSS ): 9 TLLI patched (SGSN): 8 P-TMSI patched (SGSN): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI c00f7304 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING DETACH ACC from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 89 41 c0 0d 08 06 00 aa ab ee CALLBACK, event 0, msg length 67, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 89 41 c0 0d 08 06 00 aa ab ee NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 67 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 71 00 00 10 02 00 c0 0f 73 04 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 89 41 c0 0d 08 06 00 aa ab ee result (DETACH ACC) = 71 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 9 RAID patched (SGSN): 3 TLLI patched (BSS ): 9 TLLI patched (SGSN): 9 P-TMSI patched (SGSN): 1 Attach Request count : 1 TLLI-Cache: 0 PROCESSING RA UPD REQ from 0x01020304:1111 00 00 10 02 01 80 00 de ad 00 00 04 08 88 00 f1 99 00 63 60 70 80 00 80 0e 00 3e 01 c0 15 08 08 10 11 22 33 40 50 60 1d 19 13 42 33 57 2b f7 c8 48 02 13 48 50 c8 48 02 14 48 50 c8 48 02 17 49 10 c8 48 02 00 19 8b b2 92 17 16 27 07 04 31 02 e5 e0 32 02 20 00 96 3e 97 CALLBACK, event 0, msg length 85, bvci 0x1002 00 00 10 02 01 80 00 de ad 00 00 04 08 88 00 f1 99 00 63 60 70 80 00 80 0e 00 3e 01 c0 15 08 08 10 11 22 33 40 50 60 1d 19 13 42 33 57 2b f7 c8 48 02 13 48 50 c8 48 02 14 48 50 c8 48 02 17 49 10 c8 48 02 00 19 8b b2 92 17 16 27 07 04 31 02 e5 e0 32 02 20 00 96 3e 97 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 24 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 28 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 0e 00 09 41 c4 01 08 15 01 b7 f8 36 result (RA UPD REQ) = 0 PROCESSING IDENT RESPONSE from 0x01020304:1111 00 00 10 02 01 80 00 de ad 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 19 08 16 08 11 12 13 14 15 16 17 18 35 23 fc CALLBACK, event 0, msg length 40, bvci 0x1002 00 00 10 02 01 80 00 de ad 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 19 08 16 08 11 12 13 14 15 16 17 18 35 23 fc NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 85 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 89 00 00 10 02 01 7e b5 2d fb 00 00 04 08 88 21 63 54 00 63 60 70 80 00 80 0e 00 3e 01 c0 15 08 08 10 11 22 33 40 50 60 1d 19 13 42 33 57 2b f7 c8 48 02 13 48 50 c8 48 02 14 48 50 c8 48 02 17 49 10 c8 48 02 00 19 8b b2 92 17 16 27 07 04 31 02 e5 e0 32 02 20 00 96 3e 97 result (IDENT RESPONSE) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 10 RAID patched (SGSN): 3 TLLI patched (BSS ): 10 TLLI patched (SGSN): 9 P-TMSI patched (SGSN): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead -> 7eb52dfb, IMSI 12131415161718, AGE 0 PROCESSING RA UDP ACC from 0x05060708:32000 00 00 10 02 00 7e b5 2d fb 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9d 41 c0 11 08 09 00 49 21 63 54 40 50 60 19 54 ab b3 18 05 f4 ef e2 b7 00 17 16 36 98 77 CALLBACK, event 0, msg length 87, bvci 0x1002 00 00 10 02 00 7e b5 2d fb 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9d 41 c0 11 08 09 00 49 21 63 54 40 50 60 19 54 ab b3 18 05 f4 ef e2 b7 00 17 16 36 98 77 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 87 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 91 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9d 41 c0 11 08 09 00 49 11 22 33 40 50 60 19 54 ab b3 18 05 f4 e6 56 aa 1f 17 16 cb d8 0b result (RA UDP ACC) = 91 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 10 RAID patched (SGSN): 4 TLLI patched (BSS ): 10 TLLI patched (SGSN): 10 P-TMSI patched (SGSN): 2 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead/e656aa1f -> 7eb52dfb/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING DETACH REQ from 0x01020304:1111 00 00 10 02 01 c0 0f 73 04 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 15 01 c0 1d 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb aa cc a3 CALLBACK, event 0, msg length 44, bvci 0x1002 00 00 10 02 01 c0 0f 73 04 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 15 01 c0 1d 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb aa cc a3 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 24 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 28 00 00 10 02 00 c0 0f 73 04 00 50 20 16 82 02 58 0e 00 09 41 c4 01 08 06 00 11 f5 c0 result (DETACH REQ) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 10 RAID patched (SGSN): 4 TLLI patched (BSS ): 10 TLLI patched (SGSN): 10 P-TMSI patched (SGSN): 2 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead/e656aa1f -> 7eb52dfb/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING DETACH ACC from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 89 41 c0 15 08 06 00 f7 35 f0 CALLBACK, event 0, msg length 67, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 89 41 c0 15 08 06 00 f7 35 f0 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 67 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 71 00 00 10 02 00 e6 56 aa 1f 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 89 41 c0 15 08 06 00 f7 35 f0 result (DETACH ACC) = 71 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 10 RAID patched (SGSN): 4 TLLI patched (BSS ): 10 TLLI patched (SGSN): 11 P-TMSI patched (SGSN): 2 Attach Request count : 1 TLLI-Cache: 0 PROCESSING ATTACH REQUEST from 0x01020304:1111 00 00 10 02 01 80 00 de ad 00 00 04 08 88 00 f1 99 00 63 60 12 34 00 80 0e 00 34 01 c0 21 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 44 b6 bb CALLBACK, event 0, msg length 75, bvci 0x1002 00 00 10 02 01 80 00 de ad 00 00 04 08 88 00 f1 99 00 63 60 12 34 00 80 0e 00 34 01 c0 21 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 44 b6 bb NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 24 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 28 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 0e 00 09 41 c4 01 08 15 01 b7 f8 36 result (ATTACH REQUEST) = 0 PROCESSING ATTACH REQUEST from 0x01020304:1111 00 00 10 02 01 80 00 de ad 00 00 04 08 88 00 f1 99 00 63 60 12 34 00 80 0e 00 34 01 c0 25 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 1d aa 57 CALLBACK, event 0, msg length 75, bvci 0x1002 00 00 10 02 01 80 00 de ad 00 00 04 08 88 00 f1 99 00 63 60 12 34 00 80 0e 00 34 01 c0 25 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 1d aa 57 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 24 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 28 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 0e 00 09 41 c4 05 08 15 01 8f 47 9e result (ATTACH REQUEST) = 0 PROCESSING DETACH REQ from 0x01020304:1111 00 00 10 02 01 80 00 de ad 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 15 01 c0 29 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb d9 1d ef CALLBACK, event 0, msg length 44, bvci 0x1002 00 00 10 02 01 80 00 de ad 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 15 01 c0 29 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb d9 1d ef NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 24 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 28 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 0e 00 09 41 c4 09 08 06 00 da 80 ca result (DETACH REQ) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 10 RAID patched (SGSN): 4 TLLI patched (BSS ): 10 TLLI patched (SGSN): 11 P-TMSI patched (SGSN): 2 Attach Request count : 3 TLLI-Cache: 0 PROCESSING DETACH REQ (unknown TLLI) from 0x01020304:1111 00 00 10 02 01 80 00 be ef 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 15 01 c0 2d 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 0d 30 0d CALLBACK, event 0, msg length 44, bvci 0x1002 00 00 10 02 01 80 00 be ef 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 15 01 c0 2d 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 0d 30 0d NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 24 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 28 00 00 10 02 00 80 00 be ef 00 50 20 16 82 02 58 0e 00 09 41 c4 01 08 06 00 11 f5 c0 result (DETACH REQ (unknown TLLI)) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 10 RAID patched (SGSN): 4 TLLI patched (BSS ): 10 TLLI patched (SGSN): 11 P-TMSI patched (SGSN): 2 Attach Request count : 3 TLLI-Cache: 0 PROCESSING RA UPD REQ from 0x01020304:1111 00 00 10 02 01 80 00 de ad 00 00 04 08 88 00 f1 99 00 63 60 70 80 00 80 0e 00 3e 01 c0 31 08 08 10 11 22 33 40 50 60 1d 19 13 42 33 57 2b f7 c8 48 02 13 48 50 c8 48 02 14 48 50 c8 48 02 17 49 10 c8 48 02 00 19 8b b2 92 17 16 27 07 04 31 02 e5 e0 32 02 20 00 d8 cf d8 CALLBACK, event 0, msg length 85, bvci 0x1002 00 00 10 02 01 80 00 de ad 00 00 04 08 88 00 f1 99 00 63 60 70 80 00 80 0e 00 3e 01 c0 31 08 08 10 11 22 33 40 50 60 1d 19 13 42 33 57 2b f7 c8 48 02 13 48 50 c8 48 02 14 48 50 c8 48 02 17 49 10 c8 48 02 00 19 8b b2 92 17 16 27 07 04 31 02 e5 e0 32 02 20 00 d8 cf d8 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 24 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 28 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 0e 00 09 41 c4 01 08 15 01 b7 f8 36 result (RA UPD REQ) = 0 PROCESSING RA UPD REQ from 0x01020304:1111 00 00 10 02 01 80 00 de ad 00 00 04 08 88 00 f1 99 00 63 60 70 80 00 80 0e 00 3e 01 c0 35 08 08 10 11 22 33 40 50 60 1d 19 13 42 33 57 2b f7 c8 48 02 13 48 50 c8 48 02 14 48 50 c8 48 02 17 49 10 c8 48 02 00 19 8b b2 92 17 16 27 07 04 31 02 e5 e0 32 02 20 00 ac 9c 37 CALLBACK, event 0, msg length 85, bvci 0x1002 00 00 10 02 01 80 00 de ad 00 00 04 08 88 00 f1 99 00 63 60 70 80 00 80 0e 00 3e 01 c0 35 08 08 10 11 22 33 40 50 60 1d 19 13 42 33 57 2b f7 c8 48 02 13 48 50 c8 48 02 14 48 50 c8 48 02 17 49 10 c8 48 02 00 19 8b b2 92 17 16 27 07 04 31 02 e5 e0 32 02 20 00 ac 9c 37 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 24 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 28 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 0e 00 09 41 c4 05 08 15 01 8f 47 9e result (RA UPD REQ) = 0 PROCESSING DETACH REQ from 0x01020304:1111 00 00 10 02 01 80 00 de ad 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 15 01 c0 39 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 44 b6 8a CALLBACK, event 0, msg length 44, bvci 0x1002 00 00 10 02 01 80 00 de ad 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 15 01 c0 39 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 44 b6 8a NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 24 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 28 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 0e 00 09 41 c4 09 08 06 00 da 80 ca result (DETACH REQ) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 10 RAID patched (SGSN): 4 TLLI patched (BSS ): 10 TLLI patched (SGSN): 11 P-TMSI patched (SGSN): 2 Attach Request count : 3 TLLI-Cache: 0 Gbproxy global: Invalid Routing Area Identifier : 1 BSSGP protocol error (SGSN): 1 Patch error: no peer : 1 === test_gbproxy_secondary_sgsn === --- Initialise SGSN 1 --- MESSAGE to SGSN at 0x05060708:32000, msg length 12 02 00 81 01 01 82 01 01 04 82 01 00 PROCESSING RESET_ACK from 0x05060708:32000 03 01 82 01 01 04 82 01 00 MESSAGE to SGSN at 0x05060708:32000, msg length 1 0a result (RESET_ACK) = 1 PROCESSING ALIVE_ACK from 0x05060708:32000 0b MESSAGE to SGSN at 0x05060708:32000, msg length 1 06 result (ALIVE_ACK) = 1 PROCESSING UNBLOCK_ACK from 0x05060708:32000 07 ==> got signal NS_UNBLOCK, NS-VC 0x0101/5.6.7.8:32000 result (UNBLOCK_ACK) = 0 PROCESSING ALIVE from 0x05060708:32000 0a MESSAGE to SGSN at 0x05060708:32000, msg length 1 0b result (ALIVE) = 1 --- Initialise SGSN 2 --- MESSAGE to SGSN 2 at 0x15161718:32001, msg length 12 02 00 81 01 01 82 01 03 04 82 01 02 PROCESSING RESET_ACK from 0x15161718:32001 03 01 82 01 03 04 82 01 02 MESSAGE to SGSN 2 at 0x15161718:32001, msg length 1 0a result (RESET_ACK) = 1 PROCESSING ALIVE_ACK from 0x15161718:32001 0b MESSAGE to SGSN 2 at 0x15161718:32001, msg length 1 06 result (ALIVE_ACK) = 1 PROCESSING UNBLOCK_ACK from 0x15161718:32001 07 ==> got signal NS_UNBLOCK, NS-VC 0x0103/21.22.23.24:32001 result (UNBLOCK_ACK) = 0 PROCESSING ALIVE from 0x15161718:32001 0a MESSAGE to SGSN 2 at 0x15161718:32001, msg length 1 0b result (ALIVE) = 1 --- Initialise BSS 1 --- Setup NS-VC: remote 0x01020304:1111, NSVCI 0x1001(4097), NSEI 0x1000(4096) PROCESSING RESET from 0x01020304:1111 02 00 81 01 01 82 10 01 04 82 10 00 ==> got signal NS_RESET, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 9 03 01 82 10 01 04 82 10 00 MESSAGE to BSS at 0x01020304:1111, msg length 1 0a result (RESET) = 9 PROCESSING ALIVE from 0x01020304:1111 0a MESSAGE to BSS at 0x01020304:1111, msg length 1 0b result (ALIVE) = 1 PROCESSING UNBLOCK from 0x01020304:1111 06 ==> got signal NS_UNBLOCK, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 1 07 result (UNBLOCK) = 1 PROCESSING ALIVE_ACK from 0x01020304:1111 0b result (ALIVE_ACK) = 0 Setup BSSGP: remote 0x01020304:1111, BVCI 0x0000(0) PROCESSING BVC_RESET from 0x01020304:1111 00 00 00 00 22 04 82 00 00 07 81 08 08 88 11 22 33 40 50 60 10 00 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 22 04 82 00 00 07 81 08 08 88 11 22 33 40 50 60 10 00 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 5 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 9 00 00 00 00 23 04 82 00 00 result (BVC_RESET) = 9 PROCESSING BVC_RESET_ACK from 0x05060708:32000 00 00 00 00 23 04 82 00 00 CALLBACK, event 0, msg length 5, bvci 0x0000 00 00 00 00 23 04 82 00 00 result (BVC_RESET_ACK) = -2 Setup BSSGP: remote 0x01020304:1111, BVCI 0x1002(4098) PROCESSING BVC_RESET from 0x01020304:1111 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 NS UNITDATA MESSAGE to SGSN 2, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to SGSN 2 at 0x15161718:32001, msg length 22 00 00 00 00 22 04 82 10 02 07 81 08 08 88 21 63 54 40 50 60 10 00 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 22 00 00 00 00 22 04 82 10 02 07 81 08 08 88 21 63 54 40 50 60 10 00 result (BVC_RESET) = 22 PROCESSING BVC_RESET_ACK from 0x05060708:32000 00 00 00 00 23 04 82 10 02 CALLBACK, event 0, msg length 5, bvci 0x0000 00 00 00 00 23 04 82 10 02 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 5 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 9 00 00 00 00 23 04 82 10 02 result (BVC_RESET_ACK) = 9 PROCESSING BVC_RESET_ACK from 0x15161718:32001 00 00 00 00 23 04 82 10 02 CALLBACK, event 0, msg length 5, bvci 0x0000 00 00 00 00 23 04 82 10 02 result (BVC_RESET_ACK) = 1 Current NS-VCIs: VCI 0x1001, NSEI 0x1000, peer 0x01020304:1111 VCI 0x0103, NSEI 0x0102, peer 0x15161718:32001 NS-VC Block count : 1 VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 NS-VC Block count : 1 Gbproxy global: Invalid BVC Identifier : 1 Patch error: no peer : 1 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 1 TLLI-Cache: 0 --- Flow control --- PROCESSING FLOW_CONTROL_BVC from 0x01020304:1111 00 00 10 02 26 1e 81 01 05 82 01 dc 03 82 02 76 01 82 00 50 1c 82 02 58 06 82 00 03 CALLBACK, event 0, msg length 24, bvci 0x1002 00 00 10 02 26 1e 81 01 05 82 01 dc 03 82 02 76 01 82 00 50 1c 82 02 58 06 82 00 03 NS UNITDATA MESSAGE to SGSN 2, BVCI 0x1002, msg length 24 (gprs_ns_sendmsg) MESSAGE to SGSN 2 at 0x15161718:32001, msg length 28 00 00 10 02 26 1e 81 01 05 82 01 dc 03 82 02 76 01 82 00 50 1c 82 02 58 06 82 00 03 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 24 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 28 00 00 10 02 26 1e 81 01 05 82 01 dc 03 82 02 76 01 82 00 50 1c 82 02 58 06 82 00 03 result (FLOW_CONTROL_BVC) = 28 PROCESSING FLOW_CONTROL_BVC_ACK from 0x05060708:32000 00 00 10 02 27 1e 81 01 CALLBACK, event 0, msg length 4, bvci 0x1002 00 00 10 02 27 1e 81 01 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 4 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 8 00 00 10 02 27 1e 81 01 result (FLOW_CONTROL_BVC_ACK) = 8 PROCESSING FLOW_CONTROL_BVC_ACK from 0x15161718:32001 00 00 10 02 27 1e 81 01 CALLBACK, event 0, msg length 4, bvci 0x1002 00 00 10 02 27 1e 81 01 result (FLOW_CONTROL_BVC_ACK) = 0 --- Establish GPRS connection (SGSN 1) --- PROCESSING ATTACH REQUEST from 0x01020304:1111 00 00 10 02 01 80 00 de ad 00 00 04 08 88 00 f1 99 00 63 60 12 34 00 80 0e 00 34 01 c0 01 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 16 6d 01 CALLBACK, event 0, msg length 75, bvci 0x1002 00 00 10 02 01 80 00 de ad 00 00 04 08 88 00 f1 99 00 63 60 12 34 00 80 0e 00 34 01 c0 01 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 16 6d 01 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 24 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 28 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 0e 00 09 41 c4 01 08 15 01 b7 f8 36 result (ATTACH REQUEST) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead -> 7c69fb81, IMSI (none), AGE 0, STORED 1, IMSI acquisition in progress, SGSN NSEI 65535 PROCESSING IDENT RESPONSE from 0x01020304:1111 00 00 10 02 01 80 00 de ad 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 05 08 16 08 11 12 13 14 15 16 17 18 ad 05 28 CALLBACK, event 0, msg length 40, bvci 0x1002 00 00 10 02 01 80 00 de ad 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 05 08 16 08 11 12 13 14 15 16 17 18 ad 05 28 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 75 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 79 00 00 10 02 01 7c 69 fb 81 00 00 04 08 88 21 63 54 00 63 60 12 34 00 80 0e 00 34 01 c0 01 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 16 6d 01 result (IDENT RESPONSE) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 2 TLLI patched (BSS ): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead -> 7c69fb81, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING IDENT REQUEST from 0x05060708:32000 00 00 10 02 00 7c 69 fb 81 00 50 20 16 82 02 58 0e 89 41 c0 01 08 15 01 ff 6c ba CALLBACK, event 0, msg length 23, bvci 0x1002 00 00 10 02 00 7c 69 fb 81 00 50 20 16 82 02 58 0e 89 41 c0 01 08 15 01 ff 6c ba NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 23 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 27 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 0e 89 41 c0 01 08 15 01 ff 6c ba result (IDENT REQUEST) = 27 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 2 TLLI patched (BSS ): 1 TLLI patched (SGSN): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead -> 7c69fb81, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING IDENT RESPONSE from 0x01020304:1111 00 00 10 02 01 80 00 de ad 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 09 08 16 08 11 12 13 14 15 16 17 18 ba 14 c3 CALLBACK, event 0, msg length 40, bvci 0x1002 00 00 10 02 01 80 00 de ad 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 09 08 16 08 11 12 13 14 15 16 17 18 ba 14 c3 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 40 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 44 00 00 10 02 01 7c 69 fb 81 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 11 01 c0 09 08 16 08 11 12 13 14 15 16 17 18 ba 14 c3 result (IDENT RESPONSE) = 44 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 3 TLLI patched (BSS ): 2 TLLI patched (SGSN): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead -> 7c69fb81, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING ATTACH ACCEPT from 0x05060708:32000 00 00 10 02 00 7c 69 fb 81 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 05 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 53 62 f1 CALLBACK, event 0, msg length 88, bvci 0x1002 00 00 10 02 00 7c 69 fb 81 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 05 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 53 62 f1 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 88 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 92 00 00 10 02 00 80 00 de ad 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 05 08 02 01 49 04 11 22 33 40 50 60 19 cd d7 08 17 16 18 05 f4 c0 0f 73 04 50 22 97 result (ATTACH ACCEPT) = 92 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 3 RAID patched (SGSN): 1 TLLI patched (BSS ): 2 TLLI patched (SGSN): 2 P-TMSI patched (SGSN): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead/c00f7304 -> 7c69fb81/efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING ATTACH COMPLETE from 0x01020304:1111 00 00 10 02 01 c0 0f 73 04 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 0d 08 03 55 1c ea CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 c0 0f 73 04 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 0d 08 03 55 1c ea NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 08 01 c0 0d 08 03 55 1c ea result (ATTACH COMPLETE) = 35 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 4 RAID patched (SGSN): 1 TLLI patched (BSS ): 3 TLLI patched (SGSN): 2 P-TMSI patched (SGSN): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 8000dead/c00f7304 -> 7c69fb81/efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING GMM INFO from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 09 08 21 04 ba 3d CALLBACK, event 0, msg length 66, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 09 08 21 04 ba 3d NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 66 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 70 00 00 10 02 00 c0 0f 73 04 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 09 08 21 04 ba 3d result (GMM INFO) = 70 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 4 RAID patched (SGSN): 1 TLLI patched (BSS ): 3 TLLI patched (SGSN): 3 P-TMSI patched (SGSN): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI c00f7304 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING XID (UL) from 0x01020304:1111 00 00 10 02 01 c0 0f 73 04 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 0f 41 fb 01 00 0e 00 64 11 05 16 01 90 66 b3 28 CALLBACK, event 0, msg length 38, bvci 0x1002 00 00 10 02 01 c0 0f 73 04 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 0f 41 fb 01 00 0e 00 64 11 05 16 01 90 66 b3 28 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 38 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 42 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 0f 41 fb 01 00 0e 00 64 11 05 16 01 90 66 b3 28 result (XID (UL)) = 42 PROCESSING XID (DL) from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8c 41 fb 30 84 10 61 b6 64 e4 a9 1a 9e CALLBACK, event 0, msg length 70, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8c 41 fb 30 84 10 61 b6 64 e4 a9 1a 9e NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 70 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 74 00 00 10 02 00 c0 0f 73 04 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8c 41 fb 30 84 10 61 b6 64 e4 a9 1a 9e result (XID (DL)) = 74 PROCESSING LL11 DNS QUERY (UL) from 0x01020304:1111 00 00 10 02 01 c0 0f 73 04 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 42 0b c0 01 65 00 00 00 45 00 00 38 95 72 00 00 45 11 20 85 0a c0 07 e4 ac 10 01 0a ad ab 00 35 00 24 0e 1c 3b e0 01 00 00 01 00 00 00 00 00 00 01 6d 05 68 65 69 73 65 02 64 65 00 00 01 00 01 47 8f 07 CALLBACK, event 0, msg length 89, bvci 0x1002 00 00 10 02 01 c0 0f 73 04 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 42 0b c0 01 65 00 00 00 45 00 00 38 95 72 00 00 45 11 20 85 0a c0 07 e4 ac 10 01 0a ad ab 00 35 00 24 0e 1c 3b e0 01 00 00 01 00 00 00 00 00 00 01 6d 05 68 65 69 73 65 02 64 65 00 00 01 00 01 47 8f 07 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 89 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 93 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 42 0b c0 01 65 00 00 00 45 00 00 38 95 72 00 00 45 11 20 85 0a c0 07 e4 ac 10 01 0a ad ab 00 35 00 24 0e 1c 3b e0 01 00 00 01 00 00 00 00 00 00 01 6d 05 68 65 69 73 65 02 64 65 00 00 01 00 01 47 8f 07 result (LL11 DNS QUERY (UL)) = 93 PROCESSING LL11 DNS RESP (DL) from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 00 d0 4b c0 01 65 00 00 00 45 00 00 c6 00 00 40 00 3e 11 7c 69 ac 10 01 0a 0a c0 07 e4 00 35 ad ab 00 b2 74 4e 3b e0 81 80 00 01 00 01 00 05 00 00 01 6d 05 68 65 69 73 65 02 64 65 00 00 01 00 01 c0 0c 00 01 00 01 00 00 0e 10 00 04 c1 63 90 58 c0 0e 00 02 00 01 00 00 0e 10 00 16 03 6e 73 32 0c 70 6f 70 2d 68 61 6e 6e 6f 76 65 72 03 6e 65 74 00 c0 0e 00 02 00 01 00 00 0e 10 00 10 02 6e 73 01 73 08 70 6c 75 73 6c 69 6e 65 c0 14 c0 0e 00 02 00 01 00 00 0e 10 00 05 02 6e 73 c0 0e c0 0e 00 02 00 01 00 00 0e 10 00 05 02 6e 73 c0 5f c0 0e 00 02 00 01 00 00 0e 10 00 12 02 6e 73 0c 70 6f 70 2d 68 61 6e 6e 6f 76 65 72 c0 14 aa df 31 CALLBACK, event 0, msg length 267, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 00 d0 4b c0 01 65 00 00 00 45 00 00 c6 00 00 40 00 3e 11 7c 69 ac 10 01 0a 0a c0 07 e4 00 35 ad ab 00 b2 74 4e 3b e0 81 80 00 01 00 01 00 05 00 00 01 6d 05 68 65 69 73 65 02 64 65 00 00 01 00 01 c0 0c 00 01 00 01 00 00 0e 10 00 04 c1 63 90 58 c0 0e 00 02 00 01 00 00 0e 10 00 16 03 6e 73 32 0c 70 6f 70 2d 68 61 6e 6e 6f 76 65 72 03 6e 65 74 00 c0 0e 00 02 00 01 00 00 0e 10 00 10 02 6e 73 01 73 08 70 6c 75 73 6c 69 6e 65 c0 14 c0 0e 00 02 00 01 00 00 0e 10 00 05 02 6e 73 c0 0e c0 0e 00 02 00 01 00 00 0e 10 00 05 02 6e 73 c0 5f c0 0e 00 02 00 01 00 00 0e 10 00 12 02 6e 73 0c 70 6f 70 2d 68 61 6e 6e 6f 76 65 72 c0 14 aa df 31 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 267 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 271 00 00 10 02 00 c0 0f 73 04 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 00 d0 4b c0 01 65 00 00 00 45 00 00 c6 00 00 40 00 3e 11 7c 69 ac 10 01 0a 0a c0 07 e4 00 35 ad ab 00 b2 74 4e 3b e0 81 80 00 01 00 01 00 05 00 00 01 6d 05 68 65 69 73 65 02 64 65 00 00 01 00 01 c0 0c 00 01 00 01 00 00 0e 10 00 04 c1 63 90 58 c0 0e 00 02 00 01 00 00 0e 10 00 16 03 6e 73 32 0c 70 6f 70 2d 68 61 6e 6e 6f 76 65 72 03 6e 65 74 00 c0 0e 00 02 00 01 00 00 0e 10 00 10 02 6e 73 01 73 08 70 6c 75 73 6c 69 6e 65 c0 14 c0 0e 00 02 00 01 00 00 0e 10 00 05 02 6e 73 c0 0e c0 0e 00 02 00 01 00 00 0e 10 00 05 02 6e 73 c0 5f c0 0e 00 02 00 01 00 00 0e 10 00 12 02 6e 73 0c 70 6f 70 2d 68 61 6e 6e 6f 76 65 72 c0 14 aa df 31 result (LL11 DNS RESP (DL)) = 271 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 6 RAID patched (SGSN): 1 TLLI patched (BSS ): 5 TLLI patched (SGSN): 5 P-TMSI patched (SGSN): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI c00f7304 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING LLC_DISCARDED from 0x01020304:1111 00 00 00 00 2c 1f 84 c0 0f 73 04 0f 81 01 04 82 10 02 25 83 00 00 0c CALLBACK, event 0, msg length 19, bvci 0x0000 00 00 00 00 2c 1f 84 c0 0f 73 04 0f 81 01 04 82 10 02 25 83 00 00 0c NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 19 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 23 00 00 00 00 2c 1f 84 ef e2 b7 00 0f 81 01 04 82 10 02 25 83 00 00 0c result (LLC_DISCARDED) = 23 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 6 RAID patched (SGSN): 1 TLLI patched (BSS ): 6 TLLI patched (SGSN): 5 P-TMSI patched (SGSN): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI c00f7304 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING LLC_DISCARDED from 0x05060708:32000 00 00 00 00 2c 1f 84 ef e2 b7 00 0f 81 01 04 82 10 02 25 83 00 00 0c CALLBACK, event 0, msg length 19, bvci 0x0000 00 00 00 00 2c 1f 84 ef e2 b7 00 0f 81 01 04 82 10 02 25 83 00 00 0c NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 25 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 29 00 00 00 00 41 07 81 27 15 93 2c 1f 84 ef e2 b7 00 0f 81 01 04 82 10 02 25 83 00 00 0c result (LLC_DISCARDED) = 29 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 6 RAID patched (SGSN): 1 TLLI patched (BSS ): 6 TLLI patched (SGSN): 6 P-TMSI patched (SGSN): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI c00f7304 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING BVC_SUSPEND from 0x01020304:1111 00 00 00 00 0b 1f 84 c0 0f 73 04 1b 86 11 22 33 40 50 60 CALLBACK, event 0, msg length 15, bvci 0x0000 00 00 00 00 0b 1f 84 c0 0f 73 04 1b 86 11 22 33 40 50 60 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 15 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 19 00 00 00 00 0b 1f 84 ef e2 b7 00 1b 86 21 63 54 40 50 60 result (BVC_SUSPEND) = 19 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 7 RAID patched (SGSN): 1 TLLI patched (BSS ): 7 TLLI patched (SGSN): 6 P-TMSI patched (SGSN): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI c00f7304 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING BVC_SUSPEND_ACK from 0x05060708:32000 00 00 00 00 0c 1f 84 ef e2 b7 00 1b 86 21 63 54 40 50 60 1d 81 01 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 0c 1f 84 ef e2 b7 00 1b 86 21 63 54 40 50 60 1d 81 01 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 22 00 00 00 00 0c 1f 84 c0 0f 73 04 1b 86 11 22 33 40 50 60 1d 81 01 result (BVC_SUSPEND_ACK) = 22 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 7 RAID patched (SGSN): 2 TLLI patched (BSS ): 7 TLLI patched (SGSN): 7 P-TMSI patched (SGSN): 1 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI c00f7304 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 --- Establish GPRS connection (SGSN 2) --- PROCESSING ATTACH REQUEST from 0x01020304:1111 00 00 10 02 01 80 00 be ef 00 00 04 08 88 00 f1 99 00 63 60 12 34 00 80 0e 00 34 01 c0 11 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 bf 00 5c CALLBACK, event 0, msg length 75, bvci 0x1002 00 00 10 02 01 80 00 be ef 00 00 04 08 88 00 f1 99 00 63 60 12 34 00 80 0e 00 34 01 c0 11 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 bf 00 5c NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 24 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 28 00 00 10 02 00 80 00 be ef 00 50 20 16 82 02 58 0e 00 09 41 c4 01 08 15 01 b7 f8 36 result (ATTACH REQUEST) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 7 RAID patched (SGSN): 2 TLLI patched (BSS ): 7 TLLI patched (SGSN): 7 P-TMSI patched (SGSN): 1 Attach Request count : 2 TLLI cache size : 2 TLLI-Cache: 2 TLLI 8000beef -> 7eb52dfb, IMSI (none), AGE 0, STORED 1, IMSI acquisition in progress, SGSN NSEI 65535 TLLI c00f7304 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING IDENT RESPONSE from 0x01020304:1111 00 00 10 02 01 80 00 be ef 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 15 08 16 08 11 12 99 99 99 16 17 18 b2 dd 58 CALLBACK, event 0, msg length 40, bvci 0x1002 00 00 10 02 01 80 00 be ef 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 15 08 16 08 11 12 99 99 99 16 17 18 b2 dd 58 NS UNITDATA MESSAGE to SGSN 2, BVCI 0x1002, msg length 75 (gprs_ns_sendmsg) MESSAGE to SGSN 2 at 0x15161718:32001, msg length 79 00 00 10 02 01 7e b5 2d fb 00 00 04 08 88 21 63 54 00 63 60 12 34 00 80 0e 00 34 01 c0 11 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 bf 00 5c result (IDENT RESPONSE) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 8 RAID patched (SGSN): 2 TLLI patched (BSS ): 8 TLLI patched (SGSN): 7 P-TMSI patched (SGSN): 1 Attach Request count : 2 TLLI cache size : 2 TLLI-Cache: 2 TLLI 8000beef -> 7eb52dfb, IMSI 12199999961718, AGE 0, IMSI matches, SGSN NSEI 258 TLLI c00f7304 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING IDENT REQUEST from 0x15161718:32001 00 00 10 02 00 7e b5 2d fb 00 50 20 16 82 02 58 0e 89 41 c0 0d 08 15 01 0c a6 18 CALLBACK, event 0, msg length 23, bvci 0x1002 00 00 10 02 00 7e b5 2d fb 00 50 20 16 82 02 58 0e 89 41 c0 0d 08 15 01 0c a6 18 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 23 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 27 00 00 10 02 00 80 00 be ef 00 50 20 16 82 02 58 0e 89 41 c0 0d 08 15 01 0c a6 18 result (IDENT REQUEST) = 27 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 8 RAID patched (SGSN): 2 TLLI patched (BSS ): 8 TLLI patched (SGSN): 8 P-TMSI patched (SGSN): 1 Attach Request count : 2 TLLI cache size : 2 TLLI-Cache: 2 TLLI 8000beef -> 7eb52dfb, IMSI 12199999961718, AGE 0, IMSI matches, SGSN NSEI 258 TLLI c00f7304 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING IDENT RESPONSE from 0x01020304:1111 00 00 10 02 01 80 00 be ef 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 19 08 16 08 11 12 99 99 99 16 17 18 a5 cc b3 CALLBACK, event 0, msg length 40, bvci 0x1002 00 00 10 02 01 80 00 be ef 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 19 08 16 08 11 12 99 99 99 16 17 18 a5 cc b3 NS UNITDATA MESSAGE to SGSN 2, BVCI 0x1002, msg length 40 (gprs_ns_sendmsg) MESSAGE to SGSN 2 at 0x15161718:32001, msg length 44 00 00 10 02 01 7e b5 2d fb 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 11 01 c0 19 08 16 08 11 12 99 99 99 16 17 18 a5 cc b3 result (IDENT RESPONSE) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 9 RAID patched (SGSN): 2 TLLI patched (BSS ): 9 TLLI patched (SGSN): 8 P-TMSI patched (SGSN): 1 Attach Request count : 2 TLLI cache size : 2 TLLI-Cache: 2 TLLI 8000beef -> 7eb52dfb, IMSI 12199999961718, AGE 0, IMSI matches, SGSN NSEI 258 TLLI c00f7304 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING ATTACH ACCEPT from 0x15161718:32001 00 00 10 02 00 7e b5 2d fb 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 16 17 18 00 81 00 0e 9e 41 c0 11 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 e0 98 76 54 cb 1c 5b CALLBACK, event 0, msg length 88, bvci 0x1002 00 00 10 02 00 7e b5 2d fb 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 16 17 18 00 81 00 0e 9e 41 c0 11 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 e0 98 76 54 cb 1c 5b NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 88 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 92 00 00 10 02 00 80 00 be ef 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 16 17 18 00 81 00 0e 9e 41 c0 11 08 02 01 49 04 11 22 33 40 50 60 19 cd d7 08 17 16 18 05 f4 e6 56 aa 1f 8c 69 67 result (ATTACH ACCEPT) = 92 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 9 RAID patched (SGSN): 3 TLLI patched (BSS ): 9 TLLI patched (SGSN): 9 P-TMSI patched (SGSN): 2 Attach Request count : 2 TLLI cache size : 2 TLLI-Cache: 2 TLLI 8000beef/e656aa1f -> 7eb52dfb/e0987654, IMSI 12199999961718, AGE 0, IMSI matches, SGSN NSEI 258 TLLI c00f7304 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING ATTACH COMPLETE from 0x01020304:1111 00 00 10 02 01 e6 56 aa 1f 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 1d 08 03 5e 3a ea CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 e6 56 aa 1f 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 1d 08 03 5e 3a ea NS UNITDATA MESSAGE to SGSN 2, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN 2 at 0x15161718:32001, msg length 35 00 00 10 02 01 e0 98 76 54 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 08 01 c0 1d 08 03 5e 3a ea result (ATTACH COMPLETE) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 10 RAID patched (SGSN): 3 TLLI patched (BSS ): 10 TLLI patched (SGSN): 9 P-TMSI patched (SGSN): 2 Attach Request count : 2 TLLI cache size : 2 TLLI-Cache: 2 TLLI 8000beef/e656aa1f -> 7eb52dfb/e0987654, IMSI 12199999961718, AGE 0, IMSI matches, SGSN NSEI 258 TLLI c00f7304 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING GMM INFO from 0x15161718:32001 00 00 10 02 00 e0 98 76 54 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 16 17 18 00 81 00 0e 88 41 c0 15 08 21 bb c1 c6 CALLBACK, event 0, msg length 66, bvci 0x1002 00 00 10 02 00 e0 98 76 54 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 16 17 18 00 81 00 0e 88 41 c0 15 08 21 bb c1 c6 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 66 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 70 00 00 10 02 00 e6 56 aa 1f 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 16 17 18 00 81 00 0e 88 41 c0 15 08 21 bb c1 c6 result (GMM INFO) = 70 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 10 RAID patched (SGSN): 3 TLLI patched (BSS ): 10 TLLI patched (SGSN): 10 P-TMSI patched (SGSN): 2 Attach Request count : 2 TLLI cache size : 2 TLLI-Cache: 2 TLLI e656aa1f -> e0987654, IMSI 12199999961718, AGE 0, IMSI matches, SGSN NSEI 258 TLLI c00f7304 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING XID (UL) from 0x01020304:1111 00 00 10 02 01 e6 56 aa 1f 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 0f 41 fb 01 00 0e 00 64 11 05 16 01 90 66 b3 28 CALLBACK, event 0, msg length 38, bvci 0x1002 00 00 10 02 01 e6 56 aa 1f 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 0f 41 fb 01 00 0e 00 64 11 05 16 01 90 66 b3 28 NS UNITDATA MESSAGE to SGSN 2, BVCI 0x1002, msg length 38 (gprs_ns_sendmsg) MESSAGE to SGSN 2 at 0x15161718:32001, msg length 42 00 00 10 02 01 e0 98 76 54 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 0f 41 fb 01 00 0e 00 64 11 05 16 01 90 66 b3 28 result (XID (UL)) = 0 PROCESSING XID (DL) from 0x15161718:32001 00 00 10 02 00 e0 98 76 54 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 16 17 18 00 81 00 0e 8c 41 fb 30 84 10 61 b6 64 e4 a9 1a 9e CALLBACK, event 0, msg length 70, bvci 0x1002 00 00 10 02 00 e0 98 76 54 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 16 17 18 00 81 00 0e 8c 41 fb 30 84 10 61 b6 64 e4 a9 1a 9e NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 70 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 74 00 00 10 02 00 e6 56 aa 1f 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 16 17 18 00 81 00 0e 8c 41 fb 30 84 10 61 b6 64 e4 a9 1a 9e result (XID (DL)) = 74 PROCESSING LL11 DNS QUERY (UL) from 0x01020304:1111 00 00 10 02 01 e6 56 aa 1f 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 42 0b c0 01 65 00 00 00 45 00 00 38 95 72 00 00 45 11 20 85 0a c0 07 e4 ac 10 01 0a ad ab 00 35 00 24 0e 1c 3b e0 01 00 00 01 00 00 00 00 00 00 01 6d 05 68 65 69 73 65 02 64 65 00 00 01 00 01 47 8f 07 CALLBACK, event 0, msg length 89, bvci 0x1002 00 00 10 02 01 e6 56 aa 1f 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 42 0b c0 01 65 00 00 00 45 00 00 38 95 72 00 00 45 11 20 85 0a c0 07 e4 ac 10 01 0a ad ab 00 35 00 24 0e 1c 3b e0 01 00 00 01 00 00 00 00 00 00 01 6d 05 68 65 69 73 65 02 64 65 00 00 01 00 01 47 8f 07 NS UNITDATA MESSAGE to SGSN 2, BVCI 0x1002, msg length 89 (gprs_ns_sendmsg) MESSAGE to SGSN 2 at 0x15161718:32001, msg length 93 00 00 10 02 01 e0 98 76 54 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 42 0b c0 01 65 00 00 00 45 00 00 38 95 72 00 00 45 11 20 85 0a c0 07 e4 ac 10 01 0a ad ab 00 35 00 24 0e 1c 3b e0 01 00 00 01 00 00 00 00 00 00 01 6d 05 68 65 69 73 65 02 64 65 00 00 01 00 01 47 8f 07 result (LL11 DNS QUERY (UL)) = 0 PROCESSING LL11 DNS RESP (DL) from 0x15161718:32001 00 00 10 02 00 e0 98 76 54 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 16 17 18 00 81 00 0e 00 d0 4b c0 01 65 00 00 00 45 00 00 c6 00 00 40 00 3e 11 7c 69 ac 10 01 0a 0a c0 07 e4 00 35 ad ab 00 b2 74 4e 3b e0 81 80 00 01 00 01 00 05 00 00 01 6d 05 68 65 69 73 65 02 64 65 00 00 01 00 01 c0 0c 00 01 00 01 00 00 0e 10 00 04 c1 63 90 58 c0 0e 00 02 00 01 00 00 0e 10 00 16 03 6e 73 32 0c 70 6f 70 2d 68 61 6e 6e 6f 76 65 72 03 6e 65 74 00 c0 0e 00 02 00 01 00 00 0e 10 00 10 02 6e 73 01 73 08 70 6c 75 73 6c 69 6e 65 c0 14 c0 0e 00 02 00 01 00 00 0e 10 00 05 02 6e 73 c0 0e c0 0e 00 02 00 01 00 00 0e 10 00 05 02 6e 73 c0 5f c0 0e 00 02 00 01 00 00 0e 10 00 12 02 6e 73 0c 70 6f 70 2d 68 61 6e 6e 6f 76 65 72 c0 14 aa df 31 CALLBACK, event 0, msg length 267, bvci 0x1002 00 00 10 02 00 e0 98 76 54 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 16 17 18 00 81 00 0e 00 d0 4b c0 01 65 00 00 00 45 00 00 c6 00 00 40 00 3e 11 7c 69 ac 10 01 0a 0a c0 07 e4 00 35 ad ab 00 b2 74 4e 3b e0 81 80 00 01 00 01 00 05 00 00 01 6d 05 68 65 69 73 65 02 64 65 00 00 01 00 01 c0 0c 00 01 00 01 00 00 0e 10 00 04 c1 63 90 58 c0 0e 00 02 00 01 00 00 0e 10 00 16 03 6e 73 32 0c 70 6f 70 2d 68 61 6e 6e 6f 76 65 72 03 6e 65 74 00 c0 0e 00 02 00 01 00 00 0e 10 00 10 02 6e 73 01 73 08 70 6c 75 73 6c 69 6e 65 c0 14 c0 0e 00 02 00 01 00 00 0e 10 00 05 02 6e 73 c0 0e c0 0e 00 02 00 01 00 00 0e 10 00 05 02 6e 73 c0 5f c0 0e 00 02 00 01 00 00 0e 10 00 12 02 6e 73 0c 70 6f 70 2d 68 61 6e 6e 6f 76 65 72 c0 14 aa df 31 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 267 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 271 00 00 10 02 00 e6 56 aa 1f 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 16 17 18 00 81 00 0e 00 d0 4b c0 01 65 00 00 00 45 00 00 c6 00 00 40 00 3e 11 7c 69 ac 10 01 0a 0a c0 07 e4 00 35 ad ab 00 b2 74 4e 3b e0 81 80 00 01 00 01 00 05 00 00 01 6d 05 68 65 69 73 65 02 64 65 00 00 01 00 01 c0 0c 00 01 00 01 00 00 0e 10 00 04 c1 63 90 58 c0 0e 00 02 00 01 00 00 0e 10 00 16 03 6e 73 32 0c 70 6f 70 2d 68 61 6e 6e 6f 76 65 72 03 6e 65 74 00 c0 0e 00 02 00 01 00 00 0e 10 00 10 02 6e 73 01 73 08 70 6c 75 73 6c 69 6e 65 c0 14 c0 0e 00 02 00 01 00 00 0e 10 00 05 02 6e 73 c0 0e c0 0e 00 02 00 01 00 00 0e 10 00 05 02 6e 73 c0 5f c0 0e 00 02 00 01 00 00 0e 10 00 12 02 6e 73 0c 70 6f 70 2d 68 61 6e 6e 6f 76 65 72 c0 14 aa df 31 result (LL11 DNS RESP (DL)) = 271 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 12 RAID patched (SGSN): 3 TLLI patched (BSS ): 12 TLLI patched (SGSN): 12 P-TMSI patched (SGSN): 2 Attach Request count : 2 TLLI cache size : 2 TLLI-Cache: 2 TLLI e656aa1f -> e0987654, IMSI 12199999961718, AGE 0, IMSI matches, SGSN NSEI 258 TLLI c00f7304 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING LLC_DISCARDED from 0x01020304:1111 00 00 00 00 2c 1f 84 e6 56 aa 1f 0f 81 01 04 82 10 02 25 83 00 00 0c CALLBACK, event 0, msg length 19, bvci 0x0000 00 00 00 00 2c 1f 84 e6 56 aa 1f 0f 81 01 04 82 10 02 25 83 00 00 0c NS UNITDATA MESSAGE to SGSN 2, BVCI 0x0000, msg length 19 (gprs_ns_sendmsg) MESSAGE to SGSN 2 at 0x15161718:32001, msg length 23 00 00 00 00 2c 1f 84 e0 98 76 54 0f 81 01 04 82 10 02 25 83 00 00 0c result (LLC_DISCARDED) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 12 RAID patched (SGSN): 3 TLLI patched (BSS ): 13 TLLI patched (SGSN): 12 P-TMSI patched (SGSN): 2 Attach Request count : 2 TLLI cache size : 2 TLLI-Cache: 2 TLLI e656aa1f -> e0987654, IMSI 12199999961718, AGE 0, IMSI matches, SGSN NSEI 258 TLLI c00f7304 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING LLC_DISCARDED from 0x15161718:32001 00 00 00 00 2c 1f 84 e0 98 76 54 0f 81 01 04 82 10 02 25 83 00 00 0c CALLBACK, event 0, msg length 19, bvci 0x0000 00 00 00 00 2c 1f 84 e0 98 76 54 0f 81 01 04 82 10 02 25 83 00 00 0c NS UNITDATA MESSAGE to SGSN 2, BVCI 0x0000, msg length 25 (gprs_ns_sendmsg) MESSAGE to SGSN 2 at 0x15161718:32001, msg length 29 00 00 00 00 41 07 81 27 15 93 2c 1f 84 e0 98 76 54 0f 81 01 04 82 10 02 25 83 00 00 0c result (LLC_DISCARDED) = 29 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 12 RAID patched (SGSN): 3 TLLI patched (BSS ): 13 TLLI patched (SGSN): 13 P-TMSI patched (SGSN): 2 Attach Request count : 2 TLLI cache size : 2 TLLI-Cache: 2 TLLI e656aa1f -> e0987654, IMSI 12199999961718, AGE 0, IMSI matches, SGSN NSEI 258 TLLI c00f7304 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING BVC_SUSPEND from 0x01020304:1111 00 00 00 00 0b 1f 84 e6 56 aa 1f 1b 86 11 22 33 40 50 60 CALLBACK, event 0, msg length 15, bvci 0x0000 00 00 00 00 0b 1f 84 e6 56 aa 1f 1b 86 11 22 33 40 50 60 NS UNITDATA MESSAGE to SGSN 2, BVCI 0x0000, msg length 15 (gprs_ns_sendmsg) MESSAGE to SGSN 2 at 0x15161718:32001, msg length 19 00 00 00 00 0b 1f 84 e0 98 76 54 1b 86 21 63 54 40 50 60 result (BVC_SUSPEND) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 13 RAID patched (SGSN): 3 TLLI patched (BSS ): 14 TLLI patched (SGSN): 13 P-TMSI patched (SGSN): 2 Attach Request count : 2 TLLI cache size : 2 TLLI-Cache: 2 TLLI e656aa1f -> e0987654, IMSI 12199999961718, AGE 0, IMSI matches, SGSN NSEI 258 TLLI c00f7304 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING BVC_SUSPEND_ACK from 0x15161718:32001 00 00 00 00 0c 1f 84 e0 98 76 54 1b 86 21 63 54 40 50 60 1d 81 01 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 0c 1f 84 e0 98 76 54 1b 86 21 63 54 40 50 60 1d 81 01 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 22 00 00 00 00 0c 1f 84 e6 56 aa 1f 1b 86 11 22 33 40 50 60 1d 81 01 result (BVC_SUSPEND_ACK) = 22 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 13 RAID patched (SGSN): 4 TLLI patched (BSS ): 14 TLLI patched (SGSN): 14 P-TMSI patched (SGSN): 2 Attach Request count : 2 TLLI cache size : 2 TLLI-Cache: 2 TLLI e656aa1f -> e0987654, IMSI 12199999961718, AGE 0, IMSI matches, SGSN NSEI 258 TLLI c00f7304 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 --- Establish GPRS connection (SGSN 2, P-TMSI collision) --- PROCESSING ATTACH REQUEST from 0x01020304:1111 00 00 10 02 01 80 00 fe ed 00 00 04 08 88 00 f1 99 00 63 60 12 34 00 80 0e 00 34 01 c0 21 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 44 b6 bb CALLBACK, event 0, msg length 75, bvci 0x1002 00 00 10 02 01 80 00 fe ed 00 00 04 08 88 00 f1 99 00 63 60 12 34 00 80 0e 00 34 01 c0 21 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 44 b6 bb NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 24 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 28 00 00 10 02 00 80 00 fe ed 00 50 20 16 82 02 58 0e 00 09 41 c4 01 08 15 01 b7 f8 36 result (ATTACH REQUEST) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 13 RAID patched (SGSN): 4 TLLI patched (BSS ): 14 TLLI patched (SGSN): 14 P-TMSI patched (SGSN): 2 Attach Request count : 3 TLLI cache size : 3 TLLI-Cache: 3 TLLI 8000feed -> 7e23ef54, IMSI (none), AGE 0, STORED 1, IMSI acquisition in progress, SGSN NSEI 65535 TLLI e656aa1f -> e0987654, IMSI 12199999961718, AGE 0, IMSI matches, SGSN NSEI 258 TLLI c00f7304 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING IDENT RESPONSE from 0x01020304:1111 00 00 10 02 01 80 00 fe ed 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 25 08 16 08 11 12 99 99 99 26 27 28 58 c7 cb CALLBACK, event 0, msg length 40, bvci 0x1002 00 00 10 02 01 80 00 fe ed 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 25 08 16 08 11 12 99 99 99 26 27 28 58 c7 cb NS UNITDATA MESSAGE to SGSN 2, BVCI 0x1002, msg length 75 (gprs_ns_sendmsg) MESSAGE to SGSN 2 at 0x15161718:32001, msg length 79 00 00 10 02 01 7e 23 ef 54 00 00 04 08 88 21 63 54 00 63 60 12 34 00 80 0e 00 34 01 c0 21 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 44 b6 bb result (IDENT RESPONSE) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 14 RAID patched (SGSN): 4 TLLI patched (BSS ): 15 TLLI patched (SGSN): 14 P-TMSI patched (SGSN): 2 Attach Request count : 3 TLLI cache size : 3 TLLI-Cache: 3 TLLI 8000feed -> 7e23ef54, IMSI 12199999962728, AGE 0, IMSI matches, SGSN NSEI 258 TLLI e656aa1f -> e0987654, IMSI 12199999961718, AGE 0, IMSI matches, SGSN NSEI 258 TLLI c00f7304 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING IDENT REQUEST from 0x15161718:32001 00 00 10 02 00 7e 23 ef 54 00 50 20 16 82 02 58 0e 89 41 c0 19 08 15 01 a2 f2 a4 CALLBACK, event 0, msg length 23, bvci 0x1002 00 00 10 02 00 7e 23 ef 54 00 50 20 16 82 02 58 0e 89 41 c0 19 08 15 01 a2 f2 a4 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 23 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 27 00 00 10 02 00 80 00 fe ed 00 50 20 16 82 02 58 0e 89 41 c0 19 08 15 01 a2 f2 a4 result (IDENT REQUEST) = 27 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 14 RAID patched (SGSN): 4 TLLI patched (BSS ): 15 TLLI patched (SGSN): 15 P-TMSI patched (SGSN): 2 Attach Request count : 3 TLLI cache size : 3 TLLI-Cache: 3 TLLI 8000feed -> 7e23ef54, IMSI 12199999962728, AGE 0, IMSI matches, SGSN NSEI 258 TLLI e656aa1f -> e0987654, IMSI 12199999961718, AGE 0, IMSI matches, SGSN NSEI 258 TLLI c00f7304 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING IDENT RESPONSE from 0x01020304:1111 00 00 10 02 01 80 00 fe ed 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 29 08 16 08 11 12 99 99 99 26 27 28 4f d6 20 CALLBACK, event 0, msg length 40, bvci 0x1002 00 00 10 02 01 80 00 fe ed 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 29 08 16 08 11 12 99 99 99 26 27 28 4f d6 20 NS UNITDATA MESSAGE to SGSN 2, BVCI 0x1002, msg length 40 (gprs_ns_sendmsg) MESSAGE to SGSN 2 at 0x15161718:32001, msg length 44 00 00 10 02 01 7e 23 ef 54 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 11 01 c0 29 08 16 08 11 12 99 99 99 26 27 28 4f d6 20 result (IDENT RESPONSE) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 15 RAID patched (SGSN): 4 TLLI patched (BSS ): 16 TLLI patched (SGSN): 15 P-TMSI patched (SGSN): 2 Attach Request count : 3 TLLI cache size : 3 TLLI-Cache: 3 TLLI 8000feed -> 7e23ef54, IMSI 12199999962728, AGE 0, IMSI matches, SGSN NSEI 258 TLLI e656aa1f -> e0987654, IMSI 12199999961718, AGE 0, IMSI matches, SGSN NSEI 258 TLLI c00f7304 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING ATTACH ACCEPT (P-TMSI 1) from 0x15161718:32001 00 00 10 02 00 7e 23 ef 54 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 26 27 28 00 81 00 0e 9e 41 c0 1d 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 74 91 01 CALLBACK, event 0, msg length 88, bvci 0x1002 00 00 10 02 00 7e 23 ef 54 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 26 27 28 00 81 00 0e 9e 41 c0 1d 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 74 91 01 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 88 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 92 00 00 10 02 00 80 00 fe ed 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 26 27 28 00 81 00 0e 9e 41 c0 1d 08 02 01 49 04 11 22 33 40 50 60 19 cd d7 08 17 16 18 05 f4 ea d4 77 5a c4 f8 6d result (ATTACH ACCEPT (P-TMSI 1)) = 92 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 15 RAID patched (SGSN): 5 TLLI patched (BSS ): 16 TLLI patched (SGSN): 16 P-TMSI patched (SGSN): 3 Attach Request count : 3 TLLI cache size : 3 TLLI-Cache: 3 TLLI 8000feed/ead4775a -> 7e23ef54/efe2b700, IMSI 12199999962728, AGE 0, IMSI matches, SGSN NSEI 258 TLLI e656aa1f -> e0987654, IMSI 12199999961718, AGE 0, IMSI matches, SGSN NSEI 258 TLLI c00f7304 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING ATTACH COMPLETE from 0x01020304:1111 00 00 10 02 01 ea d4 77 5a 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 2d 08 03 43 50 ea CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 ea d4 77 5a 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 2d 08 03 43 50 ea NS UNITDATA MESSAGE to SGSN 2, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN 2 at 0x15161718:32001, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 08 01 c0 2d 08 03 43 50 ea result (ATTACH COMPLETE) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 16 RAID patched (SGSN): 5 TLLI patched (BSS ): 17 TLLI patched (SGSN): 16 P-TMSI patched (SGSN): 3 Attach Request count : 3 TLLI cache size : 3 TLLI-Cache: 3 TLLI 8000feed/ead4775a -> 7e23ef54/efe2b700, IMSI 12199999962728, AGE 0, IMSI matches, SGSN NSEI 258 TLLI e656aa1f -> e0987654, IMSI 12199999961718, AGE 0, IMSI matches, SGSN NSEI 258 TLLI c00f7304 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 PROCESSING GMM INFO from 0x15161718:32001 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 26 27 28 00 81 00 0e 88 41 c0 21 08 21 ca 60 90 CALLBACK, event 0, msg length 66, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 26 27 28 00 81 00 0e 88 41 c0 21 08 21 ca 60 90 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 66 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 70 00 00 10 02 00 ea d4 77 5a 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 26 27 28 00 81 00 0e 88 41 c0 21 08 21 ca 60 90 result (GMM INFO) = 70 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 16 RAID patched (SGSN): 5 TLLI patched (BSS ): 17 TLLI patched (SGSN): 17 P-TMSI patched (SGSN): 3 Attach Request count : 3 TLLI cache size : 3 TLLI-Cache: 3 TLLI ead4775a -> efe2b700, IMSI 12199999962728, AGE 0, IMSI matches, SGSN NSEI 258 TLLI e656aa1f -> e0987654, IMSI 12199999961718, AGE 0, IMSI matches, SGSN NSEI 258 TLLI c00f7304 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 --- Shutdown GPRS connection (SGSN 1) --- PROCESSING DETACH REQ from 0x01020304:1111 00 00 10 02 01 c0 0f 73 04 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 15 01 c0 31 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 57 e6 15 CALLBACK, event 0, msg length 44, bvci 0x1002 00 00 10 02 01 c0 0f 73 04 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 15 01 c0 31 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 57 e6 15 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 44 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 48 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 15 01 c0 31 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 57 e6 15 result (DETACH REQ) = 48 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 17 RAID patched (SGSN): 5 TLLI patched (BSS ): 18 TLLI patched (SGSN): 17 P-TMSI patched (SGSN): 3 Attach Request count : 3 TLLI cache size : 3 TLLI-Cache: 3 TLLI c00f7304 -> efe2b700, IMSI 12131415161718, AGE 0, SGSN NSEI 256 TLLI ead4775a -> efe2b700, IMSI 12199999962728, AGE 0, IMSI matches, SGSN NSEI 258 TLLI e656aa1f -> e0987654, IMSI 12199999961718, AGE 0, IMSI matches, SGSN NSEI 258 PROCESSING DETACH ACC from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 89 41 c0 25 08 06 00 4d 09 cd CALLBACK, event 0, msg length 67, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 89 41 c0 25 08 06 00 4d 09 cd NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 67 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 71 00 00 10 02 00 c0 0f 73 04 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 89 41 c0 25 08 06 00 4d 09 cd result (DETACH ACC) = 71 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 17 RAID patched (SGSN): 5 TLLI patched (BSS ): 18 TLLI patched (SGSN): 18 P-TMSI patched (SGSN): 3 Attach Request count : 3 TLLI cache size : 2 TLLI-Cache: 2 TLLI ead4775a -> efe2b700, IMSI 12199999962728, AGE 0, IMSI matches, SGSN NSEI 258 TLLI e656aa1f -> e0987654, IMSI 12199999961718, AGE 0, IMSI matches, SGSN NSEI 258 --- Shutdown GPRS connection (SGSN 2) --- PROCESSING DETACH REQ from 0x01020304:1111 00 00 10 02 01 e6 56 aa 1f 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 15 01 c0 35 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 83 cb f7 CALLBACK, event 0, msg length 44, bvci 0x1002 00 00 10 02 01 e6 56 aa 1f 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 15 01 c0 35 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 83 cb f7 NS UNITDATA MESSAGE to SGSN 2, BVCI 0x1002, msg length 44 (gprs_ns_sendmsg) MESSAGE to SGSN 2 at 0x15161718:32001, msg length 48 00 00 10 02 01 e0 98 76 54 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 15 01 c0 35 08 05 01 18 05 f4 e0 98 76 54 19 03 b9 97 cb b4 31 31 result (DETACH REQ) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 18 RAID patched (SGSN): 5 TLLI patched (BSS ): 19 TLLI patched (SGSN): 18 P-TMSI patched (BSS ): 1 P-TMSI patched (SGSN): 3 Attach Request count : 3 TLLI cache size : 2 TLLI-Cache: 2 TLLI e656aa1f -> e0987654, IMSI 12199999961718, AGE 0, IMSI matches, SGSN NSEI 258 TLLI ead4775a -> efe2b700, IMSI 12199999962728, AGE 0, IMSI matches, SGSN NSEI 258 PROCESSING DETACH ACC from 0x15161718:32001 00 00 10 02 00 e0 98 76 54 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 16 17 18 00 81 00 0e 89 41 c0 29 08 06 00 be c3 6f CALLBACK, event 0, msg length 67, bvci 0x1002 00 00 10 02 00 e0 98 76 54 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 16 17 18 00 81 00 0e 89 41 c0 29 08 06 00 be c3 6f NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 67 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 71 00 00 10 02 00 e6 56 aa 1f 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 16 17 18 00 81 00 0e 89 41 c0 29 08 06 00 be c3 6f result (DETACH ACC) = 71 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 18 RAID patched (SGSN): 5 TLLI patched (BSS ): 19 TLLI patched (SGSN): 19 P-TMSI patched (BSS ): 1 P-TMSI patched (SGSN): 3 Attach Request count : 3 TLLI cache size : 1 TLLI-Cache: 1 TLLI ead4775a -> efe2b700, IMSI 12199999962728, AGE 0, IMSI matches, SGSN NSEI 258 --- Shutdown GPRS connection (SGSN 2, P-TMSI 1) --- PROCESSING DETACH REQ from 0x01020304:1111 00 00 10 02 01 ea d4 77 5a 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 15 01 c0 39 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 44 b6 8a CALLBACK, event 0, msg length 44, bvci 0x1002 00 00 10 02 01 ea d4 77 5a 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 15 01 c0 39 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 44 b6 8a NS UNITDATA MESSAGE to SGSN 2, BVCI 0x1002, msg length 44 (gprs_ns_sendmsg) MESSAGE to SGSN 2 at 0x15161718:32001, msg length 48 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 21 63 54 40 50 60 12 34 00 80 0e 00 15 01 c0 39 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 44 b6 8a result (DETACH REQ) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 19 RAID patched (SGSN): 5 TLLI patched (BSS ): 20 TLLI patched (SGSN): 19 P-TMSI patched (BSS ): 1 P-TMSI patched (SGSN): 3 Attach Request count : 3 TLLI cache size : 1 TLLI-Cache: 1 TLLI ead4775a -> efe2b700, IMSI 12199999962728, AGE 0, IMSI matches, SGSN NSEI 258 PROCESSING DETACH ACC from 0x15161718:32001 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 26 27 28 00 81 00 0e 89 41 c0 2d 08 06 00 86 7c c7 CALLBACK, event 0, msg length 67, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 26 27 28 00 81 00 0e 89 41 c0 2d 08 06 00 86 7c c7 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 67 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 71 00 00 10 02 00 ea d4 77 5a 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 99 99 99 26 27 28 00 81 00 0e 89 41 c0 2d 08 06 00 86 7c c7 result (DETACH ACC) = 71 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 RAID patched (BSS ): 19 RAID patched (SGSN): 5 TLLI patched (BSS ): 20 TLLI patched (SGSN): 20 P-TMSI patched (BSS ): 1 P-TMSI patched (SGSN): 3 Attach Request count : 3 TLLI-Cache: 0 Gbproxy global: Invalid BVC Identifier : 1 BSSGP protocol error (SGSN): 2 Patch error: no peer : 1 === test_gbproxy_keep_info === --- Initialise SGSN --- MESSAGE to SGSN at 0x05060708:32000, msg length 12 02 00 81 01 01 82 01 01 04 82 01 00 PROCESSING RESET_ACK from 0x05060708:32000 03 01 82 01 01 04 82 01 00 MESSAGE to SGSN at 0x05060708:32000, msg length 1 0a result (RESET_ACK) = 1 PROCESSING ALIVE_ACK from 0x05060708:32000 0b MESSAGE to SGSN at 0x05060708:32000, msg length 1 06 result (ALIVE_ACK) = 1 PROCESSING UNBLOCK_ACK from 0x05060708:32000 07 ==> got signal NS_UNBLOCK, NS-VC 0x0101/5.6.7.8:32000 result (UNBLOCK_ACK) = 0 PROCESSING ALIVE from 0x05060708:32000 0a MESSAGE to SGSN at 0x05060708:32000, msg length 1 0b result (ALIVE) = 1 --- Initialise BSS 1 --- Setup NS-VC: remote 0x01020304:1111, NSVCI 0x1001(4097), NSEI 0x1000(4096) PROCESSING RESET from 0x01020304:1111 02 00 81 01 01 82 10 01 04 82 10 00 ==> got signal NS_RESET, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 9 03 01 82 10 01 04 82 10 00 MESSAGE to BSS at 0x01020304:1111, msg length 1 0a result (RESET) = 9 PROCESSING ALIVE from 0x01020304:1111 0a MESSAGE to BSS at 0x01020304:1111, msg length 1 0b result (ALIVE) = 1 PROCESSING UNBLOCK from 0x01020304:1111 06 ==> got signal NS_UNBLOCK, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS at 0x01020304:1111, msg length 1 07 result (UNBLOCK) = 1 PROCESSING ALIVE_ACK from 0x01020304:1111 0b result (ALIVE_ACK) = 0 Setup BSSGP: remote 0x01020304:1111, BVCI 0x1002(4098) PROCESSING BVC_RESET from 0x01020304:1111 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 CALLBACK, event 0, msg length 18, bvci 0x0000 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 18 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 22 00 00 00 00 22 04 82 10 02 07 81 08 08 88 11 22 33 40 50 60 10 00 result (BVC_RESET) = 22 PROCESSING BVC_RESET_ACK from 0x05060708:32000 00 00 00 00 23 04 82 10 02 CALLBACK, event 0, msg length 5, bvci 0x0000 00 00 00 00 23 04 82 10 02 NS UNITDATA MESSAGE to BSS, BVCI 0x0000, msg length 5 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 9 00 00 00 00 23 04 82 10 02 result (BVC_RESET_ACK) = 9 Current NS-VCIs: VCI 0x1001, NSEI 0x1000, peer 0x01020304:1111 VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 NS-VC Block count : 1 Gbproxy global: Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 TLLI-Cache: 0 --- Send message from BSS 1 to SGSN, BVCI 0x1002 --- PROCESSING ATTACH REQUEST from 0x01020304:1111 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 01 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 16 6d 01 CALLBACK, event 0, msg length 75, bvci 0x1002 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 01 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 16 6d 01 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 24 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 28 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 0e 00 09 41 c4 01 08 15 01 b7 f8 36 result (ATTACH REQUEST) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700 -> afe2b700, IMSI (none), AGE 0, STORED 1, IMSI acquisition in progress PROCESSING IDENT RESPONSE from 0x01020304:1111 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 05 08 16 08 11 12 13 14 15 16 17 18 ad 05 28 CALLBACK, event 0, msg length 40, bvci 0x1002 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 05 08 16 08 11 12 13 14 15 16 17 18 ad 05 28 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 75 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 79 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 01 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 16 6d 01 result (IDENT RESPONSE) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700 -> afe2b700, IMSI 12131415161718, AGE 0 PROCESSING IDENT REQUEST from 0x05060708:32000 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 0e 89 41 c0 01 08 15 01 ff 6c ba CALLBACK, event 0, msg length 23, bvci 0x1002 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 0e 89 41 c0 01 08 15 01 ff 6c ba NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 23 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 27 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 0e 89 41 c0 01 08 15 01 ff 6c ba result (IDENT REQUEST) = 27 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700 -> afe2b700, IMSI 12131415161718, AGE 0 PROCESSING IDENT RESPONSE from 0x01020304:1111 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 09 08 16 08 11 12 13 14 15 16 17 18 ba 14 c3 CALLBACK, event 0, msg length 40, bvci 0x1002 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 09 08 16 08 11 12 13 14 15 16 17 18 ba 14 c3 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 40 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 44 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 09 08 16 08 11 12 13 14 15 16 17 18 ba 14 c3 result (IDENT RESPONSE) = 44 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700 -> afe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH ACCEPT from 0x05060708:32000 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 05 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 53 62 f1 CALLBACK, event 0, msg length 88, bvci 0x1002 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 05 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 53 62 f1 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 88 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 92 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 05 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 53 62 f1 result (ATTACH ACCEPT) = 92 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700/efe2b700 -> afe2b700/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH COMPLETE from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 0d 08 03 55 1c ea CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 0d 08 03 55 1c ea NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 0d 08 03 55 1c ea result (ATTACH COMPLETE) = 35 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700/efe2b700 -> afe2b700/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING GMM INFO from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 09 08 21 04 ba 3d CALLBACK, event 0, msg length 66, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 09 08 21 04 ba 3d NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 66 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 70 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 09 08 21 04 ba 3d result (GMM INFO) = 70 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI efe2b700 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING DETACH REQ from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 15 01 c0 11 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 6d b1 de CALLBACK, event 0, msg length 44, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 15 01 c0 11 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 6d b1 de NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 44 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 48 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 15 01 c0 11 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 6d b1 de result (DETACH REQ) = 48 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI efe2b700 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING DETACH ACC from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 89 41 c0 0d 08 06 00 aa ab ee CALLBACK, event 0, msg length 67, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 89 41 c0 0d 08 06 00 aa ab ee NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 67 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 71 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 89 41 c0 0d 08 06 00 aa ab ee result (DETACH ACC) = 71 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 00000000, IMSI 12131415161718, AGE 0, DE-REGISTERED PROCESSING ATTACH REQUEST from 0x01020304:1111 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 15 08 01 02 f5 e0 21 08 02 05 f4 ef e2 b7 00 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 e6 71 c7 CALLBACK, event 0, msg length 75, bvci 0x1002 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 15 08 01 02 f5 e0 21 08 02 05 f4 ef e2 b7 00 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 e6 71 c7 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 75 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 79 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 15 08 01 02 f5 e0 21 08 02 05 f4 ef e2 b7 00 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 e6 71 c7 result (ATTACH REQUEST) = 79 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 2 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700 -> afe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH ACCEPT from 0x05060708:32000 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 11 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 3a 6d d4 CALLBACK, event 0, msg length 88, bvci 0x1002 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 11 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 3a 6d d4 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 88 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 92 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 11 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 3a 6d d4 result (ATTACH ACCEPT) = 92 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 2 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700/efe2b700 -> afe2b700/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH COMPLETE from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 19 08 03 32 f1 bc CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 19 08 03 32 f1 bc NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 19 08 03 32 f1 bc result (ATTACH COMPLETE) = 35 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 2 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700/efe2b700 -> afe2b700/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING DETACH REQ (re-attach) from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8b 41 c0 15 08 05 01 25 0a 67 0e 96 CALLBACK, event 0, msg length 69, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8b 41 c0 15 08 05 01 25 0a 67 0e 96 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 69 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 73 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8b 41 c0 15 08 05 01 25 0a 67 0e 96 result (DETACH REQ (re-attach)) = 73 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 2 TLLI cache size : 1 TLLI-Cache: 1 TLLI efe2b700 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING DETACH ACC from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 1d 08 06 3d 1c 8b CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 1d 08 06 3d 1c 8b NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 1d 08 06 3d 1c 8b result (DETACH ACC) = 35 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 2 TLLI cache size : 1 TLLI-Cache: 1 TLLI 00000000, IMSI 12131415161718, AGE 0, DE-REGISTERED PROCESSING ATTACH REQUEST from 0x01020304:1111 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 21 08 01 02 f5 e0 21 08 02 05 f4 ef e2 b7 00 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 44 db cc CALLBACK, event 0, msg length 75, bvci 0x1002 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 21 08 01 02 f5 e0 21 08 02 05 f4 ef e2 b7 00 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 44 db cc NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 75 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 79 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 21 08 01 02 f5 e0 21 08 02 05 f4 ef e2 b7 00 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 44 db cc result (ATTACH REQUEST) = 79 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 3 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700 -> afe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH ACCEPT from 0x05060708:32000 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 19 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 27 3c 84 CALLBACK, event 0, msg length 88, bvci 0x1002 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 19 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 27 3c 84 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 88 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 92 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 19 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 27 3c 84 result (ATTACH ACCEPT) = 92 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 3 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700/efe2b700 -> afe2b700/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH COMPLETE from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 25 08 03 9b c6 47 CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 25 08 03 9b c6 47 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 25 08 03 9b c6 47 result (ATTACH COMPLETE) = 35 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 3 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700/efe2b700 -> afe2b700/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING DETACH REQ from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8b 41 c0 1d 08 05 02 25 0a dd 56 6c CALLBACK, event 0, msg length 69, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8b 41 c0 1d 08 05 02 25 0a dd 56 6c NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 69 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 73 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8b 41 c0 1d 08 05 02 25 0a dd 56 6c result (DETACH REQ) = 73 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 3 TLLI cache size : 1 TLLI-Cache: 1 TLLI efe2b700 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING DETACH ACC from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 29 08 06 4c bd dd CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 29 08 06 4c bd dd NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 29 08 06 4c bd dd result (DETACH ACC) = 35 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 3 TLLI cache size : 1 TLLI-Cache: 1 TLLI 00000000, IMSI 12131415161718, AGE 0, DE-REGISTERED PROCESSING ATTACH REQUEST (IMSI) from 0x01020304:1111 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 37 01 c0 2d 08 01 02 f5 e0 21 08 02 08 11 12 13 14 15 16 17 18 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 a5 85 76 CALLBACK, event 0, msg length 78, bvci 0x1002 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 37 01 c0 2d 08 01 02 f5 e0 21 08 02 08 11 12 13 14 15 16 17 18 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 a5 85 76 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 78 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 82 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 37 01 c0 2d 08 01 02 f5 e0 21 08 02 08 11 12 13 14 15 16 17 18 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 a5 85 76 result (ATTACH REQUEST (IMSI)) = 82 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 4 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700 -> afe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH ACCEPT from 0x05060708:32000 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 21 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 cf 80 6e CALLBACK, event 0, msg length 88, bvci 0x1002 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 21 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 cf 80 6e NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 88 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 92 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 21 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 cf 80 6e result (ATTACH ACCEPT) = 92 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 4 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700/efe2b700 -> afe2b700/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH COMPLETE from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 31 08 03 fc 2b 11 CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 31 08 03 fc 2b 11 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 31 08 03 fc 2b 11 result (ATTACH COMPLETE) = 35 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 4 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700/efe2b700 -> afe2b700/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING DETACH REQ from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8b 41 c0 25 08 05 02 25 0a 8e ee 85 CALLBACK, event 0, msg length 69, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8b 41 c0 25 08 05 02 25 0a 8e ee 85 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 69 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 73 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8b 41 c0 25 08 05 02 25 0a 8e ee 85 result (DETACH REQ) = 73 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 4 TLLI cache size : 1 TLLI-Cache: 1 TLLI efe2b700 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING DETACH ACC from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 35 08 06 f3 c6 26 CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 35 08 06 f3 c6 26 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 35 08 06 f3 c6 26 result (DETACH ACC) = 35 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 4 TLLI cache size : 1 TLLI-Cache: 1 TLLI 00000000, IMSI 12131415161718, AGE 0, DE-REGISTERED PROCESSING ATTACH REQUEST from 0x01020304:1111 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 39 08 01 02 f5 e0 21 08 02 05 f4 ef e2 b7 00 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 e4 85 12 CALLBACK, event 0, msg length 75, bvci 0x1002 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 39 08 01 02 f5 e0 21 08 02 05 f4 ef e2 b7 00 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 e4 85 12 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 75 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 79 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 39 08 01 02 f5 e0 21 08 02 05 f4 ef e2 b7 00 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 e4 85 12 result (ATTACH REQUEST) = 79 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 5 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700 -> afe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH ACCEPT from 0x05060708:32000 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 29 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 d2 d1 3e CALLBACK, event 0, msg length 88, bvci 0x1002 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 29 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 d2 d1 3e NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 88 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 92 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 29 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 d2 d1 3e result (ATTACH ACCEPT) = 92 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 5 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700/efe2b700 -> afe2b700/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH COMPLETE from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 3d 08 03 48 76 ea CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 3d 08 03 48 76 ea NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 3d 08 03 48 76 ea result (ATTACH COMPLETE) = 35 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 5 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700/efe2b700 -> afe2b700/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING RA UPD REQ from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 70 80 00 80 0e 00 3e 01 c0 41 08 08 10 11 22 33 40 50 60 1d 19 13 42 33 57 2b f7 c8 48 02 13 48 50 c8 48 02 14 48 50 c8 48 02 17 49 10 c8 48 02 00 19 8b b2 92 17 16 27 07 04 31 02 e5 e0 32 02 20 00 30 73 32 CALLBACK, event 0, msg length 85, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 70 80 00 80 0e 00 3e 01 c0 41 08 08 10 11 22 33 40 50 60 1d 19 13 42 33 57 2b f7 c8 48 02 13 48 50 c8 48 02 14 48 50 c8 48 02 17 49 10 c8 48 02 00 19 8b b2 92 17 16 27 07 04 31 02 e5 e0 32 02 20 00 30 73 32 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 85 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 89 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 70 80 00 80 0e 00 3e 01 c0 41 08 08 10 11 22 33 40 50 60 1d 19 13 42 33 57 2b f7 c8 48 02 13 48 50 c8 48 02 14 48 50 c8 48 02 17 49 10 c8 48 02 00 19 8b b2 92 17 16 27 07 04 31 02 e5 e0 32 02 20 00 30 73 32 result (RA UPD REQ) = 89 PROCESSING RA UDP REJ from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8a 41 c0 2d 08 0b 0a 00 41 30 a7 CALLBACK, event 0, msg length 68, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8a 41 c0 2d 08 0b 0a 00 41 30 a7 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 68 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 72 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8a 41 c0 2d 08 0b 0a 00 41 30 a7 result (RA UDP REJ) = 72 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 5 TLLI cache size : 1 TLLI-Cache: 1 TLLI 00000000, IMSI 12131415161718, AGE 0, DE-REGISTERED PROCESSING ATTACH REQUEST from 0x01020304:1111 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 45 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 50 cc c3 CALLBACK, event 0, msg length 75, bvci 0x1002 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 45 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 50 cc c3 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 24 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 28 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 0e 00 09 41 c4 01 08 15 01 b7 f8 36 result (ATTACH REQUEST) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 6 TLLI cache size : 2 TLLI-Cache: 2 TLLI afe2b700 -> afe2b700, IMSI (none), AGE 0, STORED 1, IMSI acquisition in progress TLLI 00000000, IMSI 12131415161718, AGE 0, DE-REGISTERED PROCESSING IDENT RESPONSE from 0x01020304:1111 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 49 08 16 08 11 12 13 14 15 16 17 18 86 ca 3f CALLBACK, event 0, msg length 40, bvci 0x1002 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 49 08 16 08 11 12 13 14 15 16 17 18 86 ca 3f NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 75 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 79 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 45 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 50 cc c3 result (IDENT RESPONSE) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 6 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700 -> afe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH ACCEPT from 0x05060708:32000 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 31 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 f5 22 ce CALLBACK, event 0, msg length 88, bvci 0x1002 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 31 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 f5 22 ce NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 88 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 92 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 31 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 f5 22 ce result (ATTACH ACCEPT) = 92 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 6 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700/efe2b700 -> afe2b700/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH COMPLETE from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 4d 08 03 79 84 ea CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 4d 08 03 79 84 ea NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 4d 08 03 79 84 ea result (ATTACH COMPLETE) = 35 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 6 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700/efe2b700 -> afe2b700/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING DETACH REQ from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8b 41 c0 35 08 05 02 25 0a 9b fa 57 CALLBACK, event 0, msg length 69, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8b 41 c0 35 08 05 02 25 0a 9b fa 57 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 69 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 73 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8b 41 c0 35 08 05 02 25 0a 9b fa 57 result (DETACH REQ) = 73 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 6 TLLI cache size : 1 TLLI-Cache: 1 TLLI efe2b700 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING DETACH ACC from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 51 08 06 a5 d9 70 CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 51 08 06 a5 d9 70 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 51 08 06 a5 d9 70 result (DETACH ACC) = 35 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 6 TLLI cache size : 1 TLLI-Cache: 1 TLLI 00000000, IMSI 12131415161718, AGE 0, DE-REGISTERED PROCESSING ATTACH REQUEST (local TLLI) from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 55 08 01 02 f5 e0 21 08 02 05 f4 ef e2 b7 00 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 f9 cc e9 CALLBACK, event 0, msg length 75, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 55 08 01 02 f5 e0 21 08 02 05 f4 ef e2 b7 00 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 f9 cc e9 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 75 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 79 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 55 08 01 02 f5 e0 21 08 02 05 f4 ef e2 b7 00 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 f9 cc e9 result (ATTACH REQUEST (local TLLI)) = 79 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 7 TLLI cache size : 1 TLLI-Cache: 1 TLLI efe2b700 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH ACCEPT from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 39 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 e8 73 9e CALLBACK, event 0, msg length 88, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 39 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 e8 73 9e NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 88 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 92 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 39 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 e8 73 9e result (ATTACH ACCEPT) = 92 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 7 TLLI cache size : 1 TLLI-Cache: 1 TLLI efe2b700 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH COMPLETE from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 59 08 03 1e 69 bc CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 59 08 03 1e 69 bc NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 59 08 03 1e 69 bc result (ATTACH COMPLETE) = 35 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 7 TLLI cache size : 1 TLLI-Cache: 1 TLLI efe2b700 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING DETACH REQ (re-attach) from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8b 41 c0 3d 08 05 01 25 0a 21 a2 ad CALLBACK, event 0, msg length 69, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8b 41 c0 3d 08 05 01 25 0a 21 a2 ad NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 69 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 73 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8b 41 c0 3d 08 05 01 25 0a 21 a2 ad result (DETACH REQ (re-attach)) = 73 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 7 TLLI cache size : 1 TLLI-Cache: 1 TLLI efe2b700 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING DETACH ACC from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 5d 08 06 11 84 8b CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 5d 08 06 11 84 8b NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 5d 08 06 11 84 8b result (DETACH ACC) = 35 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 7 TLLI cache size : 1 TLLI-Cache: 1 TLLI 00000000, IMSI 12131415161718, AGE 0, DE-REGISTERED PROCESSING ATTACH REQUEST from 0x01020304:1111 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 61 08 01 02 f5 e0 21 08 02 05 f4 ef e2 b7 00 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 5b 66 e2 CALLBACK, event 0, msg length 75, bvci 0x1002 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 61 08 01 02 f5 e0 21 08 02 05 f4 ef e2 b7 00 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 5b 66 e2 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 75 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 79 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 61 08 01 02 f5 e0 21 08 02 05 f4 ef e2 b7 00 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 5b 66 e2 result (ATTACH REQUEST) = 79 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 8 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700 -> afe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH ACCEPT from 0x05060708:32000 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 41 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 9e 50 40 CALLBACK, event 0, msg length 88, bvci 0x1002 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 41 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 9e 50 40 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 88 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 92 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 41 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 9e 50 40 result (ATTACH ACCEPT) = 92 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 8 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700/efe2b700 -> afe2b700/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH COMPLETE from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 65 08 03 b7 5e 47 CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 65 08 03 b7 5e 47 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 65 08 03 b7 5e 47 result (ATTACH COMPLETE) = 35 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 8 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700/efe2b700 -> afe2b700/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING GMM INFO from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 45 08 21 9c 7f c6 CALLBACK, event 0, msg length 66, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 45 08 21 9c 7f c6 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 66 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 70 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 45 08 21 9c 7f c6 result (GMM INFO) = 70 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 8 TLLI cache size : 1 TLLI-Cache: 1 TLLI efe2b700 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH REQUEST (unexpected, IMSI) from 0x01020304:1111 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 37 01 c0 69 08 01 02 f5 e0 21 08 02 08 11 12 13 14 15 16 17 18 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 e1 11 8e CALLBACK, event 0, msg length 78, bvci 0x1002 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 37 01 c0 69 08 01 02 f5 e0 21 08 02 08 11 12 13 14 15 16 17 18 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 e1 11 8e NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 78 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 82 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 37 01 c0 69 08 01 02 f5 e0 21 08 02 08 11 12 13 14 15 16 17 18 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 e1 11 8e result (ATTACH REQUEST (unexpected, IMSI)) = 82 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 9 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700 -> afe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH ACCEPT from 0x05060708:32000 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 49 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 83 01 10 CALLBACK, event 0, msg length 88, bvci 0x1002 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 49 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 83 01 10 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 88 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 92 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 49 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 83 01 10 result (ATTACH ACCEPT) = 92 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 9 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700/efe2b700 -> afe2b700/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH COMPLETE from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 6d 08 03 6f c8 ea CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 6d 08 03 6f c8 ea NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 6d 08 03 6f c8 ea result (ATTACH COMPLETE) = 35 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 9 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700/efe2b700 -> afe2b700/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING DETACH REQ from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8b 41 c0 4d 08 05 02 25 0a 51 0e 1b CALLBACK, event 0, msg length 69, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8b 41 c0 4d 08 05 02 25 0a 51 0e 1b NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 69 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 73 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8b 41 c0 4d 08 05 02 25 0a 51 0e 1b result (DETACH REQ) = 73 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 9 TLLI cache size : 1 TLLI-Cache: 1 TLLI efe2b700 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING DETACH ACC from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 71 08 06 b3 95 70 CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 71 08 06 b3 95 70 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 71 08 06 b3 95 70 result (DETACH ACC) = 35 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 9 TLLI cache size : 1 TLLI-Cache: 1 TLLI 00000000, IMSI 12131415161718, AGE 0, DE-REGISTERED PROCESSING ATTACH REQUEST from 0x01020304:1111 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 75 08 01 02 f5 e0 21 08 02 05 f4 ef e2 b7 00 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 ab 17 53 CALLBACK, event 0, msg length 75, bvci 0x1002 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 75 08 01 02 f5 e0 21 08 02 05 f4 ef e2 b7 00 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 ab 17 53 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 75 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 79 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 75 08 01 02 f5 e0 21 08 02 05 f4 ef e2 b7 00 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 ab 17 53 result (ATTACH REQUEST) = 79 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 10 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700 -> afe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH ACCEPT from 0x05060708:32000 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 51 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 a4 f2 e0 CALLBACK, event 0, msg length 88, bvci 0x1002 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 51 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 a4 f2 e0 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 88 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 92 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 51 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 a4 f2 e0 result (ATTACH ACCEPT) = 92 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 10 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700/efe2b700 -> afe2b700/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH COMPLETE from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 79 08 03 08 25 bc CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 79 08 03 08 25 bc NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 79 08 03 08 25 bc result (ATTACH COMPLETE) = 35 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 10 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700/efe2b700 -> afe2b700/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING GMM INFO from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 55 08 21 97 59 c6 CALLBACK, event 0, msg length 66, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 55 08 21 97 59 c6 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 66 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 70 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 88 41 c0 55 08 21 97 59 c6 result (GMM INFO) = 70 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 10 TLLI cache size : 1 TLLI-Cache: 1 TLLI efe2b700 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH REQUEST (unexpected) from 0x01020304:1111 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 7d 08 01 02 f5 e0 21 08 02 05 f4 ef e2 b7 00 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 a2 24 d0 CALLBACK, event 0, msg length 75, bvci 0x1002 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 7d 08 01 02 f5 e0 21 08 02 05 f4 ef e2 b7 00 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 a2 24 d0 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 75 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 79 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 7d 08 01 02 f5 e0 21 08 02 05 f4 ef e2 b7 00 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 a2 24 d0 result (ATTACH REQUEST (unexpected)) = 79 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 11 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700 -> afe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH ACCEPT from 0x05060708:32000 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 59 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 b9 a3 b0 CALLBACK, event 0, msg length 88, bvci 0x1002 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 59 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 b9 a3 b0 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 88 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 92 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 9e 41 c0 59 08 02 01 49 04 21 63 54 40 50 60 19 cd d7 08 17 16 18 05 f4 ef e2 b7 00 b9 a3 b0 result (ATTACH ACCEPT) = 92 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 11 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700/efe2b700 -> afe2b700/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH COMPLETE from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 81 08 03 b9 71 10 CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 81 08 03 b9 71 10 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 81 08 03 b9 71 10 result (ATTACH COMPLETE) = 35 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 11 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700/efe2b700 -> afe2b700/efe2b700, IMSI 12131415161718, AGE 0 PROCESSING DETACH REQ from 0x05060708:32000 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8b 41 c0 5d 08 05 02 25 0a 44 1a c9 CALLBACK, event 0, msg length 69, bvci 0x1002 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8b 41 c0 5d 08 05 02 25 0a 44 1a c9 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 69 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 73 00 00 10 02 00 ef e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8b 41 c0 5d 08 05 02 25 0a 44 1a c9 result (DETACH REQ) = 73 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 11 TLLI cache size : 1 TLLI-Cache: 1 TLLI efe2b700 -> efe2b700, IMSI 12131415161718, AGE 0 PROCESSING DETACH ACC from 0x01020304:1111 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 85 08 06 b6 9c 27 CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 85 08 06 b6 9c 27 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 ef e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 85 08 06 b6 9c 27 result (DETACH ACC) = 35 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 11 TLLI cache size : 1 TLLI-Cache: 1 TLLI 00000000, IMSI 12131415161718, AGE 0, DE-REGISTERED PROCESSING ATTACH REQUEST from 0x01020304:1111 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 89 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 21 24 df CALLBACK, event 0, msg length 75, bvci 0x1002 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 89 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 21 24 df NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 24 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 28 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 0e 00 09 41 c4 01 08 15 01 b7 f8 36 result (ATTACH REQUEST) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 12 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700 -> afe2b700, IMSI (none), AGE 0, STORED 1, IMSI acquisition in progress PROCESSING IDENT RESPONSE from 0x01020304:1111 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 8d 08 16 08 11 12 13 14 15 16 17 18 74 ac 38 CALLBACK, event 0, msg length 40, bvci 0x1002 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 11 01 c0 8d 08 16 08 11 12 13 14 15 16 17 18 74 ac 38 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 75 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 79 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 89 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 21 24 df result (IDENT RESPONSE) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 12 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700 -> afe2b700, IMSI 12131415161718, AGE 0 PROCESSING ATTACH REJECT from 0x05060708:32000 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 89 41 c0 61 08 04 07 79 ba a5 CALLBACK, event 0, msg length 67, bvci 0x1002 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 89 41 c0 61 08 04 07 79 ba a5 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 67 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 71 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 89 41 c0 61 08 04 07 79 ba a5 result (ATTACH REJECT) = 71 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 12 Attach Reject count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 00000000, IMSI 12131415161718, AGE 0, DE-REGISTERED PROCESSING ATTACH REQUEST from 0x01020304:1111 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 91 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 81 7a 01 CALLBACK, event 0, msg length 75, bvci 0x1002 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 91 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 81 7a 01 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 24 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 28 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 0e 00 09 41 c4 01 08 15 01 b7 f8 36 result (ATTACH REQUEST) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 13 Attach Reject count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700 -> afe2b700, IMSI (none), AGE 0, STORED 1, IMSI acquisition in progress PROCESSING DETACH REQ (MO) from 0x01020304:1111 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 15 01 c0 95 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 9c dc fc CALLBACK, event 0, msg length 44, bvci 0x1002 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 15 01 c0 95 08 05 01 18 05 f4 ef e2 b7 00 19 03 b9 97 cb 9c dc fc NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 24 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 28 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 0e 00 09 41 c4 05 08 06 00 29 4a 68 result (DETACH REQ (MO)) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 13 Attach Reject count : 1 TLLI-Cache: 0 PROCESSING ATTACH REQUEST from 0x01020304:1111 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 99 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 88 49 82 CALLBACK, event 0, msg length 75, bvci 0x1002 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 99 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 88 49 82 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 24 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 28 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 0e 00 09 41 c4 01 08 15 01 b7 f8 36 result (ATTACH REQUEST) = 0 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 14 Attach Reject count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700 -> afe2b700, IMSI (none), AGE 0, STORED 1, IMSI acquisition in progress PROCESSING DETACH REQ (MT) from 0x05060708:32000 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8b 41 c0 65 08 05 02 25 0a 17 a2 20 CALLBACK, event 0, msg length 69, bvci 0x1002 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8b 41 c0 65 08 05 02 25 0a 17 a2 20 NS UNITDATA MESSAGE to BSS, BVCI 0x1002, msg length 69 (gprs_ns_sendmsg) MESSAGE to BSS at 0x01020304:1111, msg length 73 00 00 10 02 00 af e2 b7 00 00 50 20 16 82 02 58 13 99 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 0a 82 08 02 0d 88 11 12 13 14 15 16 17 18 00 81 00 0e 8b 41 c0 65 08 05 02 25 0a 17 a2 20 result (DETACH REQ (MT)) = 73 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 14 Attach Reject count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI afe2b700 -> afe2b700, IMSI 12131415161718, AGE 0, STORED 1, IMSI acquisition in progress PROCESSING DETACH ACC from 0x01020304:1111 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 9d 08 06 65 2c 8a CALLBACK, event 0, msg length 31, bvci 0x1002 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 9d 08 06 65 2c 8a NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 75 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 79 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 34 01 c0 99 08 01 02 f5 e0 21 08 02 05 f4 fb c5 46 79 11 22 33 40 50 60 19 18 b3 43 2b 25 96 62 00 60 80 9a c2 c6 62 00 60 80 ba c8 c6 62 00 60 80 00 88 49 82 NS UNITDATA MESSAGE to SGSN, BVCI 0x1002, msg length 31 (gprs_ns_sendmsg) MESSAGE to SGSN at 0x05060708:32000, msg length 35 00 00 10 02 01 af e2 b7 00 00 00 04 08 88 11 22 33 40 50 60 12 34 00 80 0e 00 08 01 c0 9d 08 06 65 2c 8a result (DETACH ACC) = 35 Peers: NSEI 4096, BVCI 4098, not blocked, RAI 112-332-16464-96 Attach Request count : 14 Attach Reject count : 1 TLLI cache size : 1 TLLI-Cache: 1 TLLI 00000000, IMSI 12131415161718, AGE 0, DE-REGISTERED Gbproxy global: Test TLLI info expiry Test TLLI replacement: Add TLLI 1, IMSI 1 Add TLLI 2, IMSI 1 (should replace TLLI 1) Peers: NSEI 0, BVCI 20, not blocked, RAI 0-0-0-0 TLLI cache size : 1 TLLI-Cache: 1 TLLI c000162e, IMSI 03242526, AGE 0, IMSI matches Test IMSI replacement: Add TLLI 1, IMSI 1 Add TLLI 1, IMSI 2 (should replace IMSI 1) Peers: NSEI 0, BVCI 20, not blocked, RAI 0-0-0-0 TLLI cache size : 1 TLLI-Cache: 1 TLLI c00004d2, IMSI 06272829, AGE 0, IMSI matches Test TLLI expiry, max_len == 1: Add TLLI 1, IMSI 1 Add TLLI 2, IMSI 2 (should replace IMSI 1) Peers: NSEI 0, BVCI 20, not blocked, RAI 0-0-0-0 TLLI cache size : 1 TLLI-Cache: 1 TLLI c000162e, IMSI 06272829, AGE 0, IMSI matches Test TLLI expiry, max_age == 1: Add TLLI 1, IMSI 1 (should expire after timeout) Add TLLI 2, IMSI 2 (should not expire after timeout) Peers: NSEI 0, BVCI 20, not blocked, RAI 0-0-0-0 TLLI cache size : 1 TLLI-Cache: 1 TLLI c000162e, IMSI 06272829, AGE 1, IMSI matches Test TLLI expiry, max_len == 2, max_age == 1: Add TLLI 1, IMSI 1 (should expire) Add TLLI 2, IMSI 2 (should expire after timeout) Add TLLI 3, IMSI 3 (should not expire after timeout) Peers: NSEI 0, BVCI 20, not blocked, RAI 0-0-0-0 TLLI cache size : 3 TLLI-Cache: 3 TLLI c0000d80, IMSI 12345678, AGE 0, IMSI matches TLLI c000162e, IMSI 06272829, AGE 1, IMSI matches TLLI c00004d2, IMSI 03242526, AGE 2, IMSI matches Remove stale TLLIs Peers: NSEI 0, BVCI 20, not blocked, RAI 0-0-0-0 TLLI cache size : 1 TLLI-Cache: 1 TLLI c0000d80, IMSI 12345678, AGE 0, IMSI matches ===== GbProxy test END openbsc-0.15.0/openbsc/tests/gprs/000077500000000000000000000000001265565154000170055ustar00rootroot00000000000000openbsc-0.15.0/openbsc/tests/gprs/Makefile.am000066400000000000000000000005651265565154000210470ustar00rootroot00000000000000AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOABIS_CFLAGS) EXTRA_DIST = gprs_test.ok noinst_PROGRAMS = gprs_test gprs_test_SOURCES = gprs_test.c $(top_srcdir)/src/gprs/gprs_utils.c \ $(top_srcdir)/src/gprs/gprs_gsup_messages.c gprs_test_LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) openbsc-0.15.0/openbsc/tests/gprs/gprs_test.c000066400000000000000000000464101265565154000211700ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #define ASSERT_FALSE(x) if (x) { printf("Should have returned false.\n"); abort(); } #define ASSERT_TRUE(x) if (!x) { printf("Should have returned true.\n"); abort(); } #define VERBOSE_FPRINTF(...) /** * GSM 04.64 8.4.2 Receipt of unacknowledged information */ static int nu_is_retransmission(uint16_t nu, uint16_t vur) { int ret = gprs_llc_is_retransmit(nu, vur); printf("N(U) = %d, V(UR) = %d => %s\n", nu, vur, ret == 1 ? "retransmit" : "new"); return ret; } static void test_8_4_2() { printf("Testing gprs_llc_is_retransmit.\n"); ASSERT_FALSE(nu_is_retransmission(0, 0)); ASSERT_TRUE (nu_is_retransmission(0, 1)); /* expect 1... check for retransmissions */ ASSERT_TRUE (nu_is_retransmission(0, 1)); ASSERT_TRUE (nu_is_retransmission(511, 1)); ASSERT_TRUE (nu_is_retransmission(483, 1)); ASSERT_TRUE (nu_is_retransmission(482, 1)); ASSERT_FALSE(nu_is_retransmission(481, 1)); /* expect 511... check for retransmissions */ ASSERT_FALSE(nu_is_retransmission(0, 240)); // ahead ASSERT_FALSE(nu_is_retransmission(0, 511)); // ahead ASSERT_FALSE(nu_is_retransmission(1, 511)); // ahead ASSERT_FALSE(nu_is_retransmission(511, 511)); // same ASSERT_TRUE (nu_is_retransmission(510, 511)); // behind ASSERT_TRUE (nu_is_retransmission(481, 511)); // behind ASSERT_FALSE(nu_is_retransmission(479, 511)); // wrapped } static void apn_round_trip(const uint8_t *input, size_t len, const char *wanted_output) { char output[len ? len : 1]; uint8_t encoded[len + 50]; char *out_str; int enc_len; /* decode and verify we have what we want */ out_str = gprs_apn_to_str(output, input, len); OSMO_ASSERT(out_str); OSMO_ASSERT(out_str == &output[0]); OSMO_ASSERT(strlen(out_str) == strlen(wanted_output)); OSMO_ASSERT(strcmp(out_str, wanted_output) == 0); /* encode and verify it */ if (len != 0) { enc_len = gprs_str_to_apn(encoded, ARRAY_SIZE(encoded), wanted_output); OSMO_ASSERT(enc_len == len); OSMO_ASSERT(memcmp(encoded, input, enc_len) == 0); } else { enc_len = gprs_str_to_apn(encoded, 0, wanted_output); OSMO_ASSERT(enc_len == -1); } } static void test_gsm_03_03_apn(void) { { /* test invalid writes */ const uint8_t ref[10] = { 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF }; uint8_t output[10]; int enc_len; memcpy(output, ref, ARRAY_SIZE(output)); enc_len = gprs_str_to_apn(output, 0, ""); OSMO_ASSERT(enc_len == -1); OSMO_ASSERT(memcmp(ref, output, ARRAY_SIZE(ref)) == 0); memcpy(output, ref, ARRAY_SIZE(output)); enc_len = gprs_str_to_apn(output, 0, "foo"); OSMO_ASSERT(enc_len == -1); OSMO_ASSERT(memcmp(ref, output, ARRAY_SIZE(ref)) == 0); memcpy(output, ref, ARRAY_SIZE(output)); enc_len = gprs_str_to_apn(output, 1, "foo"); OSMO_ASSERT(enc_len == -1); OSMO_ASSERT(memcmp(ref + 1, output + 1, ARRAY_SIZE(ref) - 1) == 0); memcpy(output, ref, ARRAY_SIZE(output)); enc_len = gprs_str_to_apn(output, 2, "foo"); OSMO_ASSERT(enc_len == -1); OSMO_ASSERT(memcmp(ref + 2, output + 2, ARRAY_SIZE(ref) - 2) == 0); memcpy(output, ref, ARRAY_SIZE(output)); enc_len = gprs_str_to_apn(output, 3, "foo"); OSMO_ASSERT(enc_len == -1); OSMO_ASSERT(memcmp(ref + 3, output + 3, ARRAY_SIZE(ref) - 3) == 0); } { /* single empty label */ uint8_t input[] = { 0x0 }; const char *output = ""; apn_round_trip(input, ARRAY_SIZE(input), output); } { /* no label */ uint8_t input[] = { }; const char *output = ""; apn_round_trip(input, ARRAY_SIZE(input), output); } { /* single label with A */ uint8_t input[] = { 0x1, 65 }; const char *output = "A"; apn_round_trip(input, ARRAY_SIZE(input), output); OSMO_ASSERT(gprs_apn_to_str(NULL, input, ARRAY_SIZE(input) - 1) == NULL); } { uint8_t input[] = { 0x3, 65, 66, 67, 0x2, 90, 122 }; const char *output = "ABC.Zz"; char tmp[strlen(output) + 1]; apn_round_trip(input, ARRAY_SIZE(input), output); OSMO_ASSERT(gprs_apn_to_str(tmp, input, ARRAY_SIZE(input) - 1) == NULL); OSMO_ASSERT(gprs_apn_to_str(tmp, input, ARRAY_SIZE(input) - 2) == NULL); OSMO_ASSERT(gprs_apn_to_str(tmp, input, ARRAY_SIZE(input) - 4) == NULL); OSMO_ASSERT(gprs_apn_to_str(tmp, input, ARRAY_SIZE(input) - 5) == NULL); OSMO_ASSERT(gprs_apn_to_str(tmp, input, ARRAY_SIZE(input) - 6) == NULL); } } /* TODO: Move tlv testing to libosmocore */ static void check_tlv_parse(uint8_t **data, size_t *data_len, uint8_t exp_tag, size_t exp_len, const uint8_t *exp_val) { uint8_t *value; size_t value_len; uint8_t tag; int rc; uint8_t *saved_data = *data; size_t saved_data_len = *data_len; rc = gprs_match_tlv(data, data_len, exp_tag ^ 1, NULL, NULL); OSMO_ASSERT(rc == 0); rc = gprs_match_tlv(data, data_len, exp_tag, &value, &value_len); OSMO_ASSERT(rc == (int)value_len + 2); OSMO_ASSERT(value_len == exp_len); OSMO_ASSERT(memcmp(value, exp_val, exp_len) == 0); /* restore data/data_len */ *data = saved_data; *data_len = saved_data_len; rc = gprs_shift_tlv(data, data_len, &tag, &value, &value_len); OSMO_ASSERT(rc == (int)value_len + 2); OSMO_ASSERT(tag == exp_tag); OSMO_ASSERT(value_len == exp_len); OSMO_ASSERT(memcmp(value, exp_val, exp_len) == 0); } static void check_tv_fixed_match(uint8_t **data, size_t *data_len, uint8_t tag, size_t len, const uint8_t *exp_val) { uint8_t *value; int rc; rc = gprs_match_tv_fixed(data, data_len, tag ^ 1, len, NULL); OSMO_ASSERT(rc == 0); rc = gprs_match_tv_fixed(data, data_len, tag, len, &value); OSMO_ASSERT(rc == (int)len + 1); OSMO_ASSERT(memcmp(value, exp_val, len) == 0); } static void check_v_fixed_shift(uint8_t **data, size_t *data_len, size_t len, const uint8_t *exp_val) { uint8_t *value; int rc; rc = gprs_shift_v_fixed(data, data_len, len, &value); OSMO_ASSERT(rc == (int)len); OSMO_ASSERT(memcmp(value, exp_val, len) == 0); } static void check_lv_shift(uint8_t **data, size_t *data_len, size_t exp_len, const uint8_t *exp_val) { uint8_t *value; size_t value_len; int rc; rc = gprs_shift_lv(data, data_len, &value, &value_len); OSMO_ASSERT(rc == (int)value_len + 1); OSMO_ASSERT(value_len == exp_len); OSMO_ASSERT(memcmp(value, exp_val, exp_len) == 0); } static void check_tlv_match_data_len(size_t data_len, uint8_t tag, size_t len, const uint8_t *test_data) { uint8_t buf[300] = {0}; uint8_t *unchanged_ptr = buf - 1; size_t unchanged_len = 0xdead; size_t tmp_data_len = data_len; uint8_t *value = unchanged_ptr; size_t value_len = unchanged_len; uint8_t *data = buf; OSMO_ASSERT(data_len <= sizeof(buf)); tlv_put(data, tag, len, test_data); if (data_len < len + 2) { OSMO_ASSERT(-1 == gprs_match_tlv(&data, &tmp_data_len, tag, &value, &value_len)); OSMO_ASSERT(tmp_data_len == 0); OSMO_ASSERT(data == buf + data_len); OSMO_ASSERT(value == unchanged_ptr); OSMO_ASSERT(value_len == unchanged_len); } else { OSMO_ASSERT(0 <= gprs_match_tlv(&data, &tmp_data_len, tag, &value, &value_len)); OSMO_ASSERT(value != unchanged_ptr); OSMO_ASSERT(value_len != unchanged_len); } } static void check_tv_fixed_match_data_len(size_t data_len, uint8_t tag, size_t len, const uint8_t *test_data) { uint8_t buf[300] = {0}; uint8_t *unchanged_ptr = buf - 1; size_t tmp_data_len = data_len; uint8_t *value = unchanged_ptr; uint8_t *data = buf; OSMO_ASSERT(data_len <= sizeof(buf)); tv_fixed_put(data, tag, len, test_data); if (data_len < len + 1) { OSMO_ASSERT(-1 == gprs_match_tv_fixed(&data, &tmp_data_len, tag, len, &value)); OSMO_ASSERT(tmp_data_len == 0); OSMO_ASSERT(data == buf + data_len); OSMO_ASSERT(value == unchanged_ptr); } else { OSMO_ASSERT(0 <= gprs_match_tv_fixed(&data, &tmp_data_len, tag, len, &value)); OSMO_ASSERT(value != unchanged_ptr); } } static void check_v_fixed_shift_data_len(size_t data_len, size_t len, const uint8_t *test_data) { uint8_t buf[300] = {0}; uint8_t *unchanged_ptr = buf - 1; size_t tmp_data_len = data_len; uint8_t *value = unchanged_ptr; uint8_t *data = buf; OSMO_ASSERT(data_len <= sizeof(buf)); memcpy(data, test_data, len); if (data_len < len) { OSMO_ASSERT(-1 == gprs_shift_v_fixed(&data, &tmp_data_len, len, &value)); OSMO_ASSERT(tmp_data_len == 0); OSMO_ASSERT(data == buf + data_len); OSMO_ASSERT(value == unchanged_ptr); } else { OSMO_ASSERT(0 <= gprs_shift_v_fixed(&data, &tmp_data_len, len, &value)); OSMO_ASSERT(value != unchanged_ptr); } } static void check_lv_shift_data_len(size_t data_len, size_t len, const uint8_t *test_data) { uint8_t buf[300] = {0}; uint8_t *unchanged_ptr = buf - 1; size_t unchanged_len = 0xdead; size_t tmp_data_len = data_len; uint8_t *value = unchanged_ptr; size_t value_len = unchanged_len; uint8_t *data = buf; lv_put(data, len, test_data); if (data_len < len + 1) { OSMO_ASSERT(-1 == gprs_shift_lv(&data, &tmp_data_len, &value, &value_len)); OSMO_ASSERT(tmp_data_len == 0); OSMO_ASSERT(data == buf + data_len); OSMO_ASSERT(value == unchanged_ptr); OSMO_ASSERT(value_len == unchanged_len); } else { OSMO_ASSERT(0 <= gprs_shift_lv(&data, &tmp_data_len, &value, &value_len)); OSMO_ASSERT(value != unchanged_ptr); OSMO_ASSERT(value_len != unchanged_len); } } static void test_tlv_shift_functions() { uint8_t test_data[1024]; uint8_t buf[1024]; uint8_t *data_end; unsigned i, len; uint8_t *data; size_t data_len; const uint8_t tag = 0x1a; printf("Test shift functions\n"); for (i = 0; i < ARRAY_SIZE(test_data); i++) test_data[i] = (uint8_t)i; for (len = 0; len < 256; len++) { const unsigned iterations = sizeof(buf) / (len + 2) / 4; memset(buf, 0xee, sizeof(buf)); data_end = data = buf; for (i = 0; i < iterations; i++) { data_end = tlv_put(data_end, tag, len, test_data); data_end = tv_fixed_put(data_end, tag, len, test_data); /* v_fixed_put */ memcpy(data_end, test_data, len); data_end += len; data_end = lv_put(data_end, len, test_data); } data_len = data_end - data; OSMO_ASSERT(data_len <= sizeof(buf)); for (i = 0; i < iterations; i++) { check_tlv_parse(&data, &data_len, tag, len, test_data); check_tv_fixed_match(&data, &data_len, tag, len, test_data); check_v_fixed_shift(&data, &data_len, len, test_data); check_lv_shift(&data, &data_len, len, test_data); } OSMO_ASSERT(data == data_end); /* Test at end of data */ OSMO_ASSERT(-1 == gprs_match_tlv(&data, &data_len, tag, NULL, NULL)); OSMO_ASSERT(-1 == gprs_match_tv_fixed(&data, &data_len, tag, len, NULL)); OSMO_ASSERT((len ? -1 : 0) == gprs_shift_v_fixed(&data, &data_len, len, NULL)); OSMO_ASSERT(-1 == gprs_shift_lv(&data, &data_len, NULL, NULL)); /* Test invalid data_len */ for (data_len = 0; data_len <= len + 2 + 1; data_len += 1) { check_tlv_match_data_len(data_len, tag, len, test_data); check_tv_fixed_match_data_len(data_len, tag, len, test_data); check_v_fixed_shift_data_len(data_len, len, test_data); check_lv_shift_data_len(data_len, len, test_data); } } } /* Tests for gprs_gsup_messages.c */ #define TEST_IMSI_IE 0x01, 0x08, 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0xf5 #define TEST_IMSI_STR "123456789012345" static void test_gsup_messages_dec_enc(void) { int test_idx; int rc; uint8_t buf[1024]; static const uint8_t send_auth_info_req[] = { 0x08, TEST_IMSI_IE }; static const uint8_t send_auth_info_err[] = { 0x09, TEST_IMSI_IE, 0x02, 0x01, 0x07 /* GPRS no allowed */ }; static const uint8_t send_auth_info_res[] = { 0x0a, TEST_IMSI_IE, 0x03, 0x22, /* Auth tuple */ 0x20, 0x10, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x21, 0x04, 0x21, 0x22, 0x23, 0x24, 0x22, 0x08, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x03, 0x22, /* Auth tuple */ 0x20, 0x10, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x21, 0x04, 0xa1, 0xa2, 0xa3, 0xa4, 0x22, 0x08, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, }; static const uint8_t update_location_req[] = { 0x04, TEST_IMSI_IE, }; static const uint8_t update_location_err[] = { 0x05, TEST_IMSI_IE, 0x02, 0x01, 0x07 /* GPRS no allowed */ }; static const uint8_t update_location_res[] = { 0x06, TEST_IMSI_IE, 0x08, 0x07, /* MSISDN of the subscriber */ 0x91, 0x94, 0x61, 0x46, 0x32, 0x24, 0x43, 0x09, 0x07, /* HLR-Number of the subscriber */ 0x91, 0x83, 0x52, 0x38, 0x48, 0x83, 0x93, 0x04, 0x00, /* PDP info complete */ 0x05, 0x15, 0x10, 0x01, 0x01, 0x11, 0x02, 0xf1, 0x21, /* IPv4 */ 0x12, 0x09, 0x04, 't', 'e', 's', 't', 0x03, 'a', 'p', 'n', 0x13, 0x01, 0x02, 0x05, 0x11, 0x10, 0x01, 0x02, 0x11, 0x02, 0xf1, 0x21, /* IPv4 */ 0x12, 0x08, 0x03, 'f', 'o', 'o', 0x03, 'a', 'p', 'n', }; static const uint8_t location_cancellation_req[] = { 0x1c, TEST_IMSI_IE, 0x06, 0x01, 0x00, }; static const uint8_t location_cancellation_err[] = { 0x1d, TEST_IMSI_IE, 0x02, 0x01, 0x03 /* Illegal MS */ }; static const uint8_t location_cancellation_res[] = { 0x1e, TEST_IMSI_IE, }; static const uint8_t purge_ms_req[] = { 0x0c, TEST_IMSI_IE, }; static const uint8_t purge_ms_err[] = { 0x0d, TEST_IMSI_IE, 0x02, 0x01, 0x03, /* Illegal MS */ }; static const uint8_t purge_ms_res[] = { 0x0e, TEST_IMSI_IE, 0x07, 0x00, }; static const struct test { char *name; const uint8_t *data; size_t data_len; } test_messages[] = { {"Send Authentication Info Request", send_auth_info_req, sizeof(send_auth_info_req)}, {"Send Authentication Info Error", send_auth_info_err, sizeof(send_auth_info_err)}, {"Send Authentication Info Result", send_auth_info_res, sizeof(send_auth_info_res)}, {"Update Location Request", update_location_req, sizeof(update_location_req)}, {"Update Location Error", update_location_err, sizeof(update_location_err)}, {"Update Location Result", update_location_res, sizeof(update_location_res)}, {"Location Cancellation Request", location_cancellation_req, sizeof(location_cancellation_req)}, {"Location Cancellation Error", location_cancellation_err, sizeof(location_cancellation_err)}, {"Location Cancellation Result", location_cancellation_res, sizeof(location_cancellation_res)}, {"Purge MS Request", purge_ms_req, sizeof(purge_ms_req)}, {"Purge MS Error", purge_ms_err, sizeof(purge_ms_err)}, {"Purge MS Result", purge_ms_res, sizeof(purge_ms_res)}, }; printf("Test GSUP message decoding/encoding\n"); for (test_idx = 0; test_idx < ARRAY_SIZE(test_messages); test_idx++) { const struct test *t = &test_messages[test_idx]; struct gprs_gsup_message gm = {0}; struct msgb *msg = msgb_alloc(4096, "gsup_test"); printf(" Testing %s\n", t->name); rc = gprs_gsup_decode(t->data, t->data_len, &gm); OSMO_ASSERT(rc >= 0); gprs_gsup_encode(msg, &gm); fprintf(stderr, " generated message: %s\n", msgb_hexdump(msg)); fprintf(stderr, " original message: %s\n", osmo_hexdump(t->data, t->data_len)); fprintf(stderr, " IMSI: %s\n", gm.imsi); OSMO_ASSERT(strcmp(gm.imsi, TEST_IMSI_STR) == 0); OSMO_ASSERT(msgb_length(msg) == t->data_len); OSMO_ASSERT(memcmp(msgb_data(msg), t->data, t->data_len) == 0); msgb_free(msg); } /* simple truncation test */ for (test_idx = 0; test_idx < ARRAY_SIZE(test_messages); test_idx++) { int j; const struct test *t = &test_messages[test_idx]; int ie_end = t->data_len; struct gprs_gsup_message gm = {0}; int counter = 0; int parse_err = 0; for (j = t->data_len - 1; j >= 0; --j) { rc = gprs_gsup_decode(t->data, j, &gm); counter += 1; VERBOSE_FPRINTF(stderr, " partial message decoding: " "orig_len = %d, trunc = %d, rc = %d, ie_end = %d\n", t->data_len, j, rc, ie_end); if (rc >= 0) { VERBOSE_FPRINTF(stderr, " remaing partial message: %s\n", osmo_hexdump(t->data + j, ie_end - j)); OSMO_ASSERT(j <= ie_end - 2); OSMO_ASSERT(t->data[j+0] <= GPRS_GSUP_KC_IE); OSMO_ASSERT(t->data[j+1] <= ie_end - j - 2); ie_end = j; } else { parse_err += 1; } } fprintf(stderr, " message %d: tested %d truncations, %d parse failures\n", test_idx, counter, parse_err); } /* message modification test (relies on ASAN or valgrind being used) */ for (test_idx = 0; test_idx < ARRAY_SIZE(test_messages); test_idx++) { int j; const struct test *t = &test_messages[test_idx]; struct gprs_gsup_message gm = {0}; uint8_t val; int counter = 0; int parse_err = 0; OSMO_ASSERT(sizeof(buf) >= t->data_len); for (j = t->data_len - 1; j >= 0; --j) { memcpy(buf, t->data, t->data_len); val = 0; do { VERBOSE_FPRINTF(stderr, "t = %d, len = %d, val = %d\n", test_idx, j, val); buf[j] = val; rc = gprs_gsup_decode(buf, t->data_len, &gm); counter += 1; if (rc < 0) parse_err += 1; val += 1; } while (val != (uint8_t)256); } fprintf(stderr, " message %d: tested %d modifications, %d parse failures\n", test_idx, counter, parse_err); } } static void test_gprs_timer_enc_dec(void) { int i, u, secs, tmr; const int upper_secs_test_limit = 12000; int dec_secs, last_dec_secs = -1; printf("Test GPRS timer decoding/encoding\n"); /* Check gprs_tmr_to_secs with all 256 encoded values */ for (u = 0; u <= GPRS_TMR_DEACTIVATED; u += 32) { fprintf(stderr, "Testing decoding with timer value unit %u\n", u / 32); for (i = 0; i < 32; i++) { switch (u) { case GPRS_TMR_2SECONDS: OSMO_ASSERT(gprs_tmr_to_secs(u + i) == 2 * i); break; default: case GPRS_TMR_MINUTE: OSMO_ASSERT(gprs_tmr_to_secs(u + i) == 60 * i); break; case GPRS_TMR_6MINUTE: OSMO_ASSERT(gprs_tmr_to_secs(u + i) == 360 * i); break; case GPRS_TMR_DEACTIVATED: OSMO_ASSERT(gprs_tmr_to_secs(u + i) == -1); break; } OSMO_ASSERT(gprs_tmr_to_secs(u + i) < upper_secs_test_limit); } } /* Check gprs_secs_to_tmr_floor for secs that can exactly be * represented as GPRS timer values */ for (i = 0; i < GPRS_TMR_DEACTIVATED; i++) { int j; secs = gprs_tmr_to_secs(i); tmr = gprs_secs_to_tmr_floor(secs); OSMO_ASSERT(secs == gprs_tmr_to_secs(tmr)); /* Check that the highest resolution is used */ for (j = 0; j < tmr; j++) OSMO_ASSERT(secs != gprs_tmr_to_secs(j)); } OSMO_ASSERT(GPRS_TMR_DEACTIVATED == gprs_secs_to_tmr_floor(-1)); /* Check properties of gprs_secs_to_tmr_floor */ for (secs = 0; secs <= upper_secs_test_limit; secs++) { int tmr = gprs_secs_to_tmr_floor(secs); int delta_secs = gprs_tmr_to_secs((tmr & ~0x1f) | 1); dec_secs = gprs_tmr_to_secs(tmr); /* Check floor */ OSMO_ASSERT(dec_secs <= secs); /* Check monotonicity */ OSMO_ASSERT(dec_secs >= last_dec_secs); /* Check max distance (<= resolution) */ OSMO_ASSERT(dec_secs - last_dec_secs <= delta_secs); last_dec_secs = dec_secs; } } const struct log_info_cat default_categories[] = { [DGPRS] = { .name = "DGPRS", .description = "GPRS Packet Service", .enabled = 0, .loglevel = LOGL_DEBUG, }, }; static struct log_info info = { .cat = default_categories, .num_cat = ARRAY_SIZE(default_categories), }; int main(int argc, char **argv) { osmo_init_logging(&info); test_8_4_2(); test_gsm_03_03_apn(); test_tlv_shift_functions(); test_gsup_messages_dec_enc(); test_gprs_timer_enc_dec(); printf("Done.\n"); return EXIT_SUCCESS; } openbsc-0.15.0/openbsc/tests/gprs/gprs_test.ok000066400000000000000000000017561265565154000213630ustar00rootroot00000000000000Testing gprs_llc_is_retransmit. N(U) = 0, V(UR) = 0 => new N(U) = 0, V(UR) = 1 => retransmit N(U) = 0, V(UR) = 1 => retransmit N(U) = 511, V(UR) = 1 => retransmit N(U) = 483, V(UR) = 1 => retransmit N(U) = 482, V(UR) = 1 => retransmit N(U) = 481, V(UR) = 1 => new N(U) = 0, V(UR) = 240 => new N(U) = 0, V(UR) = 511 => new N(U) = 1, V(UR) = 511 => new N(U) = 511, V(UR) = 511 => new N(U) = 510, V(UR) = 511 => retransmit N(U) = 481, V(UR) = 511 => retransmit N(U) = 479, V(UR) = 511 => new Test shift functions Test GSUP message decoding/encoding Testing Send Authentication Info Request Testing Send Authentication Info Error Testing Send Authentication Info Result Testing Update Location Request Testing Update Location Error Testing Update Location Result Testing Location Cancellation Request Testing Location Cancellation Error Testing Location Cancellation Result Testing Purge MS Request Testing Purge MS Error Testing Purge MS Result Test GPRS timer decoding/encoding Done. openbsc-0.15.0/openbsc/tests/gsm0408/000077500000000000000000000000001265565154000171345ustar00rootroot00000000000000openbsc-0.15.0/openbsc/tests/gsm0408/Makefile.am000066400000000000000000000007271265565154000211760ustar00rootroot00000000000000AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOABIS_CFLAGS) noinst_PROGRAMS = gsm0408_test EXTRA_DIST = gsm0408_test.ok gsm0408_test_SOURCES = gsm0408_test.c gsm0408_test_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ $(top_builddir)/src/libmsc/libmsc.a \ $(top_builddir)/src/libbsc/libbsc.a \ $(top_builddir)/src/libcommon/libcommon.a \ $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) -ldbi openbsc-0.15.0/openbsc/tests/gsm0408/gsm0408_test.c000066400000000000000000000310471265565154000214460ustar00rootroot00000000000000/* simple test for the gsm0408 formatting functions */ /* * (C) 2008 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 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 . * */ #include #include #include #include #include #include #include #include #include #include #define COMPARE(result, op, value) \ if (!((result) op (value))) {\ fprintf(stderr, "Compare failed. Was %x should be %x in %s:%d\n",result, value, __FILE__, __LINE__); \ exit(-1); \ } #define COMPARE_STR(result, value) \ if (strcmp(result, value) != 0) { \ fprintf(stderr, "Compare failed. Was %s should be %s in %s:%d\n",result, value, __FILE__, __LINE__); \ exit(-1); \ } #define DBG(...) #define VERIFY(res, cmp, wanted) \ if (!(res cmp wanted)) { \ printf("ASSERT failed: %s:%d Wanted: %d %s %d\n", \ __FILE__, __LINE__, (int) res, # cmp, (int) wanted); \ } /* * Test Location Area Identifier formatting. Table 10.5.3 of 04.08 */ static void test_location_area_identifier(void) { struct gsm48_loc_area_id lai48; printf("Testing test location area identifier\n"); /* * Test the default/test setup. Coming from * bsc_hack.c dumps */ gsm48_generate_lai(&lai48, 1, 1, 1); COMPARE(lai48.digits[0], ==, 0x00); COMPARE(lai48.digits[1], ==, 0xF1); COMPARE(lai48.digits[2], ==, 0x10); COMPARE(lai48.lac, ==, htons(0x0001)); gsm48_generate_lai(&lai48, 602, 1, 15); COMPARE(lai48.digits[0], ==, 0x06); COMPARE(lai48.digits[1], ==, 0xF2); COMPARE(lai48.digits[2], ==, 0x10); COMPARE(lai48.lac, ==, htons(0x000f)); } static void test_mi_functionality(void) { const char *imsi_odd = "987654321098763"; const char *imsi_even = "9876543210987654"; const uint32_t tmsi = 0xfabeacd0; uint8_t mi[128]; unsigned int mi_len; char mi_parsed[GSM48_MI_SIZE]; printf("Testing parsing and generating TMSI/IMSI\n"); /* tmsi code */ mi_len = gsm48_generate_mid_from_tmsi(mi, tmsi); gsm48_mi_to_string(mi_parsed, sizeof(mi_parsed), mi + 2, mi_len - 2); COMPARE((uint32_t)strtoul(mi_parsed, NULL, 10), ==, tmsi); /* imsi code */ mi_len = gsm48_generate_mid_from_imsi(mi, imsi_odd); gsm48_mi_to_string(mi_parsed, sizeof(mi_parsed), mi + 2, mi_len -2); printf("hex: %s\n", osmo_hexdump(mi, mi_len)); COMPARE_STR(mi_parsed, imsi_odd); mi_len = gsm48_generate_mid_from_imsi(mi, imsi_even); gsm48_mi_to_string(mi_parsed, sizeof(mi_parsed), mi + 2, mi_len -2); printf("hex: %s\n", osmo_hexdump(mi, mi_len)); COMPARE_STR(mi_parsed, imsi_even); } struct { int range; int arfcns_num; int arfcns[RANGE_ENC_MAX_ARFCNS]; } arfcn_test_ranges[] = { {ARFCN_RANGE_512, 12, { 1, 12, 31, 51, 57, 91, 97, 98, 113, 117, 120, 125 }}, {ARFCN_RANGE_512, 17, { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 }}, {ARFCN_RANGE_512, 18, { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 }}, {ARFCN_RANGE_512, 18, { 1, 17, 31, 45, 58, 79, 81, 97, 113, 127, 213, 277, 287, 311, 331, 391, 417, 511 }}, {ARFCN_RANGE_512, 6, { 1, 17, 31, 45, 58, 79 }}, {ARFCN_RANGE_512, 6, { 10, 17, 31, 45, 58, 79 }}, {ARFCN_RANGE_1024, 17, { 0, 17, 31, 45, 58, 79, 81, 97, 113, 127, 213, 277, 287, 311, 331, 391, 1023 }}, {ARFCN_RANGE_1024, 16, { 17, 31, 45, 58, 79, 81, 97, 113, 127, 213, 277, 287, 311, 331, 391, 1023 }}, {-1} }; static int test_single_range_encoding(int range, const int *orig_arfcns, int arfcns_num, int silent) { int arfcns[RANGE_ENC_MAX_ARFCNS]; int w[RANGE_ENC_MAX_ARFCNS]; int f0_included = 0; int rc, f0; uint8_t chan_list[16] = {0}; struct gsm_sysinfo_freq dec_freq[1024] = {{0}}; int dec_arfcns[RANGE_ENC_MAX_ARFCNS] = {0}; int dec_arfcns_count = 0; int arfcns_used = 0; int i; arfcns_used = arfcns_num; memmove(arfcns, orig_arfcns, sizeof(arfcns)); f0 = range == ARFCN_RANGE_1024 ? 0 : arfcns[0]; /* * Manipulate the ARFCN list according to the rules in J4 depending * on the selected range. */ arfcns_used = range_enc_filter_arfcns(arfcns, arfcns_used, f0, &f0_included); memset(w, 0, sizeof(w)); rc = range_enc_arfcns(range, arfcns, arfcns_used, w, 0); if (rc != 0) { printf("Cannot compute range W(k), rc = %d\n", rc); return 1; } if (!silent) fprintf(stderr, "range=%d, arfcns_used=%d, f0=%d, f0_included=%d\n", range, arfcns_used, f0, f0_included); /* Select the range and the amount of bits needed */ switch (range) { case ARFCN_RANGE_128: rc = range_enc_range128(chan_list, f0, w); break; case ARFCN_RANGE_256: rc = range_enc_range256(chan_list, f0, w); break; case ARFCN_RANGE_512: rc = range_enc_range512(chan_list, f0, w); break; case ARFCN_RANGE_1024: rc = range_enc_range1024(chan_list, f0, f0_included, w); break; default: return 1; }; if (rc != 0) { printf("Cannot encode range, rc = %d\n", rc); return 1; } if (!silent) printf("chan_list = %s\n", osmo_hexdump(chan_list, sizeof(chan_list))); rc = gsm48_decode_freq_list(dec_freq, chan_list, sizeof(chan_list), 0xfe, 1); if (rc != 0) { printf("Cannot decode freq list, rc = %d\n", rc); return 1; } for (i = 0; i < ARRAY_SIZE(dec_freq); i++) { if (dec_freq[i].mask && dec_arfcns_count < ARRAY_SIZE(dec_arfcns)) dec_arfcns[dec_arfcns_count++] = i; } if (!silent) { printf("Decoded freqs %d (expected %d)\n", dec_arfcns_count, arfcns_num); printf("Decoded: "); for (i = 0; i < dec_arfcns_count; i++) { printf("%d ", dec_arfcns[i]); if (dec_arfcns[i] != orig_arfcns[i]) printf("(!= %d) ", orig_arfcns[i]); } printf("\n"); } if (dec_arfcns_count != arfcns_num) { printf("Wrong number of arfcns\n"); return 1; } if (memcmp(dec_arfcns, orig_arfcns, sizeof(dec_arfcns)) != 0) { printf("Decoding error, got wrong freqs\n"); fprintf(stderr, " w = "); for (i = 0; i < ARRAY_SIZE(w); i++) fprintf(stderr, "%d ", w[i]); fprintf(stderr, "\n"); return 1; } return 0; } static void test_random_range_encoding(int range, int max_arfcn_num) { int arfcns_num = 0; int test_idx; int rc, max_count; int num_tests = 1024; printf("Random range test: range %d, max num ARFCNs %d\n", range, max_arfcn_num); srandom(1); for (max_count = 1; max_count < max_arfcn_num; max_count++) { for (test_idx = 0; test_idx < num_tests; test_idx++) { int count; int i; int min_freq = 0; int rnd_arfcns[RANGE_ENC_MAX_ARFCNS] = {0}; char rnd_arfcns_set[1024] = {0}; if (range < ARFCN_RANGE_1024) min_freq = random() % (1023 - range); for (count = max_count; count; ) { int arfcn = min_freq + random() % (range + 1); OSMO_ASSERT(arfcn < ARRAY_SIZE(rnd_arfcns_set)); if (!rnd_arfcns_set[arfcn]) { rnd_arfcns_set[arfcn] = 1; count -= 1; } } arfcns_num = 0; for (i = 0; i < ARRAY_SIZE(rnd_arfcns_set); i++) if (rnd_arfcns_set[i]) rnd_arfcns[arfcns_num++] = i; rc = test_single_range_encoding(range, rnd_arfcns, arfcns_num, 1); if (rc != 0) { printf("Failed on test %d, range %d, num ARFCNs %d\n", test_idx, range, max_count); test_single_range_encoding(range, rnd_arfcns, arfcns_num, 0); return; } } } } static void test_range_encoding() { int *arfcns; int arfcns_num = 0; int test_idx; int range; for (test_idx = 0; arfcn_test_ranges[test_idx].arfcns_num > 0; test_idx++) { arfcns_num = arfcn_test_ranges[test_idx].arfcns_num; arfcns = &arfcn_test_ranges[test_idx].arfcns[0]; range = arfcn_test_ranges[test_idx].range; printf("Range test %d: range %d, num ARFCNs %d\n", test_idx, range, arfcns_num); test_single_range_encoding(range, arfcns, arfcns_num, 0); } test_random_range_encoding(ARFCN_RANGE_128, 29); test_random_range_encoding(ARFCN_RANGE_256, 22); test_random_range_encoding(ARFCN_RANGE_512, 18); test_random_range_encoding(ARFCN_RANGE_1024, 16); } static int freqs1[] = { 12, 70, 121, 190, 250, 320, 401, 475, 520, 574, 634, 700, 764, 830, 905, 980 }; static int freqs2[] = { 402, 460, 1, 67, 131, 197, 272, 347, }; static int freqs3[] = { 68, 128, 198, 279, 353, 398, 452, }; static int w_out[] = { 122, 2, 69, 204, 75, 66, 60, 70, 83, 3, 24, 67, 54, 64, 70, 9, }; static int range128[] = { 1, 1 + 127, }; static int range256[] = { 1, 1 + 128, }; static int range512[] = { 1, 1+ 511, }; static void test_arfcn_filter() { int arfcns[50], i, res, f0_included; for (i = 0; i < ARRAY_SIZE(arfcns); ++i) arfcns[i] = (i + 1) * 2; /* check that the arfcn is taken out. f0_included is only set for Range1024 */ f0_included = 24; res = range_enc_filter_arfcns(arfcns, ARRAY_SIZE(arfcns), arfcns[0], &f0_included); VERIFY(res, ==, ARRAY_SIZE(arfcns) - 1); VERIFY(f0_included, ==, 1); for (i = 0; i < res; ++i) VERIFY(arfcns[i], ==, ((i+2) * 2) - (2+1)); /* check with range1024, ARFCN 0 is included */ for (i = 0; i < ARRAY_SIZE(arfcns); ++i) arfcns[i] = i * 2; res = range_enc_filter_arfcns(arfcns, ARRAY_SIZE(arfcns), 0, &f0_included); VERIFY(res, ==, ARRAY_SIZE(arfcns) - 1); VERIFY(f0_included, ==, 1); for (i = 0; i < res; ++i) VERIFY(arfcns[i], ==, (i + 1) * 2 - 1); /* check with range1024, ARFCN 0 not included */ for (i = 0; i < ARRAY_SIZE(arfcns); ++i) arfcns[i] = (i + 1) * 2; res = range_enc_filter_arfcns(arfcns, ARRAY_SIZE(arfcns), 0, &f0_included); VERIFY(res, ==, ARRAY_SIZE(arfcns)); VERIFY(f0_included, ==, 0); for (i = 0; i < res; ++i) VERIFY(arfcns[i], ==, ((i + 1) * 2) - 1); } static void test_print_encoding() { int rc; int w[17]; uint8_t chan_list[16]; memset(chan_list, 0x23, sizeof(chan_list)); for (rc = 0; rc < ARRAY_SIZE(w); ++rc) switch (rc % 3) { case 0: w[rc] = 0xAAAA; break; case 1: w[rc] = 0x5555; break; case 2: w[rc] = 0x9696; break; } rc = range_enc_range512(chan_list, (1 << 9) | 0x96, w); VERIFY(rc, ==, 0); printf("Range512: %s\n", osmo_hexdump(chan_list, ARRAY_SIZE(chan_list))); } static void test_si_range_helpers() { int ws[(sizeof(freqs1)/sizeof(freqs1[0]))]; int i, f0 = 0xFFFFFF; memset(&ws[0], 0x23, sizeof(ws)); i = range_enc_find_index(1023, freqs1, ARRAY_SIZE(freqs1)); printf("Element is: %d => freqs[i] = %d\n", i, i >= 0 ? freqs1[i] : -1); VERIFY(i, ==, 2); i = range_enc_find_index(511, freqs2, ARRAY_SIZE(freqs2)); printf("Element is: %d => freqs[i] = %d\n", i, i >= 0 ? freqs2[i] : -1); VERIFY(i, ==, 2); i = range_enc_find_index(511, freqs3, ARRAY_SIZE(freqs3)); printf("Element is: %d => freqs[i] = %d\n", i, i >= 0 ? freqs3[i] : -1); VERIFY(i, ==, 0); i = range_enc_arfcns(1023, freqs1, ARRAY_SIZE(freqs1), ws, 0); VERIFY(i, ==, 0); for (i = 0; i < sizeof(freqs1)/sizeof(freqs1[0]); ++i) { printf("w[%d]=%d\n", i, ws[i]); VERIFY(ws[i], ==, w_out[i]); } i = range_enc_determine_range(range128, ARRAY_SIZE(range128), &f0); VERIFY(i, ==, ARFCN_RANGE_128); VERIFY(f0, ==, 1); i = range_enc_determine_range(range256, ARRAY_SIZE(range256), &f0); VERIFY(i, ==, ARFCN_RANGE_256); VERIFY(f0, ==, 1); i = range_enc_determine_range(range512, ARRAY_SIZE(range512), &f0); VERIFY(i, ==, ARFCN_RANGE_512); VERIFY(f0, ==, 1); } static void test_gsm411_rp_ref_wrap(void) { struct gsm_subscriber_connection conn; int res; printf("testing RP-Reference wrap\n"); memset(&conn, 0, sizeof(conn)); conn.next_rp_ref = 255; res = sms_next_rp_msg_ref(&conn); printf("Allocated reference: %d\n", res); OSMO_ASSERT(res == 255); res = sms_next_rp_msg_ref(&conn); printf("Allocated reference: %d\n", res); OSMO_ASSERT(res == 0); res = sms_next_rp_msg_ref(&conn); printf("Allocated reference: %d\n", res); OSMO_ASSERT(res == 1); } int main(int argc, char **argv) { osmo_init_logging(&log_info); log_set_log_level(osmo_stderr_target, LOGL_INFO); test_location_area_identifier(); test_mi_functionality(); test_si_range_helpers(); test_arfcn_filter(); test_print_encoding(); test_range_encoding(); test_gsm411_rp_ref_wrap(); printf("Done.\n"); return EXIT_SUCCESS; } openbsc-0.15.0/openbsc/tests/gsm0408/gsm0408_test.ok000066400000000000000000000042311265565154000216300ustar00rootroot00000000000000Testing test location area identifier Testing parsing and generating TMSI/IMSI hex: 17 08 99 78 56 34 12 90 78 36 hex: 17 09 91 78 56 34 12 90 78 56 f4 Element is: 2 => freqs[i] = 121 Element is: 2 => freqs[i] = 1 Element is: 0 => freqs[i] = 68 w[0]=122 w[1]=2 w[2]=69 w[3]=204 w[4]=75 w[5]=66 w[6]=60 w[7]=70 w[8]=83 w[9]=3 w[10]=24 w[11]=67 w[12]=54 w[13]=64 w[14]=70 w[15]=9 Range512: 89 4b 2a 95 65 95 55 2c a9 55 aa 55 6a 95 59 55 Range test 0: range 511, num ARFCNs 12 chan_list = 88 00 98 34 85 36 7c 50 22 dc 5e ec 00 00 00 00 Decoded freqs 12 (expected 12) Decoded: 1 12 31 51 57 91 97 98 113 117 120 125 Range test 1: range 511, num ARFCNs 17 chan_list = 88 00 82 7f 01 3f 7e 04 0b ff ff fc 10 41 07 e0 Decoded freqs 17 (expected 17) Decoded: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 Range test 2: range 511, num ARFCNs 18 chan_list = 88 00 82 7f 01 7f 7e 04 0b ff ff fc 10 41 07 ff Decoded freqs 18 (expected 18) Decoded: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 Range test 3: range 511, num ARFCNs 18 chan_list = 88 00 94 3a 44 32 d7 2a 43 2a 13 94 e5 38 39 f6 Decoded freqs 18 (expected 18) Decoded: 1 17 31 45 58 79 81 97 113 127 213 277 287 311 331 391 417 511 Range test 4: range 511, num ARFCNs 6 chan_list = 88 00 8b 3c 88 b9 6b 00 00 00 00 00 00 00 00 00 Decoded freqs 6 (expected 6) Decoded: 1 17 31 45 58 79 Range test 5: range 511, num ARFCNs 6 chan_list = 88 05 08 fc 88 b9 6b 00 00 00 00 00 00 00 00 00 Decoded freqs 6 (expected 6) Decoded: 10 17 31 45 58 79 Range test 6: range 1023, num ARFCNs 17 chan_list = 84 71 e4 ab b9 58 05 cb 39 17 fd b0 75 62 0f 2f Decoded freqs 17 (expected 17) Decoded: 0 17 31 45 58 79 81 97 113 127 213 277 287 311 331 391 1023 Range test 7: range 1023, num ARFCNs 16 chan_list = 80 71 e4 ab b9 58 05 cb 39 17 fd b0 75 62 0f 2f Decoded freqs 16 (expected 16) Decoded: 17 31 45 58 79 81 97 113 127 213 277 287 311 331 391 1023 Random range test: range 127, max num ARFCNs 29 Random range test: range 255, max num ARFCNs 22 Random range test: range 511, max num ARFCNs 18 Random range test: range 1023, max num ARFCNs 16 testing RP-Reference wrap Allocated reference: 255 Allocated reference: 0 Allocated reference: 1 Done. openbsc-0.15.0/openbsc/tests/mgcp/000077500000000000000000000000001265565154000167605ustar00rootroot00000000000000openbsc-0.15.0/openbsc/tests/mgcp/Makefile.am000066400000000000000000000020121265565154000210070ustar00rootroot00000000000000AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_srcdir) AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMONETIF_CFLAGS) $(LIBOSMOSCCP_CFLAGS) $(COVERAGE_CFLAGS) $(LIBBCG729_CFLAGS) AM_LDFLAGS = $(COVERAGE_LDFLAGS) EXTRA_DIST = mgcp_test.ok mgcp_transcoding_test.ok noinst_PROGRAMS = mgcp_test if BUILD_MGCP_TRANSCODING noinst_PROGRAMS += mgcp_transcoding_test endif mgcp_test_SOURCES = mgcp_test.c mgcp_test_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ $(top_builddir)/src/libmgcp/libmgcp.a \ $(top_builddir)/src/libcommon/libcommon.a \ $(LIBOSMOCORE_LIBS) -lrt -lm $(LIBOSMOSCCP_LIBS) $(LIBOSMOVTY_LIBS) \ $(LIBRARY_DL) $(LIBOSMONETIF_LIBS) mgcp_transcoding_test_SOURCES = mgcp_transcoding_test.c mgcp_transcoding_test_LDADD = \ $(top_builddir)/src/libbsc/libbsc.a \ $(top_builddir)/src/libmgcp/libmgcp.a \ $(top_builddir)/src/libcommon/libcommon.a \ $(LIBOSMOCORE_LIBS) $(LIBBCG729_LIBS) -lrt -lm $(LIBOSMOSCCP_LIBS) $(LIBOSMOVTY_LIBS) \ $(LIBRARY_DL) $(LIBOSMONETIF_LIBS) $(LIBRARY_GSM) openbsc-0.15.0/openbsc/tests/mgcp/mgcp_test.c000066400000000000000000001061511265565154000211150ustar00rootroot00000000000000/* * (C) 2011-2012,2014 by Holger Hans Peter Freyther * (C) 2011-2012,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 . */ #undef _GNU_SOURCE #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include char *strline_r(char *str, char **saveptr); const char *strline_test_data = "one CR\r" "two CR\r" "\r" "one CRLF\r\n" "two CRLF\r\n" "\r\n" "one LF\n" "two LF\n" "\n" "mixed (4 lines)\r\r\n\n\r\n"; #define EXPECTED_NUMBER_OF_LINES 13 static void test_strline(void) { char *save = NULL; char *line; char buf[2048]; int counter = 0; strncpy(buf, strline_test_data, sizeof(buf)); for (line = strline_r(buf, &save); line; line = strline_r(NULL, &save)) { printf("line: '%s'\n", line); counter++; } OSMO_ASSERT(counter == EXPECTED_NUMBER_OF_LINES); } #define AUEP1 "AUEP 158663169 ds/e1-1/2@172.16.6.66 MGCP 1.0\r\n" #define AUEP1_RET "200 158663169 OK\r\n" #define AUEP2 "AUEP 18983213 ds/e1-2/1@172.16.6.66 MGCP 1.0\r\n" #define AUEP2_RET "500 18983213 FAIL\r\n" #define EMPTY "\r\n" #define EMPTY_RET NULL #define SHORT "CRCX \r\n" #define SHORT_RET "510 000000 FAIL\r\n" #define MDCX_WRONG_EP "MDCX 18983213 ds/e1-3/1@172.16.6.66 MGCP 1.0\r\n" #define MDCX_ERR_RET "510 18983213 FAIL\r\n" #define MDCX_UNALLOCATED "MDCX 18983214 ds/e1-1/2@172.16.6.66 MGCP 1.0\r\n" #define MDCX_RET "400 18983214 FAIL\r\n" #define MDCX3 "MDCX 18983215 1@mgw MGCP 1.0\r\n" #define MDCX3_RET "200 18983215 OK\r\n" \ "I: 1\n" \ "\n" \ "v=0\r\n" \ "o=- 1 23 IN IP4 0.0.0.0\r\n" \ "s=-\r\n" \ "c=IN IP4 0.0.0.0\r\n" \ "t=0 0\r\n" \ "m=audio 0 RTP/AVP 126\r\n" \ "a=rtpmap:126 AMR/8000\r\n" \ "a=ptime:20\r\n" #define MDCX3_FMTP_RET "200 18983215 OK\r\n" \ "I: 3\n" \ "\n" \ "v=0\r\n" \ "o=- 3 23 IN IP4 0.0.0.0\r\n" \ "s=-\r\n" \ "c=IN IP4 0.0.0.0\r\n" \ "t=0 0\r\n" \ "m=audio 0 RTP/AVP 126\r\n" \ "a=rtpmap:126 AMR/8000\r\n" \ "a=fmtp:126 0/1/2\r\n" \ "a=ptime:20\r\n" #define MDCX4 "MDCX 18983216 1@mgw MGCP 1.0\r\n" \ "M: sendrecv\r" \ "C: 2\r\n" \ "I: 1\r\n" \ "L: p:20, a:AMR, nt:IN\r\n" \ "\n" \ "v=0\r\n" \ "o=- 1 23 IN IP4 0.0.0.0\r\n" \ "c=IN IP4 0.0.0.0\r\n" \ "t=0 0\r\n" \ "m=audio 4441 RTP/AVP 99\r\n" \ "a=rtpmap:99 AMR/8000\r\n" \ "a=ptime:40\r\n" #define MDCX4_RET(Ident) "200 " Ident " OK\r\n" \ "I: 1\n" \ "\n" \ "v=0\r\n" \ "o=- 1 23 IN IP4 0.0.0.0\r\n" \ "s=-\r\n" \ "c=IN IP4 0.0.0.0\r\n" \ "t=0 0\r\n" \ "m=audio 0 RTP/AVP 126\r\n" \ "a=rtpmap:126 AMR/8000\r\n" \ "a=ptime:20\r\n" #define MDCX4_PT1 "MDCX 18983217 1@mgw MGCP 1.0\r\n" \ "M: sendrecv\r" \ "C: 2\r\n" \ "I: 1\r\n" \ "L: p:20-40, a:AMR, nt:IN\r\n" \ "\n" \ "v=0\r\n" \ "o=- 1 23 IN IP4 0.0.0.0\r\n" \ "c=IN IP4 0.0.0.0\r\n" \ "t=0 0\r\n" \ "m=audio 4441 RTP/AVP 99\r\n" \ "a=rtpmap:99 AMR/8000\r\n" \ "a=ptime:40\r\n" #define MDCX4_PT2 "MDCX 18983218 1@mgw MGCP 1.0\r\n" \ "M: sendrecv\r" \ "C: 2\r\n" \ "I: 1\r\n" \ "L: p:20-20, a:AMR, nt:IN\r\n" \ "\n" \ "v=0\r\n" \ "o=- 1 23 IN IP4 0.0.0.0\r\n" \ "c=IN IP4 0.0.0.0\r\n" \ "t=0 0\r\n" \ "m=audio 4441 RTP/AVP 99\r\n" \ "a=rtpmap:99 AMR/8000\r\n" \ "a=ptime:40\r\n" #define MDCX4_PT3 "MDCX 18983219 1@mgw MGCP 1.0\r\n" \ "M: sendrecv\r" \ "C: 2\r\n" \ "I: 1\r\n" \ "L: a:AMR, nt:IN\r\n" \ "\n" \ "v=0\r\n" \ "o=- 1 23 IN IP4 0.0.0.0\r\n" \ "c=IN IP4 0.0.0.0\r\n" \ "t=0 0\r\n" \ "m=audio 4441 RTP/AVP 99\r\n" \ "a=rtpmap:99 AMR/8000\r\n" \ "a=ptime:40\r\n" #define MDCX4_SO "MDCX 18983220 1@mgw MGCP 1.0\r\n" \ "M: sendonly\r" \ "C: 2\r\n" \ "I: 1\r\n" \ "L: p:20, a:AMR, nt:IN\r\n" \ "\n" \ "v=0\r\n" \ "o=- 1 23 IN IP4 0.0.0.0\r\n" \ "c=IN IP4 0.0.0.0\r\n" \ "t=0 0\r\n" \ "m=audio 4441 RTP/AVP 99\r\n" \ "a=rtpmap:99 AMR/8000\r\n" \ "a=ptime:40\r\n" #define MDCX4_RO "MDCX 18983221 1@mgw MGCP 1.0\r\n" \ "M: recvonly\r" \ "C: 2\r\n" \ "I: 1\r\n" \ "L: p:20, a:AMR, nt:IN\r\n" #define SHORT2 "CRCX 1" #define SHORT2_RET "510 000000 FAIL\r\n" #define SHORT3 "CRCX 1 1@mgw" #define SHORT4 "CRCX 1 1@mgw MGCP" #define SHORT5 "CRCX 1 1@mgw MGCP 1.0" #define CRCX "CRCX 2 1@mgw MGCP 1.0\r\n" \ "M: recvonly\r\n" \ "C: 2\r\n" \ "X\r\n" \ "L: p:20\r\n" \ "\r\n" \ "v=0\r\n" \ "c=IN IP4 123.12.12.123\r\n" \ "m=audio 5904 RTP/AVP 97\r\n" \ "a=rtpmap:97 GSM-EFR/8000\r\n" \ "a=ptime:40\r\n" #define CRCX_RET "200 2 OK\r\n" \ "I: 1\n" \ "\n" \ "v=0\r\n" \ "o=- 1 23 IN IP4 0.0.0.0\r\n" \ "s=-\r\n" \ "c=IN IP4 0.0.0.0\r\n" \ "t=0 0\r\n" \ "m=audio 0 RTP/AVP 126\r\n" \ "a=rtpmap:126 AMR/8000\r\n" \ "a=ptime:20\r\n" #define CRCX_RET_NO_RTPMAP "200 2 OK\r\n" \ "I: 1\n" \ "\n" \ "v=0\r\n" \ "o=- 1 23 IN IP4 0.0.0.0\r\n" \ "s=-\r\n" \ "c=IN IP4 0.0.0.0\r\n" \ "t=0 0\r\n" \ "m=audio 0 RTP/AVP 126\r\n" \ "a=ptime:20\r\n" #define CRCX_FMTP_RET "200 2 OK\r\n" \ "I: 3\n" \ "\n" \ "v=0\r\n" \ "o=- 3 23 IN IP4 0.0.0.0\r\n" \ "s=-\r\n" \ "c=IN IP4 0.0.0.0\r\n" \ "t=0 0\r\n" \ "m=audio 0 RTP/AVP 126\r\n" \ "a=rtpmap:126 AMR/8000\r\n" \ "a=fmtp:126 0/1/2\r\n" \ "a=ptime:20\r\n" #define CRCX_ZYN "CRCX 2 1@mgw MGCP 1.0\r" \ "M: recvonly\r" \ "C: 2\r\r" \ "v=0\r" \ "c=IN IP4 123.12.12.123\r" \ "m=audio 5904 RTP/AVP 97\r" \ "a=rtpmap:97 GSM-EFR/8000\r" #define CRCX_ZYN_RET "200 2 OK\r\n" \ "I: 2\n" \ "\n" \ "v=0\r\n" \ "o=- 2 23 IN IP4 0.0.0.0\r\n" \ "s=-\r\n" \ "c=IN IP4 0.0.0.0\r\n" \ "t=0 0\r\n" \ "m=audio 0 RTP/AVP 126\r\n" \ "a=rtpmap:126 AMR/8000\r\n" \ "a=ptime:20\r\n" #define DLCX "DLCX 7 1@mgw MGCP 1.0\r\n" \ "C: 2\r\n" #define DLCX_RET "250 7 OK\r\n" \ "P: PS=0, OS=0, PR=0, OR=0, PL=0, JI=0\r\n" \ "X-Osmo-CP: EC TIS=0, TOS=0, TIR=0, TOR=0\r\n" #define RQNT "RQNT 186908780 1@mgw MGCP 1.0\r\n" \ "X: B244F267488\r\n" \ "S: D/9\r\n" #define RQNT2 "RQNT 186908781 1@mgw MGCP 1.0\r\n" \ "X: ADD4F26746F\r\n" \ "R: D/[0-9#*](N), G/ft, fxr/t38\r\n" #define RQNT1_RET "200 186908780 OK\r\n" #define RQNT2_RET "200 186908781 OK\r\n" #define PTYPE_IGNORE 0 /* == default initializer */ #define PTYPE_NONE 128 #define PTYPE_NYI PTYPE_NONE #define CRCX_MULT_1 "CRCX 2 1@mgw MGCP 1.0\r\n" \ "M: recvonly\r\n" \ "C: 2\r\n" \ "X\r\n" \ "L: p:20\r\n" \ "\r\n" \ "v=0\r\n" \ "c=IN IP4 123.12.12.123\r\n" \ "m=audio 5904 RTP/AVP 18 97\r\n"\ "a=rtpmap:18 G729/8000\r\n" \ "a=rtpmap:97 GSM-EFR/8000\r\n" \ "a=ptime:40\r\n" #define CRCX_MULT_2 "CRCX 2 2@mgw MGCP 1.0\r\n" \ "M: recvonly\r\n" \ "C: 2\r\n" \ "X\r\n" \ "L: p:20\r\n" \ "\r\n" \ "v=0\r\n" \ "c=IN IP4 123.12.12.123\r\n" \ "m=audio 5904 RTP/AVP 18 97 101\r\n"\ "a=rtpmap:18 G729/8000\r\n" \ "a=rtpmap:97 GSM-EFR/8000\r\n" \ "a=rtpmap:101 FOO/8000\r\n" \ "a=ptime:40\r\n" #define CRCX_MULT_3 "CRCX 2 3@mgw MGCP 1.0\r\n" \ "M: recvonly\r\n" \ "C: 2\r\n" \ "X\r\n" \ "L: p:20\r\n" \ "\r\n" \ "v=0\r\n" \ "c=IN IP4 123.12.12.123\r\n" \ "m=audio 5904 RTP/AVP\r\n" \ "a=rtpmap:18 G729/8000\r\n" \ "a=rtpmap:97 GSM-EFR/8000\r\n" \ "a=rtpmap:101 FOO/8000\r\n" \ "a=ptime:40\r\n" #define CRCX_MULT_4 "CRCX 2 4@mgw MGCP 1.0\r\n" \ "M: recvonly\r\n" \ "C: 2\r\n" \ "X\r\n" \ "L: p:20\r\n" \ "\r\n" \ "v=0\r\n" \ "c=IN IP4 123.12.12.123\r\n" \ "m=audio 5904 RTP/AVP 18\r\n" \ "a=rtpmap:18 G729/8000\r\n" \ "a=rtpmap:97 GSM-EFR/8000\r\n" \ "a=rtpmap:101 FOO/8000\r\n" \ "a=ptime:40\r\n" #define CRCX_MULT_GSM_EXACT \ "CRCX 259260421 5@mgw MGCP 1.0\r\n" \ "C: 1355c6041e\r\n" \ "I: 3\r\n" \ "L: p:20, a:GSM, nt:IN\r\n" \ "M: recvonly\r\n" \ "\r\n" \ "v=0\r\n" \ "o=- 1439038275 1439038275 IN IP4 192.168.181.247\r\n" \ "s=-\r\nc=IN IP4 192.168.181.247\r\n" \ "t=0 0\r\nm=audio 29084 RTP/AVP 255 0 8 3 18 4 96 97 101\r\n" \ "a=rtpmap:0 PCMU/8000\r\n" \ "a=rtpmap:8 PCMA/8000\r\n" \ "a=rtpmap:3 gsm/8000\r\n" \ "a=rtpmap:18 G729/8000\r\n" \ "a=fmtp:18 annexb=no\r\n" \ "a=rtpmap:4 G723/8000\r\n" \ "a=rtpmap:96 iLBC/8000\r\n" \ "a=fmtp:96 mode=20\r\n" \ "a=rtpmap:97 iLBC/8000\r\n" \ "a=fmtp:97 mode=30\r\n" \ "a=rtpmap:101 telephone-event/8000\r\n" \ "a=fmtp:101 0-15\r\n" \ "a=recvonly\r\n" #define MDCX_NAT_DUMMY \ "MDCX 23 5@mgw MGCP 1.0\r\n" \ "C: 1355c6041e\r\n" \ "\r\n" \ "c=IN IP4 8.8.8.8\r\n" \ "m=audio 16434 RTP/AVP 255\r\n" struct mgcp_test { const char *name; const char *req; const char *exp_resp; int exp_net_ptype; int exp_bts_ptype; const char *extra_fmtp; }; static const struct mgcp_test tests[] = { { "AUEP1", AUEP1, AUEP1_RET }, { "AUEP2", AUEP2, AUEP2_RET }, { "MDCX1", MDCX_WRONG_EP, MDCX_ERR_RET }, { "MDCX2", MDCX_UNALLOCATED, MDCX_RET }, { "CRCX", CRCX, CRCX_RET, 97, 126 }, { "MDCX3", MDCX3, MDCX3_RET, PTYPE_NONE, 126 }, { "MDCX4", MDCX4, MDCX4_RET("18983216"), 99, 126 }, { "MDCX4_PT1", MDCX4_PT1, MDCX4_RET("18983217"), 99, 126 }, { "MDCX4_PT2", MDCX4_PT2, MDCX4_RET("18983218"), 99, 126 }, { "MDCX4_PT3", MDCX4_PT3, MDCX4_RET("18983219"), 99, 126 }, { "MDCX4_SO", MDCX4_SO, MDCX4_RET("18983220"), 99, 126 }, { "MDCX4_RO", MDCX4_RO, MDCX4_RET("18983221"), PTYPE_IGNORE, 126 }, { "DLCX", DLCX, DLCX_RET, -1, -1 }, { "CRCX_ZYN", CRCX_ZYN, CRCX_ZYN_RET, 97, 126 }, { "EMPTY", EMPTY, EMPTY_RET }, { "SHORT1", SHORT, SHORT_RET }, { "SHORT2", SHORT2, SHORT2_RET }, { "SHORT3", SHORT3, SHORT2_RET }, { "SHORT4", SHORT4, SHORT2_RET }, { "RQNT1", RQNT, RQNT1_RET }, { "RQNT2", RQNT2, RQNT2_RET }, { "DLCX", DLCX, DLCX_RET, -1, -1 }, { "CRCX", CRCX, CRCX_FMTP_RET, 97, 126, .extra_fmtp = "a=fmtp:126 0/1/2" }, { "MDCX3", MDCX3, MDCX3_FMTP_RET, PTYPE_NONE, 126 , .extra_fmtp = "a=fmtp:126 0/1/2" }, { "DLCX", DLCX, DLCX_RET, -1, -1 , .extra_fmtp = "a=fmtp:126 0/1/2" }, }; static const struct mgcp_test retransmit[] = { { "CRCX", CRCX, CRCX_RET }, { "RQNT1", RQNT, RQNT1_RET }, { "RQNT2", RQNT2, RQNT2_RET }, { "MDCX3", MDCX3, MDCX3_RET }, { "DLCX", DLCX, DLCX_RET }, }; static struct msgb *create_msg(const char *str) { struct msgb *msg; msg = msgb_alloc_headroom(4096, 128, "MGCP msg"); int len = sprintf((char *)msg->data, "%s", str); msg->l2h = msgb_put(msg, len); return msg; } static int last_endpoint = -1; static int mgcp_test_policy_cb(struct mgcp_trunk_config *cfg, int endpoint, int state, const char *transactio_id) { fprintf(stderr, "Policy CB got state %d on endpoint %d\n", state, endpoint); last_endpoint = endpoint; return MGCP_POLICY_CONT; } #define MGCP_DUMMY_LOAD 0x23 static int dummy_packets = 0; /* override and forward */ ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen) { typedef ssize_t (*sendto_t)(int, const void *, size_t, int, const struct sockaddr *, socklen_t); static sendto_t real_sendto = NULL; uint32_t dest_host = htonl(((struct sockaddr_in *)dest_addr)->sin_addr.s_addr); int dest_port = htons(((struct sockaddr_in *)dest_addr)->sin_port); if (!real_sendto) real_sendto = dlsym(RTLD_NEXT, "sendto"); if (len == 1 && ((const char *)buf)[0] == MGCP_DUMMY_LOAD ) { fprintf(stderr, "Dummy packet to 0x%08x:%d, msg length %zu\n%s\n\n", dest_host, dest_port, len, osmo_hexdump(buf, len)); dummy_packets += 1; } return real_sendto(sockfd, buf, len, flags, dest_addr, addrlen); } static int64_t force_monotonic_time_us = -1; /* override and forward */ int clock_gettime(clockid_t clk_id, struct timespec *tp) { typedef int (*clock_gettime_t)(clockid_t clk_id, struct timespec *tp); static clock_gettime_t real_clock_gettime = NULL; if (!real_clock_gettime) real_clock_gettime = dlsym(RTLD_NEXT, "clock_gettime"); if (clk_id == CLOCK_MONOTONIC && force_monotonic_time_us >= 0) { tp->tv_sec = force_monotonic_time_us / 1000000; tp->tv_nsec = (force_monotonic_time_us % 1000000) * 1000; return 0; } return real_clock_gettime(clk_id, tp); } #define CONN_UNMODIFIED (0x1000) static void test_values(void) { /* Check that NONE disables all output */ OSMO_ASSERT((MGCP_CONN_NONE & MGCP_CONN_RECV_SEND) == 0) /* Check that LOOPBACK enables all output */ OSMO_ASSERT((MGCP_CONN_LOOPBACK & MGCP_CONN_RECV_SEND) == MGCP_CONN_RECV_SEND) } static void test_messages(void) { struct mgcp_config *cfg; struct mgcp_endpoint *endp; int i; cfg = mgcp_config_alloc(); cfg->trunk.number_endpoints = 64; mgcp_endpoints_allocate(&cfg->trunk); cfg->policy_cb = mgcp_test_policy_cb; mgcp_endpoints_allocate(mgcp_trunk_alloc(cfg, 1)); /* reset endpoints */ for (i = 0; i < cfg->trunk.number_endpoints; i++) { endp = &cfg->trunk.endpoints[i]; endp->net_end.codec.payload_type = PTYPE_NONE; endp->net_end.packet_duration_ms = -1; OSMO_ASSERT(endp->conn_mode == MGCP_CONN_NONE); endp->conn_mode |= CONN_UNMODIFIED; } for (i = 0; i < ARRAY_SIZE(tests); i++) { const struct mgcp_test *t = &tests[i]; struct msgb *inp; struct msgb *msg; printf("Testing %s\n", t->name); last_endpoint = -1; dummy_packets = 0; bsc_replace_string(cfg, &cfg->trunk.audio_fmtp_extra, t->extra_fmtp); inp = create_msg(t->req); msg = mgcp_handle_message(cfg, inp); msgb_free(inp); if (!t->exp_resp) { if (msg) printf("%s failed '%s'\n", t->name, (char *) msg->data); } else if (strcmp((char *) msg->data, t->exp_resp) != 0) printf("%s failed '%s'\n", t->name, (char *) msg->data); msgb_free(msg); if (dummy_packets) printf("Dummy packets: %d\n", dummy_packets); if (last_endpoint != -1) { endp = &cfg->trunk.endpoints[last_endpoint]; if (endp->net_end.packet_duration_ms != -1) printf("Detected packet duration: %d\n", endp->net_end.packet_duration_ms); else printf("Packet duration not set\n"); if (endp->local_options.pkt_period_min || endp->local_options.pkt_period_max) printf("Requested packetetization period: " "%d-%d\n", endp->local_options.pkt_period_min, endp->local_options.pkt_period_max); else printf("Requested packetization period not set\n"); if ((endp->conn_mode & CONN_UNMODIFIED) == 0) { printf("Connection mode: %d:%s%s%s%s\n", endp->conn_mode, !endp->conn_mode ? " NONE" : "", endp->conn_mode & MGCP_CONN_SEND_ONLY ? " SEND" : "", endp->conn_mode & MGCP_CONN_RECV_ONLY ? " RECV" : "", endp->conn_mode & MGCP_CONN_LOOPBACK & ~MGCP_CONN_RECV_SEND ? " LOOP" : ""); fprintf(stderr, "BTS output %sabled, NET output %sabled\n", endp->bts_end.output_enabled ? "en" : "dis", endp->net_end.output_enabled ? "en" : "dis"); } else printf("Connection mode not set\n"); OSMO_ASSERT(endp->net_end.output_enabled == (endp->conn_mode & MGCP_CONN_SEND_ONLY ? 1 : 0)); OSMO_ASSERT(endp->bts_end.output_enabled == (endp->conn_mode & MGCP_CONN_RECV_ONLY ? 1 : 0)); endp->net_end.packet_duration_ms = -1; endp->local_options.pkt_period_min = 0; endp->local_options.pkt_period_max = 0; endp->conn_mode |= CONN_UNMODIFIED; } /* Check detected payload type */ if (t->exp_net_ptype != PTYPE_IGNORE || t->exp_bts_ptype != PTYPE_IGNORE) { OSMO_ASSERT(last_endpoint != -1); endp = &cfg->trunk.endpoints[last_endpoint]; fprintf(stderr, "endpoint %d: " "payload type BTS %d (exp %d), NET %d (exp %d)\n", last_endpoint, endp->bts_end.codec.payload_type, t->exp_bts_ptype, endp->net_end.codec.payload_type, t->exp_net_ptype); if (t->exp_bts_ptype != PTYPE_IGNORE) OSMO_ASSERT(endp->bts_end.codec.payload_type == t->exp_bts_ptype); if (t->exp_net_ptype != PTYPE_IGNORE) OSMO_ASSERT(endp->net_end.codec.payload_type == t->exp_net_ptype); /* Reset them again for next test */ endp->net_end.codec.payload_type = PTYPE_NONE; } } talloc_free(cfg); } static void test_retransmission(void) { struct mgcp_config *cfg; int i; cfg = mgcp_config_alloc(); cfg->trunk.number_endpoints = 64; mgcp_endpoints_allocate(&cfg->trunk); mgcp_endpoints_allocate(mgcp_trunk_alloc(cfg, 1)); /* reset endpoints */ for (i = 0; i < cfg->trunk.number_endpoints; i++) { struct mgcp_endpoint *endp; endp = &cfg->trunk.endpoints[i]; endp->bts_end.packet_duration_ms = 20; } for (i = 0; i < ARRAY_SIZE(retransmit); i++) { const struct mgcp_test *t = &retransmit[i]; struct msgb *inp; struct msgb *msg; printf("Testing %s\n", t->name); inp = create_msg(t->req); msg = mgcp_handle_message(cfg, inp); msgb_free(inp); if (strcmp((char *) msg->data, t->exp_resp) != 0) printf("%s failed '%s'\n", t->name, (char *) msg->data); msgb_free(msg); /* Retransmit... */ printf("Re-transmitting %s\n", t->name); inp = create_msg(t->req); msg = mgcp_handle_message(cfg, inp); msgb_free(inp); if (strcmp((char *) msg->data, t->exp_resp) != 0) printf("%s failed '%s'\n", t->name, (char *) msg->data); msgb_free(msg); } talloc_free(cfg); } static int rqnt_cb(struct mgcp_endpoint *endp, char _tone) { ptrdiff_t tone = _tone; endp->cfg->data = (void *) tone; return 0; } static void test_rqnt_cb(void) { struct mgcp_config *cfg; struct msgb *inp, *msg; cfg = mgcp_config_alloc(); cfg->rqnt_cb = rqnt_cb; cfg->trunk.number_endpoints = 64; mgcp_endpoints_allocate(&cfg->trunk); mgcp_endpoints_allocate(mgcp_trunk_alloc(cfg, 1)); inp = create_msg(CRCX); msgb_free(mgcp_handle_message(cfg, inp)); msgb_free(inp); /* send the RQNT and check for the CB */ inp = create_msg(RQNT); msg = mgcp_handle_message(cfg, inp); if (strncmp((const char *) msg->l2h, "200", 3) != 0) { printf("FAILED: message is not 200. '%s'\n", msg->l2h); abort(); } if (cfg->data != (void *) '9') { printf("FAILED: callback not called: %p\n", cfg->data); abort(); } msgb_free(msg); msgb_free(inp); inp = create_msg(DLCX); msgb_free(mgcp_handle_message(cfg, inp)); msgb_free(inp); talloc_free(cfg); } struct pl_test { int cycles; uint16_t base_seq; uint16_t max_seq; uint32_t packets; uint32_t expected; int loss; }; static const struct pl_test pl_test_dat[] = { /* basic.. just one package */ { .cycles = 0, .base_seq = 0, .max_seq = 0, .packets = 1, .expected = 1, .loss = 0}, /* some packages and a bit of loss */ { .cycles = 0, .base_seq = 0, .max_seq = 100, .packets = 100, .expected = 101, .loss = 1}, /* wrap around */ { .cycles = 1<<16, .base_seq = 0xffff, .max_seq = 2, .packets = 4, .expected = 4, .loss = 0}, /* min loss */ { .cycles = 0, .base_seq = 0, .max_seq = 0, .packets = UINT_MAX, .expected = 1, .loss = INT_MIN }, /* max loss, with wrap around on expected max */ { .cycles = INT_MAX, .base_seq = 0, .max_seq = UINT16_MAX, .packets = 0, .expected = ((uint32_t)(INT_MAX) + UINT16_MAX + 1), .loss = INT_MAX }, }; static void test_packet_loss_calc(void) { int i; printf("Testing packet loss calculation.\n"); for (i = 0; i < ARRAY_SIZE(pl_test_dat); ++i) { uint32_t expected; int loss; struct mgcp_rtp_state state; struct mgcp_rtp_end rtp; memset(&state, 0, sizeof(state)); memset(&rtp, 0, sizeof(rtp)); state.stats_initialized = 1; state.stats_base_seq = pl_test_dat[i].base_seq; state.stats_max_seq = pl_test_dat[i].max_seq; state.stats_cycles = pl_test_dat[i].cycles; rtp.packets = pl_test_dat[i].packets; mgcp_state_calc_loss(&state, &rtp, &expected, &loss); if (loss != pl_test_dat[i].loss || expected != pl_test_dat[i].expected) { printf("FAIL: Wrong exp/loss at idx(%d) Loss(%d vs. %d) Exp(%u vs. %u)\n", i, loss, pl_test_dat[i].loss, expected, pl_test_dat[i].expected); } } } static void test_mgcp_stats(void) { printf("Testing stat parsing\n"); uint32_t bps, bos, pr, _or, jitter; struct msgb *msg; int loss; int rc; msg = create_msg(DLCX_RET); rc = mgcp_parse_stats(msg, &bps, &bos, &pr, &_or, &loss, &jitter); printf("Parsing result: %d\n", rc); if (bps != 0 || bos != 0 || pr != 0 || _or != 0 || loss != 0 || jitter != 0) printf("FAIL: Parsing failed1.\n"); msgb_free(msg); msg = create_msg("250 7 OK\r\nP: PS=10, OS=20, PR=30, OR=40, PL=-3, JI=40\r\n"); rc = mgcp_parse_stats(msg, &bps, &bos, &pr, &_or, &loss, &jitter); printf("Parsing result: %d\n", rc); if (bps != 10 || bos != 20 || pr != 30 || _or != 40 || loss != -3 || jitter != 40) printf("FAIL: Parsing failed2.\n"); msgb_free(msg); } struct rtp_packet_info { float txtime; int len; char *data; }; struct rtp_packet_info test_rtp_packets1[] = { /* RTP: SeqNo=0, TS=0 */ {0.000000, 20, "\x80\x62\x00\x00\x00\x00\x00\x00\x11\x22\x33\x44" "\x01\x23\x45\x67\x89\xAB\xCD\xEF"}, /* RTP: SeqNo=1, TS=160 */ {0.020000, 20, "\x80\x62\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44" "\x01\x23\x45\x67\x89\xAB\xCD\xEF"}, /* RTP: SeqNo=2, TS=320 */ {0.040000, 20, "\x80\x62\x00\x02\x00\x00\x01\x40\x11\x22\x33\x44" "\x01\x23\x45\x67\x89\xAB\xCD\xEF"}, /* Repeat RTP timestamp: */ /* RTP: SeqNo=3, TS=320 */ {0.060000, 20, "\x80\x62\x00\x03\x00\x00\x01\x40\x11\x22\x33\x44" "\x01\x23\x45\x67\x89\xAB\xCD\xEF"}, /* RTP: SeqNo=4, TS=480 */ {0.080000, 20, "\x80\x62\x00\x04\x00\x00\x01\xE0\x11\x22\x33\x44" "\x01\x23\x45\x67\x89\xAB\xCD\xEF"}, /* RTP: SeqNo=5, TS=640 */ {0.100000, 20, "\x80\x62\x00\x05\x00\x00\x02\x80\x11\x22\x33\x44" "\x01\x23\x45\x67\x89\xAB\xCD\xEF"}, /* Double skip RTP timestamp (delta = 2*160): */ /* RTP: SeqNo=6, TS=960 */ {0.120000, 20, "\x80\x62\x00\x06\x00\x00\x03\xC0\x11\x22\x33\x44" "\x01\x23\x45\x67\x89\xAB\xCD\xEF"}, /* RTP: SeqNo=7, TS=1120 */ {0.140000, 20, "\x80\x62\x00\x07\x00\x00\x04\x60\x11\x22\x33\x44" "\x01\x23\x45\x67\x89\xAB\xCD\xEF"}, /* RTP: SeqNo=8, TS=1280 */ {0.160000, 20, "\x80\x62\x00\x08\x00\x00\x05\x00\x11\x22\x33\x44" "\x01\x23\x45\x67\x89\xAB\xCD\xEF"}, /* Non 20ms RTP timestamp (delta = 120): */ /* RTP: SeqNo=9, TS=1400 */ {0.180000, 20, "\x80\x62\x00\x09\x00\x00\x05\x78\x11\x22\x33\x44" "\x01\x23\x45\x67\x89\xAB\xCD\xEF"}, /* RTP: SeqNo=10, TS=1560 */ {0.200000, 20, "\x80\x62\x00\x0A\x00\x00\x06\x18\x11\x22\x33\x44" "\x01\x23\x45\x67\x89\xAB\xCD\xEF"}, /* RTP: SeqNo=11, TS=1720 */ {0.220000, 20, "\x80\x62\x00\x0B\x00\x00\x06\xB8\x11\x22\x33\x44" "\x01\x23\x45\x67\x89\xAB\xCD\xEF"}, /* SSRC changed to 0x10203040, RTP timestamp jump */ /* RTP: SeqNo=12, TS=34688 */ {0.240000, 20, "\x80\x62\x00\x0C\x00\x00\x87\x80\x10\x20\x30\x40" "\x01\x23\x45\x67\x89\xAB\xCD\xEF"}, /* RTP: SeqNo=13, TS=34848 */ {0.260000, 20, "\x80\x62\x00\x0D\x00\x00\x88\x20\x10\x20\x30\x40" "\x01\x23\x45\x67\x89\xAB\xCD\xEF"}, /* RTP: SeqNo=14, TS=35008 */ {0.280000, 20, "\x80\x62\x00\x0E\x00\x00\x88\xC0\x10\x20\x30\x40" "\x01\x23\x45\x67\x89\xAB\xCD\xEF"}, /* Non 20ms RTP timestamp (delta = 120): */ /* RTP: SeqNo=15, TS=35128 */ {0.300000, 20, "\x80\x62\x00\x0F\x00\x00\x89\x38\x10\x20\x30\x40" "\x01\x23\x45\x67\x89\xAB\xCD\xEF"}, /* RTP: SeqNo=16, TS=35288 */ {0.320000, 20, "\x80\x62\x00\x10\x00\x00\x89\xD8\x10\x20\x30\x40" "\x01\x23\x45\x67\x89\xAB\xCD\xEF"}, /* RTP: SeqNo=17, TS=35448 */ {0.340000, 20, "\x80\x62\x00\x11\x00\x00\x8A\x78\x10\x20\x30\x40" "\x01\x23\x45\x67\x8A\xAB\xCD\xEF"}, /* SeqNo increment by 2, RTP timestamp delta = 320: */ /* RTP: SeqNo=19, TS=35768 */ {0.360000, 20, "\x80\x62\x00\x13\x00\x00\x8B\xB8\x10\x20\x30\x40" "\x01\x23\x45\x67\x89\xAB\xCD\xEF"}, /* RTP: SeqNo=20, TS=35928 */ {0.380000, 20, "\x80\x62\x00\x14\x00\x00\x8C\x58\x10\x20\x30\x40" "\x01\x23\x45\x67\x89\xAB\xCD\xEF"}, /* RTP: SeqNo=21, TS=36088 */ {0.380000, 20, "\x80\x62\x00\x15\x00\x00\x8C\xF8\x10\x20\x30\x40" "\x01\x23\x45\x67\x89\xAB\xCD\xEF"}, /* Repeat last packet */ /* RTP: SeqNo=21, TS=36088 */ {0.400000, 20, "\x80\x62\x00\x15\x00\x00\x8C\xF8\x10\x20\x30\x40" "\x01\x23\x45\x67\x89\xAB\xCD\xEF"}, /* RTP: SeqNo=22, TS=36248 */ {0.420000, 20, "\x80\x62\x00\x16\x00\x00\x8D\x98\x10\x20\x30\x40" "\x01\x23\x45\x67\x89\xAB\xCD\xEF"}, /* RTP: SeqNo=23, TS=36408 */ {0.440000, 20, "\x80\x62\x00\x17\x00\x00\x8E\x38\x10\x20\x30\x40" "\x01\x23\x45\x67\x89\xAB\xCD\xEF"}, /* Don't increment SeqNo but increment timestamp by 160 */ /* RTP: SeqNo=23, TS=36568 */ {0.460000, 20, "\x80\x62\x00\x17\x00\x00\x8E\xD8\x10\x20\x30\x40" "\x01\x23\x45\x67\x89\xAB\xCD\xEF"}, /* RTP: SeqNo=24, TS=36728 */ {0.480000, 20, "\x80\x62\x00\x18\x00\x00\x8F\x78\x10\x20\x30\x40" "\x01\x23\x45\x67\x89\xAB\xCD\xEF"}, /* RTP: SeqNo=25, TS=36888 */ {0.500000, 20, "\x80\x62\x00\x19\x00\x00\x90\x18\x10\x20\x30\x40" "\x01\x23\x45\x67\x89\xAB\xCD\xEF"}, /* SSRC changed to 0x50607080, RTP timestamp jump, Delay of 1.5s, * SeqNo jump */ /* RTP: SeqNo=1000, TS=160000 */ {2.000000, 20, "\x80\x62\x03\xE8\x00\x02\x71\x00\x50\x60\x70\x80" "\x01\x23\x45\x67\x89\xAB\xCD\xEF"}, /* RTP: SeqNo=1001, TS=160160 */ {2.020000, 20, "\x80\x62\x03\xE9\x00\x02\x71\xA0\x50\x60\x70\x80" "\x01\x23\x45\x67\x89\xAB\xCD\xEF"}, /* RTP: SeqNo=1002, TS=160320 */ {2.040000, 20, "\x80\x62\x03\xEA\x00\x02\x72\x40\x50\x60\x70\x80" "\x01\x23\x45\x67\x89\xAB\xCD\xEF"}, }; void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *state, struct mgcp_rtp_end *rtp_end, struct sockaddr_in *addr, char *data, int len); static void test_packet_error_detection(int patch_ssrc, int patch_ts) { int i; struct mgcp_trunk_config trunk; struct mgcp_endpoint endp; struct mgcp_rtp_state state; struct mgcp_rtp_end *rtp = &endp.net_end; struct sockaddr_in addr = {0}; char buffer[4096]; uint32_t last_ssrc = 0; uint32_t last_timestamp = 0; uint32_t last_seqno = 0; int last_in_ts_err_cnt = 0; int last_out_ts_err_cnt = 0; printf("Testing packet error detection%s%s.\n", patch_ssrc ? ", patch SSRC" : "", patch_ts ? ", patch timestamps" : ""); memset(&trunk, 0, sizeof(trunk)); memset(&endp, 0, sizeof(endp)); memset(&state, 0, sizeof(state)); trunk.number_endpoints = 1; trunk.endpoints = &endp; trunk.force_constant_ssrc = patch_ssrc; trunk.force_aligned_timing = patch_ts; endp.tcfg = &trunk; mgcp_initialize_endp(&endp); rtp->codec.payload_type = 98; for (i = 0; i < ARRAY_SIZE(test_rtp_packets1); ++i) { struct rtp_packet_info *info = test_rtp_packets1 + i; force_monotonic_time_us = round(1000000.0 * info->txtime); OSMO_ASSERT(info->len <= sizeof(buffer)); OSMO_ASSERT(info->len >= 0); memmove(buffer, info->data, info->len); mgcp_rtp_end_config(&endp, 1, rtp); mgcp_patch_and_count(&endp, &state, rtp, &addr, buffer, info->len); if (state.out_stream.ssrc != last_ssrc) { printf("Output SSRC changed to %08x\n", state.out_stream.ssrc); last_ssrc = state.out_stream.ssrc; } printf("In TS: %d, dTS: %d, Seq: %d\n", state.in_stream.last_timestamp, state.in_stream.last_tsdelta, state.in_stream.last_seq); printf("Out TS change: %d, dTS: %d, Seq change: %d, " "TS Err change: in %+d, out %+d\n", state.out_stream.last_timestamp - last_timestamp, state.out_stream.last_tsdelta, state.out_stream.last_seq - last_seqno, state.in_stream.err_ts_counter - last_in_ts_err_cnt, state.out_stream.err_ts_counter - last_out_ts_err_cnt); printf("Stats: Jitter = %u, Transit = %d\n", mgcp_state_calc_jitter(&state), state.stats_transit); last_in_ts_err_cnt = state.in_stream.err_ts_counter; last_out_ts_err_cnt = state.out_stream.err_ts_counter; last_timestamp = state.out_stream.last_timestamp; last_seqno = state.out_stream.last_seq; } force_monotonic_time_us = -1; } static void test_multilple_codec(void) { struct mgcp_config *cfg; struct mgcp_endpoint *endp; struct msgb *inp, *resp; struct in_addr addr; printf("Testing multiple payload types\n"); cfg = mgcp_config_alloc(); cfg->trunk.number_endpoints = 64; mgcp_endpoints_allocate(&cfg->trunk); cfg->policy_cb = mgcp_test_policy_cb; mgcp_endpoints_allocate(mgcp_trunk_alloc(cfg, 1)); /* Allocate endpoint 1@mgw with two codecs */ last_endpoint = -1; inp = create_msg(CRCX_MULT_1); resp = mgcp_handle_message(cfg, inp); msgb_free(inp); msgb_free(resp); OSMO_ASSERT(last_endpoint == 1); endp = &cfg->trunk.endpoints[last_endpoint]; OSMO_ASSERT(endp->net_end.codec.payload_type == 18); OSMO_ASSERT(endp->net_end.alt_codec.payload_type == 97); /* Allocate 2@mgw with three codecs, last one ignored */ last_endpoint = -1; inp = create_msg(CRCX_MULT_2); resp = mgcp_handle_message(cfg, inp); msgb_free(inp); msgb_free(resp); OSMO_ASSERT(last_endpoint == 2); endp = &cfg->trunk.endpoints[last_endpoint]; OSMO_ASSERT(endp->net_end.codec.payload_type == 18); OSMO_ASSERT(endp->net_end.alt_codec.payload_type == 97); /* Allocate 3@mgw with no codecs, check for PT == -1 */ last_endpoint = -1; inp = create_msg(CRCX_MULT_3); resp = mgcp_handle_message(cfg, inp); msgb_free(inp); msgb_free(resp); OSMO_ASSERT(last_endpoint == 3); endp = &cfg->trunk.endpoints[last_endpoint]; OSMO_ASSERT(endp->net_end.codec.payload_type == -1); OSMO_ASSERT(endp->net_end.alt_codec.payload_type == -1); /* Allocate 4@mgw with a single codec */ last_endpoint = -1; inp = create_msg(CRCX_MULT_4); resp = mgcp_handle_message(cfg, inp); msgb_free(inp); msgb_free(resp); OSMO_ASSERT(last_endpoint == 4); endp = &cfg->trunk.endpoints[last_endpoint]; OSMO_ASSERT(endp->net_end.codec.payload_type == 18); OSMO_ASSERT(endp->net_end.alt_codec.payload_type == -1); /* Allocate 5@mgw at select GSM.. */ last_endpoint = -1; inp = create_msg(CRCX_MULT_GSM_EXACT); talloc_free(cfg->trunk.audio_name); cfg->trunk.audio_name = "GSM/8000"; cfg->trunk.no_audio_transcoding = 1; resp = mgcp_handle_message(cfg, inp); msgb_free(inp); msgb_free(resp); OSMO_ASSERT(last_endpoint == 5); endp = &cfg->trunk.endpoints[last_endpoint]; OSMO_ASSERT(endp->net_end.codec.payload_type == 3); OSMO_ASSERT(endp->net_end.alt_codec.payload_type == -1); inp = create_msg(MDCX_NAT_DUMMY); last_endpoint = -1; resp = mgcp_handle_message(cfg, inp); msgb_free(inp); msgb_free(resp); OSMO_ASSERT(last_endpoint == 5); endp = &cfg->trunk.endpoints[last_endpoint]; OSMO_ASSERT(endp->net_end.codec.payload_type == 3); OSMO_ASSERT(endp->net_end.alt_codec.payload_type == -1); OSMO_ASSERT(endp->net_end.rtp_port == htons(16434)); memset(&addr, 0, sizeof(addr)); inet_aton("8.8.8.8", &addr); OSMO_ASSERT(endp->net_end.addr.s_addr == addr.s_addr); /* Check what happens without that flag */ /* Free the previous endpoint and the data ... */ mgcp_release_endp(endp); talloc_free(endp->last_response); talloc_free(endp->last_trans); endp->last_response = endp->last_trans = NULL; last_endpoint = -1; inp = create_msg(CRCX_MULT_GSM_EXACT); cfg->trunk.no_audio_transcoding = 0; resp = mgcp_handle_message(cfg, inp); msgb_free(inp); msgb_free(resp); OSMO_ASSERT(last_endpoint == 5); endp = &cfg->trunk.endpoints[last_endpoint]; OSMO_ASSERT(endp->net_end.codec.payload_type == 255); OSMO_ASSERT(endp->net_end.alt_codec.payload_type == 0); talloc_free(cfg); } static void test_no_cycle(void) { struct mgcp_config *cfg; struct mgcp_endpoint *endp; printf("Testing no sequence flow on initial packet\n"); cfg = mgcp_config_alloc(); cfg->trunk.number_endpoints = 64; mgcp_endpoints_allocate(&cfg->trunk); endp = &cfg->trunk.endpoints[1]; OSMO_ASSERT(endp->net_state.stats_initialized == 0); mgcp_rtp_annex_count(endp, &endp->net_state, 0, 0, 2342); OSMO_ASSERT(endp->net_state.stats_initialized == 1); OSMO_ASSERT(endp->net_state.stats_cycles == 0); OSMO_ASSERT(endp->net_state.stats_max_seq == 0); mgcp_rtp_annex_count(endp, &endp->net_state, 1, 0, 2342); OSMO_ASSERT(endp->net_state.stats_initialized == 1); OSMO_ASSERT(endp->net_state.stats_cycles == 0); OSMO_ASSERT(endp->net_state.stats_max_seq == 1); /* now jump.. */ mgcp_rtp_annex_count(endp, &endp->net_state, UINT16_MAX, 0, 2342); OSMO_ASSERT(endp->net_state.stats_initialized == 1); OSMO_ASSERT(endp->net_state.stats_cycles == 0); OSMO_ASSERT(endp->net_state.stats_max_seq == UINT16_MAX); /* and wrap */ mgcp_rtp_annex_count(endp, &endp->net_state, 0, 0, 2342); OSMO_ASSERT(endp->net_state.stats_initialized == 1); OSMO_ASSERT(endp->net_state.stats_cycles == UINT16_MAX + 1); OSMO_ASSERT(endp->net_state.stats_max_seq == 0); talloc_free(cfg); } static void test_no_name(void) { struct mgcp_config *cfg; struct mgcp_endpoint *endp; struct msgb *inp, *msg; int i; printf("Testing no rtpmap name\n"); cfg = mgcp_config_alloc(); cfg->trunk.number_endpoints = 64; cfg->trunk.audio_send_name = 0; mgcp_endpoints_allocate(&cfg->trunk); cfg->policy_cb = mgcp_test_policy_cb; mgcp_endpoints_allocate(mgcp_trunk_alloc(cfg, 1)); /* reset endpoints */ for (i = 0; i < cfg->trunk.number_endpoints; i++) { endp = &cfg->trunk.endpoints[i]; endp->net_end.codec.payload_type = PTYPE_NONE; endp->net_end.packet_duration_ms = -1; OSMO_ASSERT(endp->conn_mode == MGCP_CONN_NONE); endp->conn_mode |= CONN_UNMODIFIED; } inp = create_msg(CRCX); msg = mgcp_handle_message(cfg, inp); if (strcmp((char *) msg->data, CRCX_RET_NO_RTPMAP) != 0) printf("FAILED: there should not be a RTPMAP: %s\n", (char *) msg->data); msgb_free(inp); msgb_free(msg); mgcp_release_endp(&cfg->trunk.endpoints[1]); talloc_free(cfg); } static void test_osmux_cid(void) { int id, i; OSMO_ASSERT(osmux_used_cid() == 0); id = osmux_get_cid(); OSMO_ASSERT(id == 0); OSMO_ASSERT(osmux_used_cid() == 1); osmux_put_cid(id); OSMO_ASSERT(osmux_used_cid() == 0); for (i = 0; i < 128; ++i) { id = osmux_get_cid(); OSMO_ASSERT(id == i); OSMO_ASSERT(osmux_used_cid() == i + 1); } id = osmux_get_cid(); OSMO_ASSERT(id == -1); for (i = 0; i < 256; ++i) osmux_put_cid(i); OSMO_ASSERT(osmux_used_cid() == 0); } int main(int argc, char **argv) { osmo_init_logging(&log_info); test_strline(); test_values(); test_messages(); test_retransmission(); test_packet_loss_calc(); test_rqnt_cb(); test_mgcp_stats(); test_packet_error_detection(1, 0); test_packet_error_detection(0, 0); test_packet_error_detection(0, 1); test_packet_error_detection(1, 1); test_multilple_codec(); test_no_cycle(); test_no_name(); test_osmux_cid(); printf("Done\n"); return EXIT_SUCCESS; } openbsc-0.15.0/openbsc/tests/mgcp/mgcp_test.ok000066400000000000000000000462451265565154000213130ustar00rootroot00000000000000line: 'one CR' line: 'two CR' line: '' line: 'one CRLF' line: 'two CRLF' line: '' line: 'one LF' line: 'two LF' line: '' line: 'mixed (4 lines)' line: '' line: '' line: '' Testing AUEP1 Testing AUEP2 Testing MDCX1 Testing MDCX2 Testing CRCX Dummy packets: 1 Detected packet duration: 40 Requested packetetization period: 20-20 Connection mode: 1: RECV Testing MDCX3 Dummy packets: 1 Packet duration not set Requested packetization period not set Connection mode not set Testing MDCX4 Dummy packets: 1 Detected packet duration: 40 Requested packetetization period: 20-20 Connection mode: 3: SEND RECV Testing MDCX4_PT1 Dummy packets: 1 Detected packet duration: 40 Requested packetetization period: 20-40 Connection mode: 3: SEND RECV Testing MDCX4_PT2 Dummy packets: 1 Detected packet duration: 40 Requested packetetization period: 20-20 Connection mode: 3: SEND RECV Testing MDCX4_PT3 Dummy packets: 1 Detected packet duration: 40 Requested packetization period not set Connection mode: 3: SEND RECV Testing MDCX4_SO Detected packet duration: 40 Requested packetetization period: 20-20 Connection mode: 2: SEND Testing MDCX4_RO Dummy packets: 1 Packet duration not set Requested packetetization period: 20-20 Connection mode: 1: RECV Testing DLCX Detected packet duration: 20 Requested packetization period not set Connection mode: 0: NONE Testing CRCX_ZYN Dummy packets: 1 Packet duration not set Requested packetization period not set Connection mode: 1: RECV Testing EMPTY Testing SHORT1 Testing SHORT2 Testing SHORT3 Testing SHORT4 Testing RQNT1 Testing RQNT2 Testing DLCX Detected packet duration: 20 Requested packetization period not set Connection mode: 0: NONE Testing CRCX Dummy packets: 1 Detected packet duration: 40 Requested packetetization period: 20-20 Connection mode: 1: RECV Testing MDCX3 Dummy packets: 1 Packet duration not set Requested packetization period not set Connection mode not set Testing DLCX Detected packet duration: 20 Requested packetization period not set Connection mode: 0: NONE Testing CRCX Re-transmitting CRCX Testing RQNT1 Re-transmitting RQNT1 Testing RQNT2 Re-transmitting RQNT2 Testing MDCX3 Re-transmitting MDCX3 Testing DLCX Re-transmitting DLCX Testing packet loss calculation. Testing stat parsing Parsing result: 0 Parsing result: 0 Testing packet error detection, patch SSRC. Output SSRC changed to 11223344 In TS: 0, dTS: 0, Seq: 0 Out TS change: 0, dTS: 0, Seq change: 0, TS Err change: in +0, out +0 Stats: Jitter = 0, Transit = 0 In TS: 160, dTS: 160, Seq: 1 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 0, Transit = 0 In TS: 320, dTS: 160, Seq: 2 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 0, Transit = 0 In TS: 320, dTS: 160, Seq: 3 Out TS change: 0, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 10, Transit = 160 In TS: 480, dTS: 160, Seq: 4 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 9, Transit = 160 In TS: 640, dTS: 160, Seq: 5 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 8, Transit = 160 In TS: 960, dTS: 320, Seq: 6 Out TS change: 320, dTS: 320, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 18, Transit = 0 In TS: 1120, dTS: 160, Seq: 7 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 17, Transit = 0 In TS: 1280, dTS: 160, Seq: 8 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 16, Transit = 0 In TS: 1400, dTS: 120, Seq: 9 Out TS change: 120, dTS: 120, Seq change: 1, TS Err change: in +1, out +1 Stats: Jitter = 17, Transit = 40 In TS: 1560, dTS: 160, Seq: 10 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 16, Transit = 40 In TS: 1720, dTS: 160, Seq: 11 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 15, Transit = 40 In TS: 34688, dTS: 0, Seq: 12 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 0, Transit = -32768 In TS: 34848, dTS: 160, Seq: 13 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 0, Transit = -32768 In TS: 35008, dTS: 160, Seq: 14 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 0, Transit = -32768 In TS: 35128, dTS: 120, Seq: 15 Out TS change: 120, dTS: 120, Seq change: 1, TS Err change: in +1, out +1 Stats: Jitter = 2, Transit = -32728 In TS: 35288, dTS: 160, Seq: 16 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 2, Transit = -32728 In TS: 35448, dTS: 160, Seq: 17 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 2, Transit = -32728 In TS: 35768, dTS: 160, Seq: 19 Out TS change: 320, dTS: 160, Seq change: 2, TS Err change: in +0, out +0 Stats: Jitter = 12, Transit = -32888 In TS: 35928, dTS: 160, Seq: 20 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 11, Transit = -32888 In TS: 36088, dTS: 160, Seq: 21 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 20, Transit = -33048 In TS: 36088, dTS: 160, Seq: 21 Out TS change: 0, dTS: 160, Seq change: 0, TS Err change: in +0, out +0 Stats: Jitter = 29, Transit = -32888 In TS: 36248, dTS: 160, Seq: 22 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 27, Transit = -32888 In TS: 36408, dTS: 160, Seq: 23 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 25, Transit = -32888 In TS: 36568, dTS: 160, Seq: 23 Out TS change: 160, dTS: 160, Seq change: 0, TS Err change: in +1, out +1 Stats: Jitter = 24, Transit = -32888 In TS: 36728, dTS: 160, Seq: 24 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 22, Transit = -32888 In TS: 36888, dTS: 160, Seq: 25 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 21, Transit = -32888 In TS: 160000, dTS: 0, Seq: 1000 Out TS change: 12000, dTS: 12000, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 0, Transit = -144000 In TS: 160160, dTS: 160, Seq: 1001 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 0, Transit = -144000 In TS: 160320, dTS: 160, Seq: 1002 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 0, Transit = -144000 Testing packet error detection. Output SSRC changed to 11223344 In TS: 0, dTS: 0, Seq: 0 Out TS change: 0, dTS: 0, Seq change: 0, TS Err change: in +0, out +0 Stats: Jitter = 0, Transit = 0 In TS: 160, dTS: 160, Seq: 1 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 0, Transit = 0 In TS: 320, dTS: 160, Seq: 2 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 0, Transit = 0 In TS: 320, dTS: 160, Seq: 3 Out TS change: 0, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 10, Transit = 160 In TS: 480, dTS: 160, Seq: 4 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 9, Transit = 160 In TS: 640, dTS: 160, Seq: 5 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 8, Transit = 160 In TS: 960, dTS: 320, Seq: 6 Out TS change: 320, dTS: 320, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 18, Transit = 0 In TS: 1120, dTS: 160, Seq: 7 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 17, Transit = 0 In TS: 1280, dTS: 160, Seq: 8 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 16, Transit = 0 In TS: 1400, dTS: 120, Seq: 9 Out TS change: 120, dTS: 120, Seq change: 1, TS Err change: in +1, out +1 Stats: Jitter = 17, Transit = 40 In TS: 1560, dTS: 160, Seq: 10 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 16, Transit = 40 In TS: 1720, dTS: 160, Seq: 11 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 15, Transit = 40 Output SSRC changed to 10203040 In TS: 34688, dTS: 0, Seq: 12 Out TS change: 32968, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 0, Transit = -32768 In TS: 34848, dTS: 160, Seq: 13 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 0, Transit = -32768 In TS: 35008, dTS: 160, Seq: 14 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 0, Transit = -32768 In TS: 35128, dTS: 120, Seq: 15 Out TS change: 120, dTS: 120, Seq change: 1, TS Err change: in +1, out +1 Stats: Jitter = 2, Transit = -32728 In TS: 35288, dTS: 160, Seq: 16 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 2, Transit = -32728 In TS: 35448, dTS: 160, Seq: 17 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 2, Transit = -32728 In TS: 35768, dTS: 160, Seq: 19 Out TS change: 320, dTS: 160, Seq change: 2, TS Err change: in +0, out +0 Stats: Jitter = 12, Transit = -32888 In TS: 35928, dTS: 160, Seq: 20 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 11, Transit = -32888 In TS: 36088, dTS: 160, Seq: 21 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 20, Transit = -33048 In TS: 36088, dTS: 160, Seq: 21 Out TS change: 0, dTS: 160, Seq change: 0, TS Err change: in +0, out +0 Stats: Jitter = 29, Transit = -32888 In TS: 36248, dTS: 160, Seq: 22 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 27, Transit = -32888 In TS: 36408, dTS: 160, Seq: 23 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 25, Transit = -32888 In TS: 36568, dTS: 160, Seq: 23 Out TS change: 160, dTS: 160, Seq change: 0, TS Err change: in +1, out +1 Stats: Jitter = 24, Transit = -32888 In TS: 36728, dTS: 160, Seq: 24 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 22, Transit = -32888 In TS: 36888, dTS: 160, Seq: 25 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 21, Transit = -32888 Output SSRC changed to 50607080 In TS: 160000, dTS: 0, Seq: 1000 Out TS change: 123112, dTS: 160, Seq change: 975, TS Err change: in +0, out +0 Stats: Jitter = 0, Transit = -144000 In TS: 160160, dTS: 160, Seq: 1001 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 0, Transit = -144000 In TS: 160320, dTS: 160, Seq: 1002 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 0, Transit = -144000 Testing packet error detection, patch timestamps. Output SSRC changed to 11223344 In TS: 0, dTS: 0, Seq: 0 Out TS change: 0, dTS: 0, Seq change: 0, TS Err change: in +0, out +0 Stats: Jitter = 0, Transit = 0 In TS: 160, dTS: 160, Seq: 1 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 0, Transit = 0 In TS: 320, dTS: 160, Seq: 2 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 0, Transit = 0 In TS: 320, dTS: 160, Seq: 3 Out TS change: 0, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 10, Transit = 160 In TS: 480, dTS: 160, Seq: 4 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 9, Transit = 160 In TS: 640, dTS: 160, Seq: 5 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 8, Transit = 160 In TS: 960, dTS: 320, Seq: 6 Out TS change: 320, dTS: 320, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 18, Transit = 0 In TS: 1120, dTS: 160, Seq: 7 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 17, Transit = 0 In TS: 1280, dTS: 160, Seq: 8 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 16, Transit = 0 In TS: 1400, dTS: 120, Seq: 9 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0 Stats: Jitter = 17, Transit = 40 In TS: 1560, dTS: 160, Seq: 10 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 16, Transit = 40 In TS: 1720, dTS: 160, Seq: 11 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 15, Transit = 40 Output SSRC changed to 10203040 In TS: 34688, dTS: 0, Seq: 12 Out TS change: 32968, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 0, Transit = -32768 In TS: 34848, dTS: 160, Seq: 13 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 0, Transit = -32768 In TS: 35008, dTS: 160, Seq: 14 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 0, Transit = -32768 In TS: 35128, dTS: 120, Seq: 15 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0 Stats: Jitter = 2, Transit = -32728 In TS: 35288, dTS: 160, Seq: 16 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 2, Transit = -32728 In TS: 35448, dTS: 160, Seq: 17 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 2, Transit = -32728 In TS: 35768, dTS: 160, Seq: 19 Out TS change: 320, dTS: 160, Seq change: 2, TS Err change: in +0, out +0 Stats: Jitter = 12, Transit = -32888 In TS: 35928, dTS: 160, Seq: 20 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 11, Transit = -32888 In TS: 36088, dTS: 160, Seq: 21 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 20, Transit = -33048 In TS: 36088, dTS: 160, Seq: 21 Out TS change: 0, dTS: 160, Seq change: 0, TS Err change: in +0, out +0 Stats: Jitter = 29, Transit = -32888 In TS: 36248, dTS: 160, Seq: 22 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 27, Transit = -32888 In TS: 36408, dTS: 160, Seq: 23 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 25, Transit = -32888 In TS: 36568, dTS: 160, Seq: 23 Out TS change: 160, dTS: 160, Seq change: 0, TS Err change: in +1, out +1 Stats: Jitter = 24, Transit = -32888 In TS: 36728, dTS: 160, Seq: 24 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 22, Transit = -32888 In TS: 36888, dTS: 160, Seq: 25 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 21, Transit = -32888 Output SSRC changed to 50607080 In TS: 160000, dTS: 0, Seq: 1000 Out TS change: 123112, dTS: 160, Seq change: 975, TS Err change: in +0, out +0 Stats: Jitter = 0, Transit = -144000 In TS: 160160, dTS: 160, Seq: 1001 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 0, Transit = -144000 In TS: 160320, dTS: 160, Seq: 1002 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 0, Transit = -144000 Testing packet error detection, patch SSRC, patch timestamps. Output SSRC changed to 11223344 In TS: 0, dTS: 0, Seq: 0 Out TS change: 0, dTS: 0, Seq change: 0, TS Err change: in +0, out +0 Stats: Jitter = 0, Transit = 0 In TS: 160, dTS: 160, Seq: 1 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 0, Transit = 0 In TS: 320, dTS: 160, Seq: 2 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 0, Transit = 0 In TS: 320, dTS: 160, Seq: 3 Out TS change: 0, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 10, Transit = 160 In TS: 480, dTS: 160, Seq: 4 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 9, Transit = 160 In TS: 640, dTS: 160, Seq: 5 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 8, Transit = 160 In TS: 960, dTS: 320, Seq: 6 Out TS change: 320, dTS: 320, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 18, Transit = 0 In TS: 1120, dTS: 160, Seq: 7 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 17, Transit = 0 In TS: 1280, dTS: 160, Seq: 8 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 16, Transit = 0 In TS: 1400, dTS: 120, Seq: 9 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0 Stats: Jitter = 17, Transit = 40 In TS: 1560, dTS: 160, Seq: 10 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 16, Transit = 40 In TS: 1720, dTS: 160, Seq: 11 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 15, Transit = 40 In TS: 34688, dTS: 0, Seq: 12 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 0, Transit = -32768 In TS: 34848, dTS: 160, Seq: 13 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 0, Transit = -32768 In TS: 35008, dTS: 160, Seq: 14 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 0, Transit = -32768 In TS: 35128, dTS: 120, Seq: 15 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0 Stats: Jitter = 2, Transit = -32728 In TS: 35288, dTS: 160, Seq: 16 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 2, Transit = -32728 In TS: 35448, dTS: 160, Seq: 17 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 2, Transit = -32728 In TS: 35768, dTS: 160, Seq: 19 Out TS change: 320, dTS: 160, Seq change: 2, TS Err change: in +0, out +0 Stats: Jitter = 12, Transit = -32888 In TS: 35928, dTS: 160, Seq: 20 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 11, Transit = -32888 In TS: 36088, dTS: 160, Seq: 21 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 20, Transit = -33048 In TS: 36088, dTS: 160, Seq: 21 Out TS change: 0, dTS: 160, Seq change: 0, TS Err change: in +0, out +0 Stats: Jitter = 29, Transit = -32888 In TS: 36248, dTS: 160, Seq: 22 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 27, Transit = -32888 In TS: 36408, dTS: 160, Seq: 23 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 25, Transit = -32888 In TS: 36568, dTS: 160, Seq: 23 Out TS change: 160, dTS: 160, Seq change: 0, TS Err change: in +1, out +1 Stats: Jitter = 24, Transit = -32888 In TS: 36728, dTS: 160, Seq: 24 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 22, Transit = -32888 In TS: 36888, dTS: 160, Seq: 25 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 21, Transit = -32888 In TS: 160000, dTS: 0, Seq: 1000 Out TS change: 12000, dTS: 12000, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 0, Transit = -144000 In TS: 160160, dTS: 160, Seq: 1001 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 0, Transit = -144000 In TS: 160320, dTS: 160, Seq: 1002 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 0, Transit = -144000 Testing multiple payload types Testing no sequence flow on initial packet Testing no rtpmap name Done openbsc-0.15.0/openbsc/tests/mgcp/mgcp_transcoding_test.c000066400000000000000000000465461265565154000235230ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include "bscconfig.h" #ifndef BUILD_MGCP_TRANSCODING #error "Requires MGCP transcoding enabled (see --enable-mgcp-transcoding)" #endif #include "openbsc/mgcp_transcode.h" uint8_t *audio_frame_l16[] = { }; struct rtp_packets { float t; int len; char *data; }; struct rtp_packets audio_packets_l16[] = { /* RTP: SeqNo=1, TS=160 */ {0.020000, 332, "\x80\x0B\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44" "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED" }, }; struct rtp_packets audio_packets_gsm[] = { /* RTP: SeqNo=1, TS=160 */ {0.020000, 45, "\x80\x03\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44" "\xD4\x7C\xE3\xE9\x62\x50\x39\xF0\xF8\xB4\x68\xEA\x6C\x0E\x81\x1B" "\x56\x2A\xD5\xBC\x69\x9C\xD1\xF0\x66\x7A\xEC\x49\x7A\x33\x3D\x0A" "\xDE" }, }; struct rtp_packets audio_packets_gsm_invalid_size[] = { /* RTP: SeqNo=1, TS=160 */ {0.020000, 41, "\x80\x03\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44" "\xD4\x7C\xE3\xE9\x62\x50\x39\xF0\xF8\xB4\x68\xEA\x6C\x0E\x81\x1B" "\x56\x2A\xD5\xBC\x69\x9C\xD1\xF0\x66\x7A\xEC\x49\x7A\x33\x3D\x0A" "\xDE" }, }; struct rtp_packets audio_packets_gsm_invalid_data[] = { /* RTP: SeqNo=1, TS=160 */ {0.020000, 45, "\x80\x03\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44" "\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE" "\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE" "\xEE" }, }; struct rtp_packets audio_packets_gsm_invalid_ptype[] = { /* RTP: SeqNo=1, TS=160 */ {0.020000, 45, "\x80\x08\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44" "\xD4\x7C\xE3\xE9\x62\x50\x39\xF0\xF8\xB4\x68\xEA\x6C\x0E\x81\x1B" "\x56\x2A\xD5\xBC\x69\x9C\xD1\xF0\x66\x7A\xEC\x49\x7A\x33\x3D\x0A" "\xDE" }, }; struct rtp_packets audio_packets_g729[] = { /* RTP: SeqNo=1, TS=160 */ {0.020000, 32, "\x80\x12\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44" "\xAF\xC2\x81\x40\x00\xFA\xCE\xA4\x21\x7C\xC5\xC3\x4F\xA5\x98\xF5" "\xB2\x95\xC4\xAD" }, }; struct rtp_packets audio_packets_pcma[] = { /* RTP: SeqNo=1, TS=160 */ {0.020000, 172, "\x80\x08\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44" "\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25" "\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25" "\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25" "\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25" "\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25" "\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25" "\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25" "\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25" "\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25" "\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25" }, /* RTP: SeqNo=26527, TS=232640 */ {0.020000, 92, "\x80\x08\x67\x9f\x00\x03\x8c\xc0\x04\xaa\x67\x9f\xd5\xd5\xd5\xd5" "\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5" "\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5" "\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5" "\xd5\xd5\xd5\xd5\xd5\xd5\x55\x55\xd5\xd5\x55\x55\xd5\xd5\x55\x55" "\xd5\xd5\xd5\x55\x55\xd5\xd5\xd5\x55\x55\xd5\xd5" }, /* RTP: SeqNo=26528, TS=232720 */ {0.020000, 92, "\x80\x08\x67\xa0\x00\x03\x8d\x10\x04\xaa\x67\x9f\x55\xd5\xd5\x55" "\xd5\x55\xd5\xd5\xd5\x55\xd5\x55\xd5\xd5\x55\xd5\x55\xd5\x55\xd5" "\x55\x55\xd5\x55\xd5\xd5\x55\x55\x55\x55\x55\xd5\xd5\x55\xd5\xd5" "\xd5\x55\xd5\xd5\xd5\x55\x54\x55\xd5\xd5\x55\xd5\xd5\xd5\xd5\x55" "\x54\x55\xd5\x55\xd5\x55\x55\x55\x55\x55\xd5\xd5\xd5\xd5\xd5\xd4" "\xd5\x54\x55\xd5\xd4\xd5\x54\xd5\x55\xd5\xd5\xd5" }, }; static int audio_name_to_type(const char *name) { if (!strcasecmp(name, "gsm")) return 3; #ifdef HAVE_BCG729 else if (!strcasecmp(name, "g729")) return 18; #endif else if (!strcasecmp(name, "pcma")) return 8; else if (!strcasecmp(name, "l16")) return 11; return -1; } int mgcp_get_trans_frame_size(void *state_, int nsamples, int dst); static int given_configured_endpoint(int in_samples, int out_samples, const char *srcfmt, const char *dstfmt, void **out_ctx, struct mgcp_endpoint **out_endp) { int rc; struct mgcp_rtp_end *dst_end; struct mgcp_rtp_end *src_end; struct mgcp_config *cfg; struct mgcp_trunk_config *tcfg; struct mgcp_endpoint *endp; cfg = mgcp_config_alloc(); tcfg = talloc_zero(cfg, struct mgcp_trunk_config); endp = talloc_zero(tcfg, struct mgcp_endpoint); cfg->setup_rtp_processing_cb = mgcp_transcoding_setup; cfg->rtp_processing_cb = mgcp_transcoding_process_rtp; cfg->get_net_downlink_format_cb = mgcp_transcoding_net_downlink_format; tcfg->endpoints = endp; tcfg->number_endpoints = 1; tcfg->cfg = cfg; endp->tcfg = tcfg; endp->cfg = cfg; mgcp_initialize_endp(endp); dst_end = &endp->bts_end; dst_end->codec.payload_type = audio_name_to_type(dstfmt); src_end = &endp->net_end; src_end->codec.payload_type = audio_name_to_type(srcfmt); if (out_samples) { dst_end->codec.frame_duration_den = dst_end->codec.rate; dst_end->codec.frame_duration_num = out_samples; dst_end->frames_per_packet = 1; dst_end->force_output_ptime = 1; } rc = mgcp_transcoding_setup(endp, dst_end, src_end); if (rc < 0) { printf("setup failed: %s", strerror(-rc)); abort(); } *out_ctx = cfg; *out_endp = endp; return 0; } static int transcode_test(const char *srcfmt, const char *dstfmt, uint8_t *src_pkts, size_t src_pkt_size) { char buf[4096] = {0x80, 0}; void *ctx; struct mgcp_rtp_end *dst_end; struct mgcp_process_rtp_state *state; struct mgcp_endpoint *endp; int in_size; const int in_samples = 160; int len, cont; printf("== Transcoding test ==\n"); printf("converting %s -> %s\n", srcfmt, dstfmt); given_configured_endpoint(in_samples, 0, srcfmt, dstfmt, &ctx, &endp); dst_end = &endp->bts_end; state = dst_end->rtp_process_data; OSMO_ASSERT(state != NULL); in_size = mgcp_transcoding_get_frame_size(state, in_samples, 0); OSMO_ASSERT(sizeof(buf) >= in_size + 12); memcpy(buf, src_pkts, src_pkt_size); len = src_pkt_size; cont = mgcp_transcoding_process_rtp(endp, dst_end, buf, &len, sizeof(buf)); if (cont < 0) { printf("Nothing encoded due: %s\n", strerror(-cont)); talloc_free(ctx); return -1; } if (len < 24) { printf("encoded: %s\n", osmo_hexdump((unsigned char *)buf, len)); } else { const char *str = osmo_hexdump((unsigned char *)buf, len); int i = 0; const int prefix = 4; const int cutlen = 48; int nchars = 0; printf("encoded:\n"); do { nchars = printf("%*s%-.*s", prefix, "", cutlen, str + i); i += nchars - prefix; printf("\n"); } while (nchars - prefix >= cutlen); } printf("counted: %d\n", cont); talloc_free(ctx); return 0; } static void test_rtp_seq_state(void) { char buf[4096]; int len; int cont; void *ctx; struct mgcp_endpoint *endp; struct mgcp_process_rtp_state *state; struct rtp_hdr *hdr; uint32_t ts_no; uint16_t seq_no; given_configured_endpoint(160, 0, "pcma", "l16", &ctx, &endp); state = endp->bts_end.rtp_process_data; OSMO_ASSERT(!state->is_running); OSMO_ASSERT(state->next_seq == 0); OSMO_ASSERT(state->next_time == 0); /* initialize packet */ len = audio_packets_pcma[0].len; memcpy(buf, audio_packets_pcma[0].data, len); cont = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, len); OSMO_ASSERT(cont >= 0); OSMO_ASSERT(state->is_running); OSMO_ASSERT(state->next_seq == 2); OSMO_ASSERT(state->next_time == 240); /* verify that the right timestamp was written */ OSMO_ASSERT(len == audio_packets_pcma[0].len); hdr = (struct rtp_hdr *) &buf[0]; memcpy(&ts_no, &hdr->timestamp, sizeof(ts_no)); OSMO_ASSERT(htonl(ts_no) == 160); memcpy(&seq_no, &hdr->sequence, sizeof(seq_no)); OSMO_ASSERT(htons(seq_no) == 1); /* Check the right sequence number is written */ state->next_seq = 1234; len = audio_packets_pcma[0].len; memcpy(buf, audio_packets_pcma[0].data, len); cont = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, len); OSMO_ASSERT(cont >= 0); OSMO_ASSERT(len == audio_packets_pcma[0].len); hdr = (struct rtp_hdr *) &buf[0]; memcpy(&seq_no, &hdr->sequence, sizeof(seq_no)); OSMO_ASSERT(htons(seq_no) == 1234); talloc_free(ctx); } static void test_transcode_result(void) { char buf[4096]; int len, res; void *ctx; struct mgcp_endpoint *endp; struct mgcp_process_rtp_state *state; { /* from GSM to PCMA and same ptime */ given_configured_endpoint(160, 0, "gsm", "pcma", &ctx, &endp); state = endp->bts_end.rtp_process_data; /* result */ len = audio_packets_gsm[0].len; memcpy(buf, audio_packets_gsm[0].data, len); res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf)); OSMO_ASSERT(res == sizeof(struct rtp_hdr)); OSMO_ASSERT(state->sample_cnt == 0); len = res; res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf)); OSMO_ASSERT(res == -ENOMSG); talloc_free(ctx); } { /* from GSM to PCMA and same ptime */ given_configured_endpoint(160, 160, "gsm", "pcma", &ctx, &endp); state = endp->bts_end.rtp_process_data; /* result */ len = audio_packets_gsm[0].len; memcpy(buf, audio_packets_gsm[0].data, len); res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf)); OSMO_ASSERT(res == sizeof(struct rtp_hdr)); OSMO_ASSERT(state->sample_cnt == 0); len = res; res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf)); OSMO_ASSERT(res == -EAGAIN); talloc_free(ctx); } { /* from PCMA to GSM and wrong different ptime */ given_configured_endpoint(80, 160, "pcma", "gsm", &ctx, &endp); state = endp->bts_end.rtp_process_data; /* Add the first sample */ len = audio_packets_pcma[1].len; memcpy(buf, audio_packets_pcma[1].data, len); res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf)); OSMO_ASSERT(state->sample_cnt == 80); OSMO_ASSERT(state->next_time == 232640); OSMO_ASSERT(res < 0); /* Add the second sample and it should be consumable */ len = audio_packets_pcma[2].len; memcpy(buf, audio_packets_pcma[2].data, len); res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf)); OSMO_ASSERT(state->sample_cnt == 0); OSMO_ASSERT(state->next_time == 232640 + 80 + 160); OSMO_ASSERT(res == sizeof(struct rtp_hdr)); talloc_free(ctx); } { /* from PCMA to GSM with a big time jump */ struct rtp_hdr *hdr; uint32_t ts; given_configured_endpoint(80, 160, "pcma", "gsm", &ctx, &endp); state = endp->bts_end.rtp_process_data; /* Add the first sample */ len = audio_packets_pcma[1].len; memcpy(buf, audio_packets_pcma[1].data, len); res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf)); OSMO_ASSERT(state->sample_cnt == 80); OSMO_ASSERT(state->next_time == 232640); OSMO_ASSERT(state->next_seq == 26527); OSMO_ASSERT(res < 0); /* Add a skip to the packet to force a 'resync' */ len = audio_packets_pcma[2].len; memcpy(buf, audio_packets_pcma[2].data, len); hdr = (struct rtp_hdr *) &buf[0]; /* jump the time and add alignment error */ ts = ntohl(hdr->timestamp) + 123 * 80 + 2; hdr->timestamp = htonl(ts); res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf)); OSMO_ASSERT(res < 0); OSMO_ASSERT(state->sample_cnt == 80); OSMO_ASSERT(state->next_time == ts); OSMO_ASSERT(state->next_seq == 26527); /* TODO: this can create alignment errors */ /* Now attempt to consume 160 samples */ len = audio_packets_pcma[2].len; memcpy(buf, audio_packets_pcma[2].data, len); hdr = (struct rtp_hdr *) &buf[0]; ts += 80; hdr->timestamp = htonl(ts); res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf)); OSMO_ASSERT(res == 12); OSMO_ASSERT(state->sample_cnt == 0); OSMO_ASSERT(state->next_time == ts + 160); OSMO_ASSERT(state->next_seq == 26528); talloc_free(ctx); } } static void test_transcode_change(void) { char buf[4096] = {0x80, 0}; void *ctx; struct mgcp_endpoint *endp; struct mgcp_process_rtp_state *state; struct rtp_hdr *hdr; int len, res; { /* from GSM to PCMA and same ptime */ printf("Testing Initial L16->GSM, PCMA->GSM\n"); given_configured_endpoint(160, 0, "l16", "gsm", &ctx, &endp); endp->net_end.alt_codec = endp->net_end.codec; endp->net_end.alt_codec.payload_type = audio_name_to_type("pcma"); state = endp->bts_end.rtp_process_data; /* initial transcoding work */ OSMO_ASSERT(state->src_fmt == AF_L16); OSMO_ASSERT(state->dst_fmt == AF_GSM); OSMO_ASSERT(endp->net_end.alt_codec.payload_type == 8); OSMO_ASSERT(endp->net_end.codec.payload_type == 11); /* result */ len = audio_packets_pcma[0].len; memcpy(buf, audio_packets_pcma[0].data, len); res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf)); state = endp->bts_end.rtp_process_data; OSMO_ASSERT(res == sizeof(struct rtp_hdr)); OSMO_ASSERT(state->sample_cnt == 0); OSMO_ASSERT(state->src_fmt == AF_PCMA); OSMO_ASSERT(state->dst_fmt == AF_GSM); OSMO_ASSERT(endp->net_end.alt_codec.payload_type == 11); OSMO_ASSERT(endp->net_end.codec.payload_type == 8); len = res; res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf)); OSMO_ASSERT(res == -ENOMSG); OSMO_ASSERT(state == endp->bts_end.rtp_process_data); /* now check that comfort noise doesn't change anything */ len = audio_packets_pcma[1].len; memcpy(buf, audio_packets_pcma[1].data, len); hdr = (struct rtp_hdr *) buf; hdr->payload_type = 12; res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf)); OSMO_ASSERT(state == endp->bts_end.rtp_process_data); OSMO_ASSERT(state->sample_cnt == 80); OSMO_ASSERT(state->src_fmt == AF_PCMA); OSMO_ASSERT(state->dst_fmt == AF_GSM); OSMO_ASSERT(endp->net_end.alt_codec.payload_type == 11); OSMO_ASSERT(endp->net_end.codec.payload_type == 8); talloc_free(ctx); } } static int test_repacking(int in_samples, int out_samples, int no_transcode) { char buf[4096] = {0x80, 0}; int cc; struct mgcp_endpoint *endp; void *ctx; struct mgcp_process_rtp_state *state; int in_cnt; int out_size; int in_size; uint32_t ts = 0; uint16_t seq = 0; const char *srcfmt = "pcma"; const char *dstfmt = no_transcode ? "pcma" : "l16"; printf("== Transcoding test ==\n"); printf("converting %s -> %s\n", srcfmt, dstfmt); given_configured_endpoint(in_samples, out_samples, srcfmt, dstfmt, &ctx, &endp); state = endp->bts_end.rtp_process_data; OSMO_ASSERT(state != NULL); in_size = mgcp_transcoding_get_frame_size(state, in_samples, 0); OSMO_ASSERT(sizeof(buf) >= in_size + 12); out_size = mgcp_transcoding_get_frame_size(state, -1, 1); OSMO_ASSERT(sizeof(buf) >= out_size + 12); buf[1] = endp->net_end.codec.payload_type; *(uint16_t*)(buf+2) = htons(1); *(uint32_t*)(buf+4) = htonl(0); *(uint32_t*)(buf+8) = htonl(0xaabbccdd); for (in_cnt = 0; in_cnt < 16; in_cnt++) { int cont; int len; /* fake PCMA data */ printf("generating %d %s input samples\n", in_samples, srcfmt); for (cc = 0; cc < in_samples; cc++) buf[12+cc] = cc; *(uint16_t*)(buf+2) = htonl(seq); *(uint32_t*)(buf+4) = htonl(ts); seq += 1; ts += in_samples; cc += 12; /* include RTP header */ len = cc; do { cont = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, sizeof(buf)); if (cont == -EAGAIN) { fprintf(stderr, "Got EAGAIN\n"); break; } if (cont < 0) { printf("processing failed: %s", strerror(-cont)); abort(); } len -= 12; /* ignore RTP header */ printf("got %d %s output frames (%d octets) count=%d\n", len / out_size, dstfmt, len, cont); len = cont; } while (len > 0); } talloc_free(ctx); return 0; } int main(int argc, char **argv) { int rc; osmo_init_logging(&log_info); printf("=== Transcoding Good Cases ===\n"); transcode_test("l16", "l16", (uint8_t *)audio_packets_l16[0].data, audio_packets_l16[0].len); transcode_test("l16", "gsm", (uint8_t *)audio_packets_l16[0].data, audio_packets_l16[0].len); transcode_test("l16", "pcma", (uint8_t *)audio_packets_l16[0].data, audio_packets_l16[0].len); transcode_test("gsm", "l16", (uint8_t *)audio_packets_gsm[0].data, audio_packets_gsm[0].len); transcode_test("gsm", "gsm", (uint8_t *)audio_packets_gsm[0].data, audio_packets_gsm[0].len); transcode_test("gsm", "pcma", (uint8_t *)audio_packets_gsm[0].data, audio_packets_gsm[0].len); transcode_test("pcma", "l16", (uint8_t *)audio_packets_pcma[0].data, audio_packets_pcma[0].len); transcode_test("pcma", "gsm", (uint8_t *)audio_packets_pcma[0].data, audio_packets_pcma[0].len); transcode_test("pcma", "pcma", (uint8_t *)audio_packets_pcma[0].data, audio_packets_pcma[0].len); printf("=== Transcoding Bad Cases ===\n"); printf("Invalid size:\n"); rc = transcode_test("gsm", "pcma", (uint8_t *)audio_packets_gsm_invalid_size[0].data, audio_packets_gsm_invalid_size[0].len); OSMO_ASSERT(rc < 0); printf("Invalid data:\n"); rc = transcode_test("gsm", "pcma", (uint8_t *)audio_packets_gsm_invalid_data[0].data, audio_packets_gsm_invalid_data[0].len); OSMO_ASSERT(rc < 0); printf("Invalid payload type:\n"); rc = transcode_test("gsm", "pcma", (uint8_t *)audio_packets_gsm_invalid_ptype[0].data, audio_packets_gsm_invalid_ptype[0].len); OSMO_ASSERT(rc == 0); printf("=== Repacking ===\n"); test_repacking(160, 160, 0); test_repacking(160, 160, 1); test_repacking(160, 80, 0); test_repacking(160, 80, 1); test_repacking(160, 320, 0); test_repacking(160, 320, 1); test_repacking(160, 240, 0); test_repacking(160, 240, 1); test_repacking(160, 100, 0); test_repacking(160, 100, 1); test_rtp_seq_state(); test_transcode_result(); test_transcode_change(); return 0; } openbsc-0.15.0/openbsc/tests/mgcp/mgcp_transcoding_test.ok000066400000000000000000000523431265565154000237020ustar00rootroot00000000000000=== Transcoding Good Cases === == Transcoding test == converting l16 -> l16 encoded: 80 0b 00 01 00 00 00 a0 11 22 33 44 00 00 40 13 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13 5a 9e 40 13 00 00 bf ed a5 62 bf ed counted: 0 == Transcoding test == converting l16 -> gsm encoded: 80 0b 00 01 00 00 00 a0 11 22 33 44 d4 7c e3 e9 62 50 39 f0 f8 b4 68 ea 6c 0e 81 1b 56 2a d5 bc 69 9c d1 f0 66 7a ec 49 7a 33 3d 0a de counted: 12 == Transcoding test == converting l16 -> pcma encoded: 80 0b 00 01 00 00 00 a0 11 22 33 44 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 counted: 12 == Transcoding test == converting gsm -> l16 encoded: 80 03 00 01 00 00 00 a0 11 22 33 44 00 00 54 00 59 f0 34 20 c4 c8 b9 f8 e2 18 f1 e8 f2 28 f0 e0 46 08 4f 80 2c a0 a9 c8 80 00 c0 58 3f 80 63 c0 24 b8 fa b8 f6 88 0b a0 c8 70 a8 b0 c8 c0 3b a8 66 a0 2e 38 d1 f8 98 98 aa 18 e8 30 26 a0 37 40 37 e8 17 00 ee 50 b7 80 b1 88 de 28 18 40 45 b0 4f 48 21 d8 df 78 ae 68 ab 98 d6 b8 18 18 48 90 4e 70 27 40 e8 10 b5 b0 ac 80 d4 60 14 50 48 48 50 10 2a 00 ec 08 ba 00 af 58 d1 c0 10 60 45 c8 54 10 30 78 f1 a8 bb 18 ad 48 ce 30 0a e8 3f 30 4f 10 32 18 f6 18 bf 20 ac 30 cd 80 0b d0 43 d8 55 e0 34 a0 f5 78 bc 98 ad 98 cd c8 0a 80 40 58 51 c0 35 40 f9 60 c1 68 ac c8 cb 38 08 00 40 98 51 e0 34 d8 fa 28 c2 f0 ae 40 c7 70 02 d0 3c a8 54 78 38 a0 fc 68 c2 08 ad 50 c7 78 01 60 39 c0 51 38 3a e8 00 e8 c6 38 ab d8 c4 00 fe 08 39 18 50 30 39 50 01 d8 ca 70 b1 80 c4 c8 fc 58 36 40 51 d8 3b 08 02 80 c8 58 b0 60 c5 a8 fb d0 33 e8 4e 80 3c e0 06 10 cb 90 ae 48 c2 60 f9 58 34 08 4d a0 3a a8 06 48 cf 80 b4 60 c3 e8 f7 90 30 18 4d a0 3b 98 07 90 cf 18 b4 68 c4 88 counted: 12 == Transcoding test == converting gsm -> gsm encoded: 80 03 00 01 00 00 00 a0 11 22 33 44 d4 7c e3 e9 62 50 39 f0 f8 b4 68 ea 6c 0e 81 1b 56 2a d5 bc 69 9c d1 f0 66 7a ec 49 7a 33 3d 0a de counted: 0 == Transcoding test == converting gsm -> pcma encoded: 80 03 00 01 00 00 00 a0 11 22 33 44 d5 a0 a3 bf 38 24 08 19 1e 1b a4 a6 b3 20 2a 3a ba ad b7 60 17 92 3e 20 3e b8 ac b2 32 2c 20 02 b6 be be 82 04 27 26 35 8d a4 a6 b5 35 21 20 31 8d a7 a6 b6 02 27 21 30 81 a7 a1 b0 06 24 21 32 85 a4 a0 bd 19 24 21 3d 90 ba a6 bc 16 25 21 3c 92 a5 a0 bf 10 25 21 3c 90 a5 a1 bf 6f 3a 21 3f 95 a5 a1 bf 62 3b 21 39 f3 bb a0 b9 79 3b 21 39 c3 b9 a1 b8 db 39 20 3b 4a b9 a1 b9 c8 3f 26 38 78 be a1 b8 f1 3e 26 38 65 bc a6 bb ed 3f 21 3b 6f bf a6 b8 ec 3d 27 3b 15 bd a6 b8 eb 3d 27 38 counted: 12 == Transcoding test == converting pcma -> l16 encoded: 80 08 00 01 00 00 00 a0 11 22 33 44 00 08 42 00 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00 5a 00 42 00 00 08 be 00 a6 00 be 00 counted: 12 == Transcoding test == converting pcma -> gsm encoded: 80 08 00 01 00 00 00 a0 11 22 33 44 d4 b9 f4 5d d9 50 5a e1 a0 cd 76 ea 52 0e 87 53 ad d4 ea a2 0a 63 ca e9 60 79 e2 2a 25 d2 c0 f3 39 counted: 12 == Transcoding test == converting pcma -> pcma encoded: 80 08 00 01 00 00 00 a0 11 22 33 44 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 counted: 0 === Transcoding Bad Cases === Invalid size: == Transcoding test == converting gsm -> pcma Nothing encoded due: No message of desired type Invalid data: == Transcoding test == converting gsm -> pcma Nothing encoded due: No message of desired type Invalid payload type: == Transcoding test == converting gsm -> pcma encoded: 80 08 00 01 00 00 00 a0 11 22 33 44 d5 a0 a3 bf 38 24 08 19 1e 1b a4 a6 b3 20 2a 3a ba ad b7 60 17 92 3e 20 3e b8 ac b2 32 2c 20 02 b6 be be 82 04 27 26 35 8d a4 a6 b5 35 21 20 31 8d a7 a6 b6 02 27 21 30 81 a7 a1 b0 06 24 21 32 85 a4 a0 bd 19 24 21 3d 90 ba a6 bc 16 25 21 3c 92 a5 a0 bf 10 25 21 3c 90 a5 a1 bf 6f 3a 21 3f 95 a5 a1 bf 62 3b 21 39 f3 bb a0 b9 79 3b 21 39 c3 b9 a1 b8 db 39 20 3b 4a b9 a1 b9 c8 3f 26 38 78 be a1 b8 f1 3e 26 38 65 bc a6 bb ed 3f 21 3b 6f bf a6 b8 ec 3d 27 3b 15 bd a6 b8 eb 3d 27 38 counted: 12 === Repacking === == Transcoding test == converting pcma -> l16 generating 160 pcma input samples got 2 l16 output frames (320 octets) count=12 generating 160 pcma input samples got 2 l16 output frames (320 octets) count=12 generating 160 pcma input samples got 2 l16 output frames (320 octets) count=12 generating 160 pcma input samples got 2 l16 output frames (320 octets) count=12 generating 160 pcma input samples got 2 l16 output frames (320 octets) count=12 generating 160 pcma input samples got 2 l16 output frames (320 octets) count=12 generating 160 pcma input samples got 2 l16 output frames (320 octets) count=12 generating 160 pcma input samples got 2 l16 output frames (320 octets) count=12 generating 160 pcma input samples got 2 l16 output frames (320 octets) count=12 generating 160 pcma input samples got 2 l16 output frames (320 octets) count=12 generating 160 pcma input samples got 2 l16 output frames (320 octets) count=12 generating 160 pcma input samples got 2 l16 output frames (320 octets) count=12 generating 160 pcma input samples got 2 l16 output frames (320 octets) count=12 generating 160 pcma input samples got 2 l16 output frames (320 octets) count=12 generating 160 pcma input samples got 2 l16 output frames (320 octets) count=12 generating 160 pcma input samples got 2 l16 output frames (320 octets) count=12 == Transcoding test == converting pcma -> pcma generating 160 pcma input samples got 2 pcma output frames (160 octets) count=12 generating 160 pcma input samples got 2 pcma output frames (160 octets) count=12 generating 160 pcma input samples got 2 pcma output frames (160 octets) count=12 generating 160 pcma input samples got 2 pcma output frames (160 octets) count=12 generating 160 pcma input samples got 2 pcma output frames (160 octets) count=12 generating 160 pcma input samples got 2 pcma output frames (160 octets) count=12 generating 160 pcma input samples got 2 pcma output frames (160 octets) count=12 generating 160 pcma input samples got 2 pcma output frames (160 octets) count=12 generating 160 pcma input samples got 2 pcma output frames (160 octets) count=12 generating 160 pcma input samples got 2 pcma output frames (160 octets) count=12 generating 160 pcma input samples got 2 pcma output frames (160 octets) count=12 generating 160 pcma input samples got 2 pcma output frames (160 octets) count=12 generating 160 pcma input samples got 2 pcma output frames (160 octets) count=12 generating 160 pcma input samples got 2 pcma output frames (160 octets) count=12 generating 160 pcma input samples got 2 pcma output frames (160 octets) count=12 generating 160 pcma input samples got 2 pcma output frames (160 octets) count=12 == Transcoding test == converting pcma -> l16 generating 160 pcma input samples got 1 l16 output frames (160 octets) count=12 got 1 l16 output frames (160 octets) count=12 generating 160 pcma input samples got 1 l16 output frames (160 octets) count=12 got 1 l16 output frames (160 octets) count=12 generating 160 pcma input samples got 1 l16 output frames (160 octets) count=12 got 1 l16 output frames (160 octets) count=12 generating 160 pcma input samples got 1 l16 output frames (160 octets) count=12 got 1 l16 output frames (160 octets) count=12 generating 160 pcma input samples got 1 l16 output frames (160 octets) count=12 got 1 l16 output frames (160 octets) count=12 generating 160 pcma input samples got 1 l16 output frames (160 octets) count=12 got 1 l16 output frames (160 octets) count=12 generating 160 pcma input samples got 1 l16 output frames (160 octets) count=12 got 1 l16 output frames (160 octets) count=12 generating 160 pcma input samples got 1 l16 output frames (160 octets) count=12 got 1 l16 output frames (160 octets) count=12 generating 160 pcma input samples got 1 l16 output frames (160 octets) count=12 got 1 l16 output frames (160 octets) count=12 generating 160 pcma input samples got 1 l16 output frames (160 octets) count=12 got 1 l16 output frames (160 octets) count=12 generating 160 pcma input samples got 1 l16 output frames (160 octets) count=12 got 1 l16 output frames (160 octets) count=12 generating 160 pcma input samples got 1 l16 output frames (160 octets) count=12 got 1 l16 output frames (160 octets) count=12 generating 160 pcma input samples got 1 l16 output frames (160 octets) count=12 got 1 l16 output frames (160 octets) count=12 generating 160 pcma input samples got 1 l16 output frames (160 octets) count=12 got 1 l16 output frames (160 octets) count=12 generating 160 pcma input samples got 1 l16 output frames (160 octets) count=12 got 1 l16 output frames (160 octets) count=12 generating 160 pcma input samples got 1 l16 output frames (160 octets) count=12 got 1 l16 output frames (160 octets) count=12 == Transcoding test == converting pcma -> pcma generating 160 pcma input samples got 1 pcma output frames (80 octets) count=12 got 1 pcma output frames (80 octets) count=12 generating 160 pcma input samples got 1 pcma output frames (80 octets) count=12 got 1 pcma output frames (80 octets) count=12 generating 160 pcma input samples got 1 pcma output frames (80 octets) count=12 got 1 pcma output frames (80 octets) count=12 generating 160 pcma input samples got 1 pcma output frames (80 octets) count=12 got 1 pcma output frames (80 octets) count=12 generating 160 pcma input samples got 1 pcma output frames (80 octets) count=12 got 1 pcma output frames (80 octets) count=12 generating 160 pcma input samples got 1 pcma output frames (80 octets) count=12 got 1 pcma output frames (80 octets) count=12 generating 160 pcma input samples got 1 pcma output frames (80 octets) count=12 got 1 pcma output frames (80 octets) count=12 generating 160 pcma input samples got 1 pcma output frames (80 octets) count=12 got 1 pcma output frames (80 octets) count=12 generating 160 pcma input samples got 1 pcma output frames (80 octets) count=12 got 1 pcma output frames (80 octets) count=12 generating 160 pcma input samples got 1 pcma output frames (80 octets) count=12 got 1 pcma output frames (80 octets) count=12 generating 160 pcma input samples got 1 pcma output frames (80 octets) count=12 got 1 pcma output frames (80 octets) count=12 generating 160 pcma input samples got 1 pcma output frames (80 octets) count=12 got 1 pcma output frames (80 octets) count=12 generating 160 pcma input samples got 1 pcma output frames (80 octets) count=12 got 1 pcma output frames (80 octets) count=12 generating 160 pcma input samples got 1 pcma output frames (80 octets) count=12 got 1 pcma output frames (80 octets) count=12 generating 160 pcma input samples got 1 pcma output frames (80 octets) count=12 got 1 pcma output frames (80 octets) count=12 generating 160 pcma input samples got 1 pcma output frames (80 octets) count=12 got 1 pcma output frames (80 octets) count=12 == Transcoding test == converting pcma -> l16 generating 160 pcma input samples generating 160 pcma input samples got 4 l16 output frames (640 octets) count=12 generating 160 pcma input samples generating 160 pcma input samples got 4 l16 output frames (640 octets) count=12 generating 160 pcma input samples generating 160 pcma input samples got 4 l16 output frames (640 octets) count=12 generating 160 pcma input samples generating 160 pcma input samples got 4 l16 output frames (640 octets) count=12 generating 160 pcma input samples generating 160 pcma input samples got 4 l16 output frames (640 octets) count=12 generating 160 pcma input samples generating 160 pcma input samples got 4 l16 output frames (640 octets) count=12 generating 160 pcma input samples generating 160 pcma input samples got 4 l16 output frames (640 octets) count=12 generating 160 pcma input samples generating 160 pcma input samples got 4 l16 output frames (640 octets) count=12 == Transcoding test == converting pcma -> pcma generating 160 pcma input samples generating 160 pcma input samples got 4 pcma output frames (320 octets) count=12 generating 160 pcma input samples generating 160 pcma input samples got 4 pcma output frames (320 octets) count=12 generating 160 pcma input samples generating 160 pcma input samples got 4 pcma output frames (320 octets) count=12 generating 160 pcma input samples generating 160 pcma input samples got 4 pcma output frames (320 octets) count=12 generating 160 pcma input samples generating 160 pcma input samples got 4 pcma output frames (320 octets) count=12 generating 160 pcma input samples generating 160 pcma input samples got 4 pcma output frames (320 octets) count=12 generating 160 pcma input samples generating 160 pcma input samples got 4 pcma output frames (320 octets) count=12 generating 160 pcma input samples generating 160 pcma input samples got 4 pcma output frames (320 octets) count=12 == Transcoding test == converting pcma -> l16 generating 160 pcma input samples generating 160 pcma input samples got 3 l16 output frames (480 octets) count=12 generating 160 pcma input samples generating 160 pcma input samples got 3 l16 output frames (480 octets) count=12 generating 160 pcma input samples generating 160 pcma input samples got 3 l16 output frames (480 octets) count=12 generating 160 pcma input samples generating 160 pcma input samples got 3 l16 output frames (480 octets) count=12 generating 160 pcma input samples generating 160 pcma input samples got 3 l16 output frames (480 octets) count=12 generating 160 pcma input samples generating 160 pcma input samples got 3 l16 output frames (480 octets) count=12 generating 160 pcma input samples generating 160 pcma input samples got 3 l16 output frames (480 octets) count=12 generating 160 pcma input samples generating 160 pcma input samples got 3 l16 output frames (480 octets) count=12 == Transcoding test == converting pcma -> pcma generating 160 pcma input samples generating 160 pcma input samples got 3 pcma output frames (240 octets) count=12 generating 160 pcma input samples generating 160 pcma input samples got 3 pcma output frames (240 octets) count=12 generating 160 pcma input samples generating 160 pcma input samples got 3 pcma output frames (240 octets) count=12 generating 160 pcma input samples generating 160 pcma input samples got 3 pcma output frames (240 octets) count=12 generating 160 pcma input samples generating 160 pcma input samples got 3 pcma output frames (240 octets) count=12 generating 160 pcma input samples generating 160 pcma input samples got 3 pcma output frames (240 octets) count=12 generating 160 pcma input samples generating 160 pcma input samples got 3 pcma output frames (240 octets) count=12 generating 160 pcma input samples generating 160 pcma input samples got 3 pcma output frames (240 octets) count=12 == Transcoding test == converting pcma -> l16 generating 160 pcma input samples got 1 l16 output frames (160 octets) count=12 generating 160 pcma input samples got 1 l16 output frames (160 octets) count=12 got 1 l16 output frames (160 octets) count=12 generating 160 pcma input samples got 1 l16 output frames (160 octets) count=12 got 1 l16 output frames (160 octets) count=12 generating 160 pcma input samples got 1 l16 output frames (160 octets) count=12 got 1 l16 output frames (160 octets) count=12 generating 160 pcma input samples got 1 l16 output frames (160 octets) count=12 got 1 l16 output frames (160 octets) count=12 generating 160 pcma input samples got 1 l16 output frames (160 octets) count=12 got 1 l16 output frames (160 octets) count=12 generating 160 pcma input samples got 1 l16 output frames (160 octets) count=12 got 1 l16 output frames (160 octets) count=12 generating 160 pcma input samples got 1 l16 output frames (160 octets) count=12 got 1 l16 output frames (160 octets) count=12 generating 160 pcma input samples got 1 l16 output frames (160 octets) count=12 got 1 l16 output frames (160 octets) count=12 generating 160 pcma input samples got 1 l16 output frames (160 octets) count=12 got 1 l16 output frames (160 octets) count=12 generating 160 pcma input samples got 1 l16 output frames (160 octets) count=12 got 1 l16 output frames (160 octets) count=12 generating 160 pcma input samples got 1 l16 output frames (160 octets) count=12 got 1 l16 output frames (160 octets) count=12 generating 160 pcma input samples got 1 l16 output frames (160 octets) count=12 got 1 l16 output frames (160 octets) count=12 generating 160 pcma input samples got 1 l16 output frames (160 octets) count=12 got 1 l16 output frames (160 octets) count=12 generating 160 pcma input samples got 1 l16 output frames (160 octets) count=12 got 1 l16 output frames (160 octets) count=12 generating 160 pcma input samples got 1 l16 output frames (160 octets) count=12 got 1 l16 output frames (160 octets) count=12 == Transcoding test == converting pcma -> pcma generating 160 pcma input samples got 1 pcma output frames (80 octets) count=12 generating 160 pcma input samples got 1 pcma output frames (80 octets) count=12 got 1 pcma output frames (80 octets) count=12 generating 160 pcma input samples got 1 pcma output frames (80 octets) count=12 got 1 pcma output frames (80 octets) count=12 generating 160 pcma input samples got 1 pcma output frames (80 octets) count=12 got 1 pcma output frames (80 octets) count=12 generating 160 pcma input samples got 1 pcma output frames (80 octets) count=12 got 1 pcma output frames (80 octets) count=12 generating 160 pcma input samples got 1 pcma output frames (80 octets) count=12 got 1 pcma output frames (80 octets) count=12 generating 160 pcma input samples got 1 pcma output frames (80 octets) count=12 got 1 pcma output frames (80 octets) count=12 generating 160 pcma input samples got 1 pcma output frames (80 octets) count=12 got 1 pcma output frames (80 octets) count=12 generating 160 pcma input samples got 1 pcma output frames (80 octets) count=12 got 1 pcma output frames (80 octets) count=12 generating 160 pcma input samples got 1 pcma output frames (80 octets) count=12 got 1 pcma output frames (80 octets) count=12 generating 160 pcma input samples got 1 pcma output frames (80 octets) count=12 got 1 pcma output frames (80 octets) count=12 generating 160 pcma input samples got 1 pcma output frames (80 octets) count=12 got 1 pcma output frames (80 octets) count=12 generating 160 pcma input samples got 1 pcma output frames (80 octets) count=12 got 1 pcma output frames (80 octets) count=12 generating 160 pcma input samples got 1 pcma output frames (80 octets) count=12 got 1 pcma output frames (80 octets) count=12 generating 160 pcma input samples got 1 pcma output frames (80 octets) count=12 got 1 pcma output frames (80 octets) count=12 generating 160 pcma input samples got 1 pcma output frames (80 octets) count=12 got 1 pcma output frames (80 octets) count=12 Testing Initial L16->GSM, PCMA->GSM openbsc-0.15.0/openbsc/tests/sgsn/000077500000000000000000000000001265565154000170045ustar00rootroot00000000000000openbsc-0.15.0/openbsc/tests/sgsn/Makefile.am000066400000000000000000000023521265565154000210420ustar00rootroot00000000000000AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBCARES_CFLAGS) EXTRA_DIST = sgsn_test.ok noinst_PROGRAMS = sgsn_test sgsn_test_SOURCES = sgsn_test.c sgsn_test_LDFLAGS = \ -Wl,--wrap=sgsn_update_subscriber_data \ -Wl,--wrap=gprs_subscr_request_update_location \ -Wl,--wrap=gprs_subscr_request_auth_info \ -Wl,--wrap=gprs_gsup_client_send sgsn_test_LDADD = \ $(top_builddir)/src/gprs/gprs_llc_parse.o \ $(top_builddir)/src/gprs/gprs_llc.o \ $(top_builddir)/src/gprs/crc24.o \ $(top_builddir)/src/gprs/gprs_sndcp.o \ $(top_builddir)/src/gprs/gprs_gmm.o \ $(top_builddir)/src/gprs/gprs_sgsn.o \ $(top_builddir)/src/gprs/sgsn_vty.o \ $(top_builddir)/src/gprs/sgsn_libgtp.o \ $(top_builddir)/src/gprs/sgsn_auth.o \ $(top_builddir)/src/gprs/sgsn_ares.o \ $(top_builddir)/src/gprs/gprs_gsup_messages.o \ $(top_builddir)/src/gprs/gprs_gsup_client.o \ $(top_builddir)/src/gprs/gprs_utils.o \ $(top_builddir)/src/gprs/gprs_subscriber.o \ $(top_builddir)/src/gprs/gsm_04_08_gprs.o \ $(top_builddir)/src/libcommon/libcommon.a \ $(LIBOSMOABIS_LIBS) \ $(LIBOSMOCORE_LIBS) \ $(LIBOSMOGSM_LIBS) \ $(LIBOSMOGB_LIBS) \ $(LIBCARES_LIBS) \ $(LIBCRYPTO_LIBS) \ -lgtp -lrt openbsc-0.15.0/openbsc/tests/sgsn/sgsn_test.c000066400000000000000000001637151265565154000211760ustar00rootroot00000000000000/* Test 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern void *tall_msgb_ctx; void *tall_bsc_ctx; static struct sgsn_instance sgsn_inst = { .config_file = "osmo_sgsn.cfg", .cfg = { .gtp_statedir = "./", .auth_policy = SGSN_AUTH_POLICY_CLOSED, }, }; struct sgsn_instance *sgsn = &sgsn_inst; unsigned sgsn_tx_counter = 0; /* override */ int bssgp_tx_dl_ud(struct msgb *msg, uint16_t pdu_lifetime, struct bssgp_dl_ud_par *dup) { sgsn_tx_counter += 1; msgb_free(msg); return 0; } /* override, requires '-Wl,--wrap=sgsn_update_subscriber_data' */ void __real_sgsn_update_subscriber_data(struct sgsn_mm_ctx *); void (*update_subscriber_data_cb)(struct sgsn_mm_ctx *) = &__real_sgsn_update_subscriber_data; void __wrap_sgsn_update_subscriber_data(struct sgsn_mm_ctx *mmctx) { (*update_subscriber_data_cb)(mmctx); } /* override, requires '-Wl,--wrap=gprs_subscr_request_update_location' */ int __real_gprs_subscr_request_update_location(struct sgsn_mm_ctx *mmctx); int (*subscr_request_update_location_cb)(struct sgsn_mm_ctx *mmctx) = &__real_gprs_subscr_request_update_location; int __wrap_gprs_subscr_request_update_location(struct sgsn_mm_ctx *mmctx) { return (*subscr_request_update_location_cb)(mmctx); }; /* override, requires '-Wl,--wrap=gprs_subscr_request_auth_info' */ int __real_gprs_subscr_request_auth_info(struct sgsn_mm_ctx *mmctx); int (*subscr_request_auth_info_cb)(struct sgsn_mm_ctx *mmctx) = &__real_gprs_subscr_request_auth_info; int __wrap_gprs_subscr_request_auth_info(struct sgsn_mm_ctx *mmctx) { return (*subscr_request_auth_info_cb)(mmctx); }; /* override, requires '-Wl,--wrap=gprs_gsup_client_send' */ int __real_gprs_gsup_client_send(struct gprs_gsup_client *gsupc, struct msgb *msg); int (*gprs_gsup_client_send_cb)(struct gprs_gsup_client *gsupc, struct msgb *msg) = &__real_gprs_gsup_client_send; int __wrap_gprs_gsup_client_send(struct gprs_gsup_client *gsupc, struct msgb *msg) { return (*gprs_gsup_client_send_cb)(gsupc, msg); }; static int count(struct llist_head *head) { struct llist_head *cur; int count = 0; llist_for_each(cur, head) count += 1; return count; } static struct msgb *create_msg(const uint8_t *data, size_t len) { struct msgb *msg = msgb_alloc(len + 8, "test message"); msg->l1h = msgb_put(msg, 8); msg->l2h = msgb_put(msg, len); memcpy(msg->l2h, data, len); msgb_bcid(msg) = msg->l1h; msgb_gmmh(msg) = msg->l2h; return msg; } /* * Create a context and search for it */ static struct sgsn_mm_ctx *alloc_mm_ctx(uint32_t tlli, struct gprs_ra_id *raid) { struct sgsn_mm_ctx *ctx, *ictx; struct gprs_llc_lle *lle; int old_count = count(gprs_llme_list()); lle = gprs_lle_get_or_create(tlli, 3); ctx = sgsn_mm_ctx_alloc(tlli, raid); ctx->mm_state = GMM_REGISTERED_NORMAL; ctx->llme = lle->llme; ictx = sgsn_mm_ctx_by_tlli(tlli, raid); OSMO_ASSERT(ictx == ctx); OSMO_ASSERT(count(gprs_llme_list()) == old_count + 1); return ctx; } static void send_0408_message(struct gprs_llc_llme *llme, uint32_t tlli, const uint8_t *data, size_t data_len) { struct msgb *msg; sgsn_tx_counter = 0; msg = create_msg(data, data_len); msgb_tlli(msg) = tlli; gsm0408_gprs_rcvmsg(msg, llme); msgb_free(msg); } static void test_llme(void) { struct gprs_llc_lle *lle, *lle_copy; uint32_t local_tlli; uint32_t foreign_tlli; printf("Testing LLME allocations\n"); local_tlli = gprs_tmsi2tlli(0x234, TLLI_LOCAL); foreign_tlli = gprs_tmsi2tlli(0x234, TLLI_FOREIGN); /* initial state */ OSMO_ASSERT(count(gprs_llme_list()) == 0); /* Create a new entry */ lle = gprs_lle_get_or_create(local_tlli, 3); OSMO_ASSERT(lle); OSMO_ASSERT(count(gprs_llme_list()) == 1); /* No new entry is created */ lle_copy = gprs_lle_get_or_create(local_tlli, 3); OSMO_ASSERT(lle == lle_copy); OSMO_ASSERT(count(gprs_llme_list()) == 1); lle_copy = gprs_lle_get_or_create(foreign_tlli, 3); OSMO_ASSERT(lle == lle_copy); OSMO_ASSERT(count(gprs_llme_list()) == 1); /* unassign which should delete it*/ gprs_llgmm_assign(lle->llme, lle->llme->tlli, 0xffffffff, GPRS_ALGO_GEA0, NULL); /* Check that everything was cleaned up */ OSMO_ASSERT(count(gprs_llme_list()) == 0); } struct gsm_subscriber *last_updated_subscr = NULL; void my_dummy_sgsn_update_subscriber_data(struct sgsn_mm_ctx *mmctx) { OSMO_ASSERT(mmctx); fprintf(stderr, "Called %s, mmctx = %p, subscr = %p\n", __func__, mmctx, mmctx->subscr); last_updated_subscr = mmctx->subscr; } static void assert_subscr(const struct gsm_subscriber *subscr, const char *imsi) { struct gsm_subscriber *sfound; OSMO_ASSERT(subscr); OSMO_ASSERT(strcmp(subscr->imsi, imsi) == 0); sfound = gprs_subscr_get_by_imsi(imsi); OSMO_ASSERT(sfound == subscr); subscr_put(sfound); } static void show_subscrs(FILE *out) { struct gsm_subscriber *subscr; llist_for_each_entry(subscr, &active_subscribers, entry) { fprintf(out, " Subscriber: %s, " "use count: %d\n", subscr->imsi, subscr->use_count); } } static void assert_no_subscrs() { show_subscrs(stdout); fflush(stdout); OSMO_ASSERT(llist_empty(&active_subscribers)); } static void test_subscriber(void) { struct gsm_subscriber *s1, *s2, *s3, *sfound; const char *imsi1 = "1234567890"; const char *imsi2 = "9876543210"; const char *imsi3 = "5656565656"; update_subscriber_data_cb = my_dummy_sgsn_update_subscriber_data; printf("Testing core subscriber data API\n"); /* Check for emptiness */ OSMO_ASSERT(gprs_subscr_get_by_imsi(imsi1) == NULL); OSMO_ASSERT(gprs_subscr_get_by_imsi(imsi2) == NULL); OSMO_ASSERT(gprs_subscr_get_by_imsi(imsi3) == NULL); /* Allocate entry 1 */ s1 = gprs_subscr_get_or_create(imsi1); s1->flags |= GSM_SUBSCRIBER_FIRST_CONTACT; assert_subscr(s1, imsi1); OSMO_ASSERT(gprs_subscr_get_by_imsi(imsi2) == NULL); /* Allocate entry 2 */ s2 = gprs_subscr_get_or_create(imsi2); s2->flags |= GSM_SUBSCRIBER_FIRST_CONTACT; /* Allocate entry 3 */ s3 = gprs_subscr_get_or_create(imsi3); /* Check entries */ assert_subscr(s1, imsi1); assert_subscr(s2, imsi2); assert_subscr(s3, imsi3); /* Update entry 1 */ last_updated_subscr = NULL; gprs_subscr_update(s1); OSMO_ASSERT(last_updated_subscr == NULL); OSMO_ASSERT(s1->sgsn_data->mm == NULL); OSMO_ASSERT((s1->flags & GSM_SUBSCRIBER_FIRST_CONTACT) == 0); /* There is no subscriber cache. Verify it */ gprs_subscr_cleanup(s1); subscr_put(s1); s1 = NULL; sfound = gprs_subscr_get_by_imsi(imsi1); OSMO_ASSERT(sfound == NULL); assert_subscr(s2, imsi2); assert_subscr(s3, imsi3); /* Free entry 2 (GSM_SUBSCRIBER_FIRST_CONTACT is set) */ gprs_subscr_cleanup(s2); subscr_put(s2); s2 = NULL; OSMO_ASSERT(gprs_subscr_get_by_imsi(imsi1) == NULL); OSMO_ASSERT(gprs_subscr_get_by_imsi(imsi2) == NULL); assert_subscr(s3, imsi3); /* Try to delete entry 3 */ gprs_subscr_cleanup(s3); subscr_put(s3); s3 = NULL; OSMO_ASSERT(gprs_subscr_get_by_imsi(imsi3) == NULL); OSMO_ASSERT(llist_empty(&active_subscribers)); update_subscriber_data_cb = __real_sgsn_update_subscriber_data; } static void test_auth_triplets(void) { struct gsm_subscriber *s1, *s1found; const char *imsi1 = "1234567890"; struct gsm_auth_tuple *at; struct sgsn_mm_ctx *ctx; struct gprs_ra_id raid = { 0, }; uint32_t local_tlli = 0xffeeddcc; printf("Testing authentication triplet handling\n"); /* Check for emptiness */ OSMO_ASSERT(gprs_subscr_get_by_imsi(imsi1) == NULL); /* Allocate entry 1 */ s1 = gprs_subscr_get_or_create(imsi1); s1->flags |= GSM_SUBSCRIBER_FIRST_CONTACT; s1found = gprs_subscr_get_by_imsi(imsi1); OSMO_ASSERT(s1found == s1); subscr_put(s1found); /* Create a context */ OSMO_ASSERT(count(gprs_llme_list()) == 0); ctx = alloc_mm_ctx(local_tlli, &raid); /* Attach s1 to ctx */ ctx->subscr = subscr_get(s1); ctx->subscr->sgsn_data->mm = ctx; /* Try to get auth tuple */ at = sgsn_auth_get_tuple(ctx, GSM_KEY_SEQ_INVAL); OSMO_ASSERT(at == NULL); /* Add triplets */ s1->sgsn_data->auth_triplets[0].key_seq = 0; s1->sgsn_data->auth_triplets[1].key_seq = 1; s1->sgsn_data->auth_triplets[2].key_seq = 2; /* Try to get auth tuple */ at = sgsn_auth_get_tuple(ctx, GSM_KEY_SEQ_INVAL); OSMO_ASSERT(at != NULL); OSMO_ASSERT(at->key_seq == 0); OSMO_ASSERT(at->use_count == 1); at = sgsn_auth_get_tuple(ctx, at->key_seq); OSMO_ASSERT(at != NULL); OSMO_ASSERT(at->key_seq == 1); OSMO_ASSERT(at->use_count == 1); at = sgsn_auth_get_tuple(ctx, at->key_seq); OSMO_ASSERT(at != NULL); OSMO_ASSERT(at->key_seq == 2); OSMO_ASSERT(at->use_count == 1); at = sgsn_auth_get_tuple(ctx, at->key_seq); OSMO_ASSERT(at == NULL); /* Free MM context and subscriber */ subscr_put(s1); sgsn_mm_ctx_cleanup_free(ctx); s1found = gprs_subscr_get_by_imsi(imsi1); OSMO_ASSERT(s1found == NULL); } #define TEST_GSUP_IMSI1_IE 0x01, 0x05, 0x21, 0x43, 0x65, 0x87, 0x09 static int rx_gsup_message(const uint8_t *data, size_t data_len) { struct msgb *msg; int rc; msg = msgb_alloc(1024, __func__); msg->l2h = msgb_put(msg, data_len); OSMO_ASSERT(msg->l2h != NULL); memcpy(msg->l2h, data, data_len); rc = gprs_subscr_rx_gsup_message(msg); msgb_free(msg); return rc; } static void test_subscriber_gsup(void) { struct gsm_subscriber *s1, *s1found; const char *imsi1 = "1234567890"; struct sgsn_mm_ctx *ctx; struct gprs_ra_id raid = { 0, }; uint32_t local_tlli = 0xffeeddcc; struct sgsn_subscriber_pdp_data *pdpd; int rc; static const uint8_t send_auth_info_res[] = { 0x0a, TEST_GSUP_IMSI1_IE, 0x03, 0x22, /* Auth tuple */ 0x20, 0x10, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x21, 0x04, 0x21, 0x22, 0x23, 0x24, 0x22, 0x08, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x03, 0x22, /* Auth tuple */ 0x20, 0x10, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x21, 0x04, 0xa1, 0xa2, 0xa3, 0xa4, 0x22, 0x08, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, }; static const uint8_t send_auth_info_err[] = { 0x09, TEST_GSUP_IMSI1_IE, 0x02, 0x01, 0x07 /* GPRS not allowed */ }; #define MSISDN 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09 static const uint8_t s1_msisdn[] = { MSISDN }; static const uint8_t update_location_res[] = { 0x06, TEST_GSUP_IMSI1_IE, 0x08, 0x09, MSISDN, 0x04, 0x00, /* PDP info complete */ 0x05, 0x12, 0x10, 0x01, 0x01, 0x11, 0x02, 0xf1, 0x21, /* IPv4 */ 0x12, 0x09, 0x04, 't', 'e', 's', 't', 0x03, 'a', 'p', 'n', 0x05, 0x11, 0x10, 0x01, 0x02, 0x11, 0x02, 0xf1, 0x21, /* IPv4 */ 0x12, 0x08, 0x03, 'f', 'o', 'o', 0x03, 'a', 'p', 'n', }; #undef MSISDN static const uint8_t update_location_err[] = { 0x05, TEST_GSUP_IMSI1_IE, 0x02, 0x01, 0x07 /* GPRS not allowed */ }; static const uint8_t location_cancellation_req[] = { 0x1c, TEST_GSUP_IMSI1_IE, 0x06, 0x01, 0x00, }; static const uint8_t location_cancellation_req_withdraw[] = { 0x1c, TEST_GSUP_IMSI1_IE, 0x06, 0x01, 0x01, }; static const uint8_t location_cancellation_req_other[] = { 0x1c, 0x01, 0x05, 0x11, 0x11, 0x11, 0x11, 0x01, 0x06, 0x01, 0x00, }; static const uint8_t purge_ms_err[] = { 0x0d, TEST_GSUP_IMSI1_IE, 0x02, 0x01, 0x02, /* IMSI unknown in HLR */ }; static const uint8_t purge_ms_err_no_cause[] = { 0x0d, TEST_GSUP_IMSI1_IE, }; static const uint8_t purge_ms_res[] = { 0x0e, TEST_GSUP_IMSI1_IE, 0x07, 0x00, }; static const uint8_t insert_data_req[] = { 0x10, TEST_GSUP_IMSI1_IE, 0x05, 0x11, 0x10, 0x01, 0x03, 0x11, 0x02, 0xf1, 0x21, /* IPv4 */ 0x12, 0x08, 0x03, 'b', 'a', 'r', 0x03, 'a', 'p', 'n', }; static const uint8_t delete_data_req[] = { 0x14, TEST_GSUP_IMSI1_IE, 0x10, 0x01, 0x03, }; printf("Testing subcriber GSUP handling\n"); update_subscriber_data_cb = my_dummy_sgsn_update_subscriber_data; /* Check for emptiness */ OSMO_ASSERT(gprs_subscr_get_by_imsi(imsi1) == NULL); /* Allocate entry 1 */ s1 = gprs_subscr_get_or_create(imsi1); s1->flags |= GSM_SUBSCRIBER_FIRST_CONTACT; s1found = gprs_subscr_get_by_imsi(imsi1); OSMO_ASSERT(s1found == s1); subscr_put(s1found); /* Create a context */ OSMO_ASSERT(count(gprs_llme_list()) == 0); ctx = alloc_mm_ctx(local_tlli, &raid); /* Attach s1 to ctx */ ctx->subscr = subscr_get(s1); ctx->subscr->sgsn_data->mm = ctx; /* Inject SendAuthInfoReq GSUP message */ rc = rx_gsup_message(send_auth_info_res, sizeof(send_auth_info_res)); OSMO_ASSERT(rc >= 0); OSMO_ASSERT(last_updated_subscr == s1); /* Check triplets */ OSMO_ASSERT(s1->sgsn_data->auth_triplets[0].key_seq == 0); OSMO_ASSERT(s1->sgsn_data->auth_triplets[1].key_seq == 1); OSMO_ASSERT(s1->sgsn_data->auth_triplets[2].key_seq == GSM_KEY_SEQ_INVAL); /* Inject SendAuthInfoErr GSUP message */ rc = rx_gsup_message(send_auth_info_err, sizeof(send_auth_info_err)); OSMO_ASSERT(rc == -GMM_CAUSE_GPRS_NOTALLOWED); OSMO_ASSERT(last_updated_subscr == s1); OSMO_ASSERT(s1->sgsn_data->error_cause == GMM_CAUSE_GPRS_NOTALLOWED); /* Check triplets */ OSMO_ASSERT(s1->sgsn_data->auth_triplets[0].key_seq == GSM_KEY_SEQ_INVAL); OSMO_ASSERT(s1->sgsn_data->auth_triplets[1].key_seq == GSM_KEY_SEQ_INVAL); OSMO_ASSERT(s1->sgsn_data->auth_triplets[2].key_seq == GSM_KEY_SEQ_INVAL); /* Inject UpdateLocRes GSUP message */ rc = rx_gsup_message(update_location_res, sizeof(update_location_res)); OSMO_ASSERT(rc >= 0); OSMO_ASSERT(last_updated_subscr == s1); OSMO_ASSERT(s1->flags & GPRS_SUBSCRIBER_ENABLE_PURGE); OSMO_ASSERT(s1->sgsn_data->error_cause == SGSN_ERROR_CAUSE_NONE); OSMO_ASSERT(s1->sgsn_data->msisdn_len == sizeof(s1_msisdn)); OSMO_ASSERT(memcmp(s1->sgsn_data->msisdn, s1_msisdn, sizeof(s1_msisdn)) == 0); OSMO_ASSERT(!llist_empty(&s1->sgsn_data->pdp_list)); pdpd = llist_entry(s1->sgsn_data->pdp_list.next, struct sgsn_subscriber_pdp_data, list); OSMO_ASSERT(strcmp(pdpd->apn_str, "test.apn") == 0); pdpd = llist_entry(pdpd->list.next, struct sgsn_subscriber_pdp_data, list); OSMO_ASSERT(strcmp(pdpd->apn_str, "foo.apn") == 0); /* Check authorization */ OSMO_ASSERT(s1->authorized == 1); /* Inject UpdateLocErr GSUP message */ rc = rx_gsup_message(update_location_err, sizeof(update_location_err)); OSMO_ASSERT(rc == -GMM_CAUSE_GPRS_NOTALLOWED); OSMO_ASSERT(last_updated_subscr == s1); OSMO_ASSERT(s1->sgsn_data->error_cause == GMM_CAUSE_GPRS_NOTALLOWED); /* Check authorization */ OSMO_ASSERT(s1->authorized == 0); /* Inject InsertSubscrData GSUP message */ last_updated_subscr = NULL; rc = rx_gsup_message(insert_data_req, sizeof(insert_data_req)); OSMO_ASSERT(rc == -GMM_CAUSE_MSGT_NOTEXIST_NOTIMPL); OSMO_ASSERT(last_updated_subscr == NULL); /* Inject DeleteSubscrData GSUP message */ last_updated_subscr = NULL; rc = rx_gsup_message(delete_data_req, sizeof(delete_data_req)); OSMO_ASSERT(rc == -GMM_CAUSE_MSGT_NOTEXIST_NOTIMPL); OSMO_ASSERT(last_updated_subscr == NULL); /* Inject wrong LocCancelReq GSUP message */ last_updated_subscr = NULL; rc = rx_gsup_message(location_cancellation_req_other, sizeof(location_cancellation_req_other)); OSMO_ASSERT(rc == -GMM_CAUSE_IMSI_UNKNOWN); OSMO_ASSERT(last_updated_subscr == NULL); /* Check cancellation result */ OSMO_ASSERT(!(s1->flags & GPRS_SUBSCRIBER_CANCELLED)); OSMO_ASSERT(s1->sgsn_data->mm != NULL); /* Inject LocCancelReq GSUP message */ rc = rx_gsup_message(location_cancellation_req, sizeof(location_cancellation_req)); OSMO_ASSERT(rc >= 0); OSMO_ASSERT(last_updated_subscr == s1); OSMO_ASSERT(s1->sgsn_data->error_cause == SGSN_ERROR_CAUSE_NONE); /* Check cancellation result */ OSMO_ASSERT(s1->flags & GPRS_SUBSCRIBER_CANCELLED); OSMO_ASSERT(s1->sgsn_data->mm == NULL); /* Inject LocCancelReq(withdraw) GSUP message */ rc = rx_gsup_message(location_cancellation_req_withdraw, sizeof(location_cancellation_req_withdraw)); OSMO_ASSERT(rc >= 0); OSMO_ASSERT(s1->sgsn_data->error_cause == GMM_CAUSE_IMPL_DETACHED); /* Inject PurgeMsRes GSUP message */ rc = rx_gsup_message(purge_ms_res, sizeof(purge_ms_res)); OSMO_ASSERT(rc >= 0); OSMO_ASSERT(!(s1->flags & GPRS_SUBSCRIBER_ENABLE_PURGE)); /* Free MM context and subscriber */ OSMO_ASSERT(ctx->subscr == NULL); sgsn_mm_ctx_cleanup_free(ctx); subscr_put(s1); s1found = gprs_subscr_get_by_imsi(imsi1); OSMO_ASSERT(s1found == NULL); /* Inject PurgeMsRes GSUP message */ rc = rx_gsup_message(purge_ms_res, sizeof(purge_ms_res)); OSMO_ASSERT(rc >= 0); /* Inject PurgeMsErr(IMSI unknown in HLR) GSUP message */ rc = rx_gsup_message(purge_ms_err, sizeof(purge_ms_err)); OSMO_ASSERT(rc == -GMM_CAUSE_IMSI_UNKNOWN); /* Inject PurgeMsErr() GSUP message */ rc = rx_gsup_message(purge_ms_err_no_cause, sizeof(purge_ms_err_no_cause)); OSMO_ASSERT(rc == -GMM_CAUSE_NET_FAIL); /* Inject InsertSubscrData GSUP message (unknown IMSI) */ last_updated_subscr = NULL; rc = rx_gsup_message(insert_data_req, sizeof(insert_data_req)); OSMO_ASSERT(rc == -GMM_CAUSE_IMSI_UNKNOWN); OSMO_ASSERT(last_updated_subscr == NULL); /* Inject DeleteSubscrData GSUP message (unknown IMSI) */ rc = rx_gsup_message(delete_data_req, sizeof(delete_data_req)); OSMO_ASSERT(rc == -GMM_CAUSE_IMSI_UNKNOWN); OSMO_ASSERT(last_updated_subscr == NULL); /* Inject LocCancelReq GSUP message (unknown IMSI) */ rc = rx_gsup_message(location_cancellation_req, sizeof(location_cancellation_req)); OSMO_ASSERT(rc == -GMM_CAUSE_IMSI_UNKNOWN); OSMO_ASSERT(last_updated_subscr == NULL); update_subscriber_data_cb = __real_sgsn_update_subscriber_data; } int my_gprs_gsup_client_send_dummy(struct gprs_gsup_client *gsupc, struct msgb *msg) { msgb_free(msg); return 0; }; /* * Test that a GMM Detach will remove the MMCTX and the * associated LLME. */ static void test_gmm_detach(void) { struct gprs_ra_id raid = { 0, }; struct sgsn_mm_ctx *ctx, *ictx; uint32_t local_tlli; printf("Testing GMM detach\n"); /* DTAP - Detach Request (MO) */ /* normal detach, power_off = 0 */ static const unsigned char detach_req[] = { 0x08, 0x05, 0x01, 0x18, 0x05, 0xf4, 0xef, 0xe2, 0xb7, 0x00, 0x19, 0x03, 0xb9, 0x97, 0xcb }; local_tlli = gprs_tmsi2tlli(0x23, TLLI_LOCAL); /* Create a context */ OSMO_ASSERT(count(gprs_llme_list()) == 0); ctx = alloc_mm_ctx(local_tlli, &raid); /* inject the detach */ send_0408_message(ctx->llme, local_tlli, detach_req, ARRAY_SIZE(detach_req)); /* verify that a single message (hopefully the Detach Accept) has been * sent by the SGSN */ OSMO_ASSERT(sgsn_tx_counter == 1); /* verify that things are gone */ OSMO_ASSERT(count(gprs_llme_list()) == 0); ictx = sgsn_mm_ctx_by_tlli(local_tlli, &raid); OSMO_ASSERT(!ictx); } /* * Test that a GMM Detach will remove the MMCTX and the associated LLME but * will not sent a Detach Accept message (power_off = 1) */ static void test_gmm_detach_power_off(void) { struct gprs_ra_id raid = { 0, }; struct sgsn_mm_ctx *ctx, *ictx; uint32_t local_tlli; printf("Testing GMM detach (power off)\n"); /* DTAP - Detach Request (MO) */ /* normal detach, power_off = 1 */ static const unsigned char detach_req[] = { 0x08, 0x05, 0x09, 0x18, 0x05, 0xf4, 0xef, 0xe2, 0xb7, 0x00, 0x19, 0x03, 0xb9, 0x97, 0xcb }; local_tlli = gprs_tmsi2tlli(0x23, TLLI_LOCAL); /* Create a context */ OSMO_ASSERT(count(gprs_llme_list()) == 0); ctx = alloc_mm_ctx(local_tlli, &raid); /* inject the detach */ send_0408_message(ctx->llme, local_tlli, detach_req, ARRAY_SIZE(detach_req)); /* verify that no message (and therefore no Detach Accept) has been * sent by the SGSN */ OSMO_ASSERT(sgsn_tx_counter == 0); /* verify that things are gone */ OSMO_ASSERT(count(gprs_llme_list()) == 0); ictx = sgsn_mm_ctx_by_tlli(local_tlli, &raid); OSMO_ASSERT(!ictx); } /* * Test that a GMM Detach will remove the associated LLME if there is no MMCTX. */ static void test_gmm_detach_no_mmctx(void) { struct gprs_llc_lle *lle; uint32_t local_tlli; printf("Testing GMM detach (no MMCTX)\n"); /* DTAP - Detach Request (MO) */ /* normal detach, power_off = 0 */ static const unsigned char detach_req[] = { 0x08, 0x05, 0x01, 0x18, 0x05, 0xf4, 0xef, 0xe2, 0xb7, 0x00, 0x19, 0x03, 0xb9, 0x97, 0xcb }; /* Create an LLME */ OSMO_ASSERT(count(gprs_llme_list()) == 0); local_tlli = gprs_tmsi2tlli(0x23, TLLI_LOCAL); lle = gprs_lle_get_or_create(local_tlli, 3); OSMO_ASSERT(count(gprs_llme_list()) == 1); /* inject the detach */ send_0408_message(lle->llme, local_tlli, detach_req, ARRAY_SIZE(detach_req)); /* verify that the LLME is gone */ OSMO_ASSERT(count(gprs_llme_list()) == 0); } /* * Test that a single GMM Detach Accept message will not cause the SGSN to send * any message or leave an MM context at the SGSN. */ static void test_gmm_detach_accept_unexpected(void) { struct gprs_llc_lle *lle; uint32_t local_tlli; printf("Testing GMM detach accept (unexpected)\n"); /* DTAP - Detach Accept (MT) */ /* normal detach */ static const unsigned char detach_acc[] = { 0x08, 0x06 }; /* Create an LLME */ OSMO_ASSERT(count(gprs_llme_list()) == 0); local_tlli = gprs_tmsi2tlli(0x23, TLLI_LOCAL); lle = gprs_lle_get_or_create(local_tlli, 3); /* inject the detach */ send_0408_message(lle->llme, local_tlli, detach_acc, ARRAY_SIZE(detach_acc)); /* verify that no message (and therefore no Status or XID reset) has been * sent by the SGSN */ OSMO_ASSERT(sgsn_tx_counter == 0); /* verify that things are gone */ OSMO_ASSERT(count(gprs_llme_list()) == 0); } /* * Test that a GMM Status will remove the associated LLME if there is no MMCTX. */ static void test_gmm_status_no_mmctx(void) { struct gprs_llc_lle *lle; uint32_t local_tlli; printf("Testing GMM Status (no MMCTX)\n"); /* DTAP - GMM Status, protocol error */ static const unsigned char gmm_status[] = { 0x08, 0x20, 0x6f }; /* Create an LLME */ OSMO_ASSERT(count(gprs_llme_list()) == 0); local_tlli = gprs_tmsi2tlli(0x23, TLLI_LOCAL); lle = gprs_lle_get_or_create(local_tlli, 3); OSMO_ASSERT(count(gprs_llme_list()) == 1); /* inject the detach */ send_0408_message(lle->llme, local_tlli, gmm_status, ARRAY_SIZE(gmm_status)); /* verify that no message has been sent by the SGSN */ OSMO_ASSERT(sgsn_tx_counter == 0); /* verify that the LLME is gone */ OSMO_ASSERT(count(gprs_llme_list()) == 0); } /* * Test the GMM Attach procedure */ static void test_gmm_attach(int retry) { struct gprs_ra_id raid = { 0, }; struct sgsn_mm_ctx *ctx = NULL; struct sgsn_mm_ctx *ictx; uint32_t ptmsi1; uint32_t foreign_tlli; uint32_t local_tlli = 0; struct gprs_llc_lle *lle; /* DTAP - Attach Request */ /* The P-TMSI is not known by the SGSN */ static const unsigned char attach_req[] = { 0x08, 0x01, 0x02, 0xf5, 0xe0, 0x21, 0x08, 0x02, 0x05, 0xf4, 0xfb, 0xc5, 0x46, 0x79, 0x11, 0x22, 0x33, 0x40, 0x50, 0x60, 0x19, 0x18, 0xb3, 0x43, 0x2b, 0x25, 0x96, 0x62, 0x00, 0x60, 0x80, 0x9a, 0xc2, 0xc6, 0x62, 0x00, 0x60, 0x80, 0xba, 0xc8, 0xc6, 0x62, 0x00, 0x60, 0x80, 0x00 }; /* DTAP - Identity Response IMEI */ static const unsigned char ident_resp_imei[] = { 0x08, 0x16, 0x08, 0x9a, 0x78, 0x56, 0x34, 0x12, 0x90, 0x78, 0x56 }; /* DTAP - Identity Response IMSI */ static const unsigned char ident_resp_imsi[] = { 0x08, 0x16, 0x08, 0x19, 0x32, 0x54, 0x76, 0x98, 0x10, 0x32, 0x54 }; /* DTAP - Authentication and Ciphering Resp */ static const unsigned char auth_ciph_resp[] = { 0x08, 0x13, 0x00, 0x22, 0x51, 0xe5, 0x51, 0xe5, 0x23, 0x09, 0x9a, 0x78, 0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x01 }; /* DTAP - Attach Complete */ static const unsigned char attach_compl[] = { 0x08, 0x03 }; /* DTAP - Detach Request (MO) */ /* normal detach, power_off = 0 */ static const unsigned char detach_req[] = { 0x08, 0x05, 0x01, 0x18, 0x05, 0xf4, 0xeb, 0x8b, 0x45, 0x67, 0x19, 0x03, 0xb9, 0x97, 0xcb }; printf("Testing GMM attach%s\n", retry ? " with retry" : ""); /* reset the PRNG used by sgsn_alloc_ptmsi */ srand(1); ptmsi1 = sgsn_alloc_ptmsi(); OSMO_ASSERT(ptmsi1 != GSM_RESERVED_TMSI); /* reset the PRNG, so that the same P-TMSI sequence will be generated * again */ srand(1); foreign_tlli = gprs_tmsi2tlli(0xc0000023, TLLI_FOREIGN); /* Create a LLE/LLME */ OSMO_ASSERT(count(gprs_llme_list()) == 0); lle = gprs_lle_get_or_create(foreign_tlli, 3); OSMO_ASSERT(count(gprs_llme_list()) == 1); /* inject the attach request */ send_0408_message(lle->llme, foreign_tlli, attach_req, ARRAY_SIZE(attach_req)); ctx = sgsn_mm_ctx_by_tlli(foreign_tlli, &raid); OSMO_ASSERT(ctx != NULL); OSMO_ASSERT(ctx->mm_state == GMM_COMMON_PROC_INIT); /* we expect an identity request (IMEI) */ OSMO_ASSERT(sgsn_tx_counter == 1); /* inject the identity response (IMEI) */ send_0408_message(ctx->llme, foreign_tlli, ident_resp_imei, ARRAY_SIZE(ident_resp_imei)); /* we expect an identity request (IMSI) */ OSMO_ASSERT(sgsn_tx_counter == 1); /* inject the identity response (IMSI) */ send_0408_message(ctx->llme, foreign_tlli, ident_resp_imsi, ARRAY_SIZE(ident_resp_imsi)); /* check that the MM context has not been removed due to a failed * authorization */ OSMO_ASSERT(ctx == sgsn_mm_ctx_by_tlli(foreign_tlli, &raid)); OSMO_ASSERT(ctx->mm_state == GMM_COMMON_PROC_INIT); retry_attach_req: if (retry && sgsn_tx_counter == 0) { fprintf(stderr, "Retrying attach request\n"); /* re-inject the attach request */ send_0408_message(lle->llme, foreign_tlli, attach_req, ARRAY_SIZE(attach_req)); } if (ctx->auth_state == SGSN_AUTH_AUTHENTICATE && sgsn_tx_counter == 1) { /* we got an auth & ciph request */ /* inject the auth & ciph response */ send_0408_message(ctx->llme, foreign_tlli, auth_ciph_resp, ARRAY_SIZE(auth_ciph_resp)); /* check that the MM context has not been removed due to a * failed authorization */ OSMO_ASSERT(ctx == sgsn_mm_ctx_by_tlli(foreign_tlli, &raid)); if (ctx->subscr && ctx->subscr->sgsn_data->msisdn_len > 0) OSMO_ASSERT(strcmp(ctx->msisdn, "+49166213323") == 0); } if (retry && sgsn_tx_counter == 0) goto retry_attach_req; /* we expect an attach accept/reject */ OSMO_ASSERT(sgsn_tx_counter == 1); /* this has been randomly assigned by the SGSN */ local_tlli = gprs_tmsi2tlli(ptmsi1, TLLI_LOCAL); /* inject the attach complete */ send_0408_message(ctx->llme, local_tlli, attach_compl, ARRAY_SIZE(attach_compl)); OSMO_ASSERT(ctx->mm_state == GMM_REGISTERED_NORMAL); /* we don't expect a response */ OSMO_ASSERT(sgsn_tx_counter == 0); /* inject the detach */ send_0408_message(ctx->llme, local_tlli, detach_req, ARRAY_SIZE(detach_req)); /* verify that things are gone */ OSMO_ASSERT(count(gprs_llme_list()) == 0); ictx = sgsn_mm_ctx_by_tlli(local_tlli, &raid); OSMO_ASSERT(!ictx); } static void test_gmm_attach_acl(void) { const enum sgsn_auth_policy saved_auth_policy = sgsn->cfg.auth_policy; sgsn_inst.cfg.auth_policy = SGSN_AUTH_POLICY_CLOSED; sgsn_acl_add("123456789012345", &sgsn->cfg); printf("Auth policy 'closed': "); test_gmm_attach(0); sgsn_acl_del("123456789012345", &sgsn->cfg); sgsn->cfg.auth_policy = saved_auth_policy; } int my_subscr_request_update_location(struct sgsn_mm_ctx *mmctx) { int rc; rc = __real_gprs_subscr_request_update_location(mmctx); if (rc == -ENOTSUP) { OSMO_ASSERT(mmctx->subscr); gprs_subscr_update(mmctx->subscr); } return rc; }; int my_subscr_request_auth_info(struct sgsn_mm_ctx *mmctx) { gprs_subscr_update(mmctx->subscr); return 0; }; static void test_gmm_attach_subscr(void) { const enum sgsn_auth_policy saved_auth_policy = sgsn->cfg.auth_policy; struct gsm_subscriber *subscr; sgsn_inst.cfg.auth_policy = SGSN_AUTH_POLICY_REMOTE; subscr_request_update_location_cb = my_subscr_request_update_location; subscr_request_auth_info_cb = my_subscr_request_auth_info; subscr = gprs_subscr_get_or_create("123456789012345"); subscr->authorized = 1; printf("Auth policy 'remote': "); test_gmm_attach(0); subscr_put(subscr); assert_no_subscrs(); sgsn->cfg.auth_policy = saved_auth_policy; subscr_request_update_location_cb = __real_gprs_subscr_request_update_location; subscr_request_auth_info_cb = __real_gprs_subscr_request_auth_info; } int my_subscr_request_auth_info_fake_auth(struct sgsn_mm_ctx *mmctx) { /* Fake an authentication */ OSMO_ASSERT(mmctx->subscr); mmctx->is_authenticated = 1; gprs_subscr_update_auth_info(mmctx->subscr); return 0; }; static void test_gmm_attach_subscr_fake_auth(void) { const enum sgsn_auth_policy saved_auth_policy = sgsn->cfg.auth_policy; struct gsm_subscriber *subscr; sgsn_inst.cfg.auth_policy = SGSN_AUTH_POLICY_REMOTE; subscr_request_update_location_cb = my_subscr_request_update_location; subscr_request_auth_info_cb = my_subscr_request_auth_info_fake_auth; subscr = gprs_subscr_get_or_create("123456789012345"); subscr->authorized = 1; sgsn->cfg.require_authentication = 1; sgsn->cfg.require_update_location = 1; printf("Auth policy 'remote', auth faked: "); test_gmm_attach(0); subscr_put(subscr); assert_no_subscrs(); sgsn->cfg.auth_policy = saved_auth_policy; subscr_request_update_location_cb = __real_gprs_subscr_request_update_location; subscr_request_auth_info_cb = __real_gprs_subscr_request_auth_info; } int my_subscr_request_auth_info_real_auth(struct sgsn_mm_ctx *mmctx) { struct gsm_auth_tuple at = { .sres = {0x51, 0xe5, 0x51, 0xe5}, .key_seq = 0 }; /* Fake an authentication */ OSMO_ASSERT(mmctx->subscr); mmctx->subscr->sgsn_data->auth_triplets[0] = at; gprs_subscr_update_auth_info(mmctx->subscr); return 0; }; static void test_gmm_attach_subscr_real_auth(void) { const enum sgsn_auth_policy saved_auth_policy = sgsn->cfg.auth_policy; struct gsm_subscriber *subscr; sgsn_inst.cfg.auth_policy = SGSN_AUTH_POLICY_REMOTE; subscr_request_update_location_cb = my_subscr_request_update_location; subscr_request_auth_info_cb = my_subscr_request_auth_info_real_auth; subscr = gprs_subscr_get_or_create("123456789012345"); subscr->authorized = 1; sgsn->cfg.require_authentication = 1; sgsn->cfg.require_update_location = 1; printf("Auth policy 'remote', triplet based auth: "); test_gmm_attach(0); subscr_put(subscr); assert_no_subscrs(); sgsn->cfg.auth_policy = saved_auth_policy; subscr_request_update_location_cb = __real_gprs_subscr_request_update_location; subscr_request_auth_info_cb = __real_gprs_subscr_request_auth_info; } #define TEST_GSUP_IMSI_LONG_IE 0x01, 0x08, \ 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0xf5 static int auth_info_skip = 0; static int upd_loc_skip = 0; int my_subscr_request_auth_info_gsup_auth(struct sgsn_mm_ctx *mmctx) { static const uint8_t send_auth_info_res[] = { 0x0a, TEST_GSUP_IMSI_LONG_IE, 0x03, 0x22, /* Auth tuple */ 0x20, 0x10, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x21, 0x04, 0x51, 0xe5, 0x51, 0xe5, 0x22, 0x08, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, }; OSMO_ASSERT(!mmctx || mmctx->subscr); if (auth_info_skip > 0) { auth_info_skip -= 1; return -EAGAIN; } /* Fake an SendAuthInfoRes */ rx_gsup_message(send_auth_info_res, sizeof(send_auth_info_res)); return 0; }; int my_subscr_request_update_gsup_auth(struct sgsn_mm_ctx *mmctx) { static const uint8_t update_location_res[] = { 0x06, TEST_GSUP_IMSI_LONG_IE, 0x04, 0x00, /* PDP info complete */ 0x05, 0x12, 0x10, 0x01, 0x01, 0x11, 0x02, 0xf1, 0x21, /* IPv4 */ 0x12, 0x09, 0x04, 't', 'e', 's', 't', 0x03, 'a', 'p', 'n', 0x08, 0x07, /* MSISDN 49166213323 encoded */ 0x91, 0x94, 0x61, 0x26, 0x31, 0x23, 0xF3, 0x09, 0x07, /* MSISDN 38166213323 encoded */ 0x91, 0x83, 0x61, 0x26, 0x31, 0x23, 0xF3, }; OSMO_ASSERT(!mmctx || mmctx->subscr); if (upd_loc_skip > 0) { upd_loc_skip -= 1; return -EAGAIN; } /* Fake an UpdateLocRes */ return rx_gsup_message(update_location_res, sizeof(update_location_res)); }; static void test_gmm_attach_subscr_gsup_auth(int retry) { const enum sgsn_auth_policy saved_auth_policy = sgsn->cfg.auth_policy; struct gsm_subscriber *subscr; sgsn_inst.cfg.auth_policy = SGSN_AUTH_POLICY_REMOTE; subscr_request_update_location_cb = my_subscr_request_update_gsup_auth; subscr_request_auth_info_cb = my_subscr_request_auth_info_gsup_auth; if (retry) { upd_loc_skip = 3; auth_info_skip = 3; } subscr = gprs_subscr_get_or_create("123456789012345"); subscr->authorized = 1; sgsn->cfg.require_authentication = 1; sgsn->cfg.require_update_location = 1; subscr_put(subscr); printf("Auth policy 'remote', GSUP based auth: "); test_gmm_attach(retry); assert_no_subscrs(); sgsn->cfg.auth_policy = saved_auth_policy; subscr_request_update_location_cb = __real_gprs_subscr_request_update_location; subscr_request_auth_info_cb = __real_gprs_subscr_request_auth_info; upd_loc_skip = 0; auth_info_skip = 0; } int my_gprs_gsup_client_send(struct gprs_gsup_client *gsupc, struct msgb *msg) { struct gprs_gsup_message to_peer = {0}; struct gprs_gsup_message from_peer = {0}; struct msgb *reply_msg; int rc; /* Simulate the GSUP peer */ rc = gprs_gsup_decode(msgb_data(msg), msgb_length(msg), &to_peer); OSMO_ASSERT(rc >= 0); OSMO_ASSERT(to_peer.imsi[0] != 0); strncpy(from_peer.imsi, to_peer.imsi, sizeof(from_peer.imsi)); /* This invalidates the pointers in to_peer */ msgb_free(msg); switch (to_peer.message_type) { case GPRS_GSUP_MSGT_UPDATE_LOCATION_REQUEST: /* Send UPDATE_LOCATION_RESULT */ return my_subscr_request_update_gsup_auth(NULL); case GPRS_GSUP_MSGT_SEND_AUTH_INFO_REQUEST: /* Send SEND_AUTH_INFO_RESULT */ return my_subscr_request_auth_info_gsup_auth(NULL); case GPRS_GSUP_MSGT_PURGE_MS_REQUEST: from_peer.message_type = GPRS_GSUP_MSGT_PURGE_MS_RESULT; break; default: if ((to_peer.message_type & 0b00000011) == 0) { /* Unhandled request */ /* Send error(NOT_IMPL) */ from_peer.message_type = to_peer.message_type + 1; from_peer.cause = GMM_CAUSE_MSGT_NOTEXIST_NOTIMPL; break; } /* Ignore it */ return 0; } reply_msg = gprs_gsup_msgb_alloc(); reply_msg->l2h = reply_msg->data; gprs_gsup_encode(reply_msg, &from_peer); gprs_subscr_rx_gsup_message(reply_msg); msgb_free(reply_msg); return 0; }; static void test_gmm_attach_subscr_real_gsup_auth(int retry) { const enum sgsn_auth_policy saved_auth_policy = sgsn->cfg.auth_policy; struct gsm_subscriber *subscr; sgsn_inst.cfg.auth_policy = SGSN_AUTH_POLICY_REMOTE; gprs_gsup_client_send_cb = my_gprs_gsup_client_send; sgsn->gsup_client = talloc_zero(tall_bsc_ctx, struct gprs_gsup_client); if (retry) { upd_loc_skip = 3; auth_info_skip = 3; } printf("Auth policy 'remote', real GSUP based auth: "); test_gmm_attach(retry); subscr = gprs_subscr_get_by_imsi("123456789012345"); OSMO_ASSERT(subscr == NULL); assert_no_subscrs(); sgsn->cfg.auth_policy = saved_auth_policy; gprs_gsup_client_send_cb = __real_gprs_gsup_client_send; upd_loc_skip = 0; auth_info_skip = 0; talloc_free(sgsn->gsup_client); sgsn->gsup_client = NULL; } /* * Test the GMM Rejects */ static void test_gmm_reject(void) { struct gprs_ra_id raid = { 0, }; struct sgsn_mm_ctx *ctx = NULL; uint32_t foreign_tlli; struct gprs_llc_lle *lle; int idx; /* DTAP - Attach Request */ /* Invalid MI length */ static const unsigned char attach_req_inv_mi_len[] = { 0x08, 0x01, 0x02, 0xf5, 0xe0, 0x21, 0x08, 0x02, 0x09, 0xf4, 0xfb, 0xc5, 0x46, 0x79, 0xff, 0xff, 0xff, 0xff, 0x11, 0x22, 0x33, 0x40, 0x50, 0x60, 0x19, 0x18, 0xb3, 0x43, 0x2b, 0x25, 0x96, 0x62, 0x00, 0x60, 0x80, 0x9a, 0xc2, 0xc6, 0x62, 0x00, 0x60, 0x80, 0xba, 0xc8, 0xc6, 0x62, 0x00, 0x60, 0x80, 0x00 }; /* DTAP - Attach Request */ /* Invalid MI type (IMEI) */ static const unsigned char attach_req_inv_mi_type[] = { 0x08, 0x01, 0x02, 0xf5, 0xe0, 0x21, 0x08, 0x02, 0x05, 0xf2, 0xfb, 0xc5, 0x46, 0x79, 0x11, 0x22, 0x33, 0x40, 0x50, 0x60, 0x19, 0x18, 0xb3, 0x43, 0x2b, 0x25, 0x96, 0x62, 0x00, 0x60, 0x80, 0x9a, 0xc2, 0xc6, 0x62, 0x00, 0x60, 0x80, 0xba, 0xc8, 0xc6, 0x62, 0x00, 0x60, 0x80, 0x00 }; /* DTAP - Routing Area Update Request */ static const unsigned char dtap_ra_upd_req[] = { 0x08, 0x08, 0x10, 0x11, 0x22, 0x33, 0x40, 0x50, 0x60, 0x1d, 0x19, 0x13, 0x42, 0x33, 0x57, 0x2b, 0xf7, 0xc8, 0x48, 0x02, 0x13, 0x48, 0x50, 0xc8, 0x48, 0x02, 0x14, 0x48, 0x50, 0xc8, 0x48, 0x02, 0x17, 0x49, 0x10, 0xc8, 0x48, 0x02, 0x00, 0x19, 0x8b, 0xb2, 0x92, 0x17, 0x16, 0x27, 0x07, 0x04, 0x31, 0x02, 0xe5, 0xe0, 0x32, 0x02, 0x20, 0x00 }; /* DTAP - Routing Area Update Request */ /* Invalid type: GPRS_UPD_T_RA_LA_IMSI_ATT */ static const unsigned char dtap_ra_upd_req_inv_type[] = { 0x08, 0x08, 0x12, 0x11, 0x22, 0x33, 0x40, 0x50, 0x60, 0x1d, 0x19, 0x13, 0x42, 0x33, 0x57, 0x2b, 0xf7, 0xc8, 0x48, 0x02, 0x13, 0x48, 0x50, 0xc8, 0x48, 0x02, 0x14, 0x48, 0x50, 0xc8, 0x48, 0x02, 0x17, 0x49, 0x10, 0xc8, 0x48, 0x02, 0x00, 0x19, 0x8b, 0xb2, 0x92, 0x17, 0x16, 0x27, 0x07, 0x04, 0x31, 0x02, 0xe5, 0xe0, 0x32, 0x02, 0x20, 0x00 }; /* DTAP - Routing Area Update Request */ /* Invalid cap length */ static const unsigned char dtap_ra_upd_req_inv_cap_len[] = { 0x08, 0x08, 0x10, 0x11, 0x22, 0x33, 0x40, 0x50, 0x60, 0x3d, 0x19, 0x13, 0x42, 0x33, 0x57, 0x2b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xc8, 0x48, 0x02, 0x13, 0x48, 0x50, 0xc8, 0x48, 0x02, 0x14, 0x48, 0x50, 0xc8, 0x48, 0x02, 0x17, 0x49, 0x10, 0xc8, 0x48, 0x02, 0x00, 0x19, 0x8b, 0xb2, 0x92, 0x17, 0x16, 0x27, 0x07, 0x04, 0x31, 0x02, 0xe5, 0xe0, 0x32, 0x02, 0x20, 0x00 }; struct test { const char *title; const unsigned char *msg; unsigned msg_len; unsigned num_resp; }; static struct test tests[] = { { .title = "Attach Request (invalid MI length)", .msg = attach_req_inv_mi_len, .msg_len = sizeof(attach_req_inv_mi_len), .num_resp = 1 /* Reject */ }, { .title = "Attach Request (invalid MI type)", .msg = attach_req_inv_mi_type, .msg_len = sizeof(attach_req_inv_mi_type), .num_resp = 1 /* Reject */ }, { .title = "Routing Area Update Request (valid)", .msg = dtap_ra_upd_req, .msg_len = sizeof(dtap_ra_upd_req), .num_resp = 2 /* XID Reset + Reject */ }, { .title = "Routing Area Update Request (invalid type)", .msg = dtap_ra_upd_req_inv_type, .msg_len = sizeof(dtap_ra_upd_req_inv_type), .num_resp = 1 /* Reject */ }, { .title = "Routing Area Update Request (invalid CAP length)", .msg = dtap_ra_upd_req_inv_cap_len, .msg_len = sizeof(dtap_ra_upd_req_inv_cap_len), .num_resp = 1 /* Reject */ }, }; printf("Testing GMM reject\n"); /* reset the PRNG used by sgsn_alloc_ptmsi */ srand(1); foreign_tlli = gprs_tmsi2tlli(0xc0000023, TLLI_FOREIGN); OSMO_ASSERT(count(gprs_llme_list()) == 0); for (idx = 0; idx < ARRAY_SIZE(tests); idx++) { const struct test *test = &tests[idx]; printf(" - %s\n", test->title); /* Create a LLE/LLME */ lle = gprs_lle_get_or_create(foreign_tlli, 3); OSMO_ASSERT(count(gprs_llme_list()) == 1); /* Inject the Request message */ send_0408_message(lle->llme, foreign_tlli, test->msg, test->msg_len); /* We expect a Reject message */ fprintf(stderr, "sgsn_tx_counter = %d (expected %d)\n", sgsn_tx_counter, test->num_resp); OSMO_ASSERT(sgsn_tx_counter == test->num_resp); /* verify that LLME/MM are removed */ ctx = sgsn_mm_ctx_by_tlli(foreign_tlli, &raid); OSMO_ASSERT(ctx == NULL); OSMO_ASSERT(count(gprs_llme_list()) == 0); } } /* * Test cancellation of attached MM contexts */ static void test_gmm_cancel(void) { struct gprs_ra_id raid = { 0, }; struct sgsn_mm_ctx *ctx = NULL; struct sgsn_mm_ctx *ictx; uint32_t ptmsi1; uint32_t foreign_tlli; uint32_t local_tlli = 0; struct gprs_llc_lle *lle; const enum sgsn_auth_policy saved_auth_policy = sgsn->cfg.auth_policy; /* DTAP - Attach Request */ /* The P-TMSI is not known by the SGSN */ static const unsigned char attach_req[] = { 0x08, 0x01, 0x02, 0xf5, 0xe0, 0x21, 0x08, 0x02, 0x05, 0xf4, 0xfb, 0xc5, 0x46, 0x79, 0x11, 0x22, 0x33, 0x40, 0x50, 0x60, 0x19, 0x18, 0xb3, 0x43, 0x2b, 0x25, 0x96, 0x62, 0x00, 0x60, 0x80, 0x9a, 0xc2, 0xc6, 0x62, 0x00, 0x60, 0x80, 0xba, 0xc8, 0xc6, 0x62, 0x00, 0x60, 0x80, 0x00 }; /* DTAP - Identity Response IMEI */ static const unsigned char ident_resp_imei[] = { 0x08, 0x16, 0x08, 0x9a, 0x78, 0x56, 0x34, 0x12, 0x90, 0x78, 0x56 }; /* DTAP - Identity Response IMSI */ static const unsigned char ident_resp_imsi[] = { 0x08, 0x16, 0x08, 0x19, 0x32, 0x54, 0x76, 0x98, 0x10, 0x32, 0x54 }; /* DTAP - Attach Complete */ static const unsigned char attach_compl[] = { 0x08, 0x03 }; printf("Testing cancellation\n"); sgsn_inst.cfg.auth_policy = SGSN_AUTH_POLICY_OPEN; /* reset the PRNG used by sgsn_alloc_ptmsi */ srand(1); ptmsi1 = sgsn_alloc_ptmsi(); OSMO_ASSERT(ptmsi1 != GSM_RESERVED_TMSI); /* reset the PRNG, so that the same P-TMSI sequence will be generated * again */ srand(1); foreign_tlli = gprs_tmsi2tlli(0xc0000023, TLLI_FOREIGN); /* Create a LLE/LLME */ OSMO_ASSERT(count(gprs_llme_list()) == 0); lle = gprs_lle_get_or_create(foreign_tlli, 3); OSMO_ASSERT(count(gprs_llme_list()) == 1); /* inject the attach request */ send_0408_message(lle->llme, foreign_tlli, attach_req, ARRAY_SIZE(attach_req)); ctx = sgsn_mm_ctx_by_tlli(foreign_tlli, &raid); OSMO_ASSERT(ctx != NULL); OSMO_ASSERT(ctx->mm_state == GMM_COMMON_PROC_INIT); /* we expect an identity request (IMEI) */ OSMO_ASSERT(sgsn_tx_counter == 1); /* inject the identity response (IMEI) */ send_0408_message(ctx->llme, foreign_tlli, ident_resp_imei, ARRAY_SIZE(ident_resp_imei)); /* we expect an identity request (IMSI) */ OSMO_ASSERT(sgsn_tx_counter == 1); /* inject the identity response (IMSI) */ send_0408_message(ctx->llme, foreign_tlli, ident_resp_imsi, ARRAY_SIZE(ident_resp_imsi)); /* check that the MM context has not been removed due to a failed * authorization */ OSMO_ASSERT(ctx == sgsn_mm_ctx_by_tlli(foreign_tlli, &raid)); OSMO_ASSERT(ctx->mm_state == GMM_COMMON_PROC_INIT); /* we expect an attach accept/reject */ OSMO_ASSERT(sgsn_tx_counter == 1); /* this has been randomly assigned by the SGSN */ local_tlli = gprs_tmsi2tlli(ptmsi1, TLLI_LOCAL); /* inject the attach complete */ send_0408_message(ctx->llme, local_tlli, attach_compl, ARRAY_SIZE(attach_compl)); OSMO_ASSERT(ctx->mm_state == GMM_REGISTERED_NORMAL); /* we don't expect a response */ OSMO_ASSERT(sgsn_tx_counter == 0); /* cancel */ gsm0408_gprs_access_cancelled(ctx, 0); /* verify that things are gone */ OSMO_ASSERT(count(gprs_llme_list()) == 0); ictx = sgsn_mm_ctx_by_tlli(local_tlli, &raid); OSMO_ASSERT(!ictx); sgsn->cfg.auth_policy = saved_auth_policy; } /* * Test the dynamic allocation of P-TMSIs */ static void test_gmm_ptmsi_allocation(void) { struct gprs_ra_id raid = { 0, }; struct sgsn_mm_ctx *ctx = NULL; struct sgsn_mm_ctx *ictx; uint32_t foreign_tlli; uint32_t ptmsi1; uint32_t ptmsi2; uint32_t old_ptmsi; uint32_t local_tlli = 0; struct gprs_llc_lle *lle; const enum sgsn_auth_policy saved_auth_policy = sgsn->cfg.auth_policy; /* DTAP - Attach Request (IMSI 12131415161718) */ static const unsigned char attach_req[] = { 0x08, 0x01, 0x02, 0xf5, 0xe0, 0x21, 0x08, 0x02, 0x08, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x11, 0x22, 0x33, 0x40, 0x50, 0x60, 0x19, 0x18, 0xb3, 0x43, 0x2b, 0x25, 0x96, 0x62, 0x00, 0x60, 0x80, 0x9a, 0xc2, 0xc6, 0x62, 0x00, 0x60, 0x80, 0xba, 0xc8, 0xc6, 0x62, 0x00, 0x60, 0x80, 0x00, }; /* DTAP - Identity Response IMEI */ static const unsigned char ident_resp_imei[] = { 0x08, 0x16, 0x08, 0x9a, 0x78, 0x56, 0x34, 0x12, 0x90, 0x78, 0x56 }; /* DTAP - Attach Complete */ static const unsigned char attach_compl[] = { 0x08, 0x03 }; /* DTAP - Routing Area Update Request */ static const unsigned char ra_upd_req[] = { 0x08, 0x08, 0x10, 0x11, 0x22, 0x33, 0x40, 0x50, 0x60, 0x1d, 0x19, 0x13, 0x42, 0x33, 0x57, 0x2b, 0xf7, 0xc8, 0x48, 0x02, 0x13, 0x48, 0x50, 0xc8, 0x48, 0x02, 0x14, 0x48, 0x50, 0xc8, 0x48, 0x02, 0x17, 0x49, 0x10, 0xc8, 0x48, 0x02, 0x00, 0x19, 0x8b, 0xb2, 0x92, 0x17, 0x16, 0x27, 0x07, 0x04, 0x31, 0x02, 0xe5, 0xe0, 0x32, 0x02, 0x20, 0x00 }; /* DTAP - Routing Area Update Complete */ static const unsigned char ra_upd_complete[] = { 0x08, 0x0a }; /* DTAP - Detach Request (MO) */ /* normal detach, power_off = 1 */ static const unsigned char detach_req[] = { 0x08, 0x05, 0x09, 0x18, 0x05, 0xf4, 0xef, 0xe2, 0xb7, 0x00, 0x19, 0x03, 0xb9, 0x97, 0xcb }; sgsn->cfg.auth_policy = SGSN_AUTH_POLICY_OPEN; printf("Testing P-TMSI allocation\n"); printf(" - sgsn_alloc_ptmsi\n"); /* reset the PRNG used by sgsn_alloc_ptmsi */ srand(1); ptmsi1 = sgsn_alloc_ptmsi(); OSMO_ASSERT(ptmsi1 != GSM_RESERVED_TMSI); ptmsi2 = sgsn_alloc_ptmsi(); OSMO_ASSERT(ptmsi2 != GSM_RESERVED_TMSI); OSMO_ASSERT(ptmsi1 != ptmsi2); printf(" - Repeated Attach Request\n"); /* reset the PRNG, so that the same P-TMSI will be generated * again */ srand(1); foreign_tlli = gprs_tmsi2tlli(0xc0000023, TLLI_FOREIGN); /* Create a LLE/LLME */ OSMO_ASSERT(count(gprs_llme_list()) == 0); lle = gprs_lle_get_or_create(foreign_tlli, 3); OSMO_ASSERT(count(gprs_llme_list()) == 1); /* inject the attach request */ send_0408_message(lle->llme, foreign_tlli, attach_req, ARRAY_SIZE(attach_req)); ctx = sgsn_mm_ctx_by_tlli(foreign_tlli, &raid); OSMO_ASSERT(ctx != NULL); OSMO_ASSERT(ctx->mm_state == GMM_COMMON_PROC_INIT); OSMO_ASSERT(ctx->p_tmsi == ptmsi1); old_ptmsi = ctx->p_tmsi_old; /* we expect an identity request (IMEI) */ OSMO_ASSERT(sgsn_tx_counter == 1); /* inject the identity response (IMEI) */ send_0408_message(ctx->llme, foreign_tlli, ident_resp_imei, ARRAY_SIZE(ident_resp_imei)); /* check that the MM context has not been removed due to a failed * authorization */ OSMO_ASSERT(ctx == sgsn_mm_ctx_by_tlli(foreign_tlli, &raid)); OSMO_ASSERT(ctx->mm_state == GMM_COMMON_PROC_INIT); OSMO_ASSERT(ctx->p_tmsi == ptmsi1); /* we expect an attach accept */ OSMO_ASSERT(sgsn_tx_counter == 1); /* we ignore this and send the attach again */ send_0408_message(lle->llme, foreign_tlli, attach_req, ARRAY_SIZE(attach_req)); /* the allocated P-TMSI should be the same */ ctx = sgsn_mm_ctx_by_tlli(foreign_tlli, &raid); OSMO_ASSERT(ctx != NULL); OSMO_ASSERT(ctx->mm_state == GMM_COMMON_PROC_INIT); OSMO_ASSERT(ctx->p_tmsi_old == old_ptmsi); OSMO_ASSERT(ctx->p_tmsi == ptmsi1); /* inject the attach complete */ local_tlli = gprs_tmsi2tlli(ptmsi1, TLLI_LOCAL); send_0408_message(ctx->llme, local_tlli, attach_compl, ARRAY_SIZE(attach_compl)); /* we don't expect a response */ OSMO_ASSERT(sgsn_tx_counter == 0); OSMO_ASSERT(ctx->mm_state == GMM_REGISTERED_NORMAL); OSMO_ASSERT(ctx->p_tmsi_old == 0); OSMO_ASSERT(ctx->p_tmsi == ptmsi1); printf(" - Repeated RA Update Request\n"); /* inject the RA update request */ send_0408_message(ctx->llme, local_tlli, ra_upd_req, ARRAY_SIZE(ra_upd_req)); /* we expect an RA update accept */ OSMO_ASSERT(sgsn_tx_counter == 1); OSMO_ASSERT(ctx->mm_state == GMM_COMMON_PROC_INIT); OSMO_ASSERT(ctx->p_tmsi_old == ptmsi1); OSMO_ASSERT(ctx->p_tmsi == ptmsi2); /* repeat the RA update request */ send_0408_message(ctx->llme, local_tlli, ra_upd_req, ARRAY_SIZE(ra_upd_req)); /* we expect an RA update accept */ OSMO_ASSERT(sgsn_tx_counter == 1); OSMO_ASSERT(ctx->mm_state == GMM_COMMON_PROC_INIT); OSMO_ASSERT(ctx->p_tmsi_old == ptmsi1); OSMO_ASSERT(ctx->p_tmsi == ptmsi2); /* inject the RA update complete */ local_tlli = gprs_tmsi2tlli(ptmsi2, TLLI_LOCAL); send_0408_message(ctx->llme, local_tlli, ra_upd_complete, ARRAY_SIZE(ra_upd_complete)); /* we don't expect a response */ OSMO_ASSERT(sgsn_tx_counter == 0); OSMO_ASSERT(ctx->mm_state == GMM_REGISTERED_NORMAL); OSMO_ASSERT(ctx->p_tmsi_old == 0); OSMO_ASSERT(ctx->p_tmsi == ptmsi2); /* inject the detach */ send_0408_message(ctx->llme, local_tlli, detach_req, ARRAY_SIZE(detach_req)); /* verify that things are gone */ OSMO_ASSERT(count(gprs_llme_list()) == 0); ictx = sgsn_mm_ctx_by_tlli(local_tlli, &raid); OSMO_ASSERT(!ictx); sgsn->cfg.auth_policy = saved_auth_policy; } static void test_apn_matching(void) { struct apn_ctx *actx, *actxs[9]; printf("Testing APN matching\n"); actxs[0] = sgsn_apn_ctx_find_alloc("*.test", ""); actxs[1] = sgsn_apn_ctx_find_alloc("*.def.test", ""); actxs[2] = sgsn_apn_ctx_find_alloc("abc.def.test", ""); actxs[3] = NULL; actxs[4] = sgsn_apn_ctx_find_alloc("abc.def.test", "456"); actxs[5] = sgsn_apn_ctx_find_alloc("abc.def.test", "456123"); actxs[6] = sgsn_apn_ctx_find_alloc("*.def.test", "456"); actxs[7] = sgsn_apn_ctx_find_alloc("*.def.test", "456123"); actxs[8] = sgsn_apn_ctx_find_alloc("ghi.def.test", "456"); actx = sgsn_apn_ctx_match("abc.def.test", "12345678"); OSMO_ASSERT(actx == actxs[2]); actx = sgsn_apn_ctx_match("aBc.dEf.test", "12345678"); OSMO_ASSERT(actx == actxs[2]); actx = sgsn_apn_ctx_match("xyz.def.test", "12345678"); OSMO_ASSERT(actx == actxs[1]); actx = sgsn_apn_ctx_match("xyz.dEf.test", "12345678"); OSMO_ASSERT(actx == actxs[1]); actx = sgsn_apn_ctx_match("xyz.uvw.test", "12345678"); OSMO_ASSERT(actx == actxs[0]); actx = sgsn_apn_ctx_match("xyz.uvw.foo", "12345678"); OSMO_ASSERT(actx == NULL); actxs[3] = sgsn_apn_ctx_find_alloc("*", ""); actx = sgsn_apn_ctx_match("xyz.uvw.foo", "12345678"); OSMO_ASSERT(actx == actxs[3]); actx = sgsn_apn_ctx_match("abc.def.test", "45699900"); OSMO_ASSERT(actx == actxs[4]); actx = sgsn_apn_ctx_match("xyz.def.test", "45699900"); OSMO_ASSERT(actx == actxs[6]); actx = sgsn_apn_ctx_match("abc.def.test", "45612300"); OSMO_ASSERT(actx == actxs[5]); actx = sgsn_apn_ctx_match("xyz.def.test", "45612300"); OSMO_ASSERT(actx == actxs[7]); actx = sgsn_apn_ctx_match("ghi.def.test", "45699900"); OSMO_ASSERT(actx == actxs[8]); actx = sgsn_apn_ctx_match("ghi.def.test", "45612300"); OSMO_ASSERT(actx == actxs[7]); /* Free APN contexts and check how the matching changes */ sgsn_apn_ctx_free(actxs[7]); actx = sgsn_apn_ctx_match("ghi.def.test", "45612300"); OSMO_ASSERT(actx == actxs[8]); sgsn_apn_ctx_free(actxs[8]); actx = sgsn_apn_ctx_match("ghi.def.test", "45612300"); OSMO_ASSERT(actx == actxs[6]); sgsn_apn_ctx_free(actxs[6]); actx = sgsn_apn_ctx_match("ghi.def.test", "45612300"); OSMO_ASSERT(actx == actxs[1]); sgsn_apn_ctx_free(actxs[5]); actx = sgsn_apn_ctx_match("abc.def.test", "45612300"); OSMO_ASSERT(actx == actxs[4]); sgsn_apn_ctx_free(actxs[4]); actx = sgsn_apn_ctx_match("abc.def.test", "45612300"); OSMO_ASSERT(actx == actxs[2]); sgsn_apn_ctx_free(actxs[2]); actx = sgsn_apn_ctx_match("abc.def.test", "12345678"); OSMO_ASSERT(actx == actxs[1]); sgsn_apn_ctx_free(actxs[1]); actx = sgsn_apn_ctx_match("abc.def.test", "12345678"); OSMO_ASSERT(actx == actxs[0]); sgsn_apn_ctx_free(actxs[0]); actx = sgsn_apn_ctx_match("abc.def.test", "12345678"); OSMO_ASSERT(actx == actxs[3]); sgsn_apn_ctx_free(actxs[3]); actx = sgsn_apn_ctx_match("abc.def.test", "12345678"); OSMO_ASSERT(actx == NULL); } struct sgsn_subscriber_pdp_data* sgsn_subscriber_pdp_data_alloc( struct sgsn_subscriber_data *sdata); static void test_ggsn_selection(void) { struct apn_ctx *actxs[4]; struct sgsn_ggsn_ctx *ggc, *ggcs[3]; struct gsm_subscriber *s1; const char *imsi1 = "1234567890"; struct sgsn_mm_ctx *ctx; struct gprs_ra_id raid = { 0, }; uint32_t local_tlli = 0xffeeddcc; enum gsm48_gsm_cause gsm_cause; struct tlv_parsed tp; uint8_t apn_enc[GSM_APN_LENGTH + 10]; struct sgsn_subscriber_pdp_data *pdp_data; char apn_str[GSM_APN_LENGTH]; printf("Testing GGSN selection\n"); gprs_gsup_client_send_cb = my_gprs_gsup_client_send_dummy; /* Check for emptiness */ OSMO_ASSERT(gprs_subscr_get_by_imsi(imsi1) == NULL); /* Create a context */ OSMO_ASSERT(count(gprs_llme_list()) == 0); ctx = alloc_mm_ctx(local_tlli, &raid); strncpy(ctx->imsi, imsi1, sizeof(ctx->imsi) - 1); /* Allocate and attach a subscriber */ s1 = gprs_subscr_get_or_create_by_mmctx(ctx); assert_subscr(s1, imsi1); tp.lv[GSM48_IE_GSM_APN].len = 0; tp.lv[GSM48_IE_GSM_APN].val = apn_enc; /* TODO: Add PDP info entries to s1 */ ggcs[0] = sgsn_ggsn_ctx_find_alloc(0); ggcs[1] = sgsn_ggsn_ctx_find_alloc(1); ggcs[2] = sgsn_ggsn_ctx_find_alloc(2); actxs[0] = sgsn_apn_ctx_find_alloc("test.apn", "123456"); actxs[0]->ggsn = ggcs[0]; actxs[1] = sgsn_apn_ctx_find_alloc("*.apn", "123456"); actxs[1]->ggsn = ggcs[1]; actxs[2] = sgsn_apn_ctx_find_alloc("*", "456789"); actxs[2]->ggsn = ggcs[2]; pdp_data = sgsn_subscriber_pdp_data_alloc(s1->sgsn_data); pdp_data->context_id = 1; pdp_data->pdp_type = 0x0121; strncpy(pdp_data->apn_str, "*", sizeof(pdp_data->apn_str)-1); /* Resolve GGSNs */ tp.lv[GSM48_IE_GSM_APN].len = gprs_str_to_apn(apn_enc, sizeof(apn_enc), "Test.Apn"); ggc = sgsn_mm_ctx_find_ggsn_ctx(ctx, &tp, &gsm_cause, apn_str); OSMO_ASSERT(ggc != NULL); OSMO_ASSERT(ggc->id == 0); OSMO_ASSERT(strcmp(apn_str, "Test.Apn") == 0); tp.lv[GSM48_IE_GSM_APN].len = gprs_str_to_apn(apn_enc, sizeof(apn_enc), "Other.Apn"); ggc = sgsn_mm_ctx_find_ggsn_ctx(ctx, &tp, &gsm_cause, apn_str); OSMO_ASSERT(ggc != NULL); OSMO_ASSERT(ggc->id == 1); OSMO_ASSERT(strcmp(apn_str, "Other.Apn") == 0); tp.lv[GSM48_IE_GSM_APN].len = 0; tp.lv[GSM48_IE_GSM_APN].val = NULL; ggc = sgsn_mm_ctx_find_ggsn_ctx(ctx, &tp, &gsm_cause, apn_str); OSMO_ASSERT(ggc != NULL); OSMO_ASSERT(ggc->id == 0); OSMO_ASSERT(strcmp(apn_str, "") == 0); actxs[3] = sgsn_apn_ctx_find_alloc("*", "123456"); actxs[3]->ggsn = ggcs[2]; ggc = sgsn_mm_ctx_find_ggsn_ctx(ctx, &tp, &gsm_cause, apn_str); OSMO_ASSERT(ggc != NULL); OSMO_ASSERT(ggc->id == 2); OSMO_ASSERT(strcmp(apn_str, "") == 0); sgsn_apn_ctx_free(actxs[3]); tp.lv[GSM48_IE_GSM_APN].val = apn_enc; tp.lv[GSM48_IE_GSM_APN].len = gprs_str_to_apn(apn_enc, sizeof(apn_enc), "Foo.Bar"); ggc = sgsn_mm_ctx_find_ggsn_ctx(ctx, &tp, &gsm_cause, apn_str); OSMO_ASSERT(ggc == NULL); OSMO_ASSERT(gsm_cause == GSM_CAUSE_MISSING_APN); OSMO_ASSERT(strcmp(apn_str, "Foo.Bar") == 0); tp.lv[GSM48_IE_GSM_APN].len = sizeof(apn_enc); ggc = sgsn_mm_ctx_find_ggsn_ctx(ctx, &tp, &gsm_cause, apn_str); OSMO_ASSERT(ggc == NULL); OSMO_ASSERT(gsm_cause == GSM_CAUSE_INV_MAND_INFO); /* Add PDP data entry to subscriber */ strncpy(pdp_data->apn_str, "Test.Apn", sizeof(pdp_data->apn_str)-1); tp.lv[GSM48_IE_GSM_APN].len = gprs_str_to_apn(apn_enc, sizeof(apn_enc), "Test.Apn"); ggc = sgsn_mm_ctx_find_ggsn_ctx(ctx, &tp, &gsm_cause, apn_str); OSMO_ASSERT(ggc != NULL); OSMO_ASSERT(ggc->id == 0); OSMO_ASSERT(strcmp(apn_str, "Test.Apn") == 0); tp.lv[GSM48_IE_GSM_APN].len = gprs_str_to_apn(apn_enc, sizeof(apn_enc), "Other.Apn"); ggc = sgsn_mm_ctx_find_ggsn_ctx(ctx, &tp, &gsm_cause, apn_str); OSMO_ASSERT(ggc == NULL); OSMO_ASSERT(gsm_cause == GSM_CAUSE_REQ_SERV_OPT_NOTSUB); OSMO_ASSERT(strcmp(apn_str, "") == 0); /* Cleanup */ subscr_put(s1); sgsn_mm_ctx_cleanup_free(ctx); assert_no_subscrs(); sgsn_apn_ctx_free(actxs[0]); sgsn_apn_ctx_free(actxs[1]); sgsn_apn_ctx_free(actxs[2]); sgsn_ggsn_ctx_free(ggcs[0]); sgsn_ggsn_ctx_free(ggcs[1]); sgsn_ggsn_ctx_free(ggcs[2]); gprs_gsup_client_send_cb = __real_gprs_gsup_client_send; } static struct log_info_cat gprs_categories[] = { [DMM] = { .name = "DMM", .description = "Layer3 Mobility Management (MM)", .color = "\033[1;33m", .enabled = 1, .loglevel = LOGL_DEBUG, }, [DPAG] = { .name = "DPAG", .description = "Paging Subsystem", .color = "\033[1;38m", .enabled = 1, .loglevel = LOGL_NOTICE, }, [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, }, }; static struct log_info info = { .cat = gprs_categories, .num_cat = ARRAY_SIZE(gprs_categories), }; int main(int argc, char **argv) { void *osmo_sgsn_ctx; osmo_init_logging(&info); osmo_sgsn_ctx = talloc_named_const(NULL, 0, "osmo_sgsn"); tall_bsc_ctx = talloc_named_const(osmo_sgsn_ctx, 0, "bsc"); tall_msgb_ctx = talloc_named_const(osmo_sgsn_ctx, 0, "msgb"); sgsn_auth_init(); gprs_subscr_init(sgsn); test_llme(); test_subscriber(); test_auth_triplets(); test_subscriber_gsup(); test_gmm_detach(); test_gmm_detach_power_off(); test_gmm_detach_no_mmctx(); test_gmm_detach_accept_unexpected(); test_gmm_status_no_mmctx(); test_gmm_attach_acl(); test_gmm_attach_subscr(); test_gmm_attach_subscr_fake_auth(); test_gmm_attach_subscr_real_auth(); test_gmm_attach_subscr_gsup_auth(0); test_gmm_attach_subscr_gsup_auth(1); test_gmm_attach_subscr_real_gsup_auth(0); test_gmm_reject(); test_gmm_cancel(); test_gmm_ptmsi_allocation(); test_apn_matching(); test_ggsn_selection(); printf("Done\n"); talloc_report_full(osmo_sgsn_ctx, stderr); OSMO_ASSERT(talloc_total_blocks(tall_msgb_ctx) == 1); OSMO_ASSERT(talloc_total_blocks(tall_bsc_ctx) == 1); return 0; } /* stubs */ struct osmo_prim_hdr; int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx) { abort(); } openbsc-0.15.0/openbsc/tests/sgsn/sgsn_test.ok000066400000000000000000000020641265565154000213520ustar00rootroot00000000000000Testing LLME allocations Testing core subscriber data API Testing authentication triplet handling Testing subcriber GSUP handling Testing GMM detach Testing GMM detach (power off) Testing GMM detach (no MMCTX) Testing GMM detach accept (unexpected) Testing GMM Status (no MMCTX) Auth policy 'closed': Testing GMM attach Auth policy 'remote': Testing GMM attach Auth policy 'remote', auth faked: Testing GMM attach Auth policy 'remote', triplet based auth: Testing GMM attach Auth policy 'remote', GSUP based auth: Testing GMM attach Auth policy 'remote', GSUP based auth: Testing GMM attach with retry Auth policy 'remote', real GSUP based auth: Testing GMM attach Testing GMM reject - Attach Request (invalid MI length) - Attach Request (invalid MI type) - Routing Area Update Request (valid) - Routing Area Update Request (invalid type) - Routing Area Update Request (invalid CAP length) Testing cancellation Testing P-TMSI allocation - sgsn_alloc_ptmsi - Repeated Attach Request - Repeated RA Update Request Testing APN matching Testing GGSN selection Done openbsc-0.15.0/openbsc/tests/smpp/000077500000000000000000000000001265565154000170115ustar00rootroot00000000000000openbsc-0.15.0/openbsc/tests/smpp/Makefile.am000066400000000000000000000007301265565154000210450ustar00rootroot00000000000000AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_srcdir)/src/libmsc AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOSCCP_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) AM_LDFLAGS = $(COVERAGE_LDFLAGS) EXTRA_DIST = smpp_test.ok smpp_test.err noinst_PROGRAMS = smpp_test smpp_test_SOURCES = smpp_test.c \ $(top_builddir)/src/libmsc/smpp_utils.c smpp_test_LDADD = $(LIBOSMOCORE_LIBS) \ $(top_builddir)/src/libcommon/libcommon.a openbsc-0.15.0/openbsc/tests/smpp/smpp_test.c000066400000000000000000000040331265565154000211730ustar00rootroot00000000000000/* * (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 . * */ #include #include #include #include #include #include "smpp_smsc.h" struct coding_test { uint8_t dcs; uint8_t coding; int mode; int res; }; static struct coding_test codecs[] = { { .dcs = 0xf6 , .coding = 0x02, .mode = MODE_8BIT, .res = 0 }, { .dcs = 0xf2 , .coding = 0x01, .mode = MODE_7BIT, .res = 0 }, { .dcs = 0x02 , .coding = 0x01, .mode = MODE_7BIT, .res = 0 }, { .dcs = 0x06 , .coding = 0x02, .mode = MODE_8BIT, .res = 0 }, { .dcs = 0x0A , .coding = 0x08, .mode = MODE_8BIT, .res = 0 }, { .dcs = 0x0E , .coding = 0xFF, .mode = 0xFF, .res = -1 }, { .dcs = 0xE0 , .coding = 0xFF, .mode = 0xFF, .res = -1 }, }; static void test_coding_scheme(void) { int i; printf("Testing coding scheme support\n"); for (i = 0; i < ARRAY_SIZE(codecs); ++i) { uint8_t coding; int mode, res; res = smpp_determine_scheme(codecs[i].dcs, &coding, &mode); OSMO_ASSERT(res == codecs[i].res); if (res != -1) { OSMO_ASSERT(mode == codecs[i].mode); OSMO_ASSERT(coding == codecs[i].coding); } } } int main(int argc, char **argv) { osmo_init_logging(&log_info); log_set_use_color(osmo_stderr_target, 0); log_set_print_filename(osmo_stderr_target, 0); test_coding_scheme(); return EXIT_SUCCESS; } openbsc-0.15.0/openbsc/tests/smpp/smpp_test.err000066400000000000000000000001021265565154000215320ustar00rootroot00000000000000SMPP MO Unknown Data Coding 0x0e SMPP MO Unknown Data Coding 0xe0 openbsc-0.15.0/openbsc/tests/smpp/smpp_test.ok000066400000000000000000000000361265565154000213610ustar00rootroot00000000000000Testing coding scheme support openbsc-0.15.0/openbsc/tests/smpp_test_runner.py000066400000000000000000000107211265565154000220140ustar00rootroot00000000000000#!/usr/bin/env python # (C) 2014 by Holger Hans Peter Freyther # based on vty_test_runner.py: # (C) 2013 by Katerina Barone-Adesi # (C) 2013 by Holger Hans Peter Freyther # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU 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 General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . import os import time import unittest import socket import osmopy.obscvty as obscvty import osmopy.osmoutil as osmoutil confpath = '.' class TestVTYBase(unittest.TestCase): def vty_command(self): raise Exception("Needs to be implemented by a subclass") def vty_app(self): raise Exception("Needs to be implemented by a subclass") def setUp(self): osmo_vty_cmd = self.vty_command()[:] config_index = osmo_vty_cmd.index('-c') if config_index: cfi = config_index + 1 osmo_vty_cmd[cfi] = os.path.join(confpath, osmo_vty_cmd[cfi]) try: print "Launch: %s from %s" % (' '.join(osmo_vty_cmd), os.getcwd()) self.proc = osmoutil.popen_devnull(osmo_vty_cmd) except OSError: print >> sys.stderr, "Current directory: %s" % os.getcwd() print >> sys.stderr, "Consider setting -b" time.sleep(1) appstring = self.vty_app()[2] appport = self.vty_app()[0] self.vty = obscvty.VTYInteract(appstring, "127.0.0.1", appport) def tearDown(self): self.vty = None osmoutil.end_proc(self.proc) class TestSMPPNITB(TestVTYBase): def vty_command(self): return ["./src/osmo-nitb/osmo-nitb", "-c", "doc/examples/osmo-nitb/nanobts/openbsc.cfg"] def vty_app(self): return (4242, "./src/osmo-nitb/osmo-nitb", "OpenBSC", "nitb") def testSMPPCrashes(self): # Enable the configuration self.vty.enable() self.assertTrue(self.vty.verify("configure terminal", [''])) self.assertEquals(self.vty.node(), 'config') self.assertTrue(self.vty.verify('smpp', [''])) self.assertEquals(self.vty.node(), 'config-smpp') self.assertTrue(self.vty.verify('system-id test', [''])) self.assertTrue(self.vty.verify('local-tcp-port 2775', [''])) self.assertTrue(self.vty.verify('esme test', [''])) self.assertEquals(self.vty.node(), 'config-smpp-esme') self.assertTrue(self.vty.verify('default-route', [''])) self.assertTrue(self.vty.verify('end', [''])) # NITB should listen to 2775 now! sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sck.setblocking(1) sck.connect(('0.0.0.0', 2775)) sck.sendall('\x00\x00\x00\x02\x00') sck.close() # Check if the VTY is still there self.vty.verify('disable',['']) # Now for the second packet sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sck.setblocking(1) sck.connect(('0.0.0.0', 2775)) sck.sendall('\x00\x01\x00\x01\x01') sck.close() self.vty.verify('enable',['']) if __name__ == '__main__': import argparse import sys workdir = '.' parser = argparse.ArgumentParser() parser.add_argument("-v", "--verbose", dest="verbose", action="store_true", help="verbose mode") parser.add_argument("-p", "--pythonconfpath", dest="p", help="searchpath for config") parser.add_argument("-w", "--workdir", dest="w", help="Working directory") args = parser.parse_args() verbose_level = 1 if args.verbose: verbose_level = 2 if args.w: workdir = args.w if args.p: confpath = args.p print "confpath %s, workdir %s" % (confpath, workdir) os.chdir(workdir) print "Running tests for specific SMPP" suite = unittest.TestSuite() suite.addTest(unittest.TestLoader().loadTestsFromTestCase(TestSMPPNITB)) res = unittest.TextTestRunner(verbosity=verbose_level).run(suite) sys.exit(len(res.errors) + len(res.failures)) openbsc-0.15.0/openbsc/tests/subscr/000077500000000000000000000000001265565154000173335ustar00rootroot00000000000000openbsc-0.15.0/openbsc/tests/subscr/Makefile.am000066400000000000000000000012201265565154000213620ustar00rootroot00000000000000AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(LIBSMPP34_CFLAGS) $(COVERAGE_CFLAGS) AM_LDFLAGS = $(COVERAGE_LDFLAGS) EXTRA_DIST = subscr_test.ok noinst_PROGRAMS = subscr_test subscr_test_SOURCES = subscr_test.c subscr_test_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ $(top_builddir)/src/libmsc/libmsc.a \ $(top_builddir)/src/libbsc/libbsc.a \ $(top_builddir)/src/libtrau/libtrau.a \ $(top_builddir)/src/libcommon/libcommon.a \ $(LIBOSMOCORE_LIBS) $(LIBOSMOABIS_LIBS) \ $(LIBOSMOGSM_LIBS) $(LIBSMPP34_LIBS) $(LIBOSMOVTY_LIBS) # -ldbi openbsc-0.15.0/openbsc/tests/subscr/subscr_test.c000066400000000000000000000056131265565154000220440ustar00rootroot00000000000000/* (C) 2008 by Jan Luebbe * (C) 2009 by Holger Hans Peter Freyther * (C) 2014 by Alexander Chemeris * 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 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 . * */ #include #include #include #include #include #include #include #include #include static struct gsm_network dummy_net; static struct gsm_subscriber_group dummy_sgrp; static void test_subscr(void) { struct gsm_subscriber *subscr; const char *imsi = "1234567890"; printf("Test subscriber allocation and deletion\n"); /* Don't keep subscr */ dummy_sgrp.keep_subscr = 0; OSMO_ASSERT(llist_empty(&active_subscribers)); subscr = subscr_get_or_create(&dummy_sgrp, imsi); OSMO_ASSERT(!llist_empty(&active_subscribers)); OSMO_ASSERT(subscr->use_count == 1); subscr_put(subscr); OSMO_ASSERT(llist_empty(&active_subscribers)); /* Keep subscr */ dummy_sgrp.keep_subscr = 1; subscr = subscr_get_or_create(&dummy_sgrp, imsi); OSMO_ASSERT(!llist_empty(&active_subscribers)); OSMO_ASSERT(subscr->use_count == 1); subscr_put(subscr); OSMO_ASSERT(!llist_empty(&active_subscribers)); OSMO_ASSERT(subscr->use_count == 0); subscr_get(subscr); OSMO_ASSERT(subscr->use_count == 1); subscr_purge_inactive(&dummy_sgrp); OSMO_ASSERT(!llist_empty(&active_subscribers)); OSMO_ASSERT(subscr->use_count == 1); subscr_put(subscr); OSMO_ASSERT(!llist_empty(&active_subscribers)); OSMO_ASSERT(subscr->use_count == 0); subscr_purge_inactive(&dummy_sgrp); OSMO_ASSERT(llist_empty(&active_subscribers)); /* Test force_no_keep */ dummy_sgrp.keep_subscr = 0; subscr = subscr_get_or_create(&dummy_sgrp, imsi); OSMO_ASSERT(subscr); subscr->keep_in_ram = 1; OSMO_ASSERT(!llist_empty(&active_subscribers)); OSMO_ASSERT(subscr->use_count == 1); subscr->keep_in_ram = 0; subscr_put(subscr); OSMO_ASSERT(llist_empty(&active_subscribers)); } int main() { printf("Testing subscriber core code.\n"); osmo_init_logging(&log_info); log_set_print_filename(osmo_stderr_target, 0); dummy_net.subscr_group = &dummy_sgrp; dummy_sgrp.net = &dummy_net; test_subscr(); printf("Done\n"); return 0; } openbsc-0.15.0/openbsc/tests/subscr/subscr_test.ok000066400000000000000000000001131265565154000222210ustar00rootroot00000000000000Testing subscriber core code. Test subscriber allocation and deletion Done openbsc-0.15.0/openbsc/tests/testsuite.at000066400000000000000000000063561265565154000204230ustar00rootroot00000000000000AT_INIT AT_BANNER([Regression tests.]) AT_SETUP([gsm0408]) AT_KEYWORDS([gsm0408]) cat $abs_srcdir/gsm0408/gsm0408_test.ok > expout AT_CHECK([$abs_top_builddir/tests/gsm0408/gsm0408_test], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([subscr]) AT_KEYWORDS([subscr]) cat $abs_srcdir/subscr/subscr_test.ok > expout AT_CHECK([$abs_top_builddir/tests/subscr/subscr_test], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([db]) AT_KEYWORDS([db]) cat $abs_srcdir/db/db_test.ok > expout cat $abs_srcdir/db/db_test.err > experr cat $abs_srcdir/db/hlr.sqlite3 > hlr.sqlite3 AT_CHECK([$abs_top_builddir/tests/db/db_test], [], [expout], [experr]) AT_CLEANUP AT_SETUP([channel]) AT_KEYWORDS([channel]) cat $abs_srcdir/channel/channel_test.ok > expout AT_CHECK([$abs_top_builddir/tests/channel/channel_test], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([mgcp]) AT_KEYWORDS([mgcp]) cat $abs_srcdir/mgcp/mgcp_test.ok > expout AT_CHECK([$abs_top_builddir/tests/mgcp/mgcp_test], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([mgcp-trans]) AT_KEYWORDS([mgcp-trans]) AT_CHECK([test "$enable_mgcp_transcoding_test" == yes || exit 77]) cat $abs_srcdir/mgcp/mgcp_transcoding_test.ok > expout AT_CHECK([$abs_top_builddir/tests/mgcp/mgcp_transcoding_test], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([gprs]) AT_KEYWORDS([gprs]) cat $abs_srcdir/gprs/gprs_test.ok > expout AT_CHECK([$abs_top_builddir/tests/gprs/gprs_test], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([bsc-nat]) AT_KEYWORDS([bsc-nat]) AT_CHECK([test "$enable_nat_test" != no || exit 77]) cp $abs_srcdir/bsc-nat/prefixes.csv . cp $abs_srcdir/bsc-nat/barr.cfg . cp $abs_srcdir/bsc-nat/barr_dup.cfg . cat $abs_srcdir/bsc-nat/bsc_nat_test.ok > expout AT_CHECK([$abs_top_builddir/tests/bsc-nat/bsc_nat_test], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([smpp]) AT_KEYWORDS([smpp]) AT_CHECK([test "$enable_smpp_test" != no || exit 77]) cat $abs_srcdir/smpp/smpp_test.ok > expout cat $abs_srcdir/smpp/smpp_test.err > experr AT_CHECK([$abs_top_builddir/tests/smpp/smpp_test], [], [expout], [experr]) AT_CLEANUP AT_SETUP([bsc-nat-trie]) AT_KEYWORDS([bsc-nat-trie]) AT_CHECK([test "$enable_nat_test" != no || exit 77]) cp $abs_srcdir/bsc-nat-trie/prefixes.csv . cat $abs_srcdir/bsc-nat-trie/bsc_nat_trie_test.ok > expout AT_CHECK([$abs_top_builddir/tests/bsc-nat-trie/bsc_nat_trie_test], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([abis]) AT_KEYWORDS([abis]) cat $abs_srcdir/abis/abis_test.ok > expout AT_CHECK([$abs_top_builddir/tests/abis/abis_test], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([bsc]) AT_KEYWORDS([bsc]) AT_CHECK([test "$enable_bsc_test" != no || exit 77]) cat $abs_srcdir/bsc/bsc_test.ok > expout AT_CHECK([$abs_top_builddir/tests/bsc/bsc_test], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([gbproxy]) AT_KEYWORDS([gbproxy]) cat $abs_srcdir/gbproxy/gbproxy_test.ok > expout AT_CHECK([$abs_top_builddir/tests/gbproxy/gbproxy_test], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([trau]) AT_KEYWORDS([trau]) cat $abs_srcdir/trau/trau_test.ok > expout AT_CHECK([$abs_top_builddir/tests/trau/trau_test], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([sgsn]) AT_KEYWORDS([sgsn]) AT_CHECK([test "$enable_sgsn_test" != no || exit 77]) cat $abs_srcdir/sgsn/sgsn_test.ok > expout AT_CHECK([$abs_top_builddir/tests/sgsn/sgsn_test], [], [expout], [ignore]) AT_CLEANUP openbsc-0.15.0/openbsc/tests/trau/000077500000000000000000000000001265565154000170055ustar00rootroot00000000000000openbsc-0.15.0/openbsc/tests/trau/Makefile.am000066400000000000000000000012121265565154000210350ustar00rootroot00000000000000AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(LIBSMPP34_CFLAGS) $(COVERAGE_CFLAGS) AM_LDFLAGS = $(COVERAGE_LDFLAGS) EXTRA_DIST = trau_test.ok noinst_PROGRAMS = trau_test trau_test_SOURCES = trau_test.c trau_test_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ $(top_builddir)/src/libmsc/libmsc.a \ $(top_builddir)/src/libbsc/libbsc.a \ $(top_builddir)/src/libtrau/libtrau.a \ $(top_builddir)/src/libcommon/libcommon.a \ $(LIBOSMOCORE_LIBS) $(LIBOSMOABIS_LIBS) \ $(LIBOSMOGSM_LIBS) $(LIBSMPP34_LIBS) $(LIBOSMOVTY_LIBS) $(LIBRARY_DL) -ldbi openbsc-0.15.0/openbsc/tests/trau/trau_test.c000066400000000000000000000045021265565154000211640ustar00rootroot00000000000000/* (C) 2013 by Andreas Eversberg * 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 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 . * */ #include #include #include #include #include #include void test_trau_fr_efr(unsigned char *data) { struct decoded_trau_frame tf; struct msgb *msg; struct gsm_data_frame *frame; printf("Testing TRAU FR transcoding.\n"); data[0] = 0xd0; trau_encode_fr(&tf, data); tf.c_bits[11] = 0; /* clear BFI */ msg = trau_decode_fr(1, &tf); OSMO_ASSERT(msg != NULL); frame = (struct gsm_data_frame *)msg->data; OSMO_ASSERT(frame->msg_type == GSM_TCHF_FRAME); OSMO_ASSERT(!memcmp(frame->data, data, 33)); msgb_free(msg); printf("Testing TRAU EFR transcoding.\n"); data[0] = 0xc0; trau_encode_efr(&tf, data); OSMO_ASSERT(tf.d_bits[0] == 1); /* spare bit must be 1 */ tf.c_bits[11] = 0; /* clear BFI */ msg = trau_decode_efr(1, &tf); OSMO_ASSERT(msg != NULL); frame = (struct gsm_data_frame *)msg->data; OSMO_ASSERT(frame->msg_type == GSM_TCHF_FRAME_EFR); OSMO_ASSERT(!memcmp(frame->data, data, 31)); printf("Testing TRAU EFR decoding with CRC error.\n"); tf.d_bits[0] = 0; /* spare bit must be included */ msg = trau_decode_efr(1, &tf); OSMO_ASSERT(msg != NULL); frame = (struct gsm_data_frame *)msg->data; OSMO_ASSERT(frame->msg_type == GSM_BAD_FRAME); msgb_free(msg); } int main() { unsigned char data[33]; int i; memset(data, 0x00, sizeof(data)); test_trau_fr_efr(data); memset(data, 0xff, sizeof(data)); test_trau_fr_efr(data); srandom(42); for (i = 0; i < sizeof(data); i++) data[i] = random(); test_trau_fr_efr(data); printf("Done\n"); return 0; } /* stubs */ void vty_out() {} openbsc-0.15.0/openbsc/tests/trau/trau_test.ok000066400000000000000000000004641265565154000213560ustar00rootroot00000000000000Testing TRAU FR transcoding. Testing TRAU EFR transcoding. Testing TRAU EFR decoding with CRC error. Testing TRAU FR transcoding. Testing TRAU EFR transcoding. Testing TRAU EFR decoding with CRC error. Testing TRAU FR transcoding. Testing TRAU EFR transcoding. Testing TRAU EFR decoding with CRC error. Done openbsc-0.15.0/openbsc/tests/vty_test_runner.py000066400000000000000000001221771265565154000216700ustar00rootroot00000000000000#!/usr/bin/env python # (C) 2013 by Katerina Barone-Adesi # (C) 2013 by Holger Hans Peter Freyther # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU 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 General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . import os import time import unittest import socket import osmopy.obscvty as obscvty import osmopy.osmoutil as osmoutil confpath = '.' class TestVTYBase(unittest.TestCase): def vty_command(self): raise Exception("Needs to be implemented by a subclass") def vty_app(self): raise Exception("Needs to be implemented by a subclass") def setUp(self): osmo_vty_cmd = self.vty_command()[:] config_index = osmo_vty_cmd.index('-c') if config_index: cfi = config_index + 1 osmo_vty_cmd[cfi] = os.path.join(confpath, osmo_vty_cmd[cfi]) try: print "Launch: %s from %s" % (' '.join(osmo_vty_cmd), os.getcwd()) self.proc = osmoutil.popen_devnull(osmo_vty_cmd) except OSError: print >> sys.stderr, "Current directory: %s" % os.getcwd() print >> sys.stderr, "Consider setting -b" time.sleep(1) appstring = self.vty_app()[2] appport = self.vty_app()[0] self.vty = obscvty.VTYInteract(appstring, "127.0.0.1", appport) def tearDown(self): self.vty = None osmoutil.end_proc(self.proc) class TestVTYMGCP(TestVTYBase): def vty_command(self): return ["./src/osmo-bsc_mgcp/osmo-bsc_mgcp", "-c", "doc/examples/osmo-bsc_mgcp/mgcp.cfg"] def vty_app(self): return (4243, "./src/osmo-bsc_mgcp/osmo-bsc_mgcp", "OpenBSC MGCP", "mgcp") def testForcePtime(self): self.vty.enable() res = self.vty.command("show running-config") self.assert_(res.find(' rtp force-ptime 20\r') > 0) self.assertEquals(res.find(' no rtp force-ptime\r'), -1) self.vty.command("configure terminal") self.vty.command("mgcp") self.vty.command("no rtp force-ptime") res = self.vty.command("show running-config") self.assertEquals(res.find(' rtp force-ptime 20\r'), -1) self.assertEquals(res.find(' no rtp force-ptime\r'), -1) def testOmitAudio(self): self.vty.enable() res = self.vty.command("show running-config") self.assert_(res.find(' sdp audio-payload send-name\r') > 0) self.assertEquals(res.find(' no sdp audio-payload send-name\r'), -1) self.vty.command("configure terminal") self.vty.command("mgcp") self.vty.command("no sdp audio-payload send-name") res = self.vty.command("show running-config") self.assertEquals(res.find(' rtp sdp audio-payload send-name\r'), -1) self.assert_(res.find(' no sdp audio-payload send-name\r') > 0) # TODO: test it for the trunk! def testBindAddr(self): self.vty.enable() self.vty.command("configure terminal") self.vty.command("mgcp") # enable.. disable bts-bind-ip self.vty.command("rtp bts-bind-ip 254.253.252.250") res = self.vty.command("show running-config") self.assert_(res.find('rtp bts-bind-ip 254.253.252.250') > 0) self.vty.command("no rtp bts-bind-ip") res = self.vty.command("show running-config") self.assertEquals(res.find(' rtp bts-bind-ip'), -1) # enable.. disable net-bind-ip self.vty.command("rtp net-bind-ip 254.253.252.250") res = self.vty.command("show running-config") self.assert_(res.find('rtp net-bind-ip 254.253.252.250') > 0) self.vty.command("no rtp net-bind-ip") res = self.vty.command("show running-config") self.assertEquals(res.find(' rtp net-bind-ip'), -1) class TestVTYGenericBSC(TestVTYBase): def checkForEndAndExit(self): res = self.vty.command("list") #print ('looking for "exit"\n') self.assert_(res.find(' exit\r') > 0) #print 'found "exit"\nlooking for "end"\n' self.assert_(res.find(' end\r') > 0) #print 'found "end"\n' def _testConfigNetworkTree(self): self.vty.enable() self.assertTrue(self.vty.verify("configure terminal",[''])) self.assertEquals(self.vty.node(), 'config') self.checkForEndAndExit() self.assertTrue(self.vty.verify("network",[''])) self.assertEquals(self.vty.node(), 'config-net') self.checkForEndAndExit() self.assertTrue(self.vty.verify("bts 0",[''])) self.assertEquals(self.vty.node(), 'config-net-bts') self.checkForEndAndExit() self.assertTrue(self.vty.verify("trx 0",[''])) self.assertEquals(self.vty.node(), 'config-net-bts-trx') self.checkForEndAndExit() self.vty.command("write terminal") self.assertTrue(self.vty.verify("exit",[''])) self.assertEquals(self.vty.node(), 'config-net-bts') self.assertTrue(self.vty.verify("exit",[''])) self.assertTrue(self.vty.verify("bts 1",[''])) self.assertEquals(self.vty.node(), 'config-net-bts') self.checkForEndAndExit() self.assertTrue(self.vty.verify("trx 1",[''])) self.assertEquals(self.vty.node(), 'config-net-bts-trx') self.checkForEndAndExit() self.vty.command("write terminal") self.assertTrue(self.vty.verify("exit",[''])) self.assertEquals(self.vty.node(), 'config-net-bts') self.assertTrue(self.vty.verify("exit",[''])) self.assertEquals(self.vty.node(), 'config-net') self.assertTrue(self.vty.verify("exit",[''])) self.assertEquals(self.vty.node(), 'config') self.assertTrue(self.vty.verify("exit",[''])) self.assertTrue(self.vty.node() is None) class TestVTYNITB(TestVTYGenericBSC): def vty_command(self): return ["./src/osmo-nitb/osmo-nitb", "-c", "doc/examples/osmo-nitb/nanobts/openbsc.cfg"] def vty_app(self): return (4242, "./src/osmo-nitb/osmo-nitb", "OpenBSC", "nitb") def testConfigNetworkTree(self): self._testConfigNetworkTree() def checkForSmpp(self): """SMPP is not always enabled, check if it is""" res = self.vty.command("list") return "smpp" in res def testSmppFirst(self): # enable the configuration self.vty.enable() self.vty.command("configure terminal") if not self.checkForSmpp(): return self.vty.command("smpp") # check the default res = self.vty.command("write terminal") self.assert_(res.find(' no smpp-first') > 0) self.vty.verify("smpp-first", ['']) res = self.vty.command("write terminal") self.assert_(res.find(' smpp-first') > 0) self.assertEquals(res.find('no smpp-first'), -1) self.vty.verify("no smpp-first", ['']) res = self.vty.command("write terminal") self.assert_(res.find('no smpp-first') > 0) def testVtyTree(self): self.vty.enable() self.assertTrue(self.vty.verify("configure terminal", [''])) self.assertEquals(self.vty.node(), 'config') self.checkForEndAndExit() self.assertTrue(self.vty.verify('mncc-int', [''])) self.assertEquals(self.vty.node(), 'config-mncc-int') self.checkForEndAndExit() self.assertTrue(self.vty.verify('exit', [''])) if self.checkForSmpp(): self.assertEquals(self.vty.node(), 'config') self.assertTrue(self.vty.verify('smpp', [''])) self.assertEquals(self.vty.node(), 'config-smpp') self.checkForEndAndExit() self.assertTrue(self.vty.verify("exit", [''])) self.assertEquals(self.vty.node(), 'config') self.assertTrue(self.vty.verify("exit", [''])) self.assertTrue(self.vty.node() is None) # Check searching for outer node's commands self.vty.command("configure terminal") self.vty.command('mncc-int') if self.checkForSmpp(): self.vty.command('smpp') self.assertEquals(self.vty.node(), 'config-smpp') self.vty.command('mncc-int') self.assertEquals(self.vty.node(), 'config-mncc-int') def testEnableDisablePeriodicLU(self): self.vty.enable() self.vty.command("configure terminal") self.vty.command("network") self.vty.command("bts 0") # Test invalid input self.vty.verify("periodic location update 0", ['% Unknown command.']) self.vty.verify("periodic location update 5", ['% Unknown command.']) self.vty.verify("periodic location update 1531", ['% Unknown command.']) # Enable periodic lu.. self.vty.verify("periodic location update 60", ['']) res = self.vty.command("write terminal") self.assert_(res.find('periodic location update 60') > 0) self.assertEquals(res.find('no periodic location update'), -1) # Now disable it.. self.vty.verify("no periodic location update", ['']) res = self.vty.command("write terminal") self.assertEquals(res.find('periodic location update 60'), -1) self.assert_(res.find('no periodic location update') > 0) def testEnableDisableSiHacks(self): self.vty.enable() self.vty.command("configure terminal") self.vty.command("network") self.vty.command("bts 0") # Enable periodic lu.. self.vty.verify("force-combined-si", ['']) res = self.vty.command("write terminal") self.assert_(res.find(' force-combined-si') > 0) self.assertEquals(res.find('no force-combined-si'), -1) # Now disable it.. self.vty.verify("no force-combined-si", ['']) res = self.vty.command("write terminal") self.assertEquals(res.find(' force-combined-si'), -1) self.assert_(res.find('no force-combined-si') > 0) def testRachAccessControlClass(self): self.vty.enable() self.vty.command("configure terminal") self.vty.command("network") self.vty.command("bts 0") # Test invalid input self.vty.verify("rach access-control-class", ['% Command incomplete.']) self.vty.verify("rach access-control-class 1", ['% Command incomplete.']) self.vty.verify("rach access-control-class -1", ['% Unknown command.']) self.vty.verify("rach access-control-class 10", ['% Unknown command.']) self.vty.verify("rach access-control-class 16", ['% Unknown command.']) # Barred rach access control classes for classNum in range(16): if classNum != 10: self.vty.verify("rach access-control-class " + str(classNum) + " barred", ['']) # Verify settings res = self.vty.command("write terminal") for classNum in range(16): if classNum != 10: self.assert_(res.find("rach access-control-class " + str(classNum) + " barred") > 0) # Allowed rach access control classes for classNum in range(16): if classNum != 10: self.vty.verify("rach access-control-class " + str(classNum) + " allowed", ['']) # Verify settings res = self.vty.command("write terminal") for classNum in range(16): if classNum != 10: self.assertEquals(res.find("rach access-control-class " + str(classNum) + " barred"), -1) def testSubscriberCreateDelete(self): self.vty.enable() imsi = "204300854013739" # Initially we don't have this subscriber self.vty.verify('show subscriber imsi '+imsi, ['% No subscriber found for imsi '+imsi]) # Lets create one res = self.vty.command('subscriber create imsi '+imsi) self.assert_(res.find(" IMSI: "+imsi) > 0) # Now we have it res = self.vty.command('show subscriber imsi '+imsi) self.assert_(res.find(" IMSI: "+imsi) > 0) # Delete it res = self.vty.command('subscriber delete imsi '+imsi) self.assert_(res != "") # Now it should not be there anymore res = self.vty.command('show subscriber imsi '+imsi) self.assert_(res != '% No subscriber found for imsi '+imsi) def testSubscriberSettings(self): self.vty.enable() imsi = "204300854013739" wrong_imsi = "204300999999999" # Lets create one res = self.vty.command('subscriber create imsi '+imsi) self.assert_(res.find(" IMSI: "+imsi) > 0) self.vty.verify('subscriber imsi '+wrong_imsi+' name wrong', ['% No subscriber found for imsi '+wrong_imsi]) res = self.vty.command('subscriber imsi '+imsi+' name '+('X' * 160)) self.assert_(res.find("NAME is too long") > 0) self.vty.verify('subscriber imsi '+imsi+' name '+('G' * 159), ['']) self.vty.verify('subscriber imsi '+wrong_imsi+' extension 840', ['% No subscriber found for imsi '+wrong_imsi]) res = self.vty.command('subscriber imsi '+imsi+' extension '+('9' * 15)) self.assert_(res.find("EXTENSION is too long") > 0) self.vty.verify('subscriber imsi '+imsi+' extension '+('1' * 14), ['']) # Delete it res = self.vty.command('subscriber delete imsi '+imsi) self.assert_(res != "") def testShowPagingGroup(self): res = self.vty.command("show paging-group 255 1234567") self.assertEqual(res, "% can't find BTS 255") res = self.vty.command("show paging-group 0 1234567") self.assertEquals(res, "%Paging group for IMSI 1234567 on BTS #0 is 7") def testShowNetwork(self): res = self.vty.command("show network") self.assert_(res.startswith('BSC is on Country Code') >= 0) def testMeasurementFeed(self): self.vty.enable() self.vty.command("configure terminal") self.vty.command("mncc-int") res = self.vty.command("write terminal") self.assertEquals(res.find('meas-feed scenario'), -1) self.vty.command("meas-feed scenario bla") res = self.vty.command("write terminal") self.assert_(res.find('meas-feed scenario bla') > 0) self.vty.command("meas-feed scenario abcdefghijklmnopqrstuvwxyz01234567890") res = self.vty.command("write terminal") self.assertEquals(res.find('meas-feed scenario abcdefghijklmnopqrstuvwxyz01234567890'), -1) self.assertEquals(res.find('meas-feed scenario abcdefghijklmnopqrstuvwxyz012345'), -1) self.assert_(res.find('meas-feed scenario abcdefghijklmnopqrstuvwxyz01234') > 0) class TestVTYBSC(TestVTYGenericBSC): def vty_command(self): return ["./src/osmo-bsc/osmo-bsc", "-c", "doc/examples/osmo-bsc/osmo-bsc.cfg"] def vty_app(self): return (4242, "./src/osmo-bsc/osmo-bsc", "OsmoBSC", "bsc") def testConfigNetworkTree(self): self._testConfigNetworkTree() def testVtyTree(self): self.vty.enable() self.assertTrue(self.vty.verify("configure terminal", [''])) self.assertEquals(self.vty.node(), 'config') self.checkForEndAndExit() self.assertTrue(self.vty.verify("msc 0", [''])) self.assertEquals(self.vty.node(), 'config-msc') self.checkForEndAndExit() self.assertTrue(self.vty.verify("exit", [''])) self.assertEquals(self.vty.node(), 'config') self.assertTrue(self.vty.verify("bsc", [''])) self.assertEquals(self.vty.node(), 'config-bsc') self.checkForEndAndExit() self.assertTrue(self.vty.verify("exit", [''])) self.assertEquals(self.vty.node(), 'config') self.assertTrue(self.vty.verify("exit", [''])) self.assertTrue(self.vty.node() is None) # Check searching for outer node's commands self.vty.command("configure terminal") self.vty.command('msc 0') self.vty.command("bsc") self.assertEquals(self.vty.node(), 'config-bsc') self.vty.command("msc 0") self.assertEquals(self.vty.node(), 'config-msc') def testUssdNotificationsMsc(self): self.vty.enable() self.vty.command("configure terminal") self.vty.command("msc") # Test invalid input self.vty.verify("bsc-msc-lost-text", ['% Command incomplete.']) self.vty.verify("bsc-welcome-text", ['% Command incomplete.']) self.vty.verify("bsc-grace-text", ['% Command incomplete.']) # Enable USSD notifications self.vty.verify("bsc-msc-lost-text MSC disconnected", ['']) self.vty.verify("bsc-welcome-text Hello MS", ['']) self.vty.verify("bsc-grace-text In grace period", ['']) # Verify settings res = self.vty.command("write terminal") self.assert_(res.find('bsc-msc-lost-text MSC disconnected') > 0) self.assertEquals(res.find('no bsc-msc-lost-text'), -1) self.assert_(res.find('bsc-welcome-text Hello MS') > 0) self.assertEquals(res.find('no bsc-welcome-text'), -1) self.assert_(res.find('bsc-grace-text In grace period') > 0) self.assertEquals(res.find('no bsc-grace-text'), -1) # Now disable it.. self.vty.verify("no bsc-msc-lost-text", ['']) self.vty.verify("no bsc-welcome-text", ['']) self.vty.verify("no bsc-grace-text", ['']) # Verify settings res = self.vty.command("write terminal") self.assertEquals(res.find('bsc-msc-lost-text MSC disconnected'), -1) self.assert_(res.find('no bsc-msc-lost-text') > 0) self.assertEquals(res.find('bsc-welcome-text Hello MS'), -1) self.assert_(res.find('no bsc-welcome-text') > 0) self.assertEquals(res.find('bsc-grace-text In grace period'), -1) self.assert_(res.find('no bsc-grace-text') > 0) def testUssdNotificationsBsc(self): self.vty.enable() self.vty.command("configure terminal") self.vty.command("bsc") # Test invalid input self.vty.verify("missing-msc-text", ['% Command incomplete.']) # Enable USSD notifications self.vty.verify("missing-msc-text No MSC found", ['']) # Verify settings res = self.vty.command("write terminal") self.assert_(res.find('missing-msc-text No MSC found') > 0) self.assertEquals(res.find('no missing-msc-text'), -1) # Now disable it.. self.vty.verify("no missing-msc-text", ['']) # Verify settings res = self.vty.command("write terminal") self.assertEquals(res.find('missing-msc-text No MSC found'), -1) self.assert_(res.find('no missing-msc-text') > 0) def testNetworkTimezone(self): self.vty.enable() self.vty.verify("configure terminal", ['']) self.vty.verify("network", ['']) self.vty.verify("bts 0", ['']) # Test invalid input self.vty.verify("timezone", ['% Command incomplete.']) self.vty.verify("timezone 20 0", ['% Unknown command.']) self.vty.verify("timezone 0 11", ['% Unknown command.']) self.vty.verify("timezone 0 0 99", ['% Unknown command.']) # Set time zone without DST self.vty.verify("timezone 2 30", ['']) # Verify settings res = self.vty.command("write terminal") self.assert_(res.find('timezone 2 30') > 0) self.assertEquals(res.find('timezone 2 30 '), -1) # Set time zone with DST self.vty.verify("timezone 2 30 1", ['']) # Verify settings res = self.vty.command("write terminal") self.assert_(res.find('timezone 2 30 1') > 0) # Now disable it.. self.vty.verify("no timezone", ['']) # Verify settings res = self.vty.command("write terminal") self.assertEquals(res.find(' timezone'), -1) def testShowNetwork(self): res = self.vty.command("show network") self.assert_(res.startswith('BSC is on Country Code') >= 0) def testPingPongConfiguration(self): self.vty.enable() self.vty.verify("configure terminal", ['']) self.vty.verify("network", ['']) self.vty.verify("msc 0", ['']) self.vty.verify("timeout-ping 12", ['']) self.vty.verify("timeout-pong 14", ['']) res = self.vty.command("show running-config") self.assert_(res.find(" timeout-ping 12") > 0) self.assert_(res.find(" timeout-pong 14") > 0) self.assert_(res.find(" no timeout-ping advanced") > 0) self.vty.verify("timeout-ping advanced", ['']) res = self.vty.command("show running-config") self.assert_(res.find(" timeout-ping 12") > 0) self.assert_(res.find(" timeout-pong 14") > 0) self.assert_(res.find(" timeout-ping advanced") > 0) self.vty.verify("no timeout-ping advanced", ['']) res = self.vty.command("show running-config") self.assert_(res.find(" timeout-ping 12") > 0) self.assert_(res.find(" timeout-pong 14") > 0) self.assert_(res.find(" no timeout-ping advanced") > 0) self.vty.verify("no timeout-ping", ['']) res = self.vty.command("show running-config") self.assertEquals(res.find(" timeout-ping 12"), -1) self.assertEquals(res.find(" timeout-pong 14"), -1) self.assertEquals(res.find(" no timeout-ping advanced"), -1) self.assert_(res.find(" no timeout-ping") > 0) self.vty.verify("timeout-ping advanced", ['%ping handling is disabled. Enable it first.']) # And back to enabling it self.vty.verify("timeout-ping 12", ['']) self.vty.verify("timeout-pong 14", ['']) res = self.vty.command("show running-config") self.assert_(res.find(" timeout-ping 12") > 0) self.assert_(res.find(" timeout-pong 14") > 0) self.assert_(res.find(" timeout-ping advanced") > 0) def testMscDataCoreLACCI(self): self.vty.enable() res = self.vty.command("show running-config") self.assertEquals(res.find("core-location-area-code"), -1) self.assertEquals(res.find("core-cell-identity"), -1) self.vty.command("configure terminal") self.vty.command("msc 0") self.vty.command("core-location-area-code 666") self.vty.command("core-cell-identity 333") res = self.vty.command("show running-config") self.assert_(res.find("core-location-area-code 666") > 0) self.assert_(res.find("core-cell-identity 333") > 0) class TestVTYNAT(TestVTYGenericBSC): def vty_command(self): return ["./src/osmo-bsc_nat/osmo-bsc_nat", "-c", "doc/examples/osmo-bsc_nat/osmo-bsc_nat.cfg"] def vty_app(self): return (4244, "src/osmo-bsc_nat/osmo-bsc_nat", "OsmoBSCNAT", "nat") def testVtyTree(self): self.vty.enable() self.assertTrue(self.vty.verify('configure terminal', [''])) self.assertEquals(self.vty.node(), 'config') self.checkForEndAndExit() self.assertTrue(self.vty.verify('mgcp', [''])) self.assertEquals(self.vty.node(), 'config-mgcp') self.checkForEndAndExit() self.assertTrue(self.vty.verify('exit', [''])) self.assertEquals(self.vty.node(), 'config') self.assertTrue(self.vty.verify('nat', [''])) self.assertEquals(self.vty.node(), 'config-nat') self.checkForEndAndExit() self.assertTrue(self.vty.verify('bsc 0', [''])) self.assertEquals(self.vty.node(), 'config-nat-bsc') self.checkForEndAndExit() self.assertTrue(self.vty.verify('exit', [''])) self.assertEquals(self.vty.node(), 'config-nat') self.assertTrue(self.vty.verify('exit', [''])) self.assertEquals(self.vty.node(), 'config') self.assertTrue(self.vty.verify('exit', [''])) self.assertTrue(self.vty.node() is None) # Check searching for outer node's commands self.vty.command('configure terminal') self.vty.command('mgcp') self.vty.command('nat') self.assertEquals(self.vty.node(), 'config-nat') self.vty.command('mgcp') self.assertEquals(self.vty.node(), 'config-mgcp') self.vty.command('nat') self.assertEquals(self.vty.node(), 'config-nat') self.vty.command('bsc 0') self.vty.command('mgcp') self.assertEquals(self.vty.node(), 'config-mgcp') def testRewriteNoRewrite(self): self.vty.enable() res = self.vty.command("configure terminal") res = self.vty.command("nat") res = self.vty.command("number-rewrite rewrite.cfg") res = self.vty.command("no number-rewrite") def testEnsureNoEnsureModeSet(self): self.vty.enable() res = self.vty.command("configure terminal") res = self.vty.command("nat") # Ensure the default res = self.vty.command("show running-config") self.assert_(res.find('\n sdp-ensure-amr-mode-set') > 0) self.vty.command("sdp-ensure-amr-mode-set") res = self.vty.command("show running-config") self.assert_(res.find('\n sdp-ensure-amr-mode-set') > 0) self.vty.command("no sdp-ensure-amr-mode-set") res = self.vty.command("show running-config") self.assert_(res.find('\n no sdp-ensure-amr-mode-set') > 0) def testRewritePostNoRewrite(self): self.vty.enable() self.vty.command("configure terminal") self.vty.command("nat") self.vty.verify("number-rewrite-post rewrite.cfg", ['']) self.vty.verify("no number-rewrite-post", ['']) def testPrefixTreeLoading(self): cfg = os.path.join(confpath, "tests/bsc-nat-trie/prefixes.csv") self.vty.enable() self.vty.command("configure terminal") self.vty.command("nat") res = self.vty.command("prefix-tree %s" % cfg) self.assertEqual(res, "% prefix-tree loaded 17 rules.") self.vty.command("end") res = self.vty.command("show prefix-tree") self.assertEqual(res, '1,1\r\n12,2\r\n123,3\r\n1234,4\r\n12345,5\r\n123456,6\r\n1234567,7\r\n12345678,8\r\n123456789,9\r\n1234567890,10\r\n13,11\r\n14,12\r\n15,13\r\n16,14\r\n82,16\r\n823455,15\r\n+49123,17') self.vty.command("configure terminal") self.vty.command("nat") self.vty.command("no prefix-tree") self.vty.command("end") res = self.vty.command("show prefix-tree") self.assertEqual(res, "% there is now prefix tree loaded.") def testUssdSideChannelProvider(self): self.vty.command("end") self.vty.enable() self.vty.command("configure terminal") self.vty.command("nat") self.vty.command("ussd-token key") self.vty.command("end") res = self.vty.verify("show ussd-connection", ['The USSD side channel provider is not connected and not authorized.']) self.assertTrue(res) ussdSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) ussdSocket.connect(('127.0.0.1', 5001)) ussdSocket.settimeout(2.0) print "Connected to %s:%d" % ussdSocket.getpeername() print "Expecting ID_GET request" data = ussdSocket.recv(4) self.assertEqual(data, "\x00\x01\xfe\x04") print "Going to send ID_RESP response" res = ussdSocket.send("\x00\x07\xfe\x05\x00\x04\x01\x6b\x65\x79") self.assertEqual(res, 10) # initiating PING/PONG cycle to know, that the ID_RESP message has been processed print "Going to send PING request" res = ussdSocket.send("\x00\x01\xfe\x00") self.assertEqual(res, 4) print "Expecting PONG response" data = ussdSocket.recv(4) self.assertEqual(data, "\x00\x01\xfe\x01") res = self.vty.verify("show ussd-connection", ['The USSD side channel provider is connected and authorized.']) self.assertTrue(res) print "Going to shut down connection" ussdSocket.shutdown(socket.SHUT_WR) print "Expecting EOF" data = ussdSocket.recv(4) self.assertEqual(data, "") ussdSocket.close() res = self.vty.verify("show ussd-connection", ['The USSD side channel provider is not connected and not authorized.']) self.assertTrue(res) def testAccessList(self): """ Verify that the imsi-deny can have a reject cause or no reject cause """ self.vty.enable() self.vty.command("configure terminal") self.vty.command("nat") # Old default self.vty.command("access-list test-default imsi-deny ^123[0-9]*$") res = self.vty.command("show running-config").split("\r\n") asserted = False for line in res: if line.startswith(" access-list test-default"): self.assertEqual(line, " access-list test-default imsi-deny ^123[0-9]*$ 11 11") asserted = True self.assert_(asserted) # Check the optional CM Service Reject Cause self.vty.command("access-list test-cm-deny imsi-deny ^123[0-9]*$ 42").split("\r\n") res = self.vty.command("show running-config").split("\r\n") asserted = False for line in res: if line.startswith(" access-list test-cm"): self.assertEqual(line, " access-list test-cm-deny imsi-deny ^123[0-9]*$ 42 11") asserted = True self.assert_(asserted) # Check the optional LU Reject Cause self.vty.command("access-list test-lu-deny imsi-deny ^123[0-9]*$ 23 42").split("\r\n") res = self.vty.command("show running-config").split("\r\n") asserted = False for line in res: if line.startswith(" access-list test-lu"): self.assertEqual(line, " access-list test-lu-deny imsi-deny ^123[0-9]*$ 23 42") asserted = True self.assert_(asserted) class TestVTYGbproxy(TestVTYGenericBSC): def vty_command(self): return ["./src/gprs/osmo-gbproxy", "-c", "doc/examples/osmo-gbproxy/osmo-gbproxy.cfg"] def vty_app(self): return (4246, "./src/gprs/osmo-gbproxy", "OsmoGbProxy", "bsc") def testVtyTree(self): self.vty.enable() self.assertTrue(self.vty.verify('configure terminal', [''])) self.assertEquals(self.vty.node(), 'config') self.checkForEndAndExit() self.assertTrue(self.vty.verify('ns', [''])) self.assertEquals(self.vty.node(), 'config-ns') self.checkForEndAndExit() self.assertTrue(self.vty.verify('exit', [''])) self.assertEquals(self.vty.node(), 'config') self.assertTrue(self.vty.verify('gbproxy', [''])) self.assertEquals(self.vty.node(), 'config-gbproxy') self.checkForEndAndExit() self.assertTrue(self.vty.verify('exit', [''])) self.assertEquals(self.vty.node(), 'config') def testVtyShow(self): res = self.vty.command("show ns") self.assert_(res.find('Encapsulation NS-UDP-IP') >= 0) res = self.vty.command("show gbproxy stats") self.assert_(res.find('GBProxy Global Statistics') >= 0) def testVtyDeletePeer(self): self.vty.enable() self.assertTrue(self.vty.verify('delete-gbproxy-peer 9999 bvci 7777', ['BVC not found'])) res = self.vty.command("delete-gbproxy-peer 9999 all dry-run") self.assert_(res.find('Not Deleted 0 BVC') >= 0) self.assert_(res.find('Not Deleted 0 NS-VC') >= 0) res = self.vty.command("delete-gbproxy-peer 9999 only-bvc dry-run") self.assert_(res.find('Not Deleted 0 BVC') >= 0) self.assert_(res.find('Not Deleted 0 NS-VC') < 0) res = self.vty.command("delete-gbproxy-peer 9999 only-nsvc dry-run") self.assert_(res.find('Not Deleted 0 BVC') < 0) self.assert_(res.find('Not Deleted 0 NS-VC') >= 0) res = self.vty.command("delete-gbproxy-peer 9999 all") self.assert_(res.find('Deleted 0 BVC') >= 0) self.assert_(res.find('Deleted 0 NS-VC') >= 0) class TestVTYSGSN(TestVTYGenericBSC): def vty_command(self): return ["./src/gprs/osmo-sgsn", "-c", "doc/examples/osmo-sgsn/osmo-sgsn.cfg"] def vty_app(self): return (4245, "./src/gprs/osmo-sgsn", "OsmoSGSN", "sgsn") def testVtyTree(self): self.vty.enable() self.assertTrue(self.vty.verify('configure terminal', [''])) self.assertEquals(self.vty.node(), 'config') self.checkForEndAndExit() self.assertTrue(self.vty.verify('ns', [''])) self.assertEquals(self.vty.node(), 'config-ns') self.checkForEndAndExit() self.assertTrue(self.vty.verify('exit', [''])) self.assertEquals(self.vty.node(), 'config') self.assertTrue(self.vty.verify('sgsn', [''])) self.assertEquals(self.vty.node(), 'config-sgsn') self.checkForEndAndExit() self.assertTrue(self.vty.verify('exit', [''])) self.assertEquals(self.vty.node(), 'config') def testVtyShow(self): res = self.vty.command("show ns") self.assert_(res.find('Encapsulation NS-UDP-IP') >= 0) self.assertTrue(self.vty.verify('show bssgp', [''])) self.assertTrue(self.vty.verify('show bssgp stats', [''])) # TODO: uncomment when the command does not segfault anymore # self.assertTrue(self.vty.verify('show bssgp nsei 123', [''])) # self.assertTrue(self.vty.verify('show bssgp nsei 123 stats', [''])) self.assertTrue(self.vty.verify('show sgsn', [''])) self.assertTrue(self.vty.verify('show mm-context all', [''])) self.assertTrue(self.vty.verify('show mm-context imsi 000001234567', ['No MM context for IMSI 000001234567'])) self.assertTrue(self.vty.verify('show pdp-context all', [''])) res = self.vty.command("show sndcp") self.assert_(res.find('State of SNDCP Entities') >= 0) res = self.vty.command("show llc") self.assert_(res.find('State of LLC Entities') >= 0) def testVtyAuth(self): self.vty.enable() self.assertTrue(self.vty.verify('configure terminal', [''])) self.assertEquals(self.vty.node(), 'config') self.assertTrue(self.vty.verify('sgsn', [''])) self.assertEquals(self.vty.node(), 'config-sgsn') self.assertTrue(self.vty.verify('auth-policy accept-all', [''])) res = self.vty.command("show running-config") self.assert_(res.find('auth-policy accept-all') > 0) self.assertTrue(self.vty.verify('auth-policy acl-only', [''])) res = self.vty.command("show running-config") self.assert_(res.find('auth-policy acl-only') > 0) self.assertTrue(self.vty.verify('auth-policy closed', [''])) res = self.vty.command("show running-config") self.assert_(res.find('auth-policy closed') > 0) self.assertTrue(self.vty.verify('auth-policy remote', [''])) res = self.vty.command("show running-config") self.assert_(res.find('auth-policy remote') > 0) def testVtySubscriber(self): self.vty.enable() res = self.vty.command('show subscriber cache') self.assert_(res.find('1234567890') < 0) self.assertTrue(self.vty.verify('update-subscriber imsi 1234567890 create', [''])) res = self.vty.command('show subscriber cache') self.assert_(res.find('1234567890') >= 0) self.assert_(res.find('Authorized: 0') >= 0) self.assertTrue(self.vty.verify('update-subscriber imsi 1234567890 update-location-result ok', [''])) res = self.vty.command('show subscriber cache') self.assert_(res.find('1234567890') >= 0) self.assert_(res.find('Authorized: 1') >= 0) self.assertTrue(self.vty.verify('update-subscriber imsi 1234567890 cancel update-procedure', [''])) res = self.vty.command('show subscriber cache') self.assert_(res.find('1234567890') >= 0) self.assertTrue(self.vty.verify('update-subscriber imsi 1234567890 destroy', [''])) res = self.vty.command('show subscriber cache') self.assert_(res.find('1234567890') < 0) def testVtyGgsn(self): self.vty.enable() self.assertTrue(self.vty.verify('configure terminal', [''])) self.assertEquals(self.vty.node(), 'config') self.assertTrue(self.vty.verify('sgsn', [''])) self.assertEquals(self.vty.node(), 'config-sgsn') self.assertTrue(self.vty.verify('ggsn 0 remote-ip 127.99.99.99', [''])) self.assertTrue(self.vty.verify('ggsn 0 gtp-version 1', [''])) self.assertTrue(self.vty.verify('apn * ggsn 0', [''])) self.assertTrue(self.vty.verify('apn apn1.test ggsn 0', [''])) self.assertTrue(self.vty.verify('apn apn1.test ggsn 1', ['% a GGSN with id 1 has not been defined'])) self.assertTrue(self.vty.verify('apn apn1.test imsi-prefix 123456 ggsn 0', [''])) self.assertTrue(self.vty.verify('apn apn2.test imsi-prefix 123456 ggsn 0', [''])) res = self.vty.command("show running-config") self.assert_(res.find('ggsn 0 remote-ip 127.99.99.99') >= 0) self.assert_(res.find('ggsn 0 gtp-version 1') >= 0) self.assert_(res.find('apn * ggsn 0') >= 0) self.assert_(res.find('apn apn1.test ggsn 0') >= 0) self.assert_(res.find('apn apn1.test imsi-prefix 123456 ggsn 0') >= 0) self.assert_(res.find('apn apn2.test imsi-prefix 123456 ggsn 0') >= 0) def testVtyEasyAPN(self): self.vty.enable() self.assertTrue(self.vty.verify('configure terminal', [''])) self.assertEquals(self.vty.node(), 'config') self.assertTrue(self.vty.verify('sgsn', [''])) self.assertEquals(self.vty.node(), 'config-sgsn') res = self.vty.command("show running-config") self.assertEquals(res.find("apn internet"), -1) self.assertTrue(self.vty.verify("access-point-name internet.apn", [''])) res = self.vty.command("show running-config") self.assert_(res.find("apn internet.apn ggsn 0") >= 0) self.assertTrue(self.vty.verify("no access-point-name internet.apn", [''])) res = self.vty.command("show running-config") self.assertEquals(res.find("apn internet"), -1) def testVtyCDR(self): self.vty.enable() self.assertTrue(self.vty.verify('configure terminal', [''])) self.assertEquals(self.vty.node(), 'config') self.assertTrue(self.vty.verify('sgsn', [''])) self.assertEquals(self.vty.node(), 'config-sgsn') res = self.vty.command("show running-config") self.assert_(res.find("no cdr filename") > 0) self.vty.command("cdr filename bla.cdr") res = self.vty.command("show running-config") self.assertEquals(res.find("no cdr filename"), -1) self.assert_(res.find(" cdr filename bla.cdr") > 0) self.vty.command("no cdr filename") res = self.vty.command("show running-config") self.assert_(res.find("no cdr filename") > 0) self.assertEquals(res.find(" cdr filename bla.cdr"), -1) res = self.vty.command("show running-config") self.assert_(res.find(" cdr interval 600") > 0) self.vty.command("cdr interval 900") res = self.vty.command("show running-config") self.assert_(res.find(" cdr interval 900") > 0) self.assertEquals(res.find(" cdr interval 600"), -1) def add_nat_test(suite, workdir): if not os.path.isfile(os.path.join(workdir, "src/osmo-bsc_nat/osmo-bsc_nat")): print("Skipping the NAT test") return test = unittest.TestLoader().loadTestsFromTestCase(TestVTYNAT) suite.addTest(test) def add_bsc_test(suite, workdir): if not os.path.isfile(os.path.join(workdir, "src/osmo-bsc/osmo-bsc")): print("Skipping the BSC test") return test = unittest.TestLoader().loadTestsFromTestCase(TestVTYBSC) suite.addTest(test) def add_gbproxy_test(suite, workdir): if not os.path.isfile(os.path.join(workdir, "src/gprs/osmo-gbproxy")): print("Skipping the Gb-Proxy test") return test = unittest.TestLoader().loadTestsFromTestCase(TestVTYGbproxy) suite.addTest(test) def add_sgsn_test(suite, workdir): if not os.path.isfile(os.path.join(workdir, "src/gprs/osmo-sgsn")): print("Skipping the SGSN test") return test = unittest.TestLoader().loadTestsFromTestCase(TestVTYSGSN) suite.addTest(test) if __name__ == '__main__': import argparse import sys workdir = '.' parser = argparse.ArgumentParser() parser.add_argument("-v", "--verbose", dest="verbose", action="store_true", help="verbose mode") parser.add_argument("-p", "--pythonconfpath", dest="p", help="searchpath for config") parser.add_argument("-w", "--workdir", dest="w", help="Working directory") args = parser.parse_args() verbose_level = 1 if args.verbose: verbose_level = 2 if args.w: workdir = args.w if args.p: confpath = args.p print "confpath %s, workdir %s" % (confpath, workdir) os.chdir(workdir) print "Running tests for specific VTY commands" suite = unittest.TestSuite() suite.addTest(unittest.TestLoader().loadTestsFromTestCase(TestVTYMGCP)) suite.addTest(unittest.TestLoader().loadTestsFromTestCase(TestVTYNITB)) add_bsc_test(suite, workdir) add_nat_test(suite, workdir) add_gbproxy_test(suite, workdir) add_sgsn_test(suite, workdir) res = unittest.TextTestRunner(verbosity=verbose_level).run(suite) sys.exit(len(res.errors) + len(res.failures)) openbsc-0.15.0/openbsc/tools/000077500000000000000000000000001265565154000160305ustar00rootroot00000000000000openbsc-0.15.0/openbsc/tools/hlrstat.pl000077500000000000000000000027521265565154000200570ustar00rootroot00000000000000#!/usr/bin/perl use strict; use DBI; my $dbh = DBI->connect("dbi:SQLite:dbname=hlr.sqlite3","",""); my %mcc_names; my %mcc_mnc_names; sub get_mcc_mnc_name($) { my $mcc_mnc = shift; my $ret = $mcc_mnc; if ($mcc_mnc_names{$mcc_mnc} ne '') { $ret = $mcc_mnc_names{$mcc_mnc}; } return $ret; } sub read_networks($) { my $filename = shift; my $cur_name; open(INFILE, $filename); while (my $l = ) { chomp($l); if ($l =~ /^#/) { next; } if ($l =~ /^\t/) { my ($mcc, $mnc, $brand, $r) = split(' ', $l, 4); #printf("%s|%s|%s\n", $mcc, $mnc, $brand); $mcc_mnc_names{"$mcc-$mnc"} = $brand; $mcc_names{$mcc} = $cur_name; } elsif ($l =~ /^(\w\w)\t(.*)/) { #printf("%s|%s\n", $1, $2); $cur_name = $2; } } close(INFILE); } read_networks("networks.tab"); my %oper_count; my %country_count; #my $sth = $dbh->prepare("SELECT imsi FROM subscriber where authorized=1"); my $sth = $dbh->prepare("SELECT imsi FROM subscriber"); $sth->execute(); while (my $href = $sth->fetchrow_hashref) { my ($mcc, $mnc) = $$href{imsi} =~ /(\d{3})(\d{2}).*/; #printf("%s %s-%s \n", $$href{imsi}, $mcc, $mnc); $oper_count{"$mcc-$mnc"}++; $country_count{$mcc}++; } foreach my $c (sort{$country_count{$b} <=> $country_count{$a}} keys %country_count) { printf("%s: %d\n", $mcc_names{$c}, $country_count{$c}); foreach my $k (sort{$oper_count{$b} <=> $oper_count{$a}} keys %oper_count) { if ($k =~ /^$c-/) { printf("\t%s: %d\n", get_mcc_mnc_name($k), $oper_count{$k}); } } } openbsc-0.15.0/wireshark/000077500000000000000000000000001265565154000152365ustar00rootroot00000000000000openbsc-0.15.0/wireshark/0001-abis_oml.patch000066400000000000000000002527501265565154000204350ustar00rootroot00000000000000From c6c10dab54b9a667308004ab5c72dd08f52e4fc1 Mon Sep 17 00:00:00 2001 From: Holger Hans Peter Freyther Date: Tue, 11 Jan 2011 15:08:42 +0100 Subject: [PATCH 1/4] abis_oml Add the GSM ABIS OML dissector from OpenBSC --- epan/CMakeLists.txt | 1 + epan/dissectors/Makefile.common | 1 + epan/dissectors/packet-gsm_abis_oml.c | 1608 +++++++++++++++++++++++++++++++++ epan/dissectors/packet-gsm_abis_oml.h | 830 +++++++++++++++++ 4 files changed, 2440 insertions(+), 0 deletions(-) create mode 100644 epan/dissectors/packet-gsm_abis_oml.c create mode 100644 epan/dissectors/packet-gsm_abis_oml.h Index: wireshark/epan/CMakeLists.txt =================================================================== --- wireshark.orig/epan/CMakeLists.txt 2011-09-06 12:30:00.000000000 +0200 +++ wireshark/epan/CMakeLists.txt 2011-09-06 13:56:18.000000000 +0200 @@ -591,6 +591,7 @@ dissectors/packet-gsm_a_gm.c dissectors/packet-gsm_a_rp.c dissectors/packet-gsm_a_rr.c + dissectors/packet-gsm_abis_oml.c dissectors/packet-gsm_ipa.c dissectors/packet-gsm_bsslap.c dissectors/packet-gsm_bssmap_le.c Index: wireshark/epan/dissectors/Makefile.common =================================================================== --- wireshark.orig/epan/dissectors/Makefile.common 2011-09-06 12:29:41.000000000 +0200 +++ wireshark/epan/dissectors/Makefile.common 2011-09-06 13:56:18.000000000 +0200 @@ -509,6 +509,7 @@ packet-gsm_a_gm.c \ packet-gsm_a_rp.c \ packet-gsm_a_rr.c \ + packet-gsm_abis_oml.c \ packet-gsm_bsslap.c \ packet-gsm_bssmap_le.c \ packet-gsm_ipa.c \ Index: wireshark/epan/dissectors/packet-gsm_abis_oml.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ wireshark/epan/dissectors/packet-gsm_abis_oml.c 2011-09-06 13:56:21.000000000 +0200 @@ -0,0 +1,1746 @@ +/* packet-abis_oml.c + * Routines for packet dissection of GSM A-bis OML (3GPP TS 12.21) + * Copyright 2009-2011 by Harald Welte + * Copyright 2009 by Holger Hans Peter Freyther + * based on A-bis OML code in OpenBSC + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include +#include +#include +#include + +#include "packet-gsm_abis_oml.h" +#include "packet-gsm_a_common.h" + +#include + +/* initialize the protocol and registered fields */ +static int proto_abis_oml = -1; + +/* OML header */ +static int hf_oml_msg_disc = -1; +static int hf_oml_placement = -1; +static int hf_oml_sequence = -1; +static int hf_oml_length = -1; +/* FOM header */ +static int hf_oml_fom_msgtype = -1; +static int hf_oml_fom_objclass = -1; +static int hf_oml_fom_inst_bts = -1; +static int hf_oml_fom_inst_trx = -1; +static int hf_oml_fom_inst_ts = -1; +static int hf_oml_fom_attr_tag = -1; +static int hf_oml_fom_attr_len = -1; +static int hf_oml_fom_attr_val = -1; +/* FOM attributes */ +static int hf_attr_adm_state = -1; +static int hf_attr_arfcn = -1; +static int hf_attr_oper_state = -1; +static int hf_attr_avail_state = -1; +static int hf_attr_event_type = -1; +static int hf_attr_severity = -1; +static int hf_attr_bcch_arfcn = -1; +static int hf_attr_bsic = -1; +static int hf_attr_test_no = -1; +static int hf_attr_tsc = -1; +static int hf_attr_tei = -1; +static int hf_attr_ach_btsp = -1; +static int hf_attr_ach_tslot = -1; +static int hf_attr_ach_sslot = -1; +static int hf_attr_gsm_time = -1; +static int hf_attr_chan_comb = -1; +static int hf_attr_hsn = -1; +static int hf_attr_maio = -1; +/* Ipaccess */ +static int hf_oml_ipa_tres_attr_tag = -1; +static int hf_oml_ipa_tres_attr_len = -1; +static int hf_attr_ipa_test_res = -1; +static int hf_attr_ipa_tr_rxlev = -1; +static int hf_attr_ipa_tr_b_rxlev = -1; +static int hf_attr_ipa_tr_arfcn = -1; +static int hf_attr_ipa_tr_f_qual = -1; +static int hf_attr_ipa_tr_f_err = -1; +static int hf_attr_ipa_tr_rxqual = -1; +static int hf_attr_ipa_tr_frame_offs = -1; +static int hf_attr_ipa_tr_framenr_offs = -1; +static int hf_attr_ipa_tr_bsic = -1; +static int hf_attr_ipa_tr_cell_id = -1; +static int hf_attr_ipa_tr_si2 = -1; +static int hf_attr_ipa_tr_si2bis = -1; +static int hf_attr_ipa_tr_si2ter = -1; +static int hf_attr_ipa_tr_chan_desc = -1; +static int hf_attr_ipa_rsl_ip = -1; +static int hf_attr_ipa_rsl_port = -1; +static int hf_attr_ipa_prim_oml_ip = -1; +static int hf_attr_ipa_prim_oml_port = -1; +static int hf_attr_ipa_location_name = -1; +static int hf_attr_ipa_unit_id = -1; +static int hf_attr_ipa_unit_name = -1; +static int hf_attr_ipa_nv_flags = -1; +static int hf_attr_ipa_nv_mask = -1; +static int hf_attr_ipa_nsl_sport = -1; +static int hf_attr_ipa_nsl_daddr = -1; +static int hf_attr_ipa_nsl_dport = -1; +static int hf_attr_ipa_nsei = -1; +static int hf_attr_ipa_nsvci = -1; +static int hf_attr_ipa_bvci = -1; +static int hf_attr_ipa_rac = -1; + +/* initialize the subtree pointers */ +static int ett_oml = -1; +static int ett_oml_fom = -1; +static int ett_oml_fom_att = -1; + +/* Decode things as nanoBTS traces */ +static gboolean global_oml_use_nano_bts = TRUE; + +static proto_tree *top_tree; + +/* TS 12.21 Chapter 8.1 / TS 08.59 */ +static const value_string oml_msg_disc_vals[] = { + { ABIS_OM_MDISC_FOM, "Formatted O&M" }, + { ABIS_OM_MDISC_MMI, "MMI Transfer" }, + { ABIS_OM_MDISC_TRAU, "TRAU O&M" }, + { ABIS_OM_MDISC_MANUF, "Manufacturer specific" }, +}; + +/* TS 12.21 Chapter 8.1.1 */ +static const value_string oml_placement_vals[] = { + { ABIS_OM_PLACEMENT_ONLY, "Only" }, + { ABIS_OM_PLACEMENT_FIRST, "First" }, + { ABIS_OM_PLACEMENT_MIDDLE, "Middle" }, + { ABIS_OM_PLACEMENT_LAST, "Last" }, +}; + +/* Standard Message Types as per TS 12.21 Chapter 9.2 */ +static const value_string _oml_fom_msgtype_vals[] = { + { NM_MT_LOAD_INIT, "Software Load Init" }, + { NM_MT_LOAD_INIT_ACK, "Software Load Init ACK" }, + { NM_MT_LOAD_INIT_NACK, "Software Load Init NACK" }, + { NM_MT_LOAD_SEG, "Software Load Segment" }, + { NM_MT_LOAD_SEG_ACK, "Software Load Segment ACK" }, + { NM_MT_LOAD_END, "Software Load End" }, + { NM_MT_LOAD_END_ACK, "Software Load End ACK" }, + { NM_MT_LOAD_END_NACK, "Software Load End NACK" }, + { NM_MT_SW_ACT_REQ, "Software Activate Request" }, + { NM_MT_SW_ACT_REQ_ACK, "Software Activate Request ACK" }, + { NM_MT_SW_ACT_REQ_NACK, "Software Activate Request NACK" }, + { NM_MT_ACTIVATE_SW, "Activate Software" }, + { NM_MT_ACTIVATE_SW_ACK, "Activate Software ACK" }, + { NM_MT_ACTIVATE_SW_NACK, "Activate Software NACK" }, + { NM_MT_SW_ACTIVATED_REP, "Software Activated Report" }, + { NM_MT_ESTABLISH_TEI, "Establish TEI" }, + { NM_MT_ESTABLISH_TEI_ACK, "Establish TEI ACK" }, + { NM_MT_ESTABLISH_TEI_NACK, "Establish TEI NACK" }, + { NM_MT_CONN_TERR_SIGN, "Connect Terrestrial Signalling" }, + { NM_MT_CONN_TERR_SIGN_ACK, "Connect Terrestrial Signalling ACK" }, + { NM_MT_CONN_TERR_SIGN_NACK, "Connect Terrestrial Signalling NACK" }, + { NM_MT_DISC_TERR_SIGN, "Disconnect Terrestrial Signalling" }, + { NM_MT_DISC_TERR_SIGN_ACK, "Disconnect Terrestrial Signalling ACK" }, + { NM_MT_DISC_TERR_SIGN_NACK, "Disconnect Terrestrial Signalling NACK" }, + { NM_MT_CONN_TERR_TRAF, "Connect Terrestrial Traffic" }, + { NM_MT_CONN_TERR_TRAF_ACK, "Connect Terrestrial Traffic ACK" }, + { NM_MT_CONN_TERR_TRAF_NACK, "Connect Terrestrial Traffic NACK" }, + { NM_MT_DISC_TERR_TRAF, "Disconnect Terrestrial Traffic" }, + { NM_MT_DISC_TERR_TRAF_ACK, "Disconnect Terrestrial Traffic ACK" }, + { NM_MT_DISC_TERR_TRAF_NACK, "Disconnect Terrestrial Traffic NACK" }, + { NM_MT_CONN_MDROP_LINK, "Connect Multi-Drop Link" }, + { NM_MT_CONN_MDROP_LINK_ACK, "Connect Multi-Drop Link ACK" }, + { NM_MT_CONN_MDROP_LINK_NACK, "Connect Multi-Drop Link NACK" }, + { NM_MT_DISC_MDROP_LINK, "Disconnect Multi-Drop Link" }, + { NM_MT_DISC_MDROP_LINK_ACK, "Disconnect Multi-Drop Link ACK" }, + { NM_MT_DISC_MDROP_LINK_NACK, "Disconnect Multi-Drop Link NACK" }, + { NM_MT_SET_BTS_ATTR, "Set BTS Attributes" }, + { NM_MT_SET_BTS_ATTR_ACK, "Set BTS Attributes ACK" }, + { NM_MT_SET_BTS_ATTR_NACK, "Set BTS Attributes NACK" }, + { NM_MT_SET_RADIO_ATTR, "Set Radio Carrier Attributes" }, + { NM_MT_SET_RADIO_ATTR_ACK, "Set Radio Carrier Attributes ACK" }, + { NM_MT_SET_RADIO_ATTR_NACK, "Set Radio Carrier Attributes NACK" }, + { NM_MT_SET_CHAN_ATTR, "Set Channel Attributes" }, + { NM_MT_SET_CHAN_ATTR_ACK, "Set Channel Attributes ACK" }, + { NM_MT_SET_CHAN_ATTR_NACK, "Set Channel Attributes NACK" }, + { NM_MT_PERF_TEST, "Perform Test" }, + { NM_MT_PERF_TEST_ACK, "Perform Test ACK" }, + { NM_MT_PERF_TEST_NACK, "Perform Test NACK" }, + { NM_MT_TEST_REP, "Test Report" }, + { NM_MT_SEND_TEST_REP, "Send Test Report" }, + { NM_MT_SEND_TEST_REP_ACK, "Send Test Report ACK" }, + { NM_MT_SEND_TEST_REP_NACK, "Send Test Report NACK" }, + { NM_MT_STOP_TEST, "Stop Test" }, + { NM_MT_STOP_TEST_ACK, "Stop Test ACK" }, + { NM_MT_STOP_TEST_NACK, "Stop Test NACK" }, + { NM_MT_STATECHG_EVENT_REP, "State Changed Event Report" }, + { NM_MT_FAILURE_EVENT_REP, "Failure Event Report" }, + { NM_MT_STOP_EVENT_REP, "Stop Sending Event Reports" }, + { NM_MT_STOP_EVENT_REP_ACK, "Stop Sending Event Reports ACK" }, + { NM_MT_STOP_EVENT_REP_NACK, "Stop Sending Event Reports NACK" }, + { NM_MT_REST_EVENT_REP, "Restart Sending Event Reports" }, + { NM_MT_REST_EVENT_REP_ACK, "Restart Sending Event Reports ACK" }, + { NM_MT_REST_EVENT_REP_NACK, "Restart Sending Event Reports NACK" }, + { NM_MT_CHG_ADM_STATE, "Change Administrative State" }, + { NM_MT_CHG_ADM_STATE_ACK, "Change Administrative State ACK" }, + { NM_MT_CHG_ADM_STATE_NACK, "Change Administrative State NACK" }, + { NM_MT_CHG_ADM_STATE_REQ, "Change Administrative State Request" }, + { NM_MT_CHG_ADM_STATE_REQ_ACK, "Change Administrative State Request ACK" }, + { NM_MT_CHG_ADM_STATE_REQ_NACK, "Change Administrative State Request NACK" }, + { NM_MT_REP_OUTST_ALARMS, "Report Outstanding Alarms" }, + { NM_MT_REP_OUTST_ALARMS_ACK, "Report Outstanding Alarms ACK" }, + { NM_MT_REP_OUTST_ALARMS_NACK, "Report Outstanding Alarms NACK" }, + { NM_MT_CHANGEOVER, "Changeover" }, + { NM_MT_CHANGEOVER_ACK, "Changeover ACK" }, + { NM_MT_CHANGEOVER_NACK, "Changeover NACK" }, + { NM_MT_OPSTART, "Opstart" }, + { NM_MT_OPSTART_ACK, "Opstart ACK" }, + { NM_MT_OPSTART_NACK, "Opstart NACK" }, + { NM_MT_REINIT, "Reinitialize" }, + { NM_MT_REINIT_ACK, "Reinitialize ACK" }, + { NM_MT_REINIT_NACK, "Reinitialize NACK" }, + { NM_MT_SET_SITE_OUT, "Set Site Outputs" }, + { NM_MT_SET_SITE_OUT_ACK, "Set Site Outputs ACK" }, + { NM_MT_SET_SITE_OUT_NACK, "Set Site Outputs NACK" }, + { NM_MT_CHG_HW_CONF, "Change HW Configuration" }, + { NM_MT_CHG_HW_CONF_ACK, "Change HW Configuration ACK" }, + { NM_MT_CHG_HW_CONF_NACK, "Change HW Configuration NACK" }, + { NM_MT_MEAS_RES_REQ, "Measurement Result Request" }, + { NM_MT_MEAS_RES_RESP, "Measurement Result Response" }, + { NM_MT_STOP_MEAS, "Stop Measurement" }, + { NM_MT_START_MEAS, "Start Measurement" }, + { NM_MT_GET_ATTR, "Get Attributes" }, + { NM_MT_GET_ATTR_RESP, "Get Attributes Response" }, + { NM_MT_GET_ATTR_NACK, "Get Attributes NACK" }, + { NM_MT_SET_ALARM_THRES, "Set Alarm Threshold" }, + { NM_MT_SET_ALARM_THRES_ACK, "Set Alarm Threshold ACK" }, + { NM_MT_SET_ALARM_THRES_NACK, "Set Alarm Threshold NACK" }, + { 0, NULL } +}; + +/* proprietary ip.access message types, not in the standard */ +static const value_string _oml_fom_msgtype_vals_ipa[] = { + { NM_MT_IPACC_RESTART, "IPA Restart" }, + { NM_MT_IPACC_RESTART_ACK, "IPA Restart ACK" }, + { NM_MT_IPACC_RSL_CONNECT, "IPA RSL Connect" }, + { NM_MT_IPACC_RSL_CONNECT_ACK, "IPA RSL Connect ACK" }, + { NM_MT_IPACC_RSL_CONNECT_NACK, "IPA RSL Connect NACK" }, + { NM_MT_IPACC_RSL_DISCONNECT, "IPA RSL Disconnect" }, + { NM_MT_IPACC_RSL_DISCONNECT_ACK, "IPA RSL Disconnect ACK" }, + { NM_MT_IPACC_RSL_DISCONNECT_NACK, "IPA RSL Disconnect NACK" }, + { NM_MT_IPACC_CONN_TRAF, "IPA Connect Traffic" }, + { NM_MT_IPACC_CONN_TRAF_ACK, "IPA Connect Traffic ACK" }, + { NM_MT_IPACC_CONN_TRAF_NACK, "IPA Connect Traffic NACK" }, + { NM_MT_IPACC_DISC_TRAF, "IPA Disconnect Traffic" }, + { NM_MT_IPACC_DISC_TRAF_ACK, "IPA Disconnect Traffic ACK" }, + { NM_MT_IPACC_DISC_TRAF_NACK, "IPA Disconnect Traffic NACK" }, + { NM_MT_IPACC_DEF_BOOT_SW, "IPA Default Boot Software" }, + { NM_MT_IPACC_DEF_BOOT_SW_ACK, "IPA Default Boot Software ACK" }, + { NM_MT_IPACC_DEF_BOOT_SW_NACK, "IPA Default Boot Software NACK" }, + { NM_MT_IPACC_SET_NVATTR, "IPA Set NVRAM Attributes" }, + { NM_MT_IPACC_SET_NVATTR_ACK, "IPA Set NVRAM Attributes ACK" }, + { NM_MT_IPACC_SET_NVATTR_NACK, "IPA Set NVRAM Attributes NACK" }, + { NM_MT_IPACC_GET_NVATTR, "IPA Get NVRAM Attributes" }, + { NM_MT_IPACC_GET_NVATTR_ACK, "IPA Get NVRAM Attributes ACK" }, + { NM_MT_IPACC_GET_NVATTR_NACK, "IPA Get NVRAM Attributes NACK" }, + { NM_MT_IPACC_SET_ATTR, "IPA Set Attributes" }, + { NM_MT_IPACC_SET_ATTR_ACK, "IPA Set Attributes ACK" }, + { NM_MT_IPACC_SET_ATTR_NACK, "IPA Set Attributes NACK" }, + { NM_MT_IPACC_ATTR_CHG_EVT, "IPA Attribute Change Event" }, + { NM_MT_IPACC_SW_DEACT, "IPA Software Deactivate" }, + { NM_MT_IPACC_SW_DEACT_ACK, "IPA Software Deactivate ACK" }, + { NM_MT_IPACC_SW_DEACT_NACK, "IPA Software Deactivate NACK" }, + { NM_MT_IPACC_MEAS_RES_REQ_NACK,"IPA Measurement Result Request NACK" }, + { NM_MT_IPACC_START_MEAS_NACK, "IPA Start Measurement NACK" }, + { NM_MT_IPACC_STOP_MEAS_NACK, "IPA Stop Measurement NACK" }, + { 0, NULL } +}; + +/* proprietary Siemens message types, not in the standard */ +static const value_string _oml_fom_msgtype_vals_bs11[] = { + { NM_MT_BS11_RESET_RESOURCE, "SIE Reset Resource" }, + { NM_MT_BS11_BEGIN_DB_TX, "SIE Begin Database Transmission" }, + { NM_MT_BS11_BEGIN_DB_TX_ACK, "SIE Begin Database Transmission ACK" }, + { NM_MT_BS11_BEGIN_DB_TX_NACK, "SIE Begin Database Transmission NACK" }, + { NM_MT_BS11_END_DB_TX, "SIE End Database Transmission" }, + { NM_MT_BS11_END_DB_TX_ACK, "SIE End Database Transmission ACK" }, + { NM_MT_BS11_END_DB_TX_NACK, "SIE End Database Transmission NACK" }, + { NM_MT_BS11_CREATE_OBJ, "SIE Create Object" }, + { NM_MT_BS11_CREATE_OBJ_ACK, "SIE Create Object ACK" }, + { NM_MT_BS11_CREATE_OBJ_NACK, "SIE Create Object NACK" }, + { NM_MT_BS11_DELETE_OBJ, "SIE Delete Object" }, + { NM_MT_BS11_DELETE_OBJ_ACK, "SIE Delete Object ACK" }, + { NM_MT_BS11_DELETE_OBJ_NACK, "SIE Delete Object NACK" }, + { NM_MT_BS11_SET_ATTR, "SIE Set Attribute" }, + { NM_MT_BS11_SET_ATTR_ACK, "SIE Set Attribute ACK" }, + { NM_MT_BS11_SET_ATTR_NACK, "SIE Set Attribute NACK" }, + { NM_MT_BS11_GET_STATE, "SIE Get State" }, + { NM_MT_BS11_GET_STATE_ACK, "SIE Get State ACK" }, + { NM_MT_BS11_LMT_LOGON, "SIE LMT Logon" }, + { NM_MT_BS11_LMT_LOGON_ACK, "SIE LMT Logon ACK" }, + { NM_MT_BS11_RESTART, "SIE Restart" }, + { NM_MT_BS11_RESTART_ACK, "SIE Restart ACK" }, + { NM_MT_BS11_DISCONNECT, "SIE Disconnect BTS" }, + { NM_MT_BS11_DISCONNECT_ACK, "SIE Disconnect BTS ACK" }, + { NM_MT_BS11_LMT_LOGOFF, "SIE LMT Logoff" }, + { NM_MT_BS11_LMT_LOGOFF_ACK, "SIE LMT Logoff ACK" }, + { NM_MT_BS11_RECONNECT, "SIE Reconnect BTS" }, + { NM_MT_BS11_RECONNECT_ACK, "SIE Reconnect BTS ACK" }, + { 0, NULL } +}; + +/* initialize with the standard message types only */ +static value_string_ext oml_fom_msgtype_vse = VALUE_STRING_EXT_INIT(_oml_fom_msgtype_vals); + +/* TS 12.21 Section 9.2: Object Class */ +static const value_string oml_fom_objclass_vals[] = { + { NM_OC_SITE_MANAGER, "BTS Site Manager" }, + { NM_OC_BTS, "BTS" }, + { NM_OC_RADIO_CARRIER, "Radio Carrier" }, + { NM_OC_CHANNEL, "Radio Channel" }, + { NM_OC_BASEB_TRANSC, "Baseband Transceiver" }, + + /* proprietary, vendor specific */ + { NM_OC_BS11_ADJC, "SIE Adjacend Channel" }, + { NM_OC_BS11_HANDOVER, "SIE Handover" }, + { NM_OC_BS11_PWR_CTRL, "SIE Power Control" }, + { NM_OC_BS11_BTSE, "SIE BTSE" }, + { NM_OC_BS11_RACK, "SIE Rack" }, + { NM_OC_BS11, "SIE SiemensHW" }, + { NM_OC_BS11_TEST, "SIE Test" }, + { NM_OC_BS11_ENVABTSE, "SIE EnvaBTSE" }, + { NM_OC_BS11_BPORT, "SIE BPort" }, + + { NM_OC_GPRS_NSE, "GPRS NSE" }, + { NM_OC_GPRS_CELL, "GPRS Cell" }, + { NM_OC_GPRS_NSVC0, "GPRS NSVC0" }, + { NM_OC_GPRS_NSVC1, "GPRS NSVC1" }, + + { NM_OC_NULL, "NULL" }, + { 0, NULL } +}; + +/* TS 12.21 Section 9.4: Attributes */ +static const value_string _oml_fom_attr_vals[] = { + { NM_ATT_ABIS_CHANNEL, "A-bis Channel" }, + { NM_ATT_ADD_INFO, "Additional Information" }, + { NM_ATT_ADD_TEXT, "Additional Text" }, + { NM_ATT_ADM_STATE, "Administrative State" }, + { NM_ATT_ARFCN_LIST, "ARFCN List" }, + { NM_ATT_AUTON_REPORT, "Autonomously Report" }, + { NM_ATT_AVAIL_STATUS, "Availability Status" }, + { NM_ATT_BCCH_ARFCN, "BCCH ARFCN" }, + { NM_ATT_BSIC, "BSIC" }, + { NM_ATT_BTS_AIR_TIMER, "BTS Air Timer" }, + { NM_ATT_CCCH_L_I_P, "CCCH Load Indication Period" }, + { NM_ATT_CCCH_L_T, "CCCH Load Threshold" }, + { NM_ATT_CHAN_COMB, "Channel Combination" }, + { NM_ATT_CONN_FAIL_CRIT, "Connection Fail Criterion" }, + { NM_ATT_DEST, "Destination" }, + { NM_ATT_EVENT_TYPE, "Event Type" }, + { NM_ATT_FILE_ID, "File ID" }, + { NM_ATT_FILE_VERSION, "File Version" }, + { NM_ATT_GSM_TIME, "GSM Time" }, + { NM_ATT_HSN, "HSN" }, + { NM_ATT_HW_CONFIG, "HW Configuration" }, + { NM_ATT_HW_DESC, "HW Description" }, + { NM_ATT_INTAVE_PARAM, "Intave Parameter" }, + { NM_ATT_INTERF_BOUND, "Interference Boundaries" }, + { NM_ATT_LIST_REQ_ATTR, "List of required Attributes" }, + { NM_ATT_MAIO, "MAIO" }, + { NM_ATT_MANUF_STATE, "Manufacturer Dependent State" }, + { NM_ATT_MANUF_THRESH, "Manufacturer Dependent Thresholds" }, + { NM_ATT_MANUF_ID, "Manufacturer Id" }, + { NM_ATT_MAX_TA, "Maximum Timing Advance" }, + { NM_ATT_MDROP_LINK, "Multi-drop BSC Link" }, + { NM_ATT_MDROP_NEXT, "Multi-drop next BTS Link" }, + { NM_ATT_NACK_CAUSES, "NACK Causes" }, + { NM_ATT_NY1, "Ny1" }, + { NM_ATT_OPER_STATE, "Operational State" }, + { NM_ATT_OVERL_PERIOD, "Overload Period" }, + { NM_ATT_PHYS_CONF, "Physical Config" }, + { NM_ATT_POWER_CLASS, "Power Class" }, + { NM_ATT_POWER_THRESH, "Power Output Thresholds" }, + { NM_ATT_PROB_CAUSE, "Probable Cause" }, + { NM_ATT_RACH_B_THRESH, "RACH Busy Threshold" }, + { NM_ATT_LDAVG_SLOTS, "RACH Load Averaging Slots" }, + { NM_ATT_RAD_SUBC, "Radio Sub Channel" }, + { NM_ATT_RF_MAXPOWR_R, "RF Max Power Reduction" }, + { NM_ATT_SITE_INPUTS, "Site Inputs" }, + { NM_ATT_SITE_OUTPUTS, "Site Outputs" }, + { NM_ATT_SOURCE, "Source" }, + { NM_ATT_SPEC_PROB, "Specific Problems" }, + { NM_ATT_START_TIME, "Starting Time" }, + { NM_ATT_T200, "T200" }, + { NM_ATT_TEI, "TEI" }, + { NM_ATT_TEST_DUR, "Test Duration" }, + { NM_ATT_TEST_NO, "Test No" }, + { NM_ATT_TEST_REPORT, "Test Report Info" }, + { NM_ATT_VSWR_THRESH, "VSWR Thresholds " }, + { NM_ATT_WINDOW_SIZE, "Window Size" }, + { NM_ATT_BS11_RSSI_OFFS, "SIE RSSI Offset" }, + { NM_ATT_BS11_TXPWR, "SIE TX Power" }, + { NM_ATT_BS11_DIVERSITY, "SIE Diversity" }, + { NM_ATT_TSC, "Training Sequence Code" }, + { NM_ATT_SW_CONFIG, "SW Configuration" }, + { NM_ATT_SW_DESCR, "SW Description" }, + { NM_ATT_SEVERITY, "Perceived Severity" }, + { NM_ATT_GET_ARI, "Get ARI" }, + { NM_ATT_HW_CONF_CHG, "HW Configuration Change" }, + { NM_ATT_OUTST_ALARM, "Outstanding Alarm" }, + { NM_ATT_FILE_DATA, "File Data" }, + { NM_ATT_MEAS_RES, "Measurement Result" }, + { NM_ATT_MEAS_TYPE, "Measurement Type" }, + { 0, NULL } +}; + +static value_string_ext oml_fom_attr_vse = VALUE_STRING_EXT_INIT(_oml_fom_attr_vals); + +/* proprietary Siemens attributes, not in the standard */ +static const value_string oml_fom_attr_vals_bs11[] = { + { NM_ATT_BS11_OM_LAPD_REL_TIMER,"SIE OML LAPD Release Timer" }, + { NM_ATT_BS11_RF_RES_IND_PER, "SIE RF Resource Indication Period" }, + { NM_ATT_BS11_RX_LEV_MIN_CELL, "SIE RxLevel Min Cell" }, + { NM_ATT_BS11_ABIS_EXT_TIME, "SIE A-bis external time" }, + { NM_ATT_BS11_TIMER_HO_REQUEST, "SIE Timer Handover Request" }, + { NM_ATT_BS11_TIMER_NCELL, "SIE Timer nCell" }, + { NM_ATT_BS11_TSYNC, "SIE Timer Tsync" }, + { NM_ATT_BS11_TTRAU, "SIE Timer Ttrau" }, + { NM_ATT_BS11_EMRG_CFG_MEMBER, "SIE Emergency Config Member" }, + { NM_ATT_BS11_TRX_AREA, "SIE TRX Area" }, + { NM_ATT_BS11_BCCH_RECONF, "SIE BCCH Reconfiguration" }, + { NM_ATT_BS11_BIT_ERR_THESH, "SIE Bit Error Threshold" }, + { NM_ATT_BS11_BOOT_SW_VERS, "SIE Boot Software Version" }, + { NM_ATT_BS11_CCLK_ACCURACY, "SIE CCLK Accuracy" }, + { NM_ATT_BS11_CCLK_TYPE, "SIE CCLK Type" }, + { NM_ATT_BS11_INP_IMPEDANCE, "SIE Input Impedance" }, + { NM_ATT_BS11_L1_PROT_TYPE, "SIE L1 Protocol Type" }, + { NM_ATT_BS11_LINE_CFG, "SIE Line Configuration" }, + { NM_ATT_BS11_LI_PORT_1, "SIE Line Interface Port 1" }, + { NM_ATT_BS11_LI_PORT_2, "SIE Line Interface Port 2" }, + { NM_ATT_BS11_L1_REM_ALM_TYPE, "SIE L1 Remote Alarm Type" }, + { NM_ATT_BS11_SW_LOAD_INTENDED, "SIE Software Load Intended" }, + { NM_ATT_BS11_SW_LOAD_SAFETY, "SIE Software Load Safety" }, + { NM_ATT_BS11_SW_LOAD_STORED, "SIE Software Load Stored" }, + { NM_ATT_BS11_VENDOR_NAME, "SIE Vendor Name" }, + { NM_ATT_BS11_HOPPING_MODE, "SIE Hopping Mode" }, + { NM_ATT_BS11_LMT_LOGON_SESSION,"SIE LMT Logon Session" }, + { NM_ATT_BS11_LMT_LOGIN_TIME, "SIE LMT Login Time" }, + { NM_ATT_BS11_LMT_USER_ACC_LEV, "SIE LMT User Account Level" }, + { NM_ATT_BS11_LMT_USER_NAME, "SIE LMT User Account Name" }, + { NM_ATT_BS11_L1_CONTROL_TS, "SIE L1 Control TS" }, + { NM_ATT_BS11_RADIO_MEAS_GRAN, "SIE Radio Measurement Granularity" }, + { NM_ATT_BS11_RADIO_MEAS_REP, "SIE Rdadio Measurement Report" }, + { NM_ATT_BS11_SH_LAPD_INT_TIMER,"SIE LAPD Internal Timer" }, + { NM_ATT_BS11_BTS_STATE, "SIE BTS State" }, + { NM_ATT_BS11_E1_STATE, "SIE E1 State" }, + { NM_ATT_BS11_PLL, "SIE PLL" }, + { NM_ATT_BS11_RX_OFFSET, "SIE Rx Offset" }, + { NM_ATT_BS11_ANT_TYPE, "SIE Antenna Type" }, + { NM_ATT_BS11_PLL_MODE, "SIE PLL Mode" }, + { NM_ATT_BS11_PASSWORD, "SIE Password" }, + { NM_ATT_BS11_ESN_FW_CODE_NO, "SIE ESN FW Code Number" }, + { NM_ATT_BS11_ESN_HW_CODE_NO, "SIE ESN HW Code Number" }, + { NM_ATT_BS11_ESN_PCB_SERIAL, "SIE ESN PCB Serial Number" }, + { NM_ATT_BS11_EXCESSIVE_DISTANCE, "SIE Excessive Distance" }, + { NM_ATT_BS11_ALL_TEST_CATG, "SIE All Test Categories" }, + { NM_ATT_BS11_BTSLS_HOPPING, "SIE BTS LS Hopping" }, + { NM_ATT_BS11_CELL_ALLOC_NR, "SIE Cell Allocation Number" }, + { NM_ATT_BS11_CELL_GLOBAL_ID, "SIE Cell Global ID" }, + { NM_ATT_BS11_ENA_INTERF_CLASS, "SIE Enable Interference Class" }, + { NM_ATT_BS11_ENA_INT_INTEC_HANDO, "SIE Enable Int Intec Handover" }, + { NM_ATT_BS11_ENA_INT_INTRC_HANDO, "SIE Enable Int Intrc Handover" }, + { NM_ATT_BS11_ENA_MS_PWR_CTRL, "SIE Enable MS Power Control" }, + { NM_ATT_BS11_ENA_PWR_BDGT_HO, "SIE Enable Power Budget HO" }, + { NM_ATT_BS11_ENA_RXLEV_HO, "SIE Enable RxLevel HO" }, + { NM_ATT_BS11_ENA_RXQUAL_HO, "SIE Enable RxQual HO" }, + { NM_ATT_BS11_FACCH_QUAL, "SIE FACCH Quality" }, + { 0, NULL } +}; + +/* proprietary ip.access attributes, not in the standard */ +static const value_string oml_fom_attr_vals_ipa[] = { + { NM_ATT_IPACC_DST_IP, "IPA Destination IP Address" }, + { NM_ATT_IPACC_DST_IP_PORT, "IPA Destionation IP Port" }, + { NM_ATT_IPACC_SSRC, "IPA RTP SSRC" }, + { NM_ATT_IPACC_RTP_PAYLD_TYPE, "IPA RTP Payload Type" }, + { NM_ATT_IPACC_BASEB_ID, "IPA Baseband Identifier" }, + { NM_ATT_IPACC_STREAM_ID, "IPA Stream Identifier" }, + { NM_ATT_IPACC_NV_FLAGS, "IPA NVRAM Flags" }, + { NM_ATT_IPACC_FREQ_CTRL, "IPA Frequency Control" }, + { NM_ATT_IPACC_PRIM_OML_CFG, "IPA Primary OML Config" }, + { NM_ATT_IPACC_SEC_OML_CFG, "IPA Secondary OML Config" }, + { NM_ATT_IPACC_IP_IF_CFG, "IPA IP Interface Config" }, + { NM_ATT_IPACC_IP_GW_CFG, "IPA IP Gateway Config" }, + { NM_ATT_IPACC_IN_SERV_TIME, "IPA In Service Time" }, + { NM_ATT_IPACC_TRX_BTS_ASS, "IPA TRX BTS Assignment" }, + { NM_ATT_IPACC_LOCATION, "IPA BTS Location Name" }, + { NM_ATT_IPACC_PAGING_CFG, "IPA Paging Configuration" }, + { NM_ATT_IPACC_FILE_DATA, "IPA File Data" }, + { NM_ATT_IPACC_UNIT_ID, "IPA Unit ID" }, + { NM_ATT_IPACC_PARENT_UNIT_ID, "IPA Parent Unit ID" }, + { NM_ATT_IPACC_UNIT_NAME, "IPA Unit Name" }, + { NM_ATT_IPACC_SNMP_CFG, "IPA SNMP Config" }, + { NM_ATT_IPACC_PRIM_OML_CFG_LIST, "IPA Primary OML Config List" }, + { NM_ATT_IPACC_PRIM_OML_FB_TOUT,"IPA Primary OML Fallback Timeout" }, + { NM_ATT_IPACC_CUR_SW_CFG, "IPA Current Software Config" }, + { NM_ATT_IPACC_TIMING_BUS, "IPA Timing Bus" }, + { NM_ATT_IPACC_CGI, "IPA CGI" }, + { NM_ATT_IPACC_RAC, "IPA RAC" }, + { NM_ATT_IPACC_OBJ_VERSION, "IPA Object Version" }, + { NM_ATT_IPACC_GPRS_PAGING_CFG, "IPA GPRS Paging Configuration" }, + { NM_ATT_IPACC_NSEI, "IPA NSEI" }, + { NM_ATT_IPACC_BVCI, "IPA BVCI" }, + { NM_ATT_IPACC_NSVCI, "IPA NSVCI" }, + { NM_ATT_IPACC_NS_CFG, "IPA NS Configuration" }, + { NM_ATT_IPACC_BSSGP_CFG, "IPA BSSGP Configuration" }, + { NM_ATT_IPACC_NS_LINK_CFG, "IPA NS Link Configuration" }, + { NM_ATT_IPACC_RLC_CFG, "IPA RLC Configuration" }, + { NM_ATT_IPACC_ALM_THRESH_LIST, "IPA Alarm Threshold List" }, + { NM_ATT_IPACC_MONIT_VAL_LIST, "IPA Monitored Value List" }, + { NM_ATT_IPACC_TIB_CONTROL, "IPA Timing Interface Bus Control" }, + { NM_ATT_IPACC_SUPP_FEATURES, "IPA Supported Features" }, + { NM_ATT_IPACC_CODING_SCHEMES, "IPA Coding Schemes" }, + { NM_ATT_IPACC_RLC_CFG_2, "IPA RLC Configuration 2" }, + { NM_ATT_IPACC_HEARTB_TOUT, "IPA Heartbeat Timeout" }, + { NM_ATT_IPACC_UPTIME, "IPA Uptime" }, + { NM_ATT_IPACC_RLC_CFG_3, "IPA RLC Configuration 3" }, + { NM_ATT_IPACC_SSL_CFG, "IPA SSL Configuration" }, + { NM_ATT_IPACC_SEC_POSSIBLE, "IPA Security Possible" }, + { NM_ATT_IPACC_IML_SSL_STATE, "IPA IML SSL State" }, + { NM_ATT_IPACC_REVOC_DATE, "IPA Revocation Date" }, + { 0, NULL } +}; + +static const value_string * +_match_oml_fom_msgtype(const guint32 val, const value_string_ext *vs) +{ + const char *ret; + gint idx; + ret = match_strval_idx(val, vs->_vs_p, &idx); + if (!ret) { + ret = match_strval_idx(val, _oml_fom_msgtype_vals, &idx); + if (!ret) + return NULL; + return &(_oml_fom_msgtype_vals[idx]); + } + return &(vs->_vs_p[idx]); +} + +static const value_string * +_match_oml_fom_attr(const guint32 val, const value_string_ext *vs) +{ + const char *ret; + gint idx; + ret = match_strval_idx(val, vs->_vs_p, &idx); + if (!ret) { + ret = match_strval_idx(val, _oml_fom_attr_vals, &idx); + if (!ret) + return NULL; + return &(_oml_fom_attr_vals[idx]); + } + return &(vs->_vs_p[idx]); +} + +/* Section 9.4.4: Administrative State */ +static const value_string oml_adm_state_vals[] = { + { NM_STATE_LOCKED, "Locked" }, + { NM_STATE_UNLOCKED, "Unlocked" }, + { NM_STATE_SHUTDOWN, "Shutdown" }, + { NM_STATE_NULL, "Null" }, + { 0, NULL } +}; + +static const value_string oml_oper_state_vals[] = { + { 1, "Disabled" }, + { 2, "Enabled" }, + { 0xff, "NULL" }, + { 0, NULL } +}; + +/* Section 9.4.7 Availability Status */ +static const value_string oml_avail_state_vals[] = { + { 0, "In test" }, + { 1, "Failed" }, + { 2, "Power off" }, + { 3, "Off line" }, + { 5, "Dependency" }, + { 6, "Degraded" }, + { 7, "Not installed" }, + { 0xff, "OK" }, + { 0, NULL } +}; + +/* Section 9.4.13: Channel Combination */ +static const value_string oml_chan_comb_vals[] = { + { NM_CHANC_TCHFull, "TCH/F" }, + { NM_CHANC_TCHHalf, "TCH/H" }, + { NM_CHANC_TCHHalf2, "TCH/H 2" }, + { NM_CHANC_SDCCH, "SDCCH" }, + { NM_CHANC_mainBCCH, "Main BCCH" }, + { NM_CHANC_BCCHComb, "Combined BCCH" }, + { NM_CHANC_BCCH, "BCCH" }, + { NM_CHANC_BCCH_CBCH, "BCCH+CBCH" }, + { NM_CHANC_SDCCH_CBCH, "SDCCH+CBCH" }, + { 0, NULL } +}; + +/* Section 9.4.16: Event Type */ +static const value_string oml_event_type_vals[] = { + { NM_EVT_COMM_FAIL, "Communication Failure" }, + { NM_EVT_QOS_FAIL, "QoS Failure" }, + { NM_EVT_PROC_FAIL, "Processor Failure" }, + { NM_EVT_EQUIP_FAIL, "Equipment Failure" }, + { NM_EVT_ENV_FAIL, "Environment Failure" }, + { 0, NULL } +}; + +/* Section 9.4.63: Perceived Severity */ +static const value_string oml_severity_vals[] = { + { NM_SEVER_CEASED, "Ceased" }, + { NM_SEVER_CRITICAL, "Critical" }, + { NM_SEVER_MAJOR, "Major" }, + { NM_SEVER_MINOR, "Minor" }, + { NM_SEVER_WARNING, "Warning" }, + { NM_SEVER_INDETERMINATE, "Indeterminate" }, + { 0, NULL } +}; + +/* Section 9.4.36: NACK Causes */ +static const value_string oml_nack_cause[] = { + { NM_NACK_INCORR_STRUCT, "Incorrect message structure" }, + { NM_NACK_MSGTYPE_INVAL, "Invalid message type value" }, + { NM_NACK_OBJCLASS_INVAL, "Invalid Object class value" }, + { NM_NACK_OBJCLASS_NOTSUPP, "Object Class not supported" }, + { NM_NACK_BTSNR_UNKN, "BTS Number unknown" }, + { NM_NACK_TRXNR_UNKN, "TRX Number unknown" }, + { NM_NACK_OBJINST_UNKN, "Object Instance unknown" }, + { NM_NACK_ATTRID_INVAL, "Invalid Attribute ID value" }, + { NM_NACK_ATTRID_NOTSUPP, "Attribute ID not supported" }, + { NM_NACK_PARAM_RANGE, "Parameter value out of range" }, + { NM_NACK_ATTRLIST_INCONSISTENT, "Inconsistency in Attribute list" }, + { NM_NACK_SPEC_IMPL_NOTSUPP, "Specified Implementation not supported" }, + { NM_NACK_CANT_PERFORM, "Message cannot be performed" }, + { NM_NACK_RES_NOTIMPL, "Resource not implemented" }, + { NM_NACK_RES_NOTAVAIL, "Resource not available" }, + { NM_NACK_FREQ_NOTAVAIL, "Frequency not available" }, + { NM_NACK_TEST_NOTSUPP, "Test not supported" }, + { NM_NACK_CAPACITY_RESTR, "Capacity restrictions" }, + { NM_NACK_PHYSCFG_NOTPERFORM, "Phys config cannot be performed" }, + { NM_NACK_TEST_NOTINIT, "Test not initiated" }, + { NM_NACK_PHYSCFG_NOTRESTORE, "Phys config cannot be restored" }, + { NM_NACK_TEST_NOSUCH, "No such Test" }, + { NM_NACK_TEST_NOSTOP, "Test cannot be stopped" }, + { NM_NACK_MSGINCONSIST_PHYSCFG, "Message inconsisten with physical config" }, + { NM_NACK_FILE_INCOMPLETE, "Complete file not received" }, + { NM_NACK_FILE_NOTAVAIL, "File not available at destination" }, + { NM_NACK_FILE_NOTACTIVATE, "File cannot be activated" }, + { NM_NACK_REQ_NOT_GRANT, "Request not granted" }, + { NM_NACK_WAIT, "Wait" }, + { NM_NACK_NOTH_REPORT_EXIST, "Nothing reportable existing" }, + { NM_NACK_MEAS_NOTSUPP, "Measurement not supported" }, + { NM_NACK_MEAS_NOTSTART, "Measurement not started" }, + { 0xff, "NULL" }, + { 0, NULL } +}; + +static const value_string oml_test_no_vals[] = { + { NM_IPACC_TESTNO_RLOOP_ANT, "Radio Loop test via antenna" }, + { NM_IPACC_TESTNO_RLOOP_XCVR, "Radio Loop test via transceiver" }, + { NM_IPACC_TESTNO_FUNC_OBJ, "BTS Functional object self test" }, + { NM_IPACC_TESTNO_CHAN_USAGE, "Channel Usage" }, + { NM_IPACC_TESTNO_BCCH_CHAN_USAGE, "BCCH Channel Usage" }, + { NM_IPACC_TESTNO_FREQ_SYNC, "Frequency Synchronization" }, + { NM_IPACC_TESTNO_BCCH_INFO, "BCCH Information" }, + { NM_IPACC_TESTNO_TX_BEACON, "Transmit Beacon" }, + { NM_IPACC_TESTNO_SYSINFO_MONITOR, "SysInfo Monitor" }, + { NM_IPACC_TESTNO_BCCCH_MONITOR, "BCCH & CCCH Monitor" }, + { 0, NULL } +}; + +static const value_string ipacc_test_res_vals[] = { + { NM_IPACC_TESTRES_SUCCESS, "Success" }, + { NM_IPACC_TESTRES_TIMEOUT, "Timeout" }, + { NM_IPACC_TESTRES_NO_CHANS, "No suitable channels available" }, + { NM_IPACC_TESTRES_PARTIAL, "Partial" }, + { NM_IPACC_TESTRES_STOPPED, "Stopped" }, + { 0, NULL } +}; + +static const value_string ipacc_testres_ie_vals[] = { + { NM_IPACC_TR_IE_FREQ_ERR_LIST, "Frequency Error List" }, + { NM_IPACC_TR_IE_CHAN_USAGE, "Channel Usage" }, + { NM_IPACC_TR_IE_BCCH_INFO, "BCCH Information" }, + { NM_IPACC_TR_IE_RESULT_DETAILS,"Result Details" }, + { NM_IPACC_TR_IE_FREQ_ERR, "Frequency Error" }, + { 0, NULL } +}; + +/* ANSI C does not allow selective initialization of arrays, for that reason, + * we initialize these three TLV definitions in proto_register_abis_oml(). */ +static struct tlv_definition nm_att_tlvdef_base; +static struct tlv_definition nm_att_tlvdev_bs11; +static struct tlv_definition nm_att_tlvdef_ipa; + +static const struct tlv_def * +find_tlv_tag(guint8 tag) +{ + const struct tlv_def *specific; + + if (global_oml_use_nano_bts) + specific = &nm_att_tlvdef_ipa.def[tag]; + else + specific = &nm_att_tlvdev_bs11.def[tag]; + + if (specific->type != TLV_TYPE_UNKNOWN) + return specific; + + return &nm_att_tlvdef_base.def[tag]; +} + +/* Parse the ip.access specific BCCH Information IE embedded into the Test + * Report IE */ +static gint +ipacc_tr_ie_bcch(tvbuff_t *tvb, packet_info *pinfo, proto_tree *att_tree, + int offset) +{ + guint16 binfo_type, tmp; + + binfo_type = tvb_get_ntohs(tvb, offset); + offset += 2; + + tmp = tvb_get_ntohs(tvb, offset); + + /* FIXME: there are still some bugs remaining here */ + proto_tree_add_item(att_tree, hf_attr_ipa_tr_arfcn, + tvb, offset, 2, TRUE); + + proto_tree_add_item(att_tree, hf_attr_ipa_tr_f_qual, + tvb, offset, 2, TRUE); + offset += 2; + + proto_tree_add_item(att_tree, hf_attr_ipa_tr_b_rxlev, + tvb, offset++, 1, TRUE); + + proto_tree_add_item(att_tree, hf_attr_ipa_tr_rxqual, + tvb, offset++, 1, TRUE); + + proto_tree_add_item(att_tree, hf_attr_ipa_tr_f_err, + tvb, offset, 2, TRUE); + offset += 2; + + proto_tree_add_item(att_tree, hf_attr_ipa_tr_frame_offs, + tvb, offset, 2, TRUE); + offset += 2; + proto_tree_add_item(att_tree, hf_attr_ipa_tr_framenr_offs, + tvb, offset, 4, TRUE); + offset += 4; + + proto_tree_add_item(att_tree, hf_attr_ipa_tr_bsic, + tvb, offset++, 1, TRUE); + + de_lai(tvb, att_tree, pinfo, offset, 5, NULL, 0); + offset += 5; + + proto_tree_add_item(att_tree, hf_attr_ipa_tr_cell_id, + tvb, offset, 2, TRUE); + offset += 2; + + if (binfo_type & 0x8000) { + /* System Information 2 */ + /* FIXME: Parse 04.18 Neighbour Cell Description */ + proto_tree_add_item(att_tree, hf_attr_ipa_tr_si2, + tvb, offset, 16, TRUE); + offset += 16; + } + if (binfo_type & 0x0001) { + /* System Information 2bis */ + /* FIXME: Parse 04.18 Neighbour Cell Description */ + proto_tree_add_item(att_tree, hf_attr_ipa_tr_si2bis, + tvb, offset, 16, TRUE); + offset += 16; + } + if (binfo_type & 0x0002) { + /* System Information 2ter */ + /* FIXME: Parse 04.18 Neighbour Cell Description */ + proto_tree_add_item(att_tree, hf_attr_ipa_tr_si2ter, + tvb, offset, 16, TRUE); + offset += 16; + } + if (binfo_type & 0x0004) { + /* FIXME: Parse 04.18 Cell Channel Description */ + proto_tree_add_item(att_tree, hf_attr_ipa_tr_chan_desc, + tvb, offset, 16, TRUE); + offset += 16; + } + + return offset; +} + +/* Parse the ip.access specific Channel Usage IE embedded into the Test + * Report IE */ +static gint +ipacc_tr_ie_chan_usage(tvbuff_t *tvb, proto_tree *att_tree, int offset) +{ + while (tvb_reported_length_remaining(tvb, offset) != 0) { + guint16 result; + + result = tvb_get_ntohs(tvb, offset); + proto_tree_add_uint(att_tree, hf_attr_ipa_tr_arfcn, + tvb, offset, 2, result); + proto_tree_add_uint(att_tree, hf_attr_ipa_tr_rxlev, + tvb, offset, 2, result); + offset += 2; + } + return offset; +} + +/* Parse the ip.access specific format of the standard test report IE */ +static gint +dissect_ipacc_test_rep(proto_tree *tree, packet_info *pinfo, tvbuff_t *tvb) +{ + gint offset = 0; + + proto_tree_add_item(tree, hf_attr_ipa_test_res, tvb, offset++, + 1, FALSE); + + while (tvb_reported_length_remaining(tvb, offset) != 0) { + guint8 ie; + guint16 len; + proto_item *ti; + proto_tree *att_tree; + + ie = tvb_get_guint8(tvb, offset); + len = tvb_get_ntohs(tvb, offset+1); + ti = proto_tree_add_item(tree, hf_oml_ipa_tres_attr_tag, tvb, + offset++, 1, FALSE); + att_tree = proto_item_add_subtree(ti, ett_oml_fom_att); + proto_tree_add_uint(att_tree, hf_oml_ipa_tres_attr_len, tvb, + offset, 2, len); + offset += 2; + + switch (ie) { + case NM_IPACC_TR_IE_CHAN_USAGE: + offset = ipacc_tr_ie_chan_usage(tvb, + att_tree, offset); + break; + case NM_IPACC_TR_IE_BCCH_INFO: + offset = ipacc_tr_ie_bcch(tvb, pinfo, + att_tree, offset); + break; + default: + break; + } + } + return offset; +} + +/* Dissect OML FOM Attributes after OML + FOM header */ +static gint +dissect_oml_attrs(tvbuff_t *tvb, int base_offs, packet_info *pinfo, + proto_tree *tree) +{ + int offset = base_offs; + + while (tvb_reported_length_remaining(tvb, offset) != 0) { + guint i; + guint8 tag, val8; + guint16 val16; + guint32 val32; + unsigned int len, len_len, hlen; + const struct tlv_def *tdef; + proto_item *ti; + proto_tree *att_tree; + tvbuff_t *sub_tvb; + + tag = tvb_get_guint8(tvb, offset); + tdef = find_tlv_tag(tag); + + switch (tdef->type) { + case TLV_TYPE_FIXED: + hlen = 1; + len_len = 0; + len = tdef->fixed_len; + break; + case TLV_TYPE_T: + hlen = 1; + len_len = 0; + len = 0; + break; + case TLV_TYPE_TV: + hlen = 1; + len_len = 0; + len = 1; + break; + case TLV_TYPE_TLV: + hlen = 2; + len_len = 1; + len = tvb_get_guint8(tvb, offset+1); + break; + case TLV_TYPE_TL16V: + hlen = 3; + len_len = 2; + len = tvb_get_guint8(tvb, offset+1) << 8 | + tvb_get_guint8(tvb, offset+2); + break; + case TLV_TYPE_TLV16: + hlen = 2; + len_len = 1; + len = tvb_get_guint8(tvb, offset+1) * 2; + break; + case TLV_TYPE_UNKNOWN: /* fall through */ + default: + hlen = len_len = len = 0; + DISSECTOR_ASSERT_NOT_REACHED(); + break; + } + + ti = proto_tree_add_item(tree, hf_oml_fom_attr_tag, tvb, + offset, 1, FALSE); + att_tree = proto_item_add_subtree(ti, ett_oml_fom_att); + proto_tree_add_uint(att_tree, hf_oml_fom_attr_len, tvb, + offset+1, len_len, len); + offset += hlen; + + sub_tvb = tvb_new_subset(tvb, offset, len, len); + + switch (tag) { + /* parse only the most common IE for now */ + case NM_ATT_ABIS_CHANNEL: + proto_tree_add_item(att_tree, hf_attr_ach_btsp, tvb, + offset, 1, TRUE); + proto_tree_add_item(att_tree, hf_attr_ach_tslot, tvb, + offset+1, 1, TRUE); + proto_tree_add_item(att_tree, hf_attr_ach_sslot, tvb, + offset+2, 1, TRUE); + break; + case NM_ATT_ADM_STATE: + proto_tree_add_item(att_tree, hf_attr_adm_state, tvb, + offset, len, FALSE); + val8 = tvb_get_guint8(tvb, offset); + col_append_fstr(pinfo->cinfo, COL_INFO, "%s ", + val_to_str(val8, oml_adm_state_vals, + "%02x")); + break; + case NM_ATT_ARFCN_LIST: + for (i = 0; i < len; i += 2) { + val16 = tvb_get_ntohs(tvb, offset + i); + proto_tree_add_uint(att_tree, hf_attr_arfcn, + tvb, offset + i, 2, val16); + } + break; + case NM_ATT_AVAIL_STATUS: + /* Availability status can have length 0 */ + if (len) { + val8 = tvb_get_guint8(tvb, offset); + proto_tree_add_item(att_tree, + hf_attr_avail_state, tvb, + offset, len, FALSE); + } else + val8 = 0xff; + col_append_fstr(pinfo->cinfo, COL_INFO, "%s ", + val_to_str(val8, oml_avail_state_vals, + "%02x")); + break; + case NM_ATT_BCCH_ARFCN: + proto_tree_add_item(att_tree, hf_attr_bcch_arfcn, tvb, + offset, len, FALSE); + break; + case NM_ATT_BSIC: + proto_tree_add_item(att_tree, hf_attr_bsic, tvb, + offset, len, TRUE); + break; + case NM_ATT_CHAN_COMB: + proto_tree_add_item(att_tree, hf_attr_chan_comb, tvb, + offset, len, TRUE); + break; + case NM_ATT_EVENT_TYPE: + proto_tree_add_item(att_tree, hf_attr_event_type, tvb, + offset, len, TRUE); + break; + case NM_ATT_GSM_TIME: + proto_tree_add_item(att_tree, hf_attr_gsm_time, tvb, + offset, len, TRUE); + break; + case NM_ATT_OPER_STATE: + proto_tree_add_item(att_tree, hf_attr_oper_state, tvb, + offset, len, FALSE); + val8 = tvb_get_guint8(tvb, offset); + col_append_fstr(pinfo->cinfo, COL_INFO, "%s ", + val_to_str(val8, oml_oper_state_vals, + "%02x")); + break; + case NM_ATT_TEI: + proto_tree_add_item(att_tree, hf_attr_tei, tvb, + offset, len, TRUE); + break; + case NM_ATT_TSC: + proto_tree_add_item(att_tree, hf_attr_tsc, tvb, + offset, len, TRUE); + break; + case NM_ATT_SEVERITY: + proto_tree_add_item(att_tree, hf_attr_severity, tvb, + offset, len, TRUE); + break; + case NM_ATT_TEST_REPORT: + dissect_ipacc_test_rep(att_tree, pinfo, sub_tvb); + break; + case NM_ATT_TEST_NO: + proto_tree_add_item(att_tree, hf_attr_test_no, tvb, + offset, len, TRUE); + val8 = tvb_get_guint8(tvb, offset); + col_append_fstr(pinfo->cinfo, COL_INFO, "%s ", + val_to_str(val8, oml_test_no_vals, + "%02x")); + break; + case NM_ATT_HSN: + proto_tree_add_item(att_tree, hf_attr_hsn, tvb, + offset, len, TRUE); + break; + case NM_ATT_MAIO: + proto_tree_add_item(att_tree, hf_attr_maio, tvb, + offset, len, TRUE); + break; + default: + proto_tree_add_item(att_tree, hf_oml_fom_attr_val, tvb, + offset, len, FALSE); + } + + if (global_oml_use_nano_bts) switch (tag) { + /* proprietary ip.access extensions */ + case NM_ATT_IPACC_DST_IP: + val32 = tvb_get_ntohl(tvb, offset); + proto_tree_add_ipv4(att_tree, hf_attr_ipa_rsl_ip, tvb, + offset, len, val32); + break; + case NM_ATT_IPACC_DST_IP_PORT: + val16 = tvb_get_ntohs(tvb, offset); + proto_tree_add_uint(att_tree, hf_attr_ipa_rsl_port, tvb, + offset, len, val16); + break; + case NM_ATT_IPACC_LOCATION: + proto_tree_add_item(att_tree, hf_attr_ipa_location_name, + tvb, offset, len, TRUE); + break; + case NM_ATT_IPACC_UNIT_ID: + proto_tree_add_item(att_tree, hf_attr_ipa_unit_id, + tvb, offset, len, TRUE); + break; + case NM_ATT_IPACC_UNIT_NAME: + proto_tree_add_item(att_tree, hf_attr_ipa_unit_name, + tvb, offset, len, TRUE); + break; + case NM_ATT_IPACC_PRIM_OML_CFG_LIST: + proto_tree_add_item(att_tree, hf_attr_ipa_prim_oml_ip, + tvb, offset+1, 4, TRUE); + proto_tree_add_item(att_tree, hf_attr_ipa_prim_oml_port, + tvb, offset+1+4, 2, TRUE); + break; + case NM_ATT_IPACC_NV_FLAGS: + { + guint flags, mask; + flags = tvb_get_guint8(tvb, offset); + mask = tvb_get_guint8(tvb, offset+1); + flags |= tvb_get_guint8(tvb, offset+2) << 8; + mask |= tvb_get_guint8(tvb, offset+3) << 8; + proto_tree_add_uint(att_tree, hf_attr_ipa_nv_flags, + tvb, offset, 3, flags); + proto_tree_add_uint(att_tree, hf_attr_ipa_nv_mask, + tvb, offset+1, 3, mask); + } + break; + case NM_ATT_IPACC_RAC: + proto_tree_add_item(att_tree, hf_attr_ipa_rac, + tvb, offset, 1, TRUE); + break; + case NM_ATT_IPACC_NSEI: + val16 = tvb_get_ntohs(tvb, offset); + proto_tree_add_uint(att_tree, hf_attr_ipa_nsei, + tvb, offset, 2, val16); + break; + case NM_ATT_IPACC_NSVCI: + val16 = tvb_get_ntohs(tvb, offset); + proto_tree_add_uint(att_tree, hf_attr_ipa_nsvci, + tvb, offset, 2, val16); + break; + case NM_ATT_IPACC_BVCI: + val16 = tvb_get_ntohs(tvb, offset); + proto_tree_add_uint(att_tree, hf_attr_ipa_bvci, + tvb, offset, 2, val16); + break; + case NM_ATT_IPACC_NS_LINK_CFG: + val16 = tvb_get_ntohs(tvb, offset); + proto_tree_add_uint(att_tree, hf_attr_ipa_nsl_sport, + tvb, offset, 2, val16); + val32 = tvb_get_ipv4(tvb, offset+2); + proto_tree_add_ipv4(att_tree, hf_attr_ipa_nsl_daddr, + tvb, offset+2, 4, val32); + val16 = tvb_get_ntohs(tvb, offset+6); + proto_tree_add_uint(att_tree, hf_attr_ipa_nsl_dport, + tvb, offset+6, 2, val16); + break; + } + offset += len; + } + return offset; +} + +static int +dissect_oml_fom(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + int offset, proto_item *top_ti) +{ + guint8 msg_type, obj_class, bts_nr, trx_nr, ts_nr; + proto_item *ti; + proto_tree *fom_tree; + + msg_type = tvb_get_guint8(tvb, offset); + obj_class = tvb_get_guint8(tvb, offset+1); + bts_nr = tvb_get_guint8(tvb, offset+2); + trx_nr = tvb_get_guint8(tvb, offset+3); + ts_nr = tvb_get_guint8(tvb, offset+4); + proto_item_append_text(top_ti, ", %s(%02x,%02x,%02x) %s ", + val_to_str(obj_class, oml_fom_objclass_vals, "%02x"), + bts_nr, trx_nr, ts_nr, + val_to_str_ext(msg_type, &oml_fom_msgtype_vse, + "unknown 0x%x")); + col_append_fstr(pinfo->cinfo, COL_INFO, "%s(%02x,%02x,%02x) %s ", + val_to_str(obj_class, oml_fom_objclass_vals, "%02x"), + bts_nr, trx_nr, ts_nr, + val_to_str_ext(msg_type, &oml_fom_msgtype_vse, + "unknown 0x%x")); + ti = proto_tree_add_item(tree, hf_oml_fom_msgtype, tvb, offset++, 1, FALSE); + fom_tree = proto_item_add_subtree(ti, ett_oml_fom); + proto_tree_add_item(fom_tree, hf_oml_fom_objclass, tvb, offset++, 1, FALSE); + proto_tree_add_item(fom_tree, hf_oml_fom_inst_bts, tvb, offset++, 1, FALSE); + proto_tree_add_item(fom_tree, hf_oml_fom_inst_trx, tvb, offset++, 1, FALSE); + proto_tree_add_item(fom_tree, hf_oml_fom_inst_ts, tvb, offset++, 1, FALSE); + + + /* dissect the TLV objects in the message body */ + offset = dissect_oml_attrs(tvb, offset, pinfo, fom_tree); + + return offset; +} + +static const guint8 ipaccess_magic[] = "com.ipaccess"; + +static int +dissect_oml_manuf(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + int offset, proto_item *top_ti) +{ + if (tvb_get_guint8(tvb, offset) != 0x0d || + tvb_memeql(tvb, offset+1, ipaccess_magic, sizeof(ipaccess_magic))) + return offset; + + offset += sizeof(ipaccess_magic) + 1; + + return dissect_oml_fom(tvb, pinfo, tree, offset, top_ti); +} + +static void +dissect_abis_oml(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + proto_item *ti; + proto_tree *oml_tree; + + int offset = 0; + + col_set_str(pinfo->cinfo, COL_PROTOCOL, "OML"); + + top_tree = tree; + if (tree) { + u_int8_t msg_disc = tvb_get_guint8(tvb, offset); + + ti = proto_tree_add_item(tree, proto_abis_oml, tvb, 0, -1, FALSE); + oml_tree = proto_item_add_subtree(ti, ett_oml); + + proto_tree_add_item(oml_tree, hf_oml_msg_disc, tvb, offset++, + 1, TRUE); + proto_tree_add_item(oml_tree, hf_oml_placement, tvb, offset++, + 1, TRUE); + proto_tree_add_item(oml_tree, hf_oml_sequence, tvb, offset++, + 1, TRUE); + proto_tree_add_item(oml_tree, hf_oml_length, tvb, offset++, + 1, TRUE); + + switch (msg_disc) { + case ABIS_OM_MDISC_FOM: + offset = dissect_oml_fom(tvb, pinfo, oml_tree, + offset, ti); + break; + case ABIS_OM_MDISC_MANUF: + offset = dissect_oml_manuf(tvb, pinfo, oml_tree, offset, ti); + break; + case ABIS_OM_MDISC_MMI: + case ABIS_OM_MDISC_TRAU: + default: + break; + } + } +} + +void +proto_reg_handoff_abis_oml(void); + +void +proto_register_abis_oml(void) +{ + static hf_register_info hf[] = { + { &hf_oml_msg_disc, + { "Message Discriminator", "oml.msg_dsc", + FT_UINT8, BASE_HEX, VALS(oml_msg_disc_vals), 0, + "GSM 12.21 Message Discriminator", HFILL } + }, + { &hf_oml_placement, + { "Placement Indicator", "oml.placement", + FT_UINT8, BASE_HEX, VALS(oml_placement_vals), 0, + "GSM 12.21 Placement Indicator", HFILL } + }, + { &hf_oml_sequence, + { "Sequence Number", "oml.sequence", + FT_UINT8, BASE_HEX, NULL, 0, + "Sequence Number (if multi-part msg)", HFILL } + }, + { &hf_oml_length, + { "Length Indicator", "oml.length", + FT_UINT8, BASE_DEC, NULL, 0, + "Total length of payload", HFILL } + }, + { &hf_oml_fom_msgtype, + { "FOM Message Type", "oml.fom.msg_type", + FT_UINT8, BASE_HEX|BASE_EXT_STRING, (&oml_fom_msgtype_vse), 0, + NULL, HFILL } + }, + { &hf_oml_fom_objclass, + { "FOM Object Class", "oml.fom.obj_class", + FT_UINT8, BASE_HEX, VALS(oml_fom_objclass_vals), 0, + NULL, HFILL } + }, + { &hf_oml_fom_inst_bts, + { "FOM Object Instance BTS", "oml.fom.obj_inst.bts", + FT_UINT8, BASE_DEC, NULL, 0, + NULL, HFILL } + }, + { &hf_oml_fom_inst_trx, + { "FOM Object Instance TRX", "oml.fom.obj_inst.trx", + FT_UINT8, BASE_DEC, NULL, 0, + NULL, HFILL } + }, + { &hf_oml_fom_inst_ts, + { "FOM Object Instance TS", "oml.fom.obj_inst.ts", + FT_UINT8, BASE_DEC, NULL, 0, + NULL, HFILL } + }, + { &hf_oml_fom_attr_tag, + { "FOM Attribute ID", "oml.fom.attr_id", + FT_UINT8, BASE_HEX|BASE_EXT_STRING, (&oml_fom_attr_vse), 0, + NULL, HFILL } + }, + { &hf_oml_fom_attr_len, + { "FOM Attribute Length", "oml.fom.attr_len", + FT_UINT16, BASE_DEC, NULL, 0, + NULL, HFILL } + }, + { &hf_oml_fom_attr_val, + { "FOM Attribute Value", "oml.fom.attr_val", + FT_BYTES, BASE_NONE, NULL, 0, + NULL, HFILL } + }, + + + + /* OML Attributes */ + { &hf_attr_adm_state, + { "Administrative State", "oml.fom.attr.adm_state", + FT_UINT8, BASE_HEX, VALS(oml_adm_state_vals), 0, + NULL, HFILL } + }, + { &hf_attr_arfcn, + { "ARFCN", "oml.fom.attr.arfcn", + FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } + }, + { &hf_attr_oper_state, + { "Operational State", "oml.fom.attr.oper_state", + FT_UINT8, BASE_HEX, VALS(oml_oper_state_vals), 0, + NULL, HFILL } + }, + { &hf_attr_avail_state, + { "Availability Status", "oml.fom.attr.avail_state", + FT_UINT8, BASE_HEX, VALS(oml_avail_state_vals), 0, + NULL, HFILL } + }, + { &hf_attr_event_type, + { "Event Type", "oml.fom.attr.event_type", + FT_UINT8, BASE_HEX, VALS(oml_event_type_vals), 0, + NULL, HFILL } + }, + { &hf_attr_severity, + { "Severity", "oml.fom.attr.severity", + FT_UINT8, BASE_HEX, VALS(oml_severity_vals), 0, + NULL, HFILL } + }, + { &hf_attr_bcch_arfcn, + { "BCCH ARFCN", "oml.fom.attr.bcch_arfcn", + FT_UINT16, BASE_DEC, NULL, 0, + "ARFCN of the BCCH", HFILL } + }, + { &hf_attr_bsic, + { "BSIC", "oml.fom.attr.bsic", + FT_UINT16, BASE_HEX, NULL, 0, + "Base Station Identity Cdoe", HFILL } + }, + { &hf_attr_test_no, + { "Test Number", "oml.fom.attr.test_no", + FT_UINT8, BASE_HEX, VALS(oml_test_no_vals), 0, + NULL, HFILL } + }, + { &hf_attr_tsc, + { "TSC", "oml.fom.attr.tsc", + FT_UINT8, BASE_HEX, NULL, 0, + "Training Sequence Code", HFILL } + }, + { &hf_attr_tei, + { "TEI", "oml.fom.attr.tei", + FT_UINT8, BASE_DEC, NULL, 0, + NULL, HFILL } + }, + { &hf_attr_ach_btsp, + { "BTS E1 Port", "oml.fom.attr.abis_ch.bts_port", + FT_UINT8, BASE_DEC, NULL, 0, + NULL, HFILL } + }, + { &hf_attr_ach_tslot, + { "E1 Timeslot", "oml.fom.attr.abis_ch.timeslot", + FT_UINT8, BASE_DEC, NULL, 0, + NULL, HFILL } + }, + { &hf_attr_ach_sslot, + { "E1 Subslot", "oml.fom.attr.abis_ch.subslot", + FT_UINT8, BASE_DEC, NULL, 0, + NULL, HFILL } + }, + { &hf_attr_gsm_time, + { "GSM Time", "oml.fom.attr.gsm_time", + FT_UINT16, BASE_DEC, NULL, 0, + "GSM Time", HFILL } + }, + { &hf_attr_chan_comb, + { "Channel Combination", "oml.fom.attr.chan_comb", + FT_UINT8, BASE_HEX, VALS(oml_chan_comb_vals), 0, + NULL, HFILL } + }, + { &hf_attr_hsn, + { "HSN", "oml.fom.attr.hsn", + FT_UINT8, BASE_DEC, NULL, 0, + "Hopping Sequence Number", HFILL } + }, + { &hf_attr_maio, + { "MAIO", "oml.fom.attr.maio", + FT_UINT8, BASE_DEC, NULL, 0, + "Mobile Allocation Index Offset", HFILL } + }, + + /* IP Access */ + { &hf_oml_ipa_tres_attr_tag, + { "IPA Test Result Embedded IE", + "oml.fom.testrep.ipa_tag", + FT_UINT8, BASE_HEX, VALS(ipacc_testres_ie_vals), 0, + "Information Element embedded into the Test Result " + "of ip.access BTS", HFILL }, + }, + { &hf_oml_ipa_tres_attr_len, + { "IPA Test Result Embedded IE Length", + "oml.fom.testrep.ipa_len", + FT_UINT16, BASE_DEC, NULL, 0, + "Length of ip.access Test Result Embedded IE", HFILL } + }, + { &hf_attr_ipa_test_res, + { "IPA Test Result", "oml.fom.testrep.result", + FT_UINT8, BASE_DEC, VALS(ipacc_test_res_vals), 0, + NULL, HFILL } + }, + { &hf_attr_ipa_tr_rxlev, + { "Rx Level", "oml.fom.testrep.ipa_rxlev", + FT_UINT16, BASE_DEC, NULL, 0xfc00, NULL, HFILL } + }, + { &hf_attr_ipa_tr_b_rxlev, + { "Rx Level", "oml.fom.testrep.ipa_rxlev_b", + FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } + }, + { &hf_attr_ipa_tr_arfcn, + { "ARFCN", "oml.fom.testrep.ipa_arfcn", + FT_UINT16, BASE_DEC, NULL, 0x03ff, "ARFCN", HFILL } + }, + { &hf_attr_ipa_tr_f_qual, + { "Frequency Quality", "oml.fom.testrep.ipa.freq_qual", + FT_UINT8, BASE_DEC, NULL, 0xfc, NULL, HFILL } + }, + { &hf_attr_ipa_tr_f_err, + { "Frequency Error", "oml.fom.testrep.ipa.freq_err", + FT_INT16, BASE_DEC, NULL, 0, NULL, HFILL } + }, + { &hf_attr_ipa_tr_rxqual, + { "Rx Quality", "oml.fom.testrep.ipa.rx_qual", + FT_UINT8, BASE_DEC, NULL, 0x7, NULL, HFILL } + }, + { &hf_attr_ipa_tr_frame_offs, + { "Frame Offset", "oml.fom.testrep.ipa.frame_offset", + FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } + }, + { &hf_attr_ipa_tr_framenr_offs, + { "Frame Number Offset", + "oml.fom.testrep.ipa.framenr_offset", + FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } + }, + { &hf_attr_ipa_tr_bsic, + { "BSIC", "oml.fom.testrep.ipa.bsic", + FT_UINT8, BASE_DEC, NULL, 0x3f, + "Base Station Identity Code", HFILL } + }, + { &hf_attr_ipa_tr_cell_id, + { "Cell ID", "oml.fom.testrep.ipa.cell_id", + FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL } + }, + { &hf_attr_ipa_rsl_ip, + { "BSC RSL IP Address", "oml.fom.attr.ipa.rsl_ip", + FT_IPv4, BASE_NONE, NULL, 0, + "IP Address to which the BTS establishes " + "the RSL link", HFILL } + }, + { &hf_attr_ipa_rsl_port, + { "BSC RSL TCP Port", "oml.fom.attr.ipa.rsl_port", + FT_UINT16, BASE_DEC, NULL, 0, + "Port number to which the BST establishes " + "the RSL link", HFILL } + }, + { &hf_attr_ipa_prim_oml_ip, + { "Primary OML IP Address", + "oml.fom.attr.ipa.prim_oml_ip", + FT_IPv4, BASE_NONE, NULL, 0, + "IP Address of the BSC for the primary OML link", + HFILL } + }, + { &hf_attr_ipa_prim_oml_port, + { "Primary OML TCP Port", + "oml.fom.attr.ipa.prim_oml_port", + FT_UINT16, BASE_DEC, NULL, 0, + "TCP Port of the BSC for the primarly OML link", + HFILL } + }, + { &hf_attr_ipa_location_name, + { "Location Name", "oml.fom.attr.ipa.loc_name", + FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL } + }, + { &hf_attr_ipa_unit_name, + { "Unit Name", "oml.fom.attr.ipa.unit_name", + FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL } + }, + { &hf_attr_ipa_unit_id, + { "Unit ID", "oml.fom.attr.ipa.unit_id", + FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL } + }, + { &hf_attr_ipa_nv_flags, + { "NVRAM Config Flags", "oml.fom.attr.ipa.nv_flags", + FT_UINT16, BASE_HEX, NULL, 0xffff, NULL, HFILL } + }, + { &hf_attr_ipa_nv_mask, + { "NVRAM Config Mask", "oml.fom.attr.ipa.nv_mask", + FT_UINT16, BASE_HEX, NULL, 0xffff, NULL, HFILL } + }, + { &hf_attr_ipa_tr_si2, + { "System Information 2", "oml.fom.attr.ipa.si2", + FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL } + }, + { &hf_attr_ipa_tr_si2bis, + { "System Information 2bis", "oml.fom.attr.ipa.si2bis", + FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL } + }, + { &hf_attr_ipa_tr_si2ter, + { "System Information 2ter", "oml.fom.attr.ipa.si2ter", + FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL } + }, + { &hf_attr_ipa_tr_chan_desc, + { "Cell Channel Description", + "oml.fom.attr.ipa.chan_desc", + FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL } + }, + { &hf_attr_ipa_nsl_sport, + { "NS Link IP Source Port", + "oml.fom.attr.ipa.nsl_sport", + FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } + }, + { &hf_attr_ipa_nsl_daddr, + { "NS Link IP Destination Addr", + "oml.fom.attr.ipa.nsl_daddr", + FT_IPv4, BASE_NONE, NULL, 0, NULL, HFILL } + }, + { &hf_attr_ipa_nsl_dport, + { "NS Link IP Destination Port", + "oml.fom.attr.ipa.nsl_dport", + FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } + }, + { &hf_attr_ipa_nsei, + { "NSEI", "oml.fom.attr.ipa.nsei", + FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } + }, + { &hf_attr_ipa_nsvci, + { "NSVCI", "oml.fom.attr.ipa.nsvci", + FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } + }, + { &hf_attr_ipa_bvci, + { "BVCI", "oml.fom.attr.ipa.bvci", + FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } + }, + { &hf_attr_ipa_rac, + { "RAC", "oml.fom.attr.ipa.rac", + FT_UINT8, BASE_HEX, NULL, 0, + "Routing Area Code", HFILL } + }, + }; + static gint *ett[] = { + &ett_oml, + &ett_oml_fom, + &ett_oml_fom_att, + }; + + module_t *oml_module; + +#define NM_ATT_TLVDEF_BASE(_attr, _type, _fixed_len) \ + nm_att_tlvdef_base.def[_attr].type = _type; \ + nm_att_tlvdef_base.def[_attr].fixed_len = _fixed_len; \ + + /* From openbsc/src/abis_nm.c, converted to support ANSI C. */ + NM_ATT_TLVDEF_BASE(NM_ATT_ABIS_CHANNEL, TLV_TYPE_FIXED, 3); + NM_ATT_TLVDEF_BASE(NM_ATT_ADD_INFO, TLV_TYPE_TL16V, 0); + NM_ATT_TLVDEF_BASE(NM_ATT_ADD_TEXT, TLV_TYPE_TL16V, 0); + NM_ATT_TLVDEF_BASE(NM_ATT_ADM_STATE, TLV_TYPE_TV, 0); + NM_ATT_TLVDEF_BASE(NM_ATT_ARFCN_LIST, TLV_TYPE_TL16V, 0); + NM_ATT_TLVDEF_BASE(NM_ATT_AUTON_REPORT, TLV_TYPE_TV, 0); + NM_ATT_TLVDEF_BASE(NM_ATT_AVAIL_STATUS, TLV_TYPE_TL16V, 0); + NM_ATT_TLVDEF_BASE(NM_ATT_BCCH_ARFCN, TLV_TYPE_FIXED, 2); + NM_ATT_TLVDEF_BASE(NM_ATT_BSIC, TLV_TYPE_TV, 0); + NM_ATT_TLVDEF_BASE(NM_ATT_BTS_AIR_TIMER, TLV_TYPE_TV, 0); + NM_ATT_TLVDEF_BASE(NM_ATT_CCCH_L_I_P, TLV_TYPE_TV, 0); + NM_ATT_TLVDEF_BASE(NM_ATT_CCCH_L_T, TLV_TYPE_TV, 0); + NM_ATT_TLVDEF_BASE(NM_ATT_CHAN_COMB, TLV_TYPE_TV, 0); + NM_ATT_TLVDEF_BASE(NM_ATT_CONN_FAIL_CRIT, TLV_TYPE_TL16V, 0); + NM_ATT_TLVDEF_BASE(NM_ATT_DEST, TLV_TYPE_TL16V, 0); + NM_ATT_TLVDEF_BASE(NM_ATT_EVENT_TYPE, TLV_TYPE_TV, 0); + NM_ATT_TLVDEF_BASE(NM_ATT_FILE_ID, TLV_TYPE_TL16V, 0); + NM_ATT_TLVDEF_BASE(NM_ATT_FILE_VERSION, TLV_TYPE_TL16V, 0); + NM_ATT_TLVDEF_BASE(NM_ATT_GSM_TIME, TLV_TYPE_FIXED, 2); + NM_ATT_TLVDEF_BASE(NM_ATT_HSN, TLV_TYPE_TV, 0); + NM_ATT_TLVDEF_BASE(NM_ATT_HW_CONFIG, TLV_TYPE_TL16V, 0); + NM_ATT_TLVDEF_BASE(NM_ATT_HW_DESC, TLV_TYPE_TLV, 0); + NM_ATT_TLVDEF_BASE(NM_ATT_INTAVE_PARAM, TLV_TYPE_TV, 0); + NM_ATT_TLVDEF_BASE(NM_ATT_INTERF_BOUND, TLV_TYPE_FIXED, 6); + NM_ATT_TLVDEF_BASE(NM_ATT_LIST_REQ_ATTR, TLV_TYPE_TL16V, 0); + NM_ATT_TLVDEF_BASE(NM_ATT_MAIO, TLV_TYPE_TV, 0); + NM_ATT_TLVDEF_BASE(NM_ATT_MANUF_STATE, TLV_TYPE_TV, 0); + NM_ATT_TLVDEF_BASE(NM_ATT_MANUF_THRESH, TLV_TYPE_TL16V, 0); + NM_ATT_TLVDEF_BASE(NM_ATT_MANUF_ID, TLV_TYPE_TL16V, 0); + NM_ATT_TLVDEF_BASE(NM_ATT_MAX_TA, TLV_TYPE_TV, 0); + NM_ATT_TLVDEF_BASE(NM_ATT_MDROP_LINK, TLV_TYPE_FIXED, 2); + NM_ATT_TLVDEF_BASE(NM_ATT_MDROP_NEXT, TLV_TYPE_FIXED, 2); + NM_ATT_TLVDEF_BASE(NM_ATT_NACK_CAUSES, TLV_TYPE_TV, 0); + NM_ATT_TLVDEF_BASE(NM_ATT_NY1, TLV_TYPE_TV, 0); + NM_ATT_TLVDEF_BASE(NM_ATT_OPER_STATE, TLV_TYPE_TV, 0); + NM_ATT_TLVDEF_BASE(NM_ATT_OVERL_PERIOD, TLV_TYPE_TL16V, 0); + NM_ATT_TLVDEF_BASE(NM_ATT_PHYS_CONF, TLV_TYPE_TL16V, 0); + NM_ATT_TLVDEF_BASE(NM_ATT_POWER_CLASS, TLV_TYPE_TV, 0); + NM_ATT_TLVDEF_BASE(NM_ATT_POWER_THRESH, TLV_TYPE_FIXED, 3); + NM_ATT_TLVDEF_BASE(NM_ATT_PROB_CAUSE, TLV_TYPE_FIXED, 3); + NM_ATT_TLVDEF_BASE(NM_ATT_RACH_B_THRESH, TLV_TYPE_TV, 0); + NM_ATT_TLVDEF_BASE(NM_ATT_LDAVG_SLOTS, TLV_TYPE_FIXED, 2); + NM_ATT_TLVDEF_BASE(NM_ATT_RAD_SUBC, TLV_TYPE_TV, 0); + NM_ATT_TLVDEF_BASE(NM_ATT_RF_MAXPOWR_R, TLV_TYPE_TV, 0); + NM_ATT_TLVDEF_BASE(NM_ATT_SITE_INPUTS, TLV_TYPE_TL16V, 0); + NM_ATT_TLVDEF_BASE(NM_ATT_SITE_OUTPUTS, TLV_TYPE_TL16V, 0); + NM_ATT_TLVDEF_BASE(NM_ATT_SOURCE, TLV_TYPE_TL16V, 0); + NM_ATT_TLVDEF_BASE(NM_ATT_SPEC_PROB, TLV_TYPE_TV, 0); + NM_ATT_TLVDEF_BASE(NM_ATT_START_TIME, TLV_TYPE_FIXED, 2); + NM_ATT_TLVDEF_BASE(NM_ATT_T200, TLV_TYPE_FIXED, 7); + NM_ATT_TLVDEF_BASE(NM_ATT_TEI, TLV_TYPE_TV, 0); + NM_ATT_TLVDEF_BASE(NM_ATT_TEST_DUR, TLV_TYPE_FIXED, 2); + NM_ATT_TLVDEF_BASE(NM_ATT_TEST_NO, TLV_TYPE_TV, 0); + NM_ATT_TLVDEF_BASE(NM_ATT_TEST_REPORT, TLV_TYPE_TL16V, 0); + NM_ATT_TLVDEF_BASE(NM_ATT_VSWR_THRESH, TLV_TYPE_FIXED, 2); + NM_ATT_TLVDEF_BASE(NM_ATT_WINDOW_SIZE, TLV_TYPE_TV, 0); + NM_ATT_TLVDEF_BASE(NM_ATT_TSC, TLV_TYPE_TV, 0); + NM_ATT_TLVDEF_BASE(NM_ATT_SW_CONFIG, TLV_TYPE_TL16V, 0); + NM_ATT_TLVDEF_BASE(NM_ATT_SEVERITY, TLV_TYPE_TV, 0); + NM_ATT_TLVDEF_BASE(NM_ATT_GET_ARI, TLV_TYPE_TL16V, 0); + NM_ATT_TLVDEF_BASE(NM_ATT_HW_CONF_CHG, TLV_TYPE_TL16V, 0); + NM_ATT_TLVDEF_BASE(NM_ATT_OUTST_ALARM, TLV_TYPE_TV, 0); + NM_ATT_TLVDEF_BASE(NM_ATT_FILE_DATA, TLV_TYPE_TL16V, 0); + NM_ATT_TLVDEF_BASE(NM_ATT_MEAS_RES, TLV_TYPE_TL16V, 0); + + /* BS 11 specifics */ +#define NM_ATT_TLVDEV_BS11(_attr, _type, _fixed_len) \ + nm_att_tlvdev_bs11.def[_attr].type = _type; \ + nm_att_tlvdev_bs11.def[_attr].fixed_len = _fixed_len; \ + + /* different stndard IEs */ + NM_ATT_TLVDEV_BS11(NM_ATT_OUTST_ALARM, TLV_TYPE_TLV, 0); + NM_ATT_TLVDEV_BS11(NM_ATT_HW_DESC, TLV_TYPE_TL16V, 0); + NM_ATT_TLVDEV_BS11(NM_ATT_ARFCN_LIST, TLV_TYPE_TLV16, 0); + + /* proprietary IEs */ + NM_ATT_TLVDEV_BS11(NM_ATT_BS11_ABIS_EXT_TIME, TLV_TYPE_TLV, 0); + NM_ATT_TLVDEV_BS11(NM_ATT_BS11_OM_LAPD_REL_TIMER, TLV_TYPE_FIXED, 2); + NM_ATT_TLVDEV_BS11(NM_ATT_BS11_SH_LAPD_INT_TIMER, TLV_TYPE_TLV, 0); + NM_ATT_TLVDEV_BS11(NM_ATT_BS11_EMERG_TIMER1, TLV_TYPE_TLV, 0); + NM_ATT_TLVDEV_BS11(NM_ATT_BS11_EMERG_TIMER2, TLV_TYPE_TLV, 0); + NM_ATT_TLVDEV_BS11(NM_ATT_BS11_BTSLS_HOPPING, TLV_TYPE_FIXED, 1); + NM_ATT_TLVDEV_BS11(NM_ATT_BS11_CELL_ALLOC_NR, TLV_TYPE_FIXED, 1); + NM_ATT_TLVDEV_BS11(NM_ATT_BS11_ENA_INTERF_CLASS, TLV_TYPE_FIXED, 1); + NM_ATT_TLVDEV_BS11(NM_ATT_BS11_FACCH_QUAL, TLV_TYPE_FIXED, 1); + NM_ATT_TLVDEV_BS11(NM_ATT_BS11_TSYNC, TLV_TYPE_FIXED, 2); + NM_ATT_TLVDEV_BS11(NM_ATT_BS11_TTRAU, TLV_TYPE_FIXED, 2); + NM_ATT_TLVDEV_BS11(NM_ATT_BS11_EXCESSIVE_DISTANCE,TLV_TYPE_TLV, 1); + NM_ATT_TLVDEV_BS11(NM_ATT_BS11_HOPPING_MODE, TLV_TYPE_TLV, 1); + NM_ATT_TLVDEV_BS11(NM_ATT_BS11_RF_RES_IND_PER, TLV_TYPE_FIXED, 1); + NM_ATT_TLVDEV_BS11(NM_ATT_BS11_RADIO_MEAS_GRAN, TLV_TYPE_TLV, 0); + NM_ATT_TLVDEV_BS11(NM_ATT_BS11_RADIO_MEAS_REP, TLV_TYPE_TLV, 0); + NM_ATT_TLVDEV_BS11(NM_ATT_BS11_EMRG_CFG_MEMBER, TLV_TYPE_TLV, 0); + NM_ATT_TLVDEV_BS11(NM_ATT_BS11_TRX_AREA, TLV_TYPE_TLV, 0); + NM_ATT_TLVDEV_BS11(NM_ATT_BS11_ESN_FW_CODE_NO, TLV_TYPE_TLV, 0); + NM_ATT_TLVDEV_BS11(NM_ATT_BS11_ESN_HW_CODE_NO, TLV_TYPE_TLV, 0); + NM_ATT_TLVDEV_BS11(NM_ATT_BS11_ESN_PCB_SERIAL, TLV_TYPE_TLV, 0); + NM_ATT_TLVDEV_BS11(NM_ATT_BS11_BOOT_SW_VERS, TLV_TYPE_TLV, 0); + NM_ATT_TLVDEV_BS11(0x59, TLV_TYPE_TLV, 0); + NM_ATT_TLVDEV_BS11(0xd5, TLV_TYPE_TLV, 0); + NM_ATT_TLVDEV_BS11(0xa8, TLV_TYPE_TLV, 0); + NM_ATT_TLVDEV_BS11(NM_ATT_BS11_PASSWORD, TLV_TYPE_TLV, 0); + NM_ATT_TLVDEV_BS11(NM_ATT_BS11_TXPWR, TLV_TYPE_TLV, 0); + NM_ATT_TLVDEV_BS11(NM_ATT_BS11_RSSI_OFFS, TLV_TYPE_TLV, 0); + NM_ATT_TLVDEV_BS11(NM_ATT_BS11_LINE_CFG, TLV_TYPE_TV, 0); + NM_ATT_TLVDEV_BS11(NM_ATT_BS11_L1_PROT_TYPE, TLV_TYPE_TV, 0); + NM_ATT_TLVDEV_BS11(NM_ATT_BS11_BIT_ERR_THESH, TLV_TYPE_FIXED, 2); + NM_ATT_TLVDEV_BS11(NM_ATT_BS11_DIVERSITY, TLV_TYPE_TLV, 0); + NM_ATT_TLVDEV_BS11(NM_ATT_BS11_LMT_LOGON_SESSION, TLV_TYPE_TLV, 0); + NM_ATT_TLVDEV_BS11(NM_ATT_BS11_LMT_LOGIN_TIME, TLV_TYPE_TLV, 0); + NM_ATT_TLVDEV_BS11(NM_ATT_BS11_LMT_USER_ACC_LEV, TLV_TYPE_TLV, 0); + NM_ATT_TLVDEV_BS11(NM_ATT_BS11_LMT_USER_NAME, TLV_TYPE_TLV, 0); + NM_ATT_TLVDEV_BS11(NM_ATT_BS11_BTS_STATE, TLV_TYPE_TLV, 0); + NM_ATT_TLVDEV_BS11(NM_ATT_BS11_E1_STATE, TLV_TYPE_TLV, 0); + NM_ATT_TLVDEV_BS11(NM_ATT_BS11_PLL_MODE, TLV_TYPE_TLV, 0); + NM_ATT_TLVDEV_BS11(NM_ATT_BS11_PLL, TLV_TYPE_TLV, 0); + NM_ATT_TLVDEV_BS11(NM_ATT_BS11_CCLK_ACCURACY, TLV_TYPE_TV, 0); + NM_ATT_TLVDEV_BS11(NM_ATT_BS11_CCLK_TYPE, TLV_TYPE_TV, 0); + + /* ip.access specifics */ +#define NM_ATT_TLVDEF_IPA(_attr, _type, _fixed_len) \ + nm_att_tlvdef_ipa.def[_attr].type = _type; \ + nm_att_tlvdef_ipa.def[_attr].fixed_len = _fixed_len; \ + + NM_ATT_TLVDEF_IPA(NM_ATT_IPACC_DST_IP, TLV_TYPE_FIXED, 4); + NM_ATT_TLVDEF_IPA(NM_ATT_IPACC_DST_IP_PORT, TLV_TYPE_FIXED, 2); + NM_ATT_TLVDEF_IPA(NM_ATT_IPACC_PRIM_OML_CFG, TLV_TYPE_TL16V, 0); + NM_ATT_TLVDEF_IPA(NM_ATT_IPACC_NV_FLAGS, TLV_TYPE_TL16V, 0); + NM_ATT_TLVDEF_IPA(NM_ATT_IPACC_FREQ_CTRL, TLV_TYPE_FIXED, 2); + NM_ATT_TLVDEF_IPA(NM_ATT_IPACC_SEC_OML_CFG, TLV_TYPE_FIXED, 6); + NM_ATT_TLVDEF_IPA(NM_ATT_IPACC_IP_IF_CFG, TLV_TYPE_FIXED, 8); + NM_ATT_TLVDEF_IPA(NM_ATT_IPACC_IP_GW_CFG, TLV_TYPE_FIXED, 12); + NM_ATT_TLVDEF_IPA(NM_ATT_IPACC_LOCATION, TLV_TYPE_TL16V, 0); + NM_ATT_TLVDEF_IPA(NM_ATT_IPACC_UNIT_ID, TLV_TYPE_TL16V, 0); + NM_ATT_TLVDEF_IPA(NM_ATT_IPACC_UNIT_NAME, TLV_TYPE_TL16V, 0); + NM_ATT_TLVDEF_IPA(NM_ATT_IPACC_SNMP_CFG, TLV_TYPE_TL16V, 0); + NM_ATT_TLVDEF_IPA(NM_ATT_IPACC_ALM_THRESH_LIST, TLV_TYPE_TL16V, 0); + NM_ATT_TLVDEF_IPA(NM_ATT_IPACC_CUR_SW_CFG, TLV_TYPE_TL16V, 0); + NM_ATT_TLVDEF_IPA(NM_ATT_IPACC_STREAM_ID, TLV_TYPE_TV, 0); + NM_ATT_TLVDEF_IPA(NM_ATT_IPACC_RAC, TLV_TYPE_TL16V, 0); + NM_ATT_TLVDEF_IPA(NM_ATT_IPACC_OBJ_VERSION, TLV_TYPE_TL16V, 0); + NM_ATT_TLVDEF_IPA(NM_ATT_IPACC_GPRS_PAGING_CFG, TLV_TYPE_TL16V, 0); + NM_ATT_TLVDEF_IPA(NM_ATT_IPACC_NSEI, TLV_TYPE_TL16V, 0); + NM_ATT_TLVDEF_IPA(NM_ATT_IPACC_BVCI, TLV_TYPE_TL16V, 0); + NM_ATT_TLVDEF_IPA(NM_ATT_IPACC_NSVCI, TLV_TYPE_TL16V, 0); + NM_ATT_TLVDEF_IPA(NM_ATT_IPACC_NS_CFG, TLV_TYPE_TL16V, 0); + NM_ATT_TLVDEF_IPA(NM_ATT_IPACC_BSSGP_CFG, TLV_TYPE_TL16V, 0); + NM_ATT_TLVDEF_IPA(NM_ATT_IPACC_NS_LINK_CFG, TLV_TYPE_TL16V, 0); + NM_ATT_TLVDEF_IPA(NM_ATT_IPACC_RLC_CFG, TLV_TYPE_TL16V, 0); + NM_ATT_TLVDEF_IPA(NM_ATT_IPACC_SUPP_FEATURES, TLV_TYPE_TL16V, 0); + NM_ATT_TLVDEF_IPA(NM_ATT_IPACC_CODING_SCHEMES, TLV_TYPE_TL16V, 0); + NM_ATT_TLVDEF_IPA(NM_ATT_IPACC_RLC_CFG_2, TLV_TYPE_TL16V, 0); + NM_ATT_TLVDEF_IPA(NM_ATT_IPACC_RLC_CFG_3, TLV_TYPE_TL16V, 0); + NM_ATT_TLVDEF_IPA(NM_ATT_IPACC_PAGING_CFG, TLV_TYPE_FIXED, 2); + NM_ATT_TLVDEF_IPA(NM_ATT_IPACC_FILE_DATA, TLV_TYPE_TL16V, 0); + NM_ATT_TLVDEF_IPA(NM_ATT_IPACC_CGI, TLV_TYPE_TL16V, 0); + + /* assign our custom match functions */ + oml_fom_msgtype_vse._vs_match2 = _match_oml_fom_msgtype; + oml_fom_attr_vse._vs_match2 = _match_oml_fom_attr; + + proto_abis_oml = proto_register_protocol("GSM A-bis OML", "A-bis OML", + "gsm_abis_oml"); + + proto_register_field_array(proto_abis_oml, hf, array_length(hf)); + + proto_register_subtree_array(ett, array_length(ett)); + + register_dissector("gsm_abis_oml", dissect_abis_oml, proto_abis_oml); + + oml_module = prefs_register_protocol(proto_abis_oml, proto_reg_handoff_abis_oml); + prefs_register_bool_preference(oml_module, "use_ipaccess_oml", + "Use nanoBTS definitions", + "Use ipaccess nanoBTS specific definitions for OML", + &global_oml_use_nano_bts); +} + +/* This function is called once at startup and every time the user hits + * 'apply' in the preferences dialogue */ +void +proto_reg_handoff_abis_oml(void) +{ + static gboolean initialized = FALSE; + + if (!initialized) { + dissector_handle_t abis_oml_handle; + + abis_oml_handle = create_dissector_handle(dissect_abis_oml, proto_abis_oml); + dissector_add("lapd.gsm.sapi", LAPD_GSM_SAPI_OM_PROC, abis_oml_handle); + + } else { + /* preferences have been changed */ + } + + if (global_oml_use_nano_bts == TRUE) { + /* initialize with nanobts definitions */ + oml_fom_msgtype_vse._vs_p = _oml_fom_msgtype_vals_ipa; + oml_fom_msgtype_vse._vs_num_entries = + array_length(_oml_fom_msgtype_vals_ipa)-1; + oml_fom_attr_vse._vs_p = oml_fom_attr_vals_ipa; + oml_fom_attr_vse._vs_num_entries = + array_length(oml_fom_attr_vals_ipa)-1; + } else { + /* initialize with BS11 defaults */ + oml_fom_msgtype_vse._vs_p = _oml_fom_msgtype_vals_bs11; + oml_fom_msgtype_vse._vs_num_entries = + array_length(_oml_fom_msgtype_vals_bs11)-1; + oml_fom_attr_vse._vs_p = oml_fom_attr_vals_bs11; + oml_fom_attr_vse._vs_num_entries = + array_length(oml_fom_attr_vals_bs11)-1; + } +} Index: wireshark/epan/dissectors/packet-gsm_abis_oml.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ wireshark/epan/dissectors/packet-gsm_abis_oml.h 2011-09-06 12:30:44.000000000 +0200 @@ -0,0 +1,667 @@ +/* GSM Network Management messages on the A-bis interface + * 3GPP TS 12.21 version 8.0.0 Release 1999 / ETSI TS 100 623 V8.0.0 */ + +/* (C) 2008-2009 by Harald Welte + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef __PACKET_ABIS_OML_H__ +#define __PACKET_ABIS_OML_H__ + +#include + +/* From openbsc/include/openbsc/abis_nm.h */ + +/* generic header in front of every OML message according to TS 08.59 */ +struct abis_om_hdr { + guint8 mdisc; + guint8 placement; + guint8 sequence; + guint8 length; + guint8 data[0]; +} __attribute__ ((packed)); + +#define ABIS_OM_MDISC_FOM 0x80 +#define ABIS_OM_MDISC_MMI 0x40 +#define ABIS_OM_MDISC_TRAU 0x20 +#define ABIS_OM_MDISC_MANUF 0x10 +#define ABIS_OM_PLACEMENT_ONLY 0x80 +#define ABIS_OM_PLACEMENT_FIRST 0x40 +#define ABIS_OM_PLACEMENT_MIDDLE 0x20 +#define ABIS_OM_PLACEMENT_LAST 0x10 + +struct abis_om_obj_inst { + guint8 bts_nr; + guint8 trx_nr; + guint8 ts_nr; +} __attribute__ ((packed)); + +struct abis_om_fom_hdr { + guint8 msg_type; + guint8 obj_class; + struct abis_om_obj_inst obj_inst; + guint8 data[0]; +} __attribute__ ((packed)); + +#define ABIS_OM_FOM_HDR_SIZE (sizeof(struct abis_om_hdr) + sizeof(struct abis_om_fom_hdr)) + +/* Section 9.1: Message Types */ +enum abis_nm_msgtype { + /* SW Download Management Messages */ + NM_MT_LOAD_INIT = 0x01, + NM_MT_LOAD_INIT_ACK, + NM_MT_LOAD_INIT_NACK, + NM_MT_LOAD_SEG, + NM_MT_LOAD_SEG_ACK, + NM_MT_LOAD_ABORT, + NM_MT_LOAD_END, + NM_MT_LOAD_END_ACK, + NM_MT_LOAD_END_NACK, + NM_MT_SW_ACT_REQ, /* BTS->BSC */ + NM_MT_SW_ACT_REQ_ACK, + NM_MT_SW_ACT_REQ_NACK, + NM_MT_ACTIVATE_SW, /* BSC->BTS */ + NM_MT_ACTIVATE_SW_ACK, + NM_MT_ACTIVATE_SW_NACK, + NM_MT_SW_ACTIVATED_REP, /* 0x10 */ + /* A-bis Interface Management Messages */ + NM_MT_ESTABLISH_TEI = 0x21, + NM_MT_ESTABLISH_TEI_ACK, + NM_MT_ESTABLISH_TEI_NACK, + NM_MT_CONN_TERR_SIGN, + NM_MT_CONN_TERR_SIGN_ACK, + NM_MT_CONN_TERR_SIGN_NACK, + NM_MT_DISC_TERR_SIGN, + NM_MT_DISC_TERR_SIGN_ACK, + NM_MT_DISC_TERR_SIGN_NACK, + NM_MT_CONN_TERR_TRAF, + NM_MT_CONN_TERR_TRAF_ACK, + NM_MT_CONN_TERR_TRAF_NACK, + NM_MT_DISC_TERR_TRAF, + NM_MT_DISC_TERR_TRAF_ACK, + NM_MT_DISC_TERR_TRAF_NACK, + /* Transmission Management Messages */ + NM_MT_CONN_MDROP_LINK = 0x31, + NM_MT_CONN_MDROP_LINK_ACK, + NM_MT_CONN_MDROP_LINK_NACK, + NM_MT_DISC_MDROP_LINK, + NM_MT_DISC_MDROP_LINK_ACK, + NM_MT_DISC_MDROP_LINK_NACK, + /* Air Interface Management Messages */ + NM_MT_SET_BTS_ATTR = 0x41, + NM_MT_SET_BTS_ATTR_ACK, + NM_MT_SET_BTS_ATTR_NACK, + NM_MT_SET_RADIO_ATTR, + NM_MT_SET_RADIO_ATTR_ACK, + NM_MT_SET_RADIO_ATTR_NACK, + NM_MT_SET_CHAN_ATTR, + NM_MT_SET_CHAN_ATTR_ACK, + NM_MT_SET_CHAN_ATTR_NACK, + /* Test Management Messages */ + NM_MT_PERF_TEST = 0x51, + NM_MT_PERF_TEST_ACK, + NM_MT_PERF_TEST_NACK, + NM_MT_TEST_REP, + NM_MT_SEND_TEST_REP, + NM_MT_SEND_TEST_REP_ACK, + NM_MT_SEND_TEST_REP_NACK, + NM_MT_STOP_TEST, + NM_MT_STOP_TEST_ACK, + NM_MT_STOP_TEST_NACK, + /* State Management and Event Report Messages */ + NM_MT_STATECHG_EVENT_REP = 0x61, + NM_MT_FAILURE_EVENT_REP, + NM_MT_STOP_EVENT_REP, + NM_MT_STOP_EVENT_REP_ACK, + NM_MT_STOP_EVENT_REP_NACK, + NM_MT_REST_EVENT_REP, + NM_MT_REST_EVENT_REP_ACK, + NM_MT_REST_EVENT_REP_NACK, + NM_MT_CHG_ADM_STATE, + NM_MT_CHG_ADM_STATE_ACK, + NM_MT_CHG_ADM_STATE_NACK, + NM_MT_CHG_ADM_STATE_REQ, + NM_MT_CHG_ADM_STATE_REQ_ACK, + NM_MT_CHG_ADM_STATE_REQ_NACK, + NM_MT_REP_OUTST_ALARMS = 0x93, + NM_MT_REP_OUTST_ALARMS_ACK, + NM_MT_REP_OUTST_ALARMS_NACK, + /* Equipment Management Messages */ + NM_MT_CHANGEOVER = 0x71, + NM_MT_CHANGEOVER_ACK, + NM_MT_CHANGEOVER_NACK, + NM_MT_OPSTART, + NM_MT_OPSTART_ACK, + NM_MT_OPSTART_NACK, + NM_MT_REINIT, + NM_MT_REINIT_ACK, + NM_MT_REINIT_NACK, + NM_MT_SET_SITE_OUT, /* BS11: get alarm ?!? */ + NM_MT_SET_SITE_OUT_ACK, + NM_MT_SET_SITE_OUT_NACK, + NM_MT_CHG_HW_CONF = 0x90, + NM_MT_CHG_HW_CONF_ACK, + NM_MT_CHG_HW_CONF_NACK, + /* Measurement Management Messages */ + NM_MT_MEAS_RES_REQ = 0x8a, + NM_MT_MEAS_RES_RESP, + NM_MT_STOP_MEAS, + NM_MT_START_MEAS, + /* Other Messages */ + NM_MT_GET_ATTR = 0x81, + NM_MT_GET_ATTR_RESP, + NM_MT_GET_ATTR_NACK, + NM_MT_SET_ALARM_THRES, + NM_MT_SET_ALARM_THRES_ACK, + NM_MT_SET_ALARM_THRES_NACK, + + NM_MT_IPACC_RESTART = 0x87, + NM_MT_IPACC_RESTART_ACK, +}; + +enum abis_nm_msgtype_bs11 { + NM_MT_BS11_RESET_RESOURCE = 0x74, + + NM_MT_BS11_BEGIN_DB_TX = 0xa3, + NM_MT_BS11_BEGIN_DB_TX_ACK, + NM_MT_BS11_BEGIN_DB_TX_NACK, + NM_MT_BS11_END_DB_TX = 0xa6, + NM_MT_BS11_END_DB_TX_ACK, + NM_MT_BS11_END_DB_TX_NACK, + NM_MT_BS11_CREATE_OBJ = 0xa9, + NM_MT_BS11_CREATE_OBJ_ACK, + NM_MT_BS11_CREATE_OBJ_NACK, + NM_MT_BS11_DELETE_OBJ = 0xac, + NM_MT_BS11_DELETE_OBJ_ACK, + NM_MT_BS11_DELETE_OBJ_NACK, + + NM_MT_BS11_SET_ATTR = 0xd0, + NM_MT_BS11_SET_ATTR_ACK, + NM_MT_BS11_SET_ATTR_NACK, + NM_MT_BS11_LMT_SESSION = 0xdc, + + NM_MT_BS11_GET_STATE = 0xe3, + NM_MT_BS11_GET_STATE_ACK, + NM_MT_BS11_LMT_LOGON = 0xe5, + NM_MT_BS11_LMT_LOGON_ACK, + NM_MT_BS11_RESTART = 0xe7, + NM_MT_BS11_RESTART_ACK, + NM_MT_BS11_DISCONNECT = 0xe9, + NM_MT_BS11_DISCONNECT_ACK, + NM_MT_BS11_LMT_LOGOFF = 0xec, + NM_MT_BS11_LMT_LOGOFF_ACK, + NM_MT_BS11_RECONNECT = 0xf1, + NM_MT_BS11_RECONNECT_ACK, +}; + +enum abis_nm_msgtype_ipacc { + NM_MT_IPACC_RSL_CONNECT = 0xe0, + NM_MT_IPACC_RSL_CONNECT_ACK, + NM_MT_IPACC_RSL_CONNECT_NACK, + NM_MT_IPACC_RSL_DISCONNECT = 0xe3, + NM_MT_IPACC_RSL_DISCONNECT_ACK, + NM_MT_IPACC_RSL_DISCONNECT_NACK, + NM_MT_IPACC_CONN_TRAF = 0xe6, + NM_MT_IPACC_CONN_TRAF_ACK, + NM_MT_IPACC_CONN_TRAF_NACK, + NM_MT_IPACC_DISC_TRAF = 0xe9, + NM_MT_IPACC_DISC_TRAF_ACK, + NM_MT_IPACC_DISC_TRAF_NACK, + NM_MT_IPACC_DEF_BOOT_SW = 0xec, + NM_MT_IPACC_DEF_BOOT_SW_ACK, + NM_MT_IPACC_DEF_BOOT_SW_NACK, + NM_MT_IPACC_SET_NVATTR = 0xef, + NM_MT_IPACC_SET_NVATTR_ACK, + NM_MT_IPACC_SET_NVATTR_NACK, + NM_MT_IPACC_GET_NVATTR = 0xf2, + NM_MT_IPACC_GET_NVATTR_ACK, + NM_MT_IPACC_GET_NVATTR_NACK, + NM_MT_IPACC_SET_ATTR = 0xf5, + NM_MT_IPACC_SET_ATTR_ACK, + NM_MT_IPACC_SET_ATTR_NACK, + NM_MT_IPACC_ATTR_CHG_EVT = 0xf8, + NM_MT_IPACC_SW_DEACT = 0xf9, + NM_MT_IPACC_SW_DEACT_ACK, + NM_MT_IPACC_SW_DEACT_NACK, + NM_MT_IPACC_MEAS_RES_REQ_NACK = 0xfc, + NM_MT_IPACC_START_MEAS_NACK, + NM_MT_IPACC_STOP_MEAS_NACK, +}; + +enum abis_nm_bs11_cell_alloc { + NM_BS11_CANR_GSM = 0x00, + NM_BS11_CANR_DCS1800 = 0x01, +}; + +/* Section 9.2: Object Class */ +enum abis_nm_obj_class { + NM_OC_SITE_MANAGER = 0x00, + NM_OC_BTS, + NM_OC_RADIO_CARRIER, + NM_OC_CHANNEL, + NM_OC_BASEB_TRANSC, + /* RFU: 05-FE */ + NM_OC_BS11_ADJC = 0xa0, + NM_OC_BS11_HANDOVER = 0xa1, + NM_OC_BS11_PWR_CTRL = 0xa2, + NM_OC_BS11_BTSE = 0xa3, /* LMT? */ + NM_OC_BS11_RACK = 0xa4, + NM_OC_BS11 = 0xa5, /* 01: ALCO */ + NM_OC_BS11_TEST = 0xa6, + NM_OC_BS11_ENVABTSE = 0xa8, + NM_OC_BS11_BPORT = 0xa9, + + NM_OC_GPRS_NSE = 0xf0, + NM_OC_GPRS_CELL = 0xf1, + NM_OC_GPRS_NSVC0 = 0xf2, + NM_OC_GPRS_NSVC1 = 0xf3, + + NM_OC_NULL = 0xff, +}; + +/* Section 9.4: Attributes */ +enum abis_nm_attr { + NM_ATT_ABIS_CHANNEL = 0x01, + NM_ATT_ADD_INFO, + NM_ATT_ADD_TEXT, + NM_ATT_ADM_STATE, + NM_ATT_ARFCN_LIST, + NM_ATT_AUTON_REPORT, + NM_ATT_AVAIL_STATUS, + NM_ATT_BCCH_ARFCN, + NM_ATT_BSIC, + NM_ATT_BTS_AIR_TIMER, + NM_ATT_CCCH_L_I_P, + NM_ATT_CCCH_L_T, + NM_ATT_CHAN_COMB, + NM_ATT_CONN_FAIL_CRIT, + NM_ATT_DEST, + /* res */ + NM_ATT_EVENT_TYPE = 0x11, /* BS11: file data ?!? */ + NM_ATT_FILE_ID, + NM_ATT_FILE_VERSION, + NM_ATT_GSM_TIME, + NM_ATT_HSN, + NM_ATT_HW_CONFIG, + NM_ATT_HW_DESC, + NM_ATT_INTAVE_PARAM, + NM_ATT_INTERF_BOUND, + NM_ATT_LIST_REQ_ATTR, + NM_ATT_MAIO, + NM_ATT_MANUF_STATE, + NM_ATT_MANUF_THRESH, + NM_ATT_MANUF_ID, + NM_ATT_MAX_TA, + NM_ATT_MDROP_LINK, /* 0x20 */ + NM_ATT_MDROP_NEXT, + NM_ATT_NACK_CAUSES, + NM_ATT_NY1, + NM_ATT_OPER_STATE, + NM_ATT_OVERL_PERIOD, + NM_ATT_PHYS_CONF, + NM_ATT_POWER_CLASS, + NM_ATT_POWER_THRESH, + NM_ATT_PROB_CAUSE, + NM_ATT_RACH_B_THRESH, + NM_ATT_LDAVG_SLOTS, + NM_ATT_RAD_SUBC, + NM_ATT_RF_MAXPOWR_R, + NM_ATT_SITE_INPUTS, + NM_ATT_SITE_OUTPUTS, + NM_ATT_SOURCE, /* 0x30 */ + NM_ATT_SPEC_PROB, + NM_ATT_START_TIME, + NM_ATT_T200, + NM_ATT_TEI, + NM_ATT_TEST_DUR, + NM_ATT_TEST_NO, + NM_ATT_TEST_REPORT, + NM_ATT_VSWR_THRESH, + NM_ATT_WINDOW_SIZE, + /* Res */ + NM_ATT_BS11_RSSI_OFFS = 0x3d, + NM_ATT_BS11_TXPWR = 0x3e, + NM_ATT_BS11_DIVERSITY = 0x3f, + /* Res */ + NM_ATT_TSC = 0x40, + NM_ATT_SW_CONFIG, + NM_ATT_SW_DESCR, + NM_ATT_SEVERITY, + NM_ATT_GET_ARI, + NM_ATT_HW_CONF_CHG, + NM_ATT_OUTST_ALARM, + NM_ATT_FILE_DATA, + NM_ATT_MEAS_RES, + NM_ATT_MEAS_TYPE, +}; + +enum abis_nm_attr_bs11 { + NM_ATT_BS11_OM_LAPD_REL_TIMER = 0x02, + NM_ATT_BS11_EMERG_TIMER1 = 0x42, + NM_ATT_BS11_EMERG_TIMER2 = 0x44, + NM_ATT_BS11_ESN_FW_CODE_NO = 0x4c, + NM_ATT_BS11_ESN_HW_CODE_NO = 0x4f, + + NM_ATT_BS11_FILE_DATA = NM_ATT_EVENT_TYPE, + + NM_ATT_BS11_ESN_PCB_SERIAL = 0x55, + NM_ATT_BS11_EXCESSIVE_DISTANCE = 0x58, + + NM_ATT_BS11_ALL_TEST_CATG = 0x60, + NM_ATT_BS11_BTSLS_HOPPING, + NM_ATT_BS11_CELL_ALLOC_NR, + NM_ATT_BS11_CELL_GLOBAL_ID, + NM_ATT_BS11_ENA_INTERF_CLASS = 0x66, + NM_ATT_BS11_ENA_INT_INTEC_HANDO = 0x67, + NM_ATT_BS11_ENA_INT_INTRC_HANDO = 0x68, + NM_ATT_BS11_ENA_MS_PWR_CTRL = 0x69, + NM_ATT_BS11_ENA_PWR_BDGT_HO = 0x6a, + NM_ATT_BS11_ENA_PWR_CTRL_RLFW = 0x6b, + NM_ATT_BS11_ENA_RXLEV_HO = 0x6c, + NM_ATT_BS11_ENA_RXQUAL_HO = 0x6d, + NM_ATT_BS11_FACCH_QUAL = 0x6e, + + NM_ATT_BS11_RF_RES_IND_PER = 0x8f, + + NM_ATT_BS11_RX_LEV_MIN_CELL = 0x90, + NM_ATT_BS11_ABIS_EXT_TIME = 0x91, + NM_ATT_BS11_TIMER_HO_REQUEST = 0x92, + NM_ATT_BS11_TIMER_NCELL = 0x93, + NM_ATT_BS11_TSYNC = 0x94, + NM_ATT_BS11_TTRAU = 0x95, + NM_ATT_BS11_EMRG_CFG_MEMBER = 0x9b, + NM_ATT_BS11_TRX_AREA = 0x9f, + + NM_ATT_BS11_BCCH_RECONF = 0xd7, + NM_ATT_BS11_BIT_ERR_THESH = 0xa0, + NM_ATT_BS11_BOOT_SW_VERS = 0xa1, + NM_ATT_BS11_CCLK_ACCURACY = 0xa3, + NM_ATT_BS11_CCLK_TYPE = 0xa4, + NM_ATT_BS11_INP_IMPEDANCE = 0xaa, + NM_ATT_BS11_L1_PROT_TYPE = 0xab, + NM_ATT_BS11_LINE_CFG = 0xac, + NM_ATT_BS11_LI_PORT_1 = 0xad, + NM_ATT_BS11_LI_PORT_2 = 0xae, + + NM_ATT_BS11_L1_REM_ALM_TYPE = 0xb0, + NM_ATT_BS11_SW_LOAD_INTENDED = 0xbb, + NM_ATT_BS11_SW_LOAD_SAFETY = 0xbc, + NM_ATT_BS11_SW_LOAD_STORED = 0xbd, + + NM_ATT_BS11_VENDOR_NAME = 0xc1, + NM_ATT_BS11_HOPPING_MODE = 0xc5, + NM_ATT_BS11_LMT_LOGON_SESSION = 0xc6, + NM_ATT_BS11_LMT_LOGIN_TIME = 0xc7, + NM_ATT_BS11_LMT_USER_ACC_LEV = 0xc8, + NM_ATT_BS11_LMT_USER_NAME = 0xc9, + + NM_ATT_BS11_L1_CONTROL_TS = 0xd8, + NM_ATT_BS11_RADIO_MEAS_GRAN = 0xdc, /* in SACCH multiframes */ + NM_ATT_BS11_RADIO_MEAS_REP = 0xdd, + + NM_ATT_BS11_SH_LAPD_INT_TIMER = 0xe8, + + NM_ATT_BS11_BTS_STATE = 0xf0, + NM_ATT_BS11_E1_STATE = 0xf1, + NM_ATT_BS11_PLL = 0xf2, + NM_ATT_BS11_RX_OFFSET = 0xf3, + NM_ATT_BS11_ANT_TYPE = 0xf4, + NM_ATT_BS11_PLL_MODE = 0xfc, + NM_ATT_BS11_PASSWORD = 0xfd, +}; + +enum abis_nm_attr_ipa { + NM_ATT_IPACC_DST_IP = 0x80, + NM_ATT_IPACC_DST_IP_PORT = 0x81, + NM_ATT_IPACC_SSRC = 0x82, /* RTP Sync Source */ + NM_ATT_IPACC_RTP_PAYLD_TYPE = 0x83, + NM_ATT_IPACC_BASEB_ID = 0x84, + NM_ATT_IPACC_STREAM_ID = 0x85, + NM_ATT_IPACC_NV_FLAGS = 0x86, + NM_ATT_IPACC_FREQ_CTRL = 0x87, + NM_ATT_IPACC_PRIM_OML_CFG = 0x88, + NM_ATT_IPACC_SEC_OML_CFG = 0x89, + NM_ATT_IPACC_IP_IF_CFG = 0x8a, /* IP interface */ + NM_ATT_IPACC_IP_GW_CFG = 0x8b, /* IP gateway */ + NM_ATT_IPACC_IN_SERV_TIME = 0x8c, + NM_ATT_IPACC_TRX_BTS_ASS = 0x8d, + NM_ATT_IPACC_LOCATION = 0x8e, /* string describing location */ + NM_ATT_IPACC_PAGING_CFG = 0x8f, + NM_ATT_IPACC_FILE_DATA = 0x90, + NM_ATT_IPACC_UNIT_ID = 0x91, /* Site/BTS/TRX */ + NM_ATT_IPACC_PARENT_UNIT_ID = 0x92, + NM_ATT_IPACC_UNIT_NAME = 0x93, /* default: nbts- */ + NM_ATT_IPACC_SNMP_CFG = 0x94, + NM_ATT_IPACC_PRIM_OML_CFG_LIST = 0x95, + NM_ATT_IPACC_PRIM_OML_FB_TOUT = 0x96, /* fallback timeout */ + NM_ATT_IPACC_CUR_SW_CFG = 0x97, + NM_ATT_IPACC_TIMING_BUS = 0x98, + NM_ATT_IPACC_CGI = 0x99, /* Cell Global ID */ + NM_ATT_IPACC_RAC = 0x9a, + NM_ATT_IPACC_OBJ_VERSION = 0x9b, + NM_ATT_IPACC_GPRS_PAGING_CFG = 0x9c, + NM_ATT_IPACC_NSEI = 0x9d, + NM_ATT_IPACC_BVCI = 0x9e, + NM_ATT_IPACC_NSVCI = 0x9f, + NM_ATT_IPACC_NS_CFG = 0xa0, + NM_ATT_IPACC_BSSGP_CFG = 0xa1, + NM_ATT_IPACC_NS_LINK_CFG = 0xa2, + NM_ATT_IPACC_RLC_CFG = 0xa3, + NM_ATT_IPACC_ALM_THRESH_LIST = 0xa4, + NM_ATT_IPACC_MONIT_VAL_LIST = 0xa5, + NM_ATT_IPACC_TIB_CONTROL = 0xa6, + NM_ATT_IPACC_SUPP_FEATURES = 0xa7, + NM_ATT_IPACC_CODING_SCHEMES = 0xa8, + NM_ATT_IPACC_RLC_CFG_2 = 0xa9, + NM_ATT_IPACC_HEARTB_TOUT = 0xaa, + NM_ATT_IPACC_UPTIME = 0xab, + NM_ATT_IPACC_RLC_CFG_3 = 0xac, + NM_ATT_IPACC_SSL_CFG = 0xad, + NM_ATT_IPACC_SEC_POSSIBLE = 0xae, + NM_ATT_IPACC_IML_SSL_STATE = 0xaf, + NM_ATT_IPACC_REVOC_DATE = 0xb0, +}; + +/* Section 9.4.4: Administrative State */ +enum abis_nm_adm_state { + NM_STATE_LOCKED = 0x01, + NM_STATE_UNLOCKED = 0x02, + NM_STATE_SHUTDOWN = 0x03, + NM_STATE_NULL = 0xff, +}; + +/* Section 9.4.13: Channel Combination */ +enum abis_nm_chan_comb { + NM_CHANC_TCHFull = 0x00, + NM_CHANC_TCHHalf = 0x01, + NM_CHANC_TCHHalf2 = 0x02, + NM_CHANC_SDCCH = 0x03, + NM_CHANC_mainBCCH = 0x04, + NM_CHANC_BCCHComb = 0x05, + NM_CHANC_BCCH = 0x06, + NM_CHANC_BCCH_CBCH = 0x07, + NM_CHANC_SDCCH_CBCH = 0x08, +}; + +/* Section 9.4.16: Event Type */ +enum abis_nm_event_type { + NM_EVT_COMM_FAIL = 0x00, + NM_EVT_QOS_FAIL = 0x01, + NM_EVT_PROC_FAIL = 0x02, + NM_EVT_EQUIP_FAIL = 0x03, + NM_EVT_ENV_FAIL = 0x04, +}; + +/* Section: 9.4.63: Perceived Severity */ +enum abis_nm_severity { + NM_SEVER_CEASED = 0x00, + NM_SEVER_CRITICAL = 0x01, + NM_SEVER_MAJOR = 0x02, + NM_SEVER_MINOR = 0x03, + NM_SEVER_WARNING = 0x04, + NM_SEVER_INDETERMINATE = 0x05, +}; + +/* Section 9.4.43: Probable Cause Type */ +enum abis_nm_pcause_type { + NM_PCAUSE_T_X721 = 0x01, + NM_PCAUSE_T_GSM = 0x02, + NM_PCAUSE_T_MANUF = 0x03, +}; + +/* Section 9.4.36: NACK Causes */ +enum abis_nm_nack_cause { + /* General Nack Causes */ + NM_NACK_INCORR_STRUCT = 0x01, + NM_NACK_MSGTYPE_INVAL = 0x02, + NM_NACK_OBJCLASS_INVAL = 0x05, + NM_NACK_OBJCLASS_NOTSUPP = 0x06, + NM_NACK_BTSNR_UNKN = 0x07, + NM_NACK_TRXNR_UNKN = 0x08, + NM_NACK_OBJINST_UNKN = 0x09, + NM_NACK_ATTRID_INVAL = 0x0c, + NM_NACK_ATTRID_NOTSUPP = 0x0d, + NM_NACK_PARAM_RANGE = 0x0e, + NM_NACK_ATTRLIST_INCONSISTENT = 0x0f, + NM_NACK_SPEC_IMPL_NOTSUPP = 0x10, + NM_NACK_CANT_PERFORM = 0x11, + /* Specific Nack Causes */ + NM_NACK_RES_NOTIMPL = 0x19, + NM_NACK_RES_NOTAVAIL = 0x1a, + NM_NACK_FREQ_NOTAVAIL = 0x1b, + NM_NACK_TEST_NOTSUPP = 0x1c, + NM_NACK_CAPACITY_RESTR = 0x1d, + NM_NACK_PHYSCFG_NOTPERFORM = 0x1e, + NM_NACK_TEST_NOTINIT = 0x1f, + NM_NACK_PHYSCFG_NOTRESTORE = 0x20, + NM_NACK_TEST_NOSUCH = 0x21, + NM_NACK_TEST_NOSTOP = 0x22, + NM_NACK_MSGINCONSIST_PHYSCFG = 0x23, + NM_NACK_FILE_INCOMPLETE = 0x25, + NM_NACK_FILE_NOTAVAIL = 0x26, + NM_NACK_FILE_NOTACTIVATE = 0x27, + NM_NACK_REQ_NOT_GRANT = 0x28, + NM_NACK_WAIT = 0x29, + NM_NACK_NOTH_REPORT_EXIST = 0x2a, + NM_NACK_MEAS_NOTSUPP = 0x2b, + NM_NACK_MEAS_NOTSTART = 0x2c, +}; + +/* Section 9.4.1 */ +struct abis_nm_channel { + guint8 attrib; + guint8 bts_port; + guint8 timeslot; + guint8 subslot; +} __attribute__ ((packed)); + +/* Siemens BS-11 specific objects in the SienemsHW (0xA5) object class */ +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*/ +}; + +enum abis_bs11_trx_power { + BS11_TRX_POWER_GSM_2W = 0x06, + BS11_TRX_POWER_GSM_250mW= 0x07, + BS11_TRX_POWER_GSM_80mW = 0x08, + BS11_TRX_POWER_GSM_30mW = 0x09, + BS11_TRX_POWER_DCS_3W = 0x0a, + BS11_TRX_POWER_DCS_1W6 = 0x0b, + BS11_TRX_POWER_DCS_500mW= 0x0c, + BS11_TRX_POWER_DCS_160mW= 0x0d, +}; + +enum abis_bs11_li_pll_mode { + BS11_LI_PLL_LOCKED = 2, + BS11_LI_PLL_STANDALONE = 3, +}; + +enum abis_bs11_phase { + BS11_STATE_SOFTWARE_RQD = 0x01, + BS11_STATE_LOAD_SMU_INTENDED = 0x11, + BS11_STATE_LOAD_SMU_SAFETY = 0x21, + BS11_STATE_LOAD_FAILED = 0x31, + BS11_STATE_LOAD_DIAGNOSTIC = 0x41, + BS11_STATE_WARM_UP = 0x51, + BS11_STATE_WARM_UP_2 = 0x52, + BS11_STATE_WAIT_MIN_CFG = 0x62, + BS11_STATE_MAINTENANCE = 0x72, + BS11_STATE_LOAD_MBCCU = 0x92, + BS11_STATE_WAIT_MIN_CFG_2 = 0xA2, + BS11_STATE_NORMAL = 0x03, + BS11_STATE_ABIS_LOAD = 0x13, +}; + +/* From openbsc/include/openbsc/tlv.h */ +enum tlv_type { + TLV_TYPE_UNKNOWN, + TLV_TYPE_FIXED, + TLV_TYPE_T, + TLV_TYPE_TV, + TLV_TYPE_TLV, + TLV_TYPE_TL16V, + TLV_TYPE_TLV16, +}; + +struct tlv_def { + enum tlv_type type; + u_int8_t fixed_len; +}; + +struct tlv_definition { + struct tlv_def def[0xff]; +}; + +enum abis_nm_ipacc_test_no { + NM_IPACC_TESTNO_RLOOP_ANT = 0x01, + NM_IPACC_TESTNO_RLOOP_XCVR = 0x02, + NM_IPACC_TESTNO_FUNC_OBJ = 0x03, + NM_IPACC_TESTNO_CHAN_USAGE = 0x40, + NM_IPACC_TESTNO_BCCH_CHAN_USAGE = 0x41, + NM_IPACC_TESTNO_FREQ_SYNC = 0x42, + NM_IPACC_TESTNO_BCCH_INFO = 0x43, + NM_IPACC_TESTNO_TX_BEACON = 0x44, + NM_IPACC_TESTNO_SYSINFO_MONITOR = 0x45, + NM_IPACC_TESTNO_BCCCH_MONITOR = 0x46, +}; + +/* first byte after length inside NM_ATT_TEST_REPORT */ +enum abis_nm_ipacc_test_res { + NM_IPACC_TESTRES_SUCCESS = 0, + NM_IPACC_TESTRES_TIMEOUT = 1, + NM_IPACC_TESTRES_NO_CHANS = 2, + NM_IPACC_TESTRES_PARTIAL = 3, + NM_IPACC_TESTRES_STOPPED = 4, +}; + +/* internal IE inside NM_ATT_TEST_REPORT */ +enum abis_nm_ipacc_testres_ie { + NM_IPACC_TR_IE_FREQ_ERR_LIST = 3, + NM_IPACC_TR_IE_CHAN_USAGE = 4, + NM_IPACC_TR_IE_BCCH_INFO = 6, + NM_IPACC_TR_IE_RESULT_DETAILS = 8, + NM_IPACC_TR_IE_FREQ_ERR = 18, +}; + +#endif /* _NM_H */ openbsc-0.15.0/wireshark/0002-ericsson_rbs2409.patch000066400000000000000000001625501265565154000216610ustar00rootroot00000000000000From 6254ee454147eab6f6a51792544716472e20073f Mon Sep 17 00:00:00 2001 From: Holger Hans Peter Freyther Date: Tue, 11 Jan 2011 15:09:18 +0100 Subject: [PATCH 2/4] ericsson_rbs2409 Add Ericsson RBS2409 dissector --- epan/CMakeLists.txt | 2 + epan/dissectors/Makefile.common | 2 + epan/dissectors/packet-ehdlc.c | 319 +++++++ epan/dissectors/packet-gsm_abis_om2000.c | 1439 ++++++++++++++++++++++++++++++ epan/dissectors/packet-gsm_abis_oml.c | 21 + epan/dissectors/packet-l2tp.c | 7 + 6 files changed, 1790 insertions(+), 0 deletions(-) create mode 100644 epan/dissectors/packet-ehdlc.c create mode 100644 epan/dissectors/packet-gsm_abis_om2000.c Index: wireshark/epan/CMakeLists.txt =================================================================== --- wireshark.orig/epan/CMakeLists.txt 2011-09-06 13:37:42.000000000 +0200 +++ wireshark/epan/CMakeLists.txt 2011-09-06 13:53:22.000000000 +0200 @@ -180,6 +180,8 @@ dissectors/packet-dcerpc-eventlog.c dissectors/packet-dcerpc-lsa.c dissectors/packet-dcerpc-winreg.c + dissectors/packet-ehdlc.c + dissectors/packet-gsm_abis_om2000.c ) set(ASN1_DISSECTOR_SRC dissectors/packet-acp133.c Index: wireshark/epan/dissectors/Makefile.common =================================================================== --- wireshark.orig/epan/dissectors/Makefile.common 2011-09-06 13:37:42.000000000 +0200 +++ wireshark/epan/dissectors/Makefile.common 2011-09-06 13:53:22.000000000 +0200 @@ -67,6 +67,8 @@ packet-dcerpc-dnsserver.c \ packet-dcerpc-eventlog.c \ packet-dcerpc-lsa.c \ + packet-ehdlc.c \ + packet-gsm_abis_om2000.c \ packet-dcerpc-winreg.c # Index: wireshark/epan/dissectors/packet-ehdlc.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ wireshark/epan/dissectors/packet-ehdlc.c 2011-09-06 13:37:48.000000000 +0200 @@ -0,0 +1,321 @@ +/* packet-ehdlc.c + * Routines for packet dissection of Ericsson HDLC as used in A-bis over IP + * Copyright 2010 by Harald Welte + * + * $Id: packet-ehdlc.c 33767 2010-08-11 11:59:47Z etxrab $ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include +#include +#include + +/* Initialize the protocol and registered fields */ +static int proto_ehdlc = -1; + +static int hf_ehdlc_data_len = -1; +static int hf_ehdlc_protocol = -1; +static int hf_ehdlc_sapi = -1; +static int hf_ehdlc_c_r = -1; + +static int hf_ehdlc_xid_payload = -1; +static int hf_ehdlc_control = -1; + +static int hf_ehdlc_p = -1; +static int hf_ehdlc_f = -1; +static int hf_ehdlc_u_modifier_cmd = -1; +static int hf_ehdlc_u_modifier_resp = -1; +static int hf_ehdlc_ftype_s_u = -1; + +static int hf_ehdlc_n_r = -1; +static int hf_ehdlc_n_s = -1; +static int hf_ehdlc_p_ext = -1; +static int hf_ehdlc_f_ext = -1; +static int hf_ehdlc_s_ftype = -1; +static int hf_ehdlc_ftype_i = -1; +static int hf_ehdlc_ftype_s_u_ext = -1; + +/* Used only for U frames */ +static const xdlc_cf_items ehdlc_cf_items = { + NULL, + NULL, + &hf_ehdlc_p, + &hf_ehdlc_f, + NULL, + &hf_ehdlc_u_modifier_cmd, + &hf_ehdlc_u_modifier_resp, + NULL, + &hf_ehdlc_ftype_s_u +}; + +/* Used only for I and S frames */ +static const xdlc_cf_items ehdlc_cf_items_ext = { + &hf_ehdlc_n_r, + &hf_ehdlc_n_s, + &hf_ehdlc_p_ext, + &hf_ehdlc_f_ext, + &hf_ehdlc_s_ftype, + NULL, + NULL, + &hf_ehdlc_ftype_i, + &hf_ehdlc_ftype_s_u_ext, +}; + +/* Initialize the subtree pointers */ +static gint ett_ehdlc = -1; +static gint ett_ehdlc_control = -1; + +static const value_string ehdlc_protocol_vals[] = { + { 0x20, "RSL" }, + { 0xa0, "ACK" }, + { 0xc0, "OML" }, + { 0, NULL } +}; + +enum { + SUB_RSL, + SUB_OML, + SUB_DATA, + + SUB_MAX +}; + +static dissector_handle_t sub_handles[SUB_MAX]; + +/* Code to actually dissect the packets */ +static void +dissect_ehdlc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + gint remaining; + int offset = 4; + + col_set_str(pinfo->cinfo, COL_PROTOCOL, "EHDLC"); + col_clear(pinfo->cinfo, COL_INFO); + + while ((remaining = tvb_reported_length_remaining(tvb, offset)) > 0) { + proto_item *ti = NULL; + proto_tree *ehdlc_tree = NULL; + guint16 len, msg_type; + tvbuff_t *next_tvb; + guint16 control; + gboolean is_response = 0, is_extended = TRUE; + gint header_length = 2; /* Address + Length field */ + + msg_type = tvb_get_guint8(tvb, offset); + len = tvb_get_guint8(tvb, offset+1); +#if 0 + col_append_fstr(pinfo->cinfo, COL_INFO, "%s ", + val_to_str(msg_type, ehdlc_protocol_vals, + "unknown 0x%02x")); +#endif + if (tree) { + ti = proto_tree_add_protocol_format(tree, proto_ehdlc, + tvb, offset, len, + "Ericsson HDLC protocol, type: %s", + val_to_str(msg_type, ehdlc_protocol_vals, + "unknown 0x%02x")); + ehdlc_tree = proto_item_add_subtree(ti, ett_ehdlc); + proto_tree_add_item(ehdlc_tree, hf_ehdlc_protocol, + tvb, offset, 1, FALSE); +#if 0 + proto_tree_add_item(ehdlc_tree, hf_ehdlc_sapi, + tvb, offset, 1, FALSE); + proto_tree_add_item(ehdlc_tree, hf_ehdlc_c_r, + tvb, offset, 1, FALSE); +#endif + proto_tree_add_item(ehdlc_tree, hf_ehdlc_data_len, + tvb, offset+1, 1, FALSE); + } + + control = dissect_xdlc_control(tvb, offset+2, pinfo, ehdlc_tree, hf_ehdlc_control, + ett_ehdlc_control, &ehdlc_cf_items, &ehdlc_cf_items_ext, + NULL, NULL, is_response, is_extended, FALSE); + header_length += XDLC_CONTROL_LEN(control, is_extended); + + if (XDLC_IS_INFORMATION(control)) { + next_tvb = tvb_new_subset(tvb, offset+header_length, + len-header_length, len); + + switch (msg_type) { + case 0x20: + /* len == 4 seems to be some kind of ACK */ + if (len <= 4) + break; + call_dissector(sub_handles[SUB_RSL], next_tvb, pinfo, tree); + break; + case 0xbc: + case 0xdc: + case 0xa0: + case 0xc0: + /* len == 4 seems to be some kind of ACK */ + if (len <= 4) + break; + call_dissector(sub_handles[SUB_OML], next_tvb, pinfo, tree); + break; + default: + call_dissector(sub_handles[SUB_DATA], next_tvb, pinfo, tree); + break; + } + } else if (control == XDLC_U | XDLC_XID) { + /* XID is formatted like ISO 8885, typically we see + * something like + * 82 format identifier + * 80 group identifier + * 00 09 length + * 07 01 05 Window Size Tx + * 09 01 04 Ack Timer (msec) + * 08 01 05 Window Size Rx */ + proto_tree_add_item(ehdlc_tree, hf_ehdlc_xid_payload, + tvb, offset+header_length, + len-header_length, FALSE); + } + + if (len == 0) + len = 1; + offset += len; + } +} + +void proto_register_ehdlc(void) +{ + static hf_register_info hf[] = { + { &hf_ehdlc_data_len, + { "DataLen", "ehdlc.data_len", + FT_UINT8, BASE_DEC, NULL, 0x0, + "The length of the data (in bytes)", HFILL } + }, + { &hf_ehdlc_protocol, + { "Protocol", "ehdlc.protocol", + FT_UINT8, BASE_HEX, VALS(ehdlc_protocol_vals), 0x0, + "The HDLC Sub-Protocol", HFILL } + }, + { &hf_ehdlc_sapi, + { "SAPI", "ehdlc.sapi", + FT_UINT8, BASE_DEC, NULL, 0x1f, + NULL, HFILL } + }, + { &hf_ehdlc_c_r, + { "C/R", "ehdlc.c_r", + FT_UINT8, BASE_HEX, NULL, 0x20, + NULL, HFILL } + }, + { &hf_ehdlc_xid_payload, + { "XID Payload", "ehdlc.xid_payload", + FT_BYTES, BASE_NONE, NULL, 0, + NULL, HFILL } + }, + { &hf_ehdlc_control, + { "Control Field", "ehdlc.control", + FT_UINT16, BASE_HEX, NULL, 0, + NULL, HFILL } + }, + { &hf_ehdlc_n_r, + { "N(R)", "ehdlc.control.n_r", + FT_UINT16, BASE_DEC, NULL, XDLC_N_R_EXT_MASK, + NULL, HFILL } + }, + { &hf_ehdlc_n_s, + { "N(S)", "ehdlc.control.n_s", + FT_UINT16, BASE_DEC, NULL, XDLC_N_S_EXT_MASK, + NULL, HFILL } + }, + { &hf_ehdlc_p, + { "Poll", "ehdlc.control.p", + FT_BOOLEAN, 8, TFS(&tfs_set_notset), XDLC_P_F, + NULL, HFILL } + }, + { &hf_ehdlc_p_ext, + { "Poll", "ehdlc.control.p", + FT_BOOLEAN, 16, TFS(&tfs_set_notset), XDLC_P_F_EXT, + NULL, HFILL } + }, + { &hf_ehdlc_f, + { "Final", "ehdlc.control.f", + FT_BOOLEAN, 8, TFS(&tfs_set_notset), XDLC_P_F, + NULL, HFILL } + }, + { &hf_ehdlc_f_ext, + { "Final", "ehdlc.control.f", + FT_BOOLEAN, 16, TFS(&tfs_set_notset), XDLC_P_F_EXT, + NULL, HFILL } + }, + { &hf_ehdlc_s_ftype, + { "Supervisory frame type", "ehdlc.control.s_ftype", + FT_UINT16, BASE_HEX, VALS(stype_vals), XDLC_S_FTYPE_MASK, + NULL, HFILL } + }, + { &hf_ehdlc_u_modifier_cmd, + { "Command", "ehdlc.control.u_modifier_cmd", + FT_UINT8, BASE_HEX, VALS(modifier_vals_cmd), XDLC_U_MODIFIER_MASK, + NULL, HFILL } + }, + { &hf_ehdlc_u_modifier_resp, + { "Response", "ehdlc.control.u_modifier_resp", + FT_UINT8, BASE_HEX, VALS(modifier_vals_resp), XDLC_U_MODIFIER_MASK, + NULL, HFILL } + }, + { &hf_ehdlc_ftype_i, + { "Frame Type", "ehdlc.control.ftype", + FT_UINT16, BASE_HEX, VALS(ftype_vals), XDLC_I_MASK, + NULL, HFILL } + }, + { &hf_ehdlc_ftype_s_u, + { "Frame Type", "ehdlc.control.ftype", + FT_UINT8, BASE_HEX, VALS(ftype_vals), XDLC_S_U_MASK, + NULL, HFILL } + }, + { &hf_ehdlc_ftype_s_u_ext, + { "Frame Type", "ehdlc.control.ftype", + FT_UINT16, BASE_HEX, VALS(ftype_vals), XDLC_S_U_MASK, + NULL, HFILL } + }, + }; + + static gint *ett[] = { + &ett_ehdlc, + &ett_ehdlc_control, + }; + + proto_ehdlc = + proto_register_protocol("Ericsson HDLC", + "Ericsson HDLC as used in A-bis over IP", "ehdlc"); + + proto_register_field_array(proto_ehdlc, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); + + register_dissector("ehdlc", dissect_ehdlc, proto_ehdlc); +} + +void proto_reg_handoff_ehdlc(void) +{ + dissector_handle_t ehdlc_handle; + + sub_handles[SUB_RSL] = find_dissector("gsm_abis_rsl"); + sub_handles[SUB_OML] = find_dissector("gsm_abis_oml"); + sub_handles[SUB_DATA] = find_dissector("data"); + + ehdlc_handle = create_dissector_handle(dissect_ehdlc, proto_ehdlc); +} Index: wireshark/epan/dissectors/packet-gsm_abis_om2000.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ wireshark/epan/dissectors/packet-gsm_abis_om2000.c 2011-09-06 13:37:48.000000000 +0200 @@ -0,0 +1,1439 @@ +/* packet-abis_om2000.c + * Routines for packet dissection of Ericsson A-bis OML (OM 2000) + * Copyright 2010 by Harald Welte + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include +#include +#include +#include + +#include "packet-gsm_abis_oml.h" +#include "packet-gsm_a_common.h" + +#include + +/* initialize the protocol and registered fields */ +static int proto_abis_om2000 = -1; + +static int hf_om2k_msg_code = -1; +static int hf_om2k_mo_if = -1; +static int hf_om2k_mo_class = -1; +static int hf_om2k_mo_instance = -1; + +static int hf_om2k_aip = -1; +static int hf_om2k_oip = -1; +static int hf_om2k_comb = -1; +static int hf_om2k_ts = -1; +static int hf_om2k_hsn = -1; +static int hf_om2k_maio = -1; +static int hf_om2k_bsic = -1; +static int hf_om2k_diversity = -1; +static int hf_om2k_fn_offs = -1; +static int hf_om2k_ext_range = -1; +static int hf_om2k_irc = -1; +static int hf_om2k_bs_pa_mfrms = -1; +static int hf_om2k_bs_ag_blks_res= -1; +static int hf_om2k_drx_dev_max = -1; +static int hf_om2k_cr = -1; +static int hf_om2k_ipt3 = -1; +static int hf_om2k_aop = -1; +static int hf_om2k_t3105 = -1; +static int hf_om2k_ny1 = -1; +static int hf_om2k_cbi = -1; +static int hf_om2k_tsc = -1; +static int hf_om2k_icm = -1; +static int hf_om2k_tta = -1; +static int hf_om2k_icm_cr = -1; +static int hf_om2k_lsc_fm = -1; +static int hf_om2k_lsc_lsi = -1; +static int hf_om2k_lsc_lsa = -1; +static int hf_om2k_ls_ft = -1; +static int hf_om2k_cst = -1; +static int hf_om2k_ea = -1; +static int hf_om2k_unknown_tag = -1; +static int hf_om2k_unknown_val = -1; +static int hf_om2k_nom_pwr = -1; +static int hf_om2k_fill_mark = -1; +static int hf_om2k_bcc = -1; +static int hf_om2k_mo_state = -1; +static int hf_om2k_la_state = -1; +static int hf_om2k_tsn_state = -1; +static int hf_om2k_bts_manuf = -1; +static int hf_om2k_bts_gen = -1; +static int hf_om2k_bts_rev = -1; +static int hf_om2k_bts_var = -1; +static int hf_om2k_brr = -1; +static int hf_om2k_bfr = -1; +static int hf_om2k_hwinfo_sig = -1; +static int hf_om2k_capa_sig = -1; +static int hf_om2k_file_rev = -1; +static int hf_om2k_filerel_ilr = -1; +static int hf_om2k_filerel_cur = -1; +static int hf_om2k_filerel_other = -1; +static int hf_om2k_cal_time = -1; +static int hf_om2k_list_nr = -1; +static int hf_om2k_list_nr_end = -1; +static int hf_om2k_isl = -1; +static int hf_om2k_isl_icp1 = -1; +static int hf_om2k_isl_icp2 = -1; +static int hf_om2k_isl_ci = -1; +static int hf_om2k_conl = -1; +static int hf_om2k_conl_nr_cgs = -1; +static int hf_om2k_conl_nr_cps_cg = -1; +static int hf_om2k_conl_ccp = -1; +static int hf_om2k_conl_ci = -1; +static int hf_om2k_conl_tag = -1; +static int hf_om2k_conl_tei = -1; +static int hf_om2k_tf_mode = -1; +static int hf_om2k_tf_fs_offset = -1; + + + +/* initialize the subtree pointers */ +static int ett_om2000 = -1; +static int ett_om2k_mo = -1; +static int ett_om2k_isl = -1; +static int ett_om2k_conl = -1; + +static proto_tree *top_tree; + +static const value_string om2k_msgcode_vals[] = { + { 0x0000, "Abort SP Command" }, + { 0x0002, "Abort SP Complete" }, + { 0x0004, "Alarm Report ACK" }, + { 0x0005, "Alarm Report NACK" }, + { 0x0006, "Alarm Report" }, + { 0x0008, "Alarm Status Request" }, + { 0x000a, "Alarm Status Request Accept" }, + { 0x000b, "Alarm Status Request Reject" }, + { 0x000c, "Alarm Status Result ACK" }, + { 0x000d, "Alarm Status Result NACK" }, + { 0x000e, "Alarm Status Result" }, + { 0x0010, "Calendar Time Response" }, + { 0x0011, "Calendar Time Reject" }, + { 0x0012, "Calendar Time Request" }, + { 0x0014, "CON Configuration Request" }, + { 0x0016, "CON Configuration Request Accept" }, + { 0x0017, "CON Configuration Request Reject" }, + { 0x0018, "CON Configuration Result ACK" }, + { 0x0019, "CON Configuration Result NACK" }, + { 0x001a, "CON Configuration Result" }, + { 0x001c, "Connect Command" }, + { 0x001e, "Connect Complete" }, + { 0x001f, "Connect Rejecte" }, + { 0x0028, "Disable Request" }, + { 0x002a, "Disable Request Accept" }, + { 0x002b, "Disable Request Reject" }, + { 0x002c, "Disable Result ACK" }, + { 0x002d, "Disable Result NACK" }, + { 0x002e, "Disable Result" }, + { 0x0030, "Disconnect Command" }, + { 0x0032, "Disconnect Complete" }, + { 0x0033, "Disconnect Reject" }, + { 0x0034, "Enable Request" }, + { 0x0036, "Enable Request Accept" }, + { 0x0037, "Enable Request Reject" }, + { 0x0038, "Enable Result ACK" }, + { 0x0039, "Enable Result NACK" }, + { 0x003a, "Enable Result" }, + { 0x003c, "Escape Downlink Normal" }, + { 0x003d, "Escape Downlink NACK" }, + { 0x003e, "Escape Uplink Normal" }, + { 0x003f, "Escape Uplink NACK" }, + { 0x0040, "Fault Report ACK" }, + { 0x0041, "Fault Report NACK" }, + { 0x0042, "Fault Report" }, + { 0x0044, "File Package End Command" }, + { 0x0046, "File Package End Result" }, + { 0x0047, "File Package End Reject" }, + { 0x0048, "File Relation Request" }, + { 0x004a, "File Relation Response" }, + { 0x004b, "File Relation Request Reject" }, + { 0x004c, "File Segment Transfer" }, + { 0x004e, "File Segment Transfer Complete" }, + { 0x004f, "File Segment Transfer Reject" }, + { 0x0050, "HW Information Request" }, + { 0x0052, "HW Information Request Accept" }, + { 0x0053, "HW Information Request Reject" }, + { 0x0054, "HW Information Result ACK" }, + { 0x0055, "HW Information Result NACK" }, + { 0x0056, "HW Information Result" }, + { 0x0060, "IS Configuration Request" }, + { 0x0062, "IS Configuration Request Accept" }, + { 0x0063, "IS Configuration Request Reject" }, + { 0x0064, "IS Configuration Result ACK" }, + { 0x0065, "IS Configuration Result NACK" }, + { 0x0066, "IS Configuration Result" }, + { 0x0068, "Load Data End" }, + { 0x006a, "Load Data End Result" }, + { 0x006b, "Load Data End Reject" }, + { 0x006c, "Load Data Init" }, + { 0x006e, "Load Data Init Accept" }, + { 0x006f, "Load Data Init Reject" }, + { 0x0070, "Loop Control Command" }, + { 0x0072, "Loop Control Complete" }, + { 0x0073, "Loop Control Reject" }, + { 0x0074, "Operational Information" }, + { 0x0076, "Operational Information Accept" }, + { 0x0077, "Operational Information Reject" }, + { 0x0078, "Reset Command" }, + { 0x007a, "Reset Complete" }, + { 0x007b, "Reset Reject" }, + { 0x007c, "RX Configuration Request" }, + { 0x007e, "RX Configuration Request Accept" }, + { 0x007f, "RX Configuration Request Reject" }, + { 0x0080, "RX Configuration Result ACK" }, + { 0x0081, "RX Configuration Result NACK" }, + { 0x0082, "RX Configuration Result" }, + { 0x0084, "Start Request" }, + { 0x0086, "Start Request Accept" }, + { 0x0087, "Start Request Reject" }, + { 0x0088, "Start Result ACK" }, + { 0x0089, "Start Result NACK" }, + { 0x008a, "Start Result" }, + { 0x008c, "Status Request" }, + { 0x008e, "Status Response" }, + { 0x008f, "Status Reject" }, + { 0x0094, "Test Request" }, + { 0x0096, "Test Request Accept" }, + { 0x0097, "Test Request Reject" }, + { 0x0098, "Test Result ACK" }, + { 0x0099, "Test Result NACK" }, + { 0x009a, "Test Result" }, + { 0x00a0, "TF Configuration Request" }, + { 0x00a2, "TF Configuration Request Accept" }, + { 0x00a3, "TF Configuration Request Reject" }, + { 0x00a4, "TF Configuration Result ACK" }, + { 0x00a5, "TF Configuration Result NACK" }, + { 0x00a6, "TF Configuration Result" }, + { 0x00a8, "TS Configuration Request" }, + { 0x00aa, "TS Configuration Request Accept" }, + { 0x00ab, "TS Configuration Request Reject" }, + { 0x00ac, "TS Configuration Result ACK" }, + { 0x00ad, "TS Configuration Result NACK" }, + { 0x00ae, "TS Configuration Result" }, + { 0x00b0, "TX Configuration Request" }, + { 0x00b2, "TX Configuration Request Accept" }, + { 0x00b3, "TX Configuration Request Reject" }, + { 0x00b4, "TX Configuration Result ACK" }, + { 0x00b5, "TX Configuration Result NACK" }, + { 0x00b6, "TX Configuration Result" }, + { 0x00bc, "DIP Alarm Report ACK" }, + { 0x00bd, "DIP Alarm Report NACK" }, + { 0x00be, "DIP Alarm Report" }, + { 0x00c0, "DIP Alarm Status Request" }, + { 0x00c2, "DIP Alarm Status Response" }, + { 0x00c3, "DIP Alarm Status Reject" }, + { 0x00c4, "DIP Quality Report I ACK" }, + { 0x00c5, "DIP Quality Report I NACK" }, + { 0x00c6, "DIP Quality Report I" }, + { 0x00c8, "DIP Quality Report II ACK" }, + { 0x00c9, "DIP Quality Report II NACK" }, + { 0x00ca, "DIP Quality Report II" }, + { 0x00dc, "DP Configuration Request" }, + { 0x00de, "DP Configuration Request Accept" }, + { 0x00df, "DP Configuration Request Reject" }, + { 0x00e0, "DP Configuration Result ACK" }, + { 0x00e1, "DP Configuration Result NACK" }, + { 0x00e2, "DP Configuration Result" }, + { 0x00e4, "Capabilities HW Info Report ACK" }, + { 0x00e5, "Capabilities HW Info Report NACK" }, + { 0x00e6, "Capabilities HW Info Report" }, + { 0x00e8, "Capabilities Request" }, + { 0x00ea, "Capabilities Request Accept" }, + { 0x00eb, "Capabilities Request Reject" }, + { 0x00ec, "Capabilities Result ACK" }, + { 0x00ed, "Capabilities Result NACK" }, + { 0x00ee, "Capabilities Result" }, + { 0x00f0, "FM Configuration Request" }, + { 0x00f2, "FM Configuration Request Accept" }, + { 0x00f3, "FM Configuration Request Reject" }, + { 0x00f4, "FM Configuration Result ACK" }, + { 0x00f5, "FM Configuration Result NACK" }, + { 0x00f6, "FM Configuration Result" }, + { 0x00f8, "FM Report Request" }, + { 0x00fa, "FM Report Response" }, + { 0x00fb, "FM Report Reject" }, + { 0x00fc, "FM Start Command" }, + { 0x00fe, "FM Start Complete" }, + { 0x00ff, "FM Start Reject" }, + { 0x0100, "FM Stop Command" }, + { 0x0102, "FM Stop Complete" }, + { 0x0103, "FM Stop Reject" }, + { 0x0104, "Negotiation Request ACK" }, + { 0x0105, "Negotiation Request NACK" }, + { 0x0106, "Negotiation Request" }, + { 0x0108, "BTS Initiated Request ACK" }, + { 0x0109, "BTS Initiated Request NACK" }, + { 0x010a, "BTS Initiated Request" }, + { 0x010c, "Radio Channels Release Command" }, + { 0x010e, "Radio Channels Release Complete" }, + { 0x010f, "Radio Channels Release Reject" }, + { 0x0118, "Feature Control Command" }, + { 0x011a, "Feature Control Complete" }, + { 0x011b, "Feature Control Reject" }, + + { 0, NULL } +}; + +/* TS 12.21 Section 9.4: Attributes */ +static const value_string om2k_attr_vals[] = { + { 0x00, "Accordance indication" }, + { 0x01, "Alarm Id" }, + { 0x02, "Alarm Data" }, + { 0x03, "Alarm Severity" }, + { 0x04, "Alarm Status" }, + { 0x05, "Alarm Status Type" }, + { 0x06, "BCC" }, + { 0x07, "BS_AG_BKS_RES" }, + { 0x09, "BSIC" }, + { 0x0a, "BA_PA_MFRMS" }, + { 0x0b, "CBCH Indicator" }, + { 0x0c, "CCCH Options" }, + { 0x0d, "Calendar Time" }, + { 0x0f, "Channel Combination" }, + { 0x10, "CON Connection List" }, + { 0x11, "Data End Indication" }, + { 0x12, "DRX_DEV_MAX" }, + { 0x13, "End List Number" }, + { 0x14, "External Condition Map Class 1" }, + { 0x15, "External Condition Map Class 2" }, + { 0x16, "File Relation Indication" }, + { 0x17, "File Revision" }, + { 0x18, "File Segment Data" }, + { 0x19, "File Segment Length" }, + { 0x1a, "File Segment Sequence Number" }, + { 0x1b, "File Size" }, + { 0x1c, "Filling Marker" }, + { 0x1d, "FN Offset" }, + { 0x1e, "Frequency List" }, + { 0x1f, "Frequency Specifier RX" }, + { 0x20, "Frequency Specifier TX" }, + { 0x21, "HSN" }, + { 0x22, "ICM Indicator" }, + { 0x23, "Internal Fault Map Class 1A" }, + { 0x24, "Internal Fault Map Class 1B" }, + { 0x25, "Internal Fault Map Class 2A" }, + { 0x26, "Internal Fault Map Class 2A Extension" }, + { 0x27, "IS Connection List" }, + { 0x28, "List Number" }, + { 0x29, "File Package State Indication" }, + { 0x2a, "Local Access State" }, + { 0x2b, "MAIO" }, + { 0x2c, "MO State" }, + { 0x2d, "Ny1" }, + { 0x2e, "Operational Information" }, + { 0x2f, "Power" }, + { 0x30, "RU Position Data" }, + { 0x31, "Protocol Error" }, + { 0x32, "Reason Code" }, + { 0x33, "Receiver Diversity" }, + { 0x34, "Replacement Unit Map" }, + { 0x35, "Result Code" }, + { 0x36, "RU Revision Data" }, + { 0x38, "T3105" }, + { 0x39, "Test Loop Setting" }, + { 0x3a, "TF Mode" }, + { 0x3b, "TF Compensation Value" }, + { 0x3c, "Time Slot Number" }, + { 0x3d, "TSC" }, + { 0x3e, "RU Logical Id" }, + { 0x3f, "RU Serial Number Data" }, + { 0x40, "BTS Version" }, + { 0x41, "OML IWD Version" }, + { 0x42, "RWL IWD Version" }, + { 0x43, "OML Function Map 1" }, + { 0x44, "OML Function Map 2" }, + { 0x45, "RSL Function Map 1" }, + { 0x46, "RSL Function Map 2" }, + { 0x47, "Extended Range Indicator" }, + { 0x48, "Request Indicators" }, + { 0x49, "DIP Alarm Condition Map" }, + { 0x4a, "ES Incoming" }, + { 0x4b, "ES Outgoing" }, + { 0x4e, "SES Incoming" }, + { 0x4f, "SES Outgoing" }, + { 0x50, "Replacement Unit Map Extension" }, + { 0x52, "UAS Incoming" }, + { 0x53, "UAS Outgoing" }, + { 0x58, "DF Incoming" }, + { 0x5a, "DF Outgoing" }, + { 0x5c, "SF" }, + { 0x60, "S Bits Setting" }, + { 0x61, "CRC-4 Use Option" }, + { 0x62, "T Parameter" }, + { 0x63, "N Parameter" }, + { 0x64, "N1 Parameter" }, + { 0x65, "N3 Parameter" }, + { 0x66, "N4 Parameter" }, + { 0x67, "P Parameter" }, + { 0x68, "Q Parameter" }, + { 0x69, "BI_Q1" }, + { 0x6a, "BI_Q2" }, + { 0x74, "ICM Boundary Parameters" }, + { 0x77, "AFT" }, + { 0x78, "AFT RAI" }, + { 0x79, "Link Supervision Control" }, + { 0x7a, "Link Supervision Filtering Time" }, + { 0x7b, "Call Supervision Time" }, + { 0x7c, "Interval Length UAS Incoming" }, + { 0x7d, "Interval Length UAS Outgoing" }, + { 0x7e, "ICM Channel Rate" }, + { 0x7f, "Attribute Identifier" }, + { 0x80, "FM Frequency List" }, + { 0x81, "FM Frequency Report" }, + { 0x82, "FM Percentile" }, + { 0x83, "FM Clear Indication" }, + { 0x84, "HW Info Signature" }, + { 0x85, "MO Record" }, + { 0x86, "TF Synchronisation Source" }, + { 0x87, "TTA" }, + { 0x88, "End Segment Number" }, + { 0x89, "Segment Number" }, + { 0x8a, "Capabilities Signature" }, + { 0x8c, "File Relation List" }, + { 0x90, "Negotiation Record I" }, + { 0x91, "Negotiation Record II" }, + { 0x92, "Encryption Algorithm" }, + { 0x94, "Interference Rejection Combining" }, + { 0x95, "Dedication Information" }, + { 0x97, "Feature Code" }, + { 0x98, "FS Offset" }, + { 0x99, "ESB Timeslot" }, + { 0x9a, "Master TG Instance" }, + { 0x9b, "Master TX Chain Delay" }, + { 0x9c, "External Condition Class 2 Extension" }, + { 0x9d, "TSs MO State" }, + { 0, NULL } +}; + +static const value_string om2k_diversity_vals[] = { + { 0x01, "B receiver side" }, + { 0x02, "A receiver side" }, + { 0x03, "A+B receiver sides" }, + { 0x04, "A+B+C+D receiver sides" }, + { 0, NULL } +}; + +static const value_string om2k_oip_vals[] = { + { 0x00, "Not Operational" }, + { 0x01, "Operational" }, + { 0, NULL } +}; + +static const value_string om2k_aip_vals[] = { + { 0x00, "Data according to request" }, + { 0x01, "Data not according to request" }, + { 0x02, "Inconsistent MO data" }, + { 0x03, "Capability constraint violation" }, + { 0, NULL } +}; + +static const value_string om2k_comb_vals[] = { + { 0x03, "SDCCH/8 + SACCH/C8" }, + { 0x04, "BCCH, non-combined" }, + { 0x05, "BCCH, combined (SDCCH/4)" }, + { 0x08, "TCH Type, unspecified" }, + { 0, NULL } +}; + +static const value_string om2k_icmcr_vals[] = { + { 0x00, "ICM as per TCH/F" }, + { 0x01, "ICM as per TCH/H(0 and 1)" }, + { 0, NULL } +}; + +static const value_string om2k_ea_vals[] = { + { 0x00, "A5/1 and A5/2" }, + { 0x01, "A5/2 only" }, + { 0, NULL } +}; + +static const value_string om2k_fill_vals[] = { + { 0x00, "Filling" }, + { 0x01, "No filling" }, + { 0, NULL } +}; + +static const value_string om2k_mo_state_vals[] = { + { 0x00, "RESET" }, + { 0x01, "STARTED" }, + { 0x02, "ENABLED" }, + { 0x03, "DISABLED" }, + { 0, NULL } +}; + +static const value_string om2k_la_state_vals[] = { + { 0x00, "LOCALLY CONNECTED" }, + { 0x01, "LOCALLY DISCONNECTED" }, + { 0, NULL } +}; + +static const value_string filerel_state_vals[] = { + { 0x00, "Not known in current state (unknown file)" }, + { 0x01, "alllowed, already loaded" }, + { 0x02, "allowed, not loaded" }, + { 0x03, "not allowed" }, + { 0, NULL } +}; + +static const value_string om2k_mo_class_short_vals[] = { + { 0x01, "TRXC" }, + { 0x03, "TS" }, + { 0x04, "TF" }, + { 0x05, "IS" }, + { 0x06, "CON" }, + { 0x0a, "CF" }, + { 0x0b, "TX" }, + { 0x0c, "RX" }, + { 0, NULL } +}; + +static const value_string om2k_mo_class_vals[] = { + { 0x01, "TRXC (TRX Controller)" }, + { 0x03, "TS (Timeslot)" }, + { 0x04, "TF (Timing Function)" }, + { 0x05, "IS (Interface Switch)" }, + { 0x06, "CON (Concentrator)" }, + { 0x0a, "CF (Central Function)" }, + { 0x0b, "TX (Transmitter)" }, + { 0x0c, "RX (Receiver)" }, + { 0, NULL } +}; + +static const value_string om2k_tf_mode_vals[] = { + { 0x00, "Master" }, + { 0x01, "Standalone" }, + { 0x02, "Slave" }, + { 0xff, "Not defined" }, + { 0, NULL } +}; + +static gint +dissect_tss_mo_state(tvbuff_t *tvb, gint offset, packet_info *pinfo, + proto_tree *tree) +{ + guint8 tmp; + guint i = 0; + + for (i = 0; i < 8; i+= 2) { + tmp = tvb_get_guint8(tvb, offset); + proto_tree_add_uint_format(tree, hf_om2k_tsn_state, tvb, offset, 1, tmp & 0xf, + "Timslot %u MO State: %s", i, + val_to_str(tmp & 0xf, om2k_mo_state_vals, "unknown (%02d)")); + proto_tree_add_uint_format(tree, hf_om2k_tsn_state, tvb, offset, 1, tmp >> 4, + "Timslot %u MO State: %s", i+1, + val_to_str(tmp >> 4, om2k_mo_state_vals, "unknown (%02d)")); + offset++; + } + + return offset; +} + + +static gint +dissect_om2k_time(tvbuff_t *tvb, gint offset, proto_tree *tree) +{ + nstime_t tmptime; + time_t tval; + struct tm _time; + + _time.tm_year = 100 + tvb_get_guint8(tvb, offset++); + _time.tm_mon = tvb_get_guint8(tvb, offset++) -1; + _time.tm_mday = tvb_get_guint8(tvb, offset++); + _time.tm_hour = tvb_get_guint8(tvb, offset++); + _time.tm_min = tvb_get_guint8(tvb, offset++); + _time.tm_sec = tvb_get_guint8(tvb, offset++); + _time.tm_isdst = -1; + + tval = mktime(&_time); + tmptime.secs = tval; + tmptime.nsecs = 0; + + proto_tree_add_time(tree, hf_om2k_cal_time, tvb, offset, 6, + &tmptime); + return 6; +} + +static gint +dissect_om2k_attr_unkn(tvbuff_t *tvb, gint offset, gint len, gint iei, proto_tree *tree) +{ + proto_tree_add_bytes_format(tree, hf_om2k_unknown_val, tvb, + offset, len, tvb_get_ptr(tvb, offset, len), + "%s: %s", + val_to_str(iei, om2k_attr_vals, "0x%02x"), + tvb_bytes_to_str(tvb, offset, len)); + return len; +} + +static gint +dissect_om2k_is_list(tvbuff_t *tvb, gint base_offset, proto_tree *tree) +{ + gint offset = base_offset; + proto_item *ti; + proto_tree *isl_tree; + guint8 len = tvb_get_guint8(tvb, offset++); + + ti = proto_tree_add_item(tree, hf_om2k_isl, tvb, offset, len, FALSE); + isl_tree = proto_item_add_subtree(ti, ett_om2k_isl); + + while (offset < base_offset + len) { + proto_tree_add_item(isl_tree, hf_om2k_isl_icp1, tvb, + offset, 2, FALSE); + offset += 2; + proto_tree_add_item(isl_tree, hf_om2k_isl_icp2, tvb, + offset, 2, FALSE); + offset += 2; + proto_tree_add_item(isl_tree, hf_om2k_isl_ci, tvb, + offset++, 1, FALSE); + } + return offset - base_offset; +} + +static gint +dissect_om2k_con_list(tvbuff_t *tvb, gint base_offset, proto_tree *tree) +{ + gint offset = base_offset; + proto_item *ti; + proto_tree *conl_tree; + guint8 len = tvb_get_guint8(tvb, offset++); + + ti = proto_tree_add_item(tree, hf_om2k_conl, tvb, offset, len, FALSE); + conl_tree = proto_item_add_subtree(ti, ett_om2k_conl); + + proto_tree_add_item(conl_tree, hf_om2k_conl_nr_cgs, tvb, + offset++, 1, FALSE); + + while (offset < base_offset + len) { + guint8 nr_cps_cg = tvb_get_guint8(tvb, offset); + proto_tree_add_item(conl_tree, hf_om2k_conl_nr_cps_cg, tvb, + offset++, 1, FALSE); + while (nr_cps_cg--) { + proto_tree_add_item(conl_tree, hf_om2k_conl_ccp, tvb, + offset, 2, FALSE); + offset += 2; + proto_tree_add_item(conl_tree, hf_om2k_conl_ci, tvb, + offset++, 1, FALSE); + proto_tree_add_item(conl_tree, hf_om2k_conl_tag, tvb, + offset++, 1, FALSE); + proto_tree_add_item(conl_tree, hf_om2k_conl_tei, tvb, + offset++, 1, FALSE); + } + } + return offset - base_offset; +} + + +static gint +dissect_om2k_attrs(tvbuff_t *tvb, gint base_offs, packet_info *pinfo, + proto_tree *tree) +{ + int offset = base_offs; + + while (tvb_reported_length_remaining(tvb, offset) != 0) { + guint8 iei = tvb_get_guint8(tvb, offset++); + guint8 len, tmp; + switch (iei) { + case 0x00: /* Accordance Information */ + proto_tree_add_item(tree, hf_om2k_aip, tvb, + offset++, 1, FALSE); + break; + case 0x06: /* BCC */ + proto_tree_add_item(tree, hf_om2k_bcc, tvb, + offset++, 1, FALSE); + break; + case 0x07: /* BS_AG_BLKS_RES */ + proto_tree_add_item(tree, hf_om2k_bs_ag_blks_res, tvb, + offset++, 1, FALSE); + break; + case 0x09: /* BSIC */ + proto_tree_add_item(tree, hf_om2k_bsic, tvb, + offset++, 1, FALSE); + break; + case 0x0a: /* BS_PA_MFRMS */ + proto_tree_add_item(tree, hf_om2k_bs_pa_mfrms, tvb, + offset++, 1, FALSE); + break; + case 0x0b: /* CBCH indicator */ + proto_tree_add_item(tree, hf_om2k_cbi, tvb, + offset++, 1, FALSE); + break; + case 0x0c: /* CCCH Options */ + tmp = tvb_get_guint8(tvb, offset); + proto_tree_add_item(tree, hf_om2k_cr, tvb, + offset, 1, FALSE); + proto_tree_add_item(tree, hf_om2k_ipt3, tvb, + offset, 1, FALSE); + proto_tree_add_uint(tree, hf_om2k_aop, tvb, + offset, 1, (tmp & 0x3f) >> 2); + offset++; + break; + case 0x0d: /* Calendar Time */ + offset += dissect_om2k_time(tvb, offset, tree); + break; + case 0x0f: /* Combination */ + proto_tree_add_item(tree, hf_om2k_comb, tvb, + offset++, 1, FALSE); + break; + case 0x10: /* CON Connection List */ + offset += dissect_om2k_con_list(tvb, offset, tree); + break; + case 0x12: /* DRX_DEV_MAX */ + proto_tree_add_item(tree, hf_om2k_drx_dev_max, tvb, + offset++, 1, FALSE); + break; + case 0x13: /* End List Number */ + proto_tree_add_item(tree, hf_om2k_list_nr_end, tvb, + offset++, 1, FALSE); + break; + case 0x14: /* External Condition Map Class 1 */ + /* FIXME */ + case 0x15: /* External Condition Map Class 2 */ + /* FIXME */ + offset += dissect_om2k_attr_unkn(tvb, offset, 2, iei, tree); + break; + case 0x16: /* File Relation Indication */ + proto_tree_add_item(tree, hf_om2k_filerel_ilr, tvb, + offset, 1, FALSE); + proto_tree_add_item(tree, hf_om2k_filerel_cur, tvb, + offset, 1, FALSE); + offset++; + proto_tree_add_item(tree, hf_om2k_filerel_other, tvb, + offset, 1, FALSE); + offset++; + break; + case 0x17: /* File Revision */ + proto_tree_add_item(tree, hf_om2k_file_rev, tvb, + offset, 8, FALSE); + offset += 8; + break; + case 0x1c: /* Filling Marker */ + proto_tree_add_item(tree, hf_om2k_fill_mark, tvb, + offset++, 1, FALSE); + break; + case 0x1d: /* FN Offset */ + proto_tree_add_item(tree, hf_om2k_fn_offs, tvb, + offset, 2, FALSE); + offset += 2; + break; + case 0x1e: /* Frequency List */ + len = tvb_get_guint8(tvb, offset++); + /* FIXME */ + offset += dissect_om2k_attr_unkn(tvb, offset, len, iei, tree); + break; + case 0x1f: /* Frequency Specifier Rx */ + /* FIXME */ + case 0x20: /* Frequency Specifier Rx */ + /* FIXME */ + offset += dissect_om2k_attr_unkn(tvb, offset, 2, iei, tree); + break; + case 0x21: /* HSN */ + proto_tree_add_item(tree, hf_om2k_hsn, tvb, + offset++, 1, FALSE); + break; + case 0x22: /* ICM */ + proto_tree_add_item(tree, hf_om2k_icm, tvb, + offset++, 1, FALSE); + break; + case 0x23: /* Internal Fault Map Class 1A */ + /* FIXME */ + case 0x24: /* Internal Fault Map Class 1B */ + /* FIXME */ + case 0x25: /* Internal Fault Map Class 2A */ + /* FIXME */ + case 0x26: /* Internal Fault Map Class 2A Ext */ + /* FIXME */ + offset += dissect_om2k_attr_unkn(tvb, offset, 6, iei, tree); + break; + case 0x27: /* IS Connection List */ + offset += dissect_om2k_is_list(tvb, offset, tree); + break; + case 0x28: /* List Number */ + proto_tree_add_item(tree, hf_om2k_list_nr, tvb, + offset++, 1, FALSE); + break; + case 0x2a: /* Local Access State */ + proto_tree_add_item(tree, hf_om2k_la_state, tvb, + offset++, 1, FALSE); + break; + case 0x2b: /* MAIO */ + proto_tree_add_item(tree, hf_om2k_maio, tvb, + offset++, 1, FALSE); + break; + case 0x2c: /* MO State */ + proto_tree_add_item(tree, hf_om2k_mo_state, tvb, + offset++, 1, FALSE); + break; + case 0x2d: /* Ny1 */ + proto_tree_add_item(tree, hf_om2k_ny1, tvb, + offset++, 1, FALSE); + break; + case 0x2e: /* Operational Information */ + proto_tree_add_item(tree, hf_om2k_oip, tvb, + offset++, 1, FALSE); + break; + case 0x2f: /* Nominal Power */ + proto_tree_add_item(tree, hf_om2k_nom_pwr, tvb, + offset++, 1, FALSE); + break; + case 0x33: /* Receiver Diversity */ + proto_tree_add_item(tree, hf_om2k_diversity, tvb, + offset++, 1, FALSE); + break; + case 0x34: /* Replacement Unit Map */ + /* FIXME */ + offset += dissect_om2k_attr_unkn(tvb, offset, 6, iei, tree); + break; + case 0x38: /* T3105 */ + proto_tree_add_item(tree, hf_om2k_t3105, tvb, + offset++, 1, FALSE); + break; + case 0x3a: /* TF Mode */ + proto_tree_add_item(tree, hf_om2k_tf_mode, tvb, + offset++, 1, FALSE); + break; + case 0x3c: /* TS Number */ + proto_tree_add_item(tree, hf_om2k_ts, tvb, + offset++, 1, FALSE); + break; + case 0x3d: /* TSC */ + proto_tree_add_item(tree, hf_om2k_tsc, tvb, + offset++, 1, FALSE); + break; + case 0x40: /* BTS Version */ + proto_tree_add_item(tree, hf_om2k_bts_manuf, tvb, + offset, 3, FALSE); + offset += 3; + proto_tree_add_item(tree, hf_om2k_bts_gen, tvb, + offset, 3, FALSE); + offset += 3; + proto_tree_add_item(tree, hf_om2k_bts_rev, tvb, + offset, 3, FALSE); + offset += 3; + proto_tree_add_item(tree, hf_om2k_bts_var, tvb, + offset, 3, FALSE); + offset += 3; + break; + case 0x43: /* OML Function Map 1 */ + case 0x44: /* OML Function Map 2 */ + case 0x45: /* RSL Function Map 1 */ + case 0x46: /* RSL Function Map 2 */ + len = tvb_get_guint8(tvb, offset++); + /* FIXME */ + offset += dissect_om2k_attr_unkn(tvb, offset, len, iei, tree); + break; + case 0x47: /* Ext Range */ + proto_tree_add_item(tree, hf_om2k_ext_range, tvb, + offset++, 1, FALSE); + break; + case 0x48: /* Request Indicators */ + proto_tree_add_item(tree, hf_om2k_brr, tvb, + offset, 1, FALSE); + proto_tree_add_item(tree, hf_om2k_bfr, tvb, + offset, 1, FALSE); + offset++; + break; + case 0x50: /* Replacement Unit Map Extension */ + /* FIXME */ + offset += dissect_om2k_attr_unkn(tvb, offset, 6, iei, tree); + break; + case 0x74: /* ICM Boundary */ + /* FIXME */ + offset += dissect_om2k_attr_unkn(tvb, offset, 5, iei, tree); + break; + case 0x79: /* Link Supervision Control */ + proto_tree_add_item(tree, hf_om2k_lsc_fm, tvb, + offset, 1, FALSE); + proto_tree_add_item(tree, hf_om2k_lsc_lsi, tvb, + offset, 1, FALSE); + proto_tree_add_item(tree, hf_om2k_lsc_lsa, tvb, + offset, 1, FALSE); + offset++; + break; + case 0x7a: /* Link Supervision Control */ + proto_tree_add_item(tree, hf_om2k_ls_ft, tvb, + offset++, 1, FALSE); + break; + case 0x7b: /* Call Supervision Time */ + proto_tree_add_item(tree, hf_om2k_cst, tvb, + offset++, 1, FALSE); + break; + case 0x7e: /* ICM Channel Rate */ + proto_tree_add_item(tree, hf_om2k_icm_cr, tvb, + offset++, 1, FALSE); + break; + case 0x84: /* HW Info Signature */ + proto_tree_add_item(tree, hf_om2k_hwinfo_sig, tvb, + offset, 2, FALSE); + offset += 2; + break; + case 0x87: /* TTA */ + proto_tree_add_item(tree, hf_om2k_tta, tvb, + offset++, 1, FALSE); + break; + case 0x8a: /* Capabilities Signature */ + proto_tree_add_item(tree, hf_om2k_capa_sig, tvb, + offset, 2, FALSE); + offset += 2; + break; + case 0x90: /* Negotiation Record I */ + case 0x91: /* Negotiation Record II */ + len = tvb_get_guint8(tvb, offset++); + /* FIXME */ + offset += dissect_om2k_attr_unkn(tvb, offset, len, iei, tree); + break; + case 0x92: /* Encryption Algorithm */ + proto_tree_add_item(tree, hf_om2k_ea, tvb, + offset++, 1, FALSE); + break; + case 0x94: /* Interference Rejection Combining */ + proto_tree_add_item(tree, hf_om2k_irc, tvb, + offset++, 1, FALSE); + break; + case 0x95: /* Dedication information */ + /* FIXME */ + offset += dissect_om2k_attr_unkn(tvb, offset, 3, iei, tree); + break; + case 0x98: /* FS Offset */ + proto_tree_add_item(tree, hf_om2k_tf_fs_offset, tvb, + offset, 5, FALSE); + offset += 5; + break; + case 0x9c: /* External Condition Class 2 Extension */ + /* FIXME */ + offset += dissect_om2k_attr_unkn(tvb, offset, 4, iei, tree); + break; + case 0x9d: /* TSs MO State */ + dissect_tss_mo_state(tvb, offset, pinfo, tree); + offset += 4; + break; + case 0x9e: + case 0x9f: + default: + tmp = tvb_get_guint8(tvb, offset); + proto_tree_add_uint_format(tree, hf_om2k_unknown_tag, tvb, + offset-1, 1, tmp, "Tag %s: 0x%02x", + val_to_str(iei, om2k_attr_vals, "0x%02x"), tmp); + offset++; + break; + } + } + + return offset; +} + +static guint +dissect_om2k_mo(tvbuff_t *tvb, gint offset, packet_info *pinfo, proto_tree *tree) +{ + proto_item *ti; + proto_tree *mo_tree; + guint8 class = tvb_get_guint8(tvb, offset); + guint8 inst = tvb_get_guint8(tvb, offset+3); + + ti = proto_tree_add_item(tree, hf_om2k_mo_if, tvb, offset, + 4, FALSE); + mo_tree = proto_item_add_subtree(ti, ett_om2k_mo); + proto_tree_add_item(mo_tree, hf_om2k_mo_class, tvb, offset, + 1, FALSE); + proto_tree_add_item(mo_tree, hf_om2k_mo_instance, tvb, offset+3, + 1, FALSE); + proto_item_append_text(ti, ", Class: %s, Instance: %u", + val_to_str(class, om2k_mo_class_vals, "0x%02x"), + inst); + col_append_fstr(pinfo->cinfo, COL_INFO, ", (%-4s %u)", + val_to_str(class, &om2k_mo_class_short_vals, + "0x%02x"), inst); + return 4; +} + +static void +dissect_abis_om2000(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + proto_item *ti; + proto_tree *om2k_tree; + + int offset = 0; + + col_set_str(pinfo->cinfo, COL_PROTOCOL, "OM2000"); + + top_tree = tree; + if (tree) { + guint16 msg_code = tvb_get_ntohs(tvb, offset); + guint8 tmp; + + ti = proto_tree_add_item(tree, proto_abis_om2000, + tvb, 0, -1, FALSE); + om2k_tree = proto_item_add_subtree(ti, ett_om2000); + + proto_tree_add_item(om2k_tree, hf_om2k_msg_code, tvb, offset, + 2, FALSE); + offset += 2; + + offset += dissect_om2k_mo(tvb, offset, pinfo, om2k_tree); + + col_append_fstr(pinfo->cinfo, COL_INFO, " %s ", + val_to_str(msg_code, &om2k_msgcode_vals, + "unknown 0x%04x")); + proto_item_append_text(ti, " %s ", + val_to_str(msg_code, &om2k_msgcode_vals, + "unknown 0x%04x")); + + switch (msg_code) { + case 0x74: /* Operational Info */ + tmp = tvb_get_guint8(tvb, offset+1); + proto_item_append_text(ti, ": %s", + val_to_str(tmp, om2k_oip_vals, + "unknown 0x%02x")); + break; + case 0x1A: /* CON Configuration Result */ + case 0x66: /* IS Configuration Result */ + case 0x82: /* RX Configuration Result */ + case 0xA6: /* TF Configuration Result */ + case 0xAE: /* TS Configuration Result */ + case 0xB6: /* TX Configuration Result */ + case 0xE2: /* DP Configuration Result */ + case 0xF6: /* DP Configuration Result */ + tmp = tvb_get_guint8(tvb, offset+1); + proto_item_append_text(ti, ": %s", + val_to_str(tmp, om2k_aip_vals, + "unknown 0x%02x")); + break; + default: + break; + } + dissect_om2k_attrs(tvb, offset, pinfo, om2k_tree); + } +} + +void +proto_reg_handoff_abis_om2000(void); + +void +proto_register_abis_om2000(void) +{ + static hf_register_info hf[] = { + { &hf_om2k_msg_code, + { "Message Code", "om2000.msg_code", + FT_UINT16, BASE_HEX, VALS(om2k_msgcode_vals), 0, + NULL, HFILL } + }, + { &hf_om2k_mo_if, + { "MO Interface", "om2000.mo_if", + FT_BYTES, BASE_NONE, NULL, 0, + NULL, HFILL } + }, + { &hf_om2k_mo_class, + { "MO IF Class", "om2000.mo_if.class", + FT_UINT8, BASE_HEX, VALS(om2k_mo_class_vals), 0, + NULL, HFILL } + }, + { &hf_om2k_mo_instance, + { "MO IF Instance", "om2000.mo_if.instance", + FT_UINT8, BASE_DEC, NULL, 0, + NULL, HFILL } + }, + { &hf_om2k_oip, + { "OIP (Operational Info)", "om2000.oip", + FT_UINT8, BASE_HEX, VALS(om2k_oip_vals), 0, + "Operational Information Parameter", HFILL } + }, + { &hf_om2k_aip, + { "AIP (Accordance Info)", "om2000.aip", + FT_UINT8, BASE_HEX, VALS(om2k_aip_vals), 0, + "Accordance Information Parameter", HFILL } + }, + { &hf_om2k_comb, + { "Channel Combination", "om2000.chan_comb", + FT_UINT8, BASE_DEC, VALS(om2k_comb_vals), 0, + "Logical Channel Combination", HFILL } + }, + { &hf_om2k_ts, + { "Timeslot Number", "om2000.ts", + FT_UINT8, BASE_DEC, NULL, 0, + NULL, HFILL } + }, + { &hf_om2k_hsn, + { "HSN", "om2000.hsn", + FT_UINT8, BASE_DEC, NULL, 0, + "Hopping Sequence Number", HFILL } + }, + { &hf_om2k_maio, + { "MAIO", "om2000.maio", + FT_UINT8, BASE_DEC, NULL, 0, + "Mobile Allication Index Offset", HFILL } + }, + { &hf_om2k_bsic, + { "BSIC", "om2000.bsic", + FT_UINT8, BASE_HEX, NULL, 0, + "Base Station Identity Code", HFILL } + }, + { &hf_om2k_diversity, + { "Receiver Diversity", "om2000.diversity", + FT_UINT8, BASE_HEX, VALS(om2k_diversity_vals), 0, + NULL, HFILL } + }, + { &hf_om2k_fn_offs, + { "FN Offset", "om2000.fn_offset", + FT_UINT16, BASE_DEC, NULL, 0, + "GSM Frame Number Offset", HFILL } + }, + { &hf_om2k_ext_range, + { "Extended Range", "om2000.ext_range", + FT_BOOLEAN, 1, NULL, 0, + NULL, HFILL } + }, + { &hf_om2k_irc, + { "Interference Rejection Combining", "om2000.irc", + FT_BOOLEAN, 1, NULL, 0, + NULL, HFILL } + }, + { &hf_om2k_bs_pa_mfrms, + { "BS_PA_MFRMS", "om2000.bs_pa_mfrms", + FT_UINT8, BASE_DEC, NULL, 0, + NULL, HFILL } + }, + { &hf_om2k_bs_ag_blks_res, + { "BS_AG_BLKS_RES", "om2000.bs_ag_blks_res", + FT_UINT8, BASE_DEC, NULL, 0, + NULL, HFILL } + }, + { &hf_om2k_drx_dev_max, + { "DRX_DEV_MAX", "om2000.drx_dev_max", + FT_UINT8, BASE_DEC, NULL, 0, + NULL, HFILL } + }, + { &hf_om2k_cr, + { "CCCH Repeat", "om2000.ccch_repeat", + FT_BOOLEAN, 1, NULL, 0, + NULL, HFILL } + }, + { &hf_om2k_ipt3, + { "Inhibit Paging Request Type 3", "om2000.ipt3", + FT_BOOLEAN, 2, NULL, 0, + NULL, HFILL } + }, + { &hf_om2k_aop, + { "Age Of Paging", "om2000.aop", + FT_UINT8, BASE_DEC, NULL, 0, + NULL, HFILL } + }, + { &hf_om2k_t3105, + { "T3105 (in 10ms)", "om2000.t3105", + FT_UINT8, BASE_DEC, NULL, 0, + NULL, HFILL } + }, + { &hf_om2k_ny1, + { "Ny1", "om2000.ny1", + FT_UINT8, BASE_DEC, NULL, 0, + NULL, HFILL } + }, + { &hf_om2k_cbi, + { "CBCH Indicator", "om2000.ny1", + FT_BOOLEAN, 1, NULL, 0, + NULL, HFILL } + }, + { &hf_om2k_tsc, + { "Training Sequence Code", "om2000.tsc", + FT_UINT8, BASE_DEC, NULL, 0, + NULL, HFILL } + }, + { &hf_om2k_icm, + { "Idle Channel Measurement", "om2000.icm", + FT_BOOLEAN, 1, NULL, 0, + NULL, HFILL } + }, + { &hf_om2k_tta, + { "Timer for Time Alignment", "om2000.tta", + FT_UINT8, BASE_DEC, NULL, 0, + NULL, HFILL } + }, + { &hf_om2k_icm_cr, + { "ICM Channel Rate", "om2000.icm_cr", + FT_BOOLEAN, 1, VALS(om2k_icmcr_vals), 0, + NULL, HFILL } + }, + { &hf_om2k_lsc_fm, + { "LSC Dummy Frequency Measurement", "om2000.lsc.fm", + FT_BOOLEAN, 0x80, NULL, 0, + NULL, HFILL } + }, + { &hf_om2k_lsc_lsi, + { "LSC Idle Channels", "om2000.ls.lsi", + FT_BOOLEAN, 0x01, NULL, 0, + NULL, HFILL } + }, + { &hf_om2k_lsc_lsa, + { "LSC Active Channels", "om2000.ls.lsa", + FT_BOOLEAN, 0x02, NULL, 0, + NULL, HFILL } + }, + { &hf_om2k_ls_ft, + { "Link Supervision Filtering Time (100ms)", "om2000.ls_ft", + FT_UINT8, BASE_DEC, NULL, 0, + NULL, HFILL } + }, + { &hf_om2k_cst, + { "Call Supervision Time (480ms)", "om2000.cst", + FT_UINT8, BASE_DEC, NULL, 0, + NULL, HFILL } + }, + { &hf_om2k_ea, + { "Encryption Algorithm", "om2000.ea", + FT_UINT8, BASE_DEC, VALS(om2k_ea_vals), 0, + NULL, HFILL } + }, + { &hf_om2k_nom_pwr, + { "Nominal Power (dBm)", "om2000.pwr", + FT_UINT8, BASE_DEC, NULL, 0, + NULL, HFILL } + }, + { &hf_om2k_fill_mark, + { "Filling Marker", "om2000.filling", + FT_BOOLEAN, 0x01, VALS(om2k_fill_vals), 0, + NULL, HFILL } + }, + { &hf_om2k_bcc, + { "BCC", "om2000.bcc", + FT_UINT8, BASE_DEC, NULL, 0, + "Base Station Color Code", HFILL } + }, + { &hf_om2k_mo_state, + { "MO State", "om2000.mo_state", + FT_UINT8, BASE_DEC, VALS(om2k_mo_state_vals), 0, + NULL, HFILL } + }, + { &hf_om2k_la_state, + { "Local Access State", "om2000.la_state", + FT_UINT8, BASE_DEC, VALS(om2k_la_state_vals), 0, + NULL, HFILL } + }, + { &hf_om2k_tsn_state, + { "Time Slot N MO State", "om2000.tsn_mo_state", + FT_UINT8, BASE_DEC, VALS(om2k_mo_state_vals), 0, + NULL, HFILL } + }, + { &hf_om2k_bts_manuf, + { "BTS Manufacturer ID", "om2000.bts_ver.manuf", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL } + }, + { &hf_om2k_bts_gen, + { "BTS Generation", "om2000.bts_ver.gen", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL } + }, + { &hf_om2k_bts_rev, + { "BTS Revision", "om2000.bts_ver.rev", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL } + }, + { &hf_om2k_bts_var, + { "BTS Variant", "om2000.bts_ver.variant", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL } + }, + { &hf_om2k_brr, + { "BTS Requested Restart", "om2000.brr", + FT_BOOLEAN, 0x01, NULL, 0, + NULL, HFILL } + }, + { &hf_om2k_bfr, + { "BTS Requested File Relation", "om2000.bfr", + FT_BOOLEAN, 0x01, NULL, 0, + NULL, HFILL } + }, + { &hf_om2k_hwinfo_sig, + { "HW Info Signature", "om2000.hwinfo_sig", + FT_UINT16, BASE_HEX, NULL, 0, + NULL, HFILL } + }, + { &hf_om2k_capa_sig, + { "Capabilities Signature", "om2000.capa_sig", + FT_UINT16, BASE_HEX, NULL, 0, + NULL, HFILL } + }, + + { &hf_om2k_unknown_tag, + { "Unknown Tag", "om2000.unknown.tag", + FT_UINT8, BASE_HEX, NULL, 0, + NULL, HFILL } + }, + { &hf_om2k_unknown_val, + { "Unknown Value", "om2000.unknown.val", + FT_BYTES, BASE_NONE, NULL, 0, + NULL, HFILL } + }, + + { &hf_om2k_file_rev, + { "File Revision", "om2000.file_rev", + FT_STRING, BASE_NONE, NULL, 0, + NULL, HFILL } + }, + { &hf_om2k_filerel_ilr, + { "Immediate Load Requested", "om2000.filerel.ilr", + FT_BOOLEAN, 0x08, NULL, 0, + NULL, HFILL } + }, + { &hf_om2k_filerel_cur, + { "Current State", "om2000.filerel.cur", + FT_UINT8, BASE_HEX, VALS(filerel_state_vals), 0x07, + NULL, HFILL } + }, + { &hf_om2k_filerel_other, + { "Other State", "om2000.filerel.other", + FT_UINT8, BASE_HEX, VALS(filerel_state_vals), 0x07, + NULL, HFILL } + }, + { &hf_om2k_cal_time, + { "Calendar Time", "om2000.cal_time", + FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0, + NULL, HFILL } + }, + { &hf_om2k_list_nr, + { "List Number", "om2000.list_nr", + FT_UINT8, BASE_DEC, NULL, 0, + NULL, HFILL } + }, + { &hf_om2k_list_nr_end, + { "End List Number", "om2000.list_nr_end", + FT_UINT8, BASE_DEC, NULL, 0, + NULL, HFILL } + }, + { &hf_om2k_isl, + { "IS Connection List", "om2000.is_list", + FT_BYTES, BASE_NONE, NULL, 0, + NULL, HFILL } + }, + { &hf_om2k_isl_icp1, + { "ICP1", "om2000.is_list.icp1", + FT_UINT16, BASE_DEC, NULL, 0x7ff, + NULL, HFILL } + }, + { &hf_om2k_isl_icp2, + { "ICP2", "om2000.is_list.icp2", + FT_UINT16, BASE_DEC, NULL, 0x7ff, + NULL, HFILL } + }, + { &hf_om2k_isl_ci, + { "Contiguity Index", "om2000.is_list.ci", + FT_UINT8, BASE_DEC, NULL, 0, + NULL, HFILL } + }, + { &hf_om2k_conl, + { "Connection List", "om2000.con_list", + FT_BYTES, BASE_NONE, NULL, 0, + NULL, HFILL } + }, + { &hf_om2k_conl_nr_cgs, + { "Number of CGs", "om2000.con_list.nr_cgs", + FT_UINT8, BASE_DEC, NULL, 0x1f, + "Number of Concentration Groups in the DE", HFILL } + }, + { &hf_om2k_conl_nr_cps_cg, + { "Number of CPS in CG", "om2000.con_list.nr_cps_cg", + FT_UINT8, BASE_DEC, NULL, 0x1f, + "Number of CPS in Concentration Group", HFILL } + }, + { &hf_om2k_conl_ccp, + { "CON Connection Point", "om2000.con_list.cpp", + FT_UINT16, BASE_DEC, NULL, 0x3ff, + NULL, HFILL } + }, + { &hf_om2k_conl_ci, + { "Contiguity Index", "om2000.con_list.ci", + FT_UINT8, BASE_DEC, NULL, 0x7, + NULL, HFILL } + }, + { &hf_om2k_conl_tag, + { "Tag", "om2000.con_list.tag", + FT_UINT8, BASE_DEC, NULL, 0x1f, + NULL, HFILL } + }, + { &hf_om2k_conl_tei, + { "TEI", "om2000.con_list.tei", + FT_UINT8, BASE_DEC, NULL, 0, + NULL, HFILL } + }, + { &hf_om2k_tf_mode, + { "TF Mode", "om2000.tf_mode", + FT_UINT8, BASE_HEX, VALS(om2k_tf_mode_vals), 0, + NULL, HFILL } + }, + { &hf_om2k_tf_fs_offset, + { "TF FS Offset", "om2000.tf_fs_offset", + FT_UINT64, BASE_DEC, NULL, 0, + NULL, HFILL } + }, + }; + static gint *ett[] = { + &ett_om2000, + &ett_om2k_mo, + &ett_om2k_isl, + &ett_om2k_conl, + }; + + module_t *oml_module; + + proto_abis_om2000 = proto_register_protocol("Ericsson A-bis OML", + "Ericsson OML", + "gsm_abis_om2000"); + + proto_register_field_array(proto_abis_om2000, hf, array_length(hf)); + + proto_register_subtree_array(ett, array_length(ett)); + + register_dissector("gsm_abis_om2000", dissect_abis_om2000, + proto_abis_om2000); +#if 0 + oml_module = prefs_register_protocol(proto_abis_oml, proto_reg_handoff_abis_oml); + prefs_register_bool_preference(oml_module, "use_ipaccess_oml", + "Use nanoBTS definitions", + "Use ipaccess nanoBTS specific definitions for OML", + &global_oml_use_nano_bts); +#endif +} + +/* This function is called once at startup and every time the user hits + * 'apply' in the preferences dialogue */ +void +proto_reg_handoff_abis_om2000(void) +{ + static gboolean initialized = FALSE; + + if (!initialized) { + dissector_handle_t abis_om2k_handle; + + abis_om2k_handle = create_dissector_handle(dissect_abis_om2000, + proto_abis_om2000); + //dissector_add("lapd.gsm.sapi", LAPD_GSM_SAPI_OM_PROC, abis_oml_handle); + } else { + /* preferences have been changed */ + } +} Index: wireshark/epan/dissectors/packet-gsm_abis_oml.c =================================================================== --- wireshark.orig/epan/dissectors/packet-gsm_abis_oml.c 2011-09-06 13:37:42.000000000 +0200 +++ wireshark/epan/dissectors/packet-gsm_abis_oml.c 2011-09-06 13:56:14.000000000 +0200 @@ -119,9 +119,12 @@ /* Decode things as nanoBTS traces */ static gboolean global_oml_use_nano_bts = TRUE; +static gboolean global_oml_use_ericsson = TRUE; static proto_tree *top_tree; +static dissector_handle_t sub_om2000; + /* TS 12.21 Chapter 8.1 / TS 08.59 */ static const value_string oml_msg_disc_vals[] = { { ABIS_OM_MDISC_FOM, "Formatted O&M" }, @@ -1161,6 +1164,7 @@ top_tree = tree; if (tree) { u_int8_t msg_disc = tvb_get_guint8(tvb, offset); + u_int8_t len = tvb_get_guint8(tvb, offset+3); ti = proto_tree_add_item(tree, proto_abis_oml, tvb, 0, -1, FALSE); oml_tree = proto_item_add_subtree(ti, ett_oml); @@ -1174,6 +1178,14 @@ proto_tree_add_item(oml_tree, hf_oml_length, tvb, offset++, 1, TRUE); + if (global_oml_use_ericsson == TRUE) { + tvbuff_t *subtvb; + subtvb = tvb_new_subset(tvb, offset, len, len); + + if (sub_om2000) + call_dissector(sub_om2000, subtvb, pinfo, tree); + } else { + switch (msg_disc) { case ABIS_OM_MDISC_FOM: offset = dissect_oml_fom(tvb, pinfo, oml_tree, @@ -1187,6 +1199,8 @@ default: break; } + + } } } @@ -1696,6 +1710,11 @@ "Use nanoBTS definitions", "Use ipaccess nanoBTS specific definitions for OML", &global_oml_use_nano_bts); + + prefs_register_bool_preference(oml_module, "use_ericsson_oml", + "Use Ericsson definitions", + "Use Ericsson A-bis OML (OM2000) definitions for OML", + &global_oml_use_ericsson); } /* This function is called once at startup and every time the user hits @@ -1732,4 +1751,6 @@ oml_fom_attr_vse._vs_num_entries = array_length(oml_fom_attr_vals_bs11)-1; } + + sub_om2000 = find_dissector("gsm_abis_om2000"); } Index: wireshark/epan/dissectors/packet-l2tp.c =================================================================== --- wireshark.orig/epan/dissectors/packet-l2tp.c 2011-09-06 13:37:42.000000000 +0200 +++ wireshark/epan/dissectors/packet-l2tp.c 2011-09-06 13:37:48.000000000 +0200 @@ -148,6 +148,7 @@ #define L2TPv3_PROTOCOL_AAL5 6 #define L2TPv3_PROTOCOL_LAPD 7 #define L2TPv3_PROTOCOL_DOCSIS_DMPT 8 +#define L2TPv3_PROTOCOL_ERICSSON 9 static enum_val_t l2tpv3_protocols[] = { {"eth", "Ethernet", L2TPv3_PROTOCOL_ETH}, @@ -159,6 +160,7 @@ {"aal5", "AAL5", L2TPv3_PROTOCOL_AAL5}, {"lapd", "LAPD", L2TPv3_PROTOCOL_LAPD}, {"docsis-dmpt", "DOCSIS-DMPT", L2TPv3_PROTOCOL_DOCSIS_DMPT}, + {"ehdlc", "Ericsson HDLC", L2TPv3_PROTOCOL_ERICSSON}, {NULL, NULL, 0} }; @@ -583,6 +585,7 @@ static dissector_handle_t llc_handle; static dissector_handle_t lapd_handle; static dissector_handle_t mp2t_handle; +static dissector_handle_t ehdlc_handle; static dissector_handle_t data_handle; /* @@ -1495,6 +1498,9 @@ case L2TPv3_PROTOCOL_LAPD: call_dissector(lapd_handle, next_tvb, pinfo, tree); break; + case L2TPv3_PROTOCOL_ERICSSON: + call_dissector(ehdlc_handle, next_tvb, pinfo, tree); + break; default: call_dissector(data_handle, next_tvb, pinfo, tree); break; @@ -2167,5 +2173,6 @@ llc_handle = find_dissector("llc"); lapd_handle = find_dissector("lapd"); mp2t_handle = find_dissector("mp2t"); + ehdlc_handle = find_dissector("ehdlc"); data_handle = find_dissector("data"); } openbsc-0.15.0/wireshark/0003-lucent-hnb.patch000066400000000000000000000110421265565154000206740ustar00rootroot00000000000000From 48f2a191de62686c2ffdb97e46b5cc6bb61a868d Mon Sep 17 00:00:00 2001 From: Holger Hans Peter Freyther Date: Tue, 11 Jan 2011 15:16:19 +0100 Subject: [PATCH 3/4] lucent-hnb Add lucent hnb patch from OpenBSC --- epan/CMakeLists.txt | 1 + epan/dissectors/Makefile.common | 1 + epan/dissectors/packet-lucent_hnb.c | 103 +++++++++++++++++++++++++++++++++++ 3 files changed, 105 insertions(+), 0 deletions(-) create mode 100644 epan/dissectors/packet-lucent_hnb.c Index: wireshark/epan/CMakeLists.txt =================================================================== --- wireshark.orig/epan/CMakeLists.txt 2011-09-06 12:30:50.000000000 +0200 +++ wireshark/epan/CMakeLists.txt 2011-09-06 12:30:52.000000000 +0200 @@ -738,6 +738,7 @@ dissectors/packet-lpd.c dissectors/packet-lsc.c dissectors/packet-ltp.c + dissectors/packet-lucent_hnb.c dissectors/packet-lwapp.c dissectors/packet-lwres.c dissectors/packet-m2pa.c Index: wireshark/epan/dissectors/Makefile.common =================================================================== --- wireshark.orig/epan/dissectors/Makefile.common 2011-09-06 12:30:50.000000000 +0200 +++ wireshark/epan/dissectors/Makefile.common 2011-09-06 12:30:52.000000000 +0200 @@ -657,6 +657,7 @@ packet-lpd.c \ packet-lsc.c \ packet-ltp.c \ + packet-lucent_hnb.c \ packet-lwapp.c \ packet-lwres.c \ packet-m2pa.c \ Index: wireshark/epan/dissectors/packet-lucent_hnb.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ wireshark/epan/dissectors/packet-lucent_hnb.c 2011-09-06 12:30:52.000000000 +0200 @@ -0,0 +1,103 @@ +/* packet-lucent_hnb.c + * Routines for packet dissection of Alcatel/Lucent HomeNodeB + * Copyright 2009 by Harald Welte + * + * This protocol decoder is based entirely on reverse engineering, i.e. + * on educated guesses. + * + * $Id: packet-lucent_hnb.c 29254 2009-07-31 19:19:25Z gerald $ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#define LHNB_SCTP_PPI_MM 1 +#define LHNB_SCTP_PPI_GMM 6 + +#define LHNB_SCTP_PORT 6005 + +#include + +#include + +/* Initialize the protocol and registered fields */ +static int proto_lhnb = -1; + +static int hf_lhnb_length = -1; + +/* Initialize the subtree pointers */ +static gint ett_lhnb = -1; + +static dissector_handle_t ranap_handle; + +/* Code to actually dissect the packets */ +static void +dissect_lhnb(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + + int offset = 0; + u_int16_t len; + tvbuff_t *next_tvb; + + col_set_str(pinfo->cinfo, COL_PROTOCOL, "LHNB"); + col_clear(pinfo->cinfo, COL_INFO); + + proto_tree_add_item(tree, hf_lhnb_length, tvb, offset+2, 2, FALSE); + len = tvb_get_ntohs(tvb, offset+2); + next_tvb = tvb_new_subset(tvb, offset+2+6, len-4, -1); + + call_dissector(ranap_handle, next_tvb, pinfo, tree); +} + +void proto_register_lucent_hnb(void) +{ + static hf_register_info hf[] = { + {&hf_lhnb_length, + {"Length", "lhnb.len", + FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL} + }, + }; + + static gint *ett[] = { + &ett_lhnb, + }; + + proto_lhnb = + proto_register_protocol("Alcatel/Lucent HomeNodeB", + "Lucent HNB", "lhnb"); + + proto_register_field_array(proto_lhnb, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); +} + +void proto_reg_handoff_lucent_hnb(void) +{ + dissector_handle_t lhnb_handle; + + ranap_handle = find_dissector("ranap"); + + lhnb_handle = create_dissector_handle(dissect_lhnb, proto_lhnb); + + dissector_add("sctp.ppi", LHNB_SCTP_PPI_MM, lhnb_handle); + dissector_add("sctp.ppi", LHNB_SCTP_PPI_GMM, lhnb_handle); + dissector_add("sctp.port", LHNB_SCTP_PORT, lhnb_handle); +} openbsc-0.15.0/wireshark/0004-rsl-ipaccess.patch000066400000000000000000000553501265565154000212400ustar00rootroot00000000000000From 54882db58b6ec12da6b80071e0ac2344d42df24c Mon Sep 17 00:00:00 2001 From: Holger Hans Peter Freyther Subject: [PATCH 4/4] rsl-ipaccess Add vendor specific commands for RSL. --- epan/dissectors/packet-rsl.c | 544 +++++++++++++++++++++++++++++++++++++++++- 1 files changed, 536 insertions(+), 8 deletions(-) Index: wireshark/epan/dissectors/packet-rsl.c =================================================================== --- wireshark.orig/epan/dissectors/packet-rsl.c 2011-09-06 12:29:45.000000000 +0200 +++ wireshark/epan/dissectors/packet-rsl.c 2011-09-06 13:25:12.000000000 +0200 @@ -39,8 +39,11 @@ #include #include +#include #include "packet-gsm_a_common.h" +#include "packet-rtp.h" +#include "packet-rtcp.h" /* Initialize the protocol and registered fields */ static int proto_rsl = -1; @@ -115,6 +118,25 @@ static int hf_rsl_rtd = -1; static int hf_rsl_delay_ind = -1; static int hf_rsl_tfo = -1; +static int hf_rsl_speech_mode_s = -1; +static int hf_rsl_speech_mode_m = -1; +static int hf_rsl_conn_stat = -1; +static int hf_rsl_conn_id = -1; +static int hf_rsl_rtp_payload = -1; +static int hf_rsl_rtp_csd_fmt_d = -1; +static int hf_rsl_rtp_csd_fmt_ir = -1; +static int hf_rsl_local_port = -1; +static int hf_rsl_remote_port = -1; +static int hf_rsl_local_ip = -1; +static int hf_rsl_remote_ip = -1; +static int hf_rsl_cstat_tx_pkts = -1; +static int hf_rsl_cstat_tx_octs = -1; +static int hf_rsl_cstat_rx_pkts = -1; +static int hf_rsl_cstat_rx_octs = -1; +static int hf_rsl_cstat_lost_pkts = -1; +static int hf_rsl_cstat_ia_jitter = -1; +static int hf_rsl_cstat_avg_tx_dly = -1; + /* Initialize the subtree pointers */ static int ett_rsl = -1; @@ -172,6 +194,15 @@ static int ett_ie_meas_res_no = -1; static int ett_ie_message_id = -1; static int ett_ie_sys_info_type = -1; +static int ett_ie_speech_mode = -1; +static int ett_ie_conn_stat = -1; +static int ett_ie_conn_id = -1; +static int ett_ie_remote_ip = -1; +static int ett_ie_remote_port = -1; +static int ett_ie_local_port = -1; +static int ett_ie_local_ip = -1; +static int ett_ie_rtp_payload = -1; + static proto_tree *top_tree; static dissector_handle_t gsm_a_ccch_handle; @@ -179,6 +210,9 @@ static gboolean is_si2q = FALSE; +/* Decode things as nanoBTS traces */ +static gboolean global_rsl_use_nano_bts = TRUE; + /* Forward declarations */ static int dissct_rsl_msg(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset); @@ -208,8 +242,10 @@ { 0x06, "Common Channel Management messages" }, { 0x08, "TRX Management messages" }, { 0x16, "Location Services messages" }, + { 0x3f, "ip.access Vendor Specific messages" }, { 0, NULL } }; +#define RSL_MSGDISC_IPACCESS 0x3f /* * 9.2 MESSAGE TYPE */ @@ -277,6 +313,49 @@ /* 0 1 - - - - - - Location Services messages: */ #define RSL_MSG_LOC_INF 65 /* 8.7.1 */ +/* Vendor-Specific messages of ip.access nanoBTS. There is no public documentation + * about those extensions, all information in this dissector is based on lawful + * protocol reverse enginering by Harald Welte */ +#define RSL_MSG_TYPE_IPAC_MEAS_PP_DEF 0x60 +#define RSL_MSG_TYPE_IPAC_HO_CAND_INQ 0x61 +#define RSL_MSG_TYPE_IPAC_HO_CAND_RESP 0x62 + +#define RSL_MSG_TYPE_IPAC_PDCH_ACT 0x48 +#define RSL_MSG_TYPE_IPAC_PDCH_ACT_ACK 0x49 +#define RSL_MSG_TYPE_IPAC_PDCH_ACT_NACK 0x4a +#define RSL_MSG_TYPE_IPAC_PDCH_DEACT 0x4b +#define RSL_MSG_TYPE_IPAC_PDCH_DEACT_ACK 0x4c +#define RSL_MSG_TYPE_IPAC_PDCH_DEACT_NACK 0x4d + +#define RSL_MSG_TYPE_IPAC_CRCX 0x70 +#define RSL_MSG_TYPE_IPAC_CRCX_ACK 0x71 +#define RSL_MSG_TYPE_IPAC_CRCX_NACK 0x72 +#define RSL_MSG_TYPE_IPAC_MDCX 0x73 +#define RSL_MSG_TYPE_IPAC_MDCX_ACK 0x74 +#define RSL_MSG_TYPE_IPAC_MDCX_NACK 0x75 +#define RSL_MSG_TYPE_IPAC_DLCX_IND 0x76 +#define RSL_MSG_TYPE_IPAC_DLCX 0x77 +#define RSL_MSG_TYPE_IPAC_DLCX_ACK 0x78 +#define RSL_MSG_TYPE_IPAC_DLCX_NACK 0x79 + +#define RSL_IE_IPAC_SRTP_CONFIG 0xe0 +#define RSL_IE_IPAC_PROXY_UDP 0xe1 +#define RSL_IE_IPAC_BSCMPL_TOUT 0xe2 +#define RSL_IE_IPAC_REMOTE_IP 0xf0 +#define RSL_IE_IPAC_REMOTE_PORT 0xf1 +#define RSL_IE_IPAC_RTP_PAYLOAD 0xf2 +#define RSL_IE_IPAC_LOCAL_PORT 0xf3 +#define RSL_IE_IPAC_SPEECH_MODE 0xf4 +#define RSL_IE_IPAC_LOCAL_IP 0xf5 +#define RSL_IE_IPAC_CONN_STAT 0xf6 +#define RSL_IE_IPAC_HO_C_PARMS 0xf7 +#define RSL_IE_IPAC_CONN_ID 0xf8 +#define RSL_IE_IPAC_RTP_CSD_FMT 0xf9 +#define RSL_IE_IPAC_RTP_JIT_BUF 0xfa +#define RSL_IE_IPAC_RTP_COMPR 0xfb +#define RSL_IE_IPAC_RTP_PAYLOAD2 0xfc +#define RSL_IE_IPAC_RTP_MPLEX 0xfd +#define RSL_IE_IPAC_RTP_MPLEX_ID 0xfe static const value_string rsl_msg_type_vals[] = { /* 0 0 0 0 - - - - Radio Link Layer Management messages: */ @@ -339,6 +418,26 @@ { 0x3f, "TFO MODification REQuest" }, /* 8.4.31 */ /* 0 1 - - - - - - Location Services messages: */ { 0x41, "Location Information" }, /* 8.7.1 */ + /* ip.access */ + { 0x48, "ip.access PDCH ACTIVATION" }, + { 0x49, "ip.access PDCH ACTIVATION ACK" }, + { 0x4a, "ip.access PDCH ACTIVATION NACK" }, + { 0x4b, "ip.access PDCH DEACTIVATION" }, + { 0x4c, "ip.access PDCH DEACTIVATION ACK" }, + { 0x4d, "ip.access PDCH DEACTIVATION NACK" }, + { 0x60, "ip.access MEASurement PREPROCessing DeFauLT" }, + { 0x61, "ip.access HANDOover CANDidate ENQuiry" }, + { 0x62, "ip.access HANDOover CANDidate RESPonse" }, + { 0x70, "ip.access CRCX" }, + { 0x71, "ip.access CRCX ACK" }, + { 0x72, "ip.access CRCX NACK" }, + { 0x73, "ip.access MDCX" }, + { 0x74, "ip.access MDCX ACK" }, + { 0x75, "ip.access MDCX NACK" }, + { 0x76, "ip.access DLCX INDication" }, + { 0x77, "ip.access DLCX" }, + { 0x78, "ip.access DLCX ACK" }, + { 0x79, "ip.access DLCX NACK" }, { 0, NULL } }; @@ -373,9 +472,10 @@ #define RSL_IE_SYS_INFO_TYPE 30 - - - +#define RSL_IE_MS_POWER_PARAM 31 +#define RSL_IE_BS_POWER_PARAM 32 +#define RSL_IE_PREPROC_PARAM 33 +#define RSL_IE_PREPROC_MEAS 34 #define RSL_IE_FULL_IMM_ASS_INF 35 #define RSL_IE_SMSCB_INF 36 #define RSL_IE_FULL_MS_TIMING_OFFSET 37 @@ -478,6 +578,24 @@ Not used */ + { 0xe0, "SRTP Configuration" }, + { 0xe1, "BSC Proxy UDP Port" }, + { 0xe2, "BSC Multiplex Timeout" }, + { 0xf0, "Remote IP Address" }, + { 0xf1, "Remote RTP Port" }, + { 0xf2, "RTP Payload Type" }, + { 0xf3, "Local RTP Port" }, + { 0xf4, "Speech Mode" }, + { 0xf5, "Local IP Address" }, + { 0xf6, "Connection Statistics" }, + { 0xf7, "Handover C Parameters" }, + { 0xf8, "Connection Identifier" }, + { 0xf9, "RTP CSD Format" }, + { 0xfa, "RTP Jitter Buffer" }, + { 0xfb, "RTP Compression" }, + { 0xfc, "RTP Payload Type 2" }, + { 0xfd, "RTP Multiplex" }, + { 0xfe, "RTP Multiplex Identifier" }, { 0, NULL } }; @@ -514,6 +632,27 @@ { 0, NULL } }; +/* From openbsc/include/openbsc/tlv.h */ +enum tlv_type { + TLV_TYPE_FIXED, + TLV_TYPE_T, + TLV_TYPE_TV, + TLV_TYPE_TLV, + TLV_TYPE_TL16V, +}; + +struct tlv_def { + enum tlv_type type; + u_int8_t fixed_len; +}; + +struct tlv_definition { + struct tlv_def def[0xff]; +}; + +/* This structure is initialized in proto_register_rsl() */ +static struct tlv_definition rsl_att_tlvdef; + /* 9.3.1 Channel number 9.3.1 M TV 2 */ static int dissect_rsl_ie_ch_no(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, int offset, gboolean is_mandatory) @@ -2918,12 +3057,184 @@ } static int +dissct_rsl_ipaccess_msg(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset) +{ + guint8 msg_type; + guint32 local_addr = 0; + guint16 local_port = 0; + address src_addr; + + msg_type = tvb_get_guint8(tvb, offset)&0x7f; + offset++; + +#if 0 + switch (msg_type) { + case RSL_MSG_TYPE_IPAC_CRCX: + case RSL_MSG_TYPE_IPAC_CRCX_ACK: + case RSL_MSG_TYPE_IPAC_CRCX_NACK: + case RSL_MSG_TYPE_IPAC_MDCX: + case RSL_MSG_TYPE_IPAC_MDCX_ACK: + case RSL_MSG_TYPE_IPAC_MDCX_NACK: + case RSL_MSG_TYPE_IPAC_DLCX_IND: + case RSL_MSG_TYPE_IPAC_DLCX: + case RSL_MSG_TYPE_IPAC_DLCX_ACK: + case RSL_MSG_TYPE_IPAC_DLCX_NACK: + case RSL_MSG_TYPE_IPAC_PDCH_ACT: + case RSL_MSG_TYPE_IPAC_PDCH_ACT_ACK: + case RSL_MSG_TYPE_IPAC_PDCH_ACT_NACK: + case RSL_MSG_TYPE_IPAC_PDCH_DEACT: + case RSL_MSG_TYPE_IPAC_PDCH_DEACT_ACK: + case RSL_MSG_TYPE_IPAC_PDCH_DEACT_NACK: + /* Channel number 9.3.1 M TV 2 */ + offset = dissect_rsl_ie_ch_no(tvb, pinfo, tree, offset, TRUE); + break; + } +#endif + /* parse TLV attributes */ + while (tvb_reported_length_remaining(tvb, offset) != 0) { + guint8 tag; + unsigned int len, hlen, len_len; + const struct tlv_def *tdef; + proto_item *ti; + proto_tree *ie_tree; + + tag = tvb_get_guint8(tvb, offset); + tdef = &rsl_att_tlvdef.def[tag]; + + switch (tdef->type) { + case TLV_TYPE_FIXED: + hlen = 1; + len_len = 0; + len = tdef->fixed_len; + break; + case TLV_TYPE_T: + hlen = 1; + len_len = 0; + len = 0; + break; + case TLV_TYPE_TV: + hlen = 1; + len_len = 0; + len = 1; + break; + case TLV_TYPE_TLV: + hlen = 2; + len_len = 1; + len = tvb_get_guint8(tvb, offset+1); + break; + case TLV_TYPE_TL16V: + hlen = 3; + len_len = 2; + len = tvb_get_guint8(tvb, offset+1) << 8 | + tvb_get_guint8(tvb, offset+2); + break; + default: + hlen = len_len = len = 0; + DISSECTOR_ASSERT_NOT_REACHED(); + break; + } + + ti = proto_tree_add_item(tree, hf_rsl_ie_id, tvb, offset, 1, FALSE); + ie_tree = proto_item_add_subtree(ti, ett_ie_local_port); + offset += hlen; + + switch (tag) { + case RSL_IE_CH_NO: + dissect_rsl_ie_ch_no(tvb, pinfo, ie_tree, offset, FALSE); + break; + case RSL_IE_FRAME_NO: + dissect_rsl_ie_frame_no(tvb, pinfo, ie_tree, offset, FALSE); + break; + case RSL_IE_MS_POW: + dissect_rsl_ie_ms_pow(tvb, pinfo, ie_tree, offset, FALSE); + break; + case RSL_IE_IPAC_REMOTE_IP: + proto_tree_add_item(ie_tree, hf_rsl_remote_ip, tvb, + offset, len, FALSE); + break; + case RSL_IE_IPAC_REMOTE_PORT: + proto_tree_add_item(ie_tree, hf_rsl_remote_port, tvb, + offset, len, FALSE); + break; + case RSL_IE_IPAC_LOCAL_IP: + proto_tree_add_item(ie_tree, hf_rsl_local_ip, tvb, + offset, len, FALSE); + local_addr = tvb_get_ipv4(tvb, offset); + break; + case RSL_IE_IPAC_LOCAL_PORT: + proto_tree_add_item(ie_tree, hf_rsl_local_port, tvb, + offset, len, FALSE); + local_port = tvb_get_ntohs(tvb, offset); + break; + case RSL_IE_IPAC_SPEECH_MODE: + proto_tree_add_item(ie_tree, hf_rsl_speech_mode_s, tvb, + offset, len, FALSE); + proto_tree_add_item(ie_tree, hf_rsl_speech_mode_m, tvb, + offset, len, FALSE); + break; + case RSL_IE_IPAC_RTP_PAYLOAD: + case RSL_IE_IPAC_RTP_PAYLOAD2: + proto_tree_add_item(ie_tree, hf_rsl_rtp_payload, tvb, + offset, len, FALSE); + break; + case RSL_IE_IPAC_RTP_CSD_FMT: + proto_tree_add_item(ie_tree, hf_rsl_rtp_csd_fmt_d, tvb, + offset, len, FALSE); + proto_tree_add_item(ie_tree, hf_rsl_rtp_csd_fmt_ir, tvb, + offset, len, FALSE); + break; + case RSL_IE_IPAC_CONN_ID: + proto_tree_add_item(ie_tree, hf_rsl_conn_id, tvb, + offset, len, FALSE); + break; + case RSL_IE_IPAC_CONN_STAT: + proto_tree_add_item(ie_tree, hf_rsl_cstat_tx_pkts, tvb, + offset, 4, FALSE); + proto_tree_add_item(ie_tree, hf_rsl_cstat_tx_octs, tvb, + offset+4, 4, FALSE); + proto_tree_add_item(ie_tree, hf_rsl_cstat_rx_pkts, tvb, + offset+8, 4, FALSE); + proto_tree_add_item(ie_tree, hf_rsl_cstat_rx_octs, tvb, + offset+12, 4, FALSE); + proto_tree_add_item(ie_tree, hf_rsl_cstat_lost_pkts, tvb, + offset+16, 4, FALSE); + proto_tree_add_item(ie_tree, hf_rsl_cstat_ia_jitter, tvb, + offset+20, 4, FALSE); + proto_tree_add_item(ie_tree, hf_rsl_cstat_avg_tx_dly, tvb, + offset+24, 4, FALSE); + break; + } + offset += len; + } + + switch (msg_type) { + case RSL_MSG_TYPE_IPAC_CRCX_ACK: + /* Notify the RTP and RTCP dissectors about a new RTP stream */ + src_addr.type = AT_IPv4; + src_addr.len = 4; + src_addr.data = (guint8 *)&local_addr; + rtp_add_address(pinfo, &src_addr, local_port, 0, + "GSM A-bis/IP", pinfo->fd->num, 0, NULL); + rtcp_add_address(pinfo, &src_addr, local_port+1, 0, + "GSM A-bis/IP", pinfo->fd->num); + break; + } + return offset; +} + +static int dissct_rsl_msg(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset) { - guint8 msg_type; + guint8 msg_disc, msg_type; + msg_disc = tvb_get_guint8(tvb, offset++) >> 1; msg_type = tvb_get_guint8(tvb,offset)&0x7f; proto_tree_add_item(tree, hf_rsl_msg_type, tvb, offset, 1, ENC_BIG_ENDIAN); + + if (msg_disc == RSL_MSGDISC_IPACCESS) { + offset = dissct_rsl_ipaccess_msg(tvb, pinfo, tree, offset); + return offset; + } offset++; switch (msg_type){ @@ -3491,6 +3802,18 @@ /* LLP APDU 9.3.58 M LV 2-N */ offset = dissect_rsl_ie_llp_apdu(tvb, pinfo, tree, offset, TRUE); break; + /* the following messages are ip.access specific but sent without + * ip.access memssage discriminator */ + case RSL_MSG_TYPE_IPAC_MEAS_PP_DEF: + case RSL_MSG_TYPE_IPAC_HO_CAND_INQ: + case RSL_MSG_TYPE_IPAC_HO_CAND_RESP: + case RSL_MSG_TYPE_IPAC_PDCH_ACT: + case RSL_MSG_TYPE_IPAC_PDCH_ACT_ACK: + case RSL_MSG_TYPE_IPAC_PDCH_ACT_NACK: + case RSL_MSG_TYPE_IPAC_PDCH_DEACT: + case RSL_MSG_TYPE_IPAC_PDCH_DEACT_ACK: + case RSL_MSG_TYPE_IPAC_PDCH_DEACT_NACK: + offset = dissct_rsl_ipaccess_msg(tvb, pinfo, tree, offset-1); default: break; } @@ -3498,6 +3821,40 @@ return offset; } + +static const value_string rsl_ipacc_spm_s_vals[] = { + { 0, "GSM FR codec (GSM type 1, FS)" }, + { 1, "GSM EFR codec (GSM type 2, FS)" }, + { 2, "GSM AMR/FR codec (GSM type 3, FS)" }, + { 3, "GSM HR codec (GSM type 1, HS)" }, + { 5, "GSM AMR/HR codec (GSM type 3, HS)" }, + { 0xf, "As specified by RTP Payload Type IE" }, + { 0, NULL } +}; + +static const value_string rsl_ipacc_spm_m_vals[] = { + { 0, "Send and Receive" }, + { 1, "Receive Only" }, + { 2, "Send Only" }, + { 0, NULL } +}; + +static const value_string rsl_ipacc_rtp_csd_fmt_d_vals[] = { + { 0, "External TRAU format" }, + { 1, "Non-TRAU Packed format" }, + { 2, "TRAU within the BTS" }, + { 3, "IWF-Free BTS-BTS Data" }, + { 0, NULL } +}; + +static const value_string rsl_ipacc_rtp_csd_fmt_ir_vals[] = { + { 0, "8kb/s" }, + { 1, "16kb/s" }, + { 2, "32kb/s" }, + { 3, "64kb/s" }, + { 0, NULL } +}; + static void dissect_rsl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) { @@ -3518,12 +3875,19 @@ top_tree = tree; if (tree) { ti = proto_tree_add_item(tree, proto_rsl, tvb, 0, -1, ENC_BIG_ENDIAN); + + /* if nanoBTS specific vendor messages are not enabled, skip */ + if (!global_rsl_use_nano_bts) { + guint8 msg_disc = tvb_get_guint8(tvb, offset) >> 1; + + if (msg_disc == RSL_MSGDISC_IPACCESS) + return; + } rsl_tree = proto_item_add_subtree(ti, ett_rsl); /* 9.1 Message discriminator */ proto_tree_add_item(rsl_tree, hf_rsl_msg_dsc, tvb, offset, 1, ENC_BIG_ENDIAN); proto_tree_add_item(rsl_tree, hf_rsl_T_bit, tvb, offset, 1, ENC_BIG_ENDIAN); - offset++; offset = dissct_rsl_msg(tvb, pinfo, rsl_tree, offset); @@ -3898,6 +4262,86 @@ FT_UINT8, BASE_DEC, VALS(rsl_emlpp_prio_vals), 0x03, NULL, HFILL } }, + { &hf_rsl_speech_mode_s, + { "ip.access Speech Mode S", "rsl.ipacc.speech_mode_s", + FT_UINT8, BASE_HEX, VALS(rsl_ipacc_spm_s_vals), + 0xf, NULL, HFILL } + }, + { &hf_rsl_speech_mode_m, + { "ip.access Speech Mode M", "rsl.ipacc.speech_mode_m", + FT_UINT8, BASE_HEX, VALS(rsl_ipacc_spm_m_vals), + 0xf0, NULL, HFILL } + }, + { &hf_rsl_conn_stat, + { "ip.access Connection Statistics","rsl.ipacc.conn_stat", + FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } + }, + { &hf_rsl_conn_id, + { "ip.access Connection ID", "rsl.ipacc.conn_id", + FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } + }, + { &hf_rsl_rtp_payload, + { "ip.access RTP Payload Type", "rsl.ipacc.rtp_payload", + FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL } + }, + { &hf_rsl_rtp_csd_fmt_d, + { "ip.access RTP CSD Format D", "rsl.ipacc.rtp_csd_fmt_d", + FT_UINT8, BASE_HEX, VALS(rsl_ipacc_rtp_csd_fmt_d_vals), + 0x0f, NULL, HFILL }, + }, + { &hf_rsl_rtp_csd_fmt_ir, + { "ip.access RTP CSD Format IR", "rsl.ipacc.rtp_csd_fmt_ir", + FT_UINT8, BASE_HEX, VALS(rsl_ipacc_rtp_csd_fmt_ir_vals), + 0xf0, NULL, HFILL }, + }, + { &hf_rsl_local_port, + { "ip.access Local RTP Port", "rsl.ipacc.local_port", + FT_UINT16, BASE_DEC, NULL, 0x0, + "ip.access Local RTP Port", HFILL }, + }, + { &hf_rsl_remote_port, + { "ip.access Remote RTP Port", "rsl.ipacc.remote_port", + FT_UINT16, BASE_DEC, NULL, 0x0, + "ip.access Remote RTP Port", HFILL }, + }, + { &hf_rsl_local_ip, + { "ip.access Local IP Address", "rsl.ipacc.local_ip", + FT_IPv4, BASE_NONE, NULL, 0x0, + "ip.access Local IP Address", HFILL }, + }, + { &hf_rsl_remote_ip, + { "ip.access Remote IP Address", "rsl.ipacc.remote_ip", + FT_IPv4, BASE_NONE, NULL, 0x0, + "ip.access Remote IP Address", HFILL }, + }, + { &hf_rsl_cstat_tx_pkts, + { "Packets Sent", "rsl.ipacc.cstat.tx_pkts", + FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } + }, + { &hf_rsl_cstat_tx_octs, + { "Octets Sent", "rsl.ipacc.cstat.tx_octets", + FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } + }, + { &hf_rsl_cstat_rx_pkts, + { "Packets Received", "rsl.ipacc.cstat.rx_pkts", + FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } + }, + { &hf_rsl_cstat_rx_octs, + { "Octets Received", "rsl.ipacc.cstat.rx_octets", + FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } + }, + { &hf_rsl_cstat_lost_pkts, + { "Packets Lost", "rsl.ipacc.cstat.lost_pkts", + FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } + }, + { &hf_rsl_cstat_ia_jitter, + { "Inter-arrival Jitter", "rsl.ipacc.cstat.ia_jitter", + FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } + }, + { &hf_rsl_cstat_avg_tx_dly, + { "Average Tx Delay", "rsl.ipacc.cstat.avg_tx_delay", + FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } + }, }; static gint *ett[] = { &ett_rsl, @@ -3955,7 +4399,88 @@ &ett_ie_meas_res_no, &ett_ie_message_id, &ett_ie_sys_info_type, + &ett_ie_speech_mode, + &ett_ie_conn_stat, + &ett_ie_conn_id, + &ett_ie_remote_ip, + &ett_ie_remote_port, + &ett_ie_local_port, + &ett_ie_local_ip, + &ett_ie_rtp_payload, }; + module_t *rsl_module; + +#define RSL_ATT_TLVDEF(_attr, _type, _fixed_len) \ + rsl_att_tlvdef.def[_attr].type = _type; \ + rsl_att_tlvdef.def[_attr].fixed_len = _fixed_len; \ + + RSL_ATT_TLVDEF(RSL_IE_CH_NO, TLV_TYPE_TV, 0); + RSL_ATT_TLVDEF(RSL_IE_LINK_ID, TLV_TYPE_TV, 0); + RSL_ATT_TLVDEF(RSL_IE_ACT_TYPE, TLV_TYPE_TV, 0); + RSL_ATT_TLVDEF(RSL_IE_BS_POW, TLV_TYPE_TV, 0); + RSL_ATT_TLVDEF(RSL_IE_CH_ID, TLV_TYPE_TLV, 0); + RSL_ATT_TLVDEF(RSL_IE_CH_MODE, TLV_TYPE_TLV, 0); + RSL_ATT_TLVDEF(RSL_IE_ENC_INF, TLV_TYPE_TLV, 0); + RSL_ATT_TLVDEF(RSL_IE_FRAME_NO, TLV_TYPE_FIXED, 2); + RSL_ATT_TLVDEF(RSL_IE_HO_REF, TLV_TYPE_TV, 0); + RSL_ATT_TLVDEF(RSL_IE_L1_INF, TLV_TYPE_FIXED, 2); + RSL_ATT_TLVDEF(RSL_IE_L3_INF, TLV_TYPE_TL16V, 0); + RSL_ATT_TLVDEF(RSL_IE_MS_ID, TLV_TYPE_TLV, 0); + RSL_ATT_TLVDEF(RSL_IE_MS_POW, TLV_TYPE_TV, 0); + RSL_ATT_TLVDEF(RSL_IE_PAGING_GRP, TLV_TYPE_TV, 0); + RSL_ATT_TLVDEF(RSL_IE_PAGING_LOAD, TLV_TYPE_FIXED, 2); + RSL_ATT_TLVDEF(RSL_IE_PHY_CTX, TLV_TYPE_TLV, 0); + RSL_ATT_TLVDEF(RSL_IE_ACCESS_DELAY, TLV_TYPE_TV, 0); + RSL_ATT_TLVDEF(RSL_IE_RACH_LOAD, TLV_TYPE_TLV, 0); + RSL_ATT_TLVDEF(RSL_IE_REQ_REF, TLV_TYPE_FIXED, 3); + RSL_ATT_TLVDEF(RSL_IE_REL_MODE, TLV_TYPE_TV, 0); + RSL_ATT_TLVDEF(RSL_IE_RESOURCE_INF, TLV_TYPE_TLV, 0); + RSL_ATT_TLVDEF(RSL_IE_RLM_CAUSE, TLV_TYPE_TLV, 0); + RSL_ATT_TLVDEF(RSL_IE_STARTING_TIME, TLV_TYPE_FIXED, 2); + RSL_ATT_TLVDEF(RSL_IE_TIMING_ADV, TLV_TYPE_TV, 0); + RSL_ATT_TLVDEF(RSL_IE_UPLINK_MEAS, TLV_TYPE_TLV, 0); + RSL_ATT_TLVDEF(RSL_IE_CAUSE, TLV_TYPE_TLV, 0); + RSL_ATT_TLVDEF(RSL_IE_MEAS_RES_NO, TLV_TYPE_TV, 0); + RSL_ATT_TLVDEF(RSL_IE_MESSAGE_ID, TLV_TYPE_TV, 0); + RSL_ATT_TLVDEF(RSL_IE_SYS_INFO_TYPE, TLV_TYPE_TV, 0); + RSL_ATT_TLVDEF(RSL_IE_MS_POWER_PARAM, TLV_TYPE_TLV, 0); + RSL_ATT_TLVDEF(RSL_IE_BS_POWER_PARAM, TLV_TYPE_TLV, 0); + RSL_ATT_TLVDEF(RSL_IE_PREPROC_PARAM, TLV_TYPE_TLV, 0); + RSL_ATT_TLVDEF(RSL_IE_PREPROC_MEAS, TLV_TYPE_TLV, 0); +// RSL_ATT_TLVDEF(RSL_IE_IMM_ASS_INFO, TLV_TYPE_TLV, 0); +// RSL_ATT_TLVDEF(RSL_IE_SMSCB_INFO, TLV_TYPE_FIXED, 23); +// RSL_ATT_TLVDEF(RSL_IE_MS_TIMING_OFFSET, TLV_TYPE_TV, 0); + RSL_ATT_TLVDEF(RSL_IE_ERR_MSG, TLV_TYPE_TLV, 0); + RSL_ATT_TLVDEF(RSL_IE_FULL_BCCH_INF, TLV_TYPE_TLV, 0); + RSL_ATT_TLVDEF(RSL_IE_CH_NEEDED, TLV_TYPE_TV, 0); + RSL_ATT_TLVDEF(RSL_IE_CB_CMD_TYPE, TLV_TYPE_TV, 0); + RSL_ATT_TLVDEF(RSL_IE_SMSCB_MESS, TLV_TYPE_TLV, 0); + RSL_ATT_TLVDEF(RSL_IE_FULL_IMM_ASS_INF, TLV_TYPE_TLV, 0); +// RSL_ATT_TLVDEF(RSL_IE_SACCH_INFO, TLV_TYPE_TLV, 0); + RSL_ATT_TLVDEF(RSL_IE_CBCH_LOAD_INF, TLV_TYPE_TV, 0); + RSL_ATT_TLVDEF(RSL_IE_SMSCB_CH_IND, TLV_TYPE_TV, 0); + RSL_ATT_TLVDEF(RSL_IE_GRP_CALL_REF, TLV_TYPE_TLV, 0); + RSL_ATT_TLVDEF(RSL_IE_CH_DESC, TLV_TYPE_TLV, 0); + RSL_ATT_TLVDEF(RSL_IE_NCH_DRX_INF, TLV_TYPE_TLV, 0); + RSL_ATT_TLVDEF(RSL_IE_CMD_IND, TLV_TYPE_TLV, 0); + RSL_ATT_TLVDEF(RSL_IE_EMLPP_PRIO, TLV_TYPE_TV, 0); + RSL_ATT_TLVDEF(RSL_IE_UIC, TLV_TYPE_TLV, 0); + RSL_ATT_TLVDEF(RSL_IE_MAIN_CH_REF, TLV_TYPE_TV, 0); + RSL_ATT_TLVDEF(RSL_IE_MULTIRATE_CONF, TLV_TYPE_TLV, 0); + RSL_ATT_TLVDEF(RSL_IE_MULTIRATE_CNTRL, TLV_TYPE_TV, 0); + RSL_ATT_TLVDEF(RSL_IE_SUP_CODEC_TYPES, TLV_TYPE_TLV, 0); + RSL_ATT_TLVDEF(RSL_IE_CODEC_CONF, TLV_TYPE_TLV, 0); + RSL_ATT_TLVDEF(RSL_IE_RTD, TLV_TYPE_TV, 0); + RSL_ATT_TLVDEF(RSL_IE_TFO_STATUS, TLV_TYPE_TV, 0); + RSL_ATT_TLVDEF(RSL_IE_LLP_APDU, TLV_TYPE_TLV, 0); + RSL_ATT_TLVDEF(RSL_IE_IPAC_REMOTE_IP, TLV_TYPE_FIXED, 4); + RSL_ATT_TLVDEF(RSL_IE_IPAC_REMOTE_PORT, TLV_TYPE_FIXED, 2); + RSL_ATT_TLVDEF(RSL_IE_IPAC_LOCAL_IP, TLV_TYPE_FIXED, 4); + RSL_ATT_TLVDEF(RSL_IE_IPAC_CONN_STAT, TLV_TYPE_TLV, 0); + RSL_ATT_TLVDEF(RSL_IE_IPAC_LOCAL_PORT, TLV_TYPE_FIXED, 2); + RSL_ATT_TLVDEF(RSL_IE_IPAC_SPEECH_MODE, TLV_TYPE_TV, 0); + RSL_ATT_TLVDEF(RSL_IE_IPAC_CONN_ID, TLV_TYPE_FIXED, 2); + RSL_ATT_TLVDEF(RSL_IE_IPAC_RTP_PAYLOAD2,TLV_TYPE_TV, 0); /* Register the protocol name and description */ proto_rsl = proto_register_protocol("Radio Signalling Link (RSL)", @@ -3966,5 +4491,9 @@ register_dissector("gsm_abis_rsl", dissect_rsl, proto_rsl); + rsl_module = prefs_register_protocol(proto_rsl, proto_reg_handoff_rsl); + prefs_register_bool_preference(rsl_module, "use_ipaccess_rsl", + "Use nanoBTS definitions", + "Use ipaccess nanoBTS specific definitions for RSL", + &global_rsl_use_nano_bts); } - openbsc-0.15.0/wireshark/0005-rsl-hsl.patch000066400000000000000000000215761265565154000202400ustar00rootroot00000000000000Index: wireshark/epan/dissectors/packet-rsl.c =================================================================== --- wireshark.orig/epan/dissectors/packet-rsl.c 2011-09-06 13:57:15.000000000 +0200 +++ wireshark/epan/dissectors/packet-rsl.c 2011-09-06 14:11:09.000000000 +0200 @@ -2,6 +2,7 @@ * Routines for Radio Signalling Link (RSL) dissection. * * Copyright 2007, 2011, Anders Broman + * Copyright 2009-2011, Harald Welte * * $Id: packet-rsl.c 38413 2011-08-08 17:59:32Z wmeier $ * @@ -136,7 +137,14 @@ static int hf_rsl_cstat_lost_pkts = -1; static int hf_rsl_cstat_ia_jitter = -1; static int hf_rsl_cstat_avg_tx_dly = -1; - +/* HSL */ +static int hf_rsl_hsl_bts_serno = -1; +static int hf_rsl_hsl_bts_ver = -1; +static int hf_rsl_hsl_dsp_ver = -1; +static int hf_rsl_hsl_fpga_ver = -1; +static int hf_rsl_hsl_trau_remote_port = -1; +static int hf_rsl_hsl_trau_remote_ip = -1; +static int hf_rsl_hsl_tx_power = -1; /* Initialize the subtree pointers */ static int ett_rsl = -1; @@ -207,6 +215,7 @@ static proto_tree *top_tree; static dissector_handle_t gsm_a_ccch_handle; static dissector_handle_t gsm_a_dtap_handle; +static dissector_handle_t bssgp_handle; static gboolean is_si2q = FALSE; @@ -243,9 +252,11 @@ { 0x08, "TRX Management messages" }, { 0x16, "Location Services messages" }, { 0x3f, "ip.access Vendor Specific messages" }, + { 0x80, "HSL Vendor Specific messages" }, { 0, NULL } }; #define RSL_MSGDISC_IPACCESS 0x3f +#define RSL_MSGDISC_HSL 0x40 /* * 9.2 MESSAGE TYPE */ @@ -357,6 +368,22 @@ #define RSL_IE_IPAC_RTP_MPLEX 0xfd #define RSL_IE_IPAC_RTP_MPLEX_ID 0xfe +/* Vendor-Specific messages of HSL femtocell. There is no public documentation + * about those extensions, all information in this dissector is based on lawful + * protocol reverse enginering by Harald Welte */ +#define RSL_MSG_TYPE_HSL_IDENTIFY 0x80 +#define RSL_MSG_TYPE_HSL_CONN_TRAU 0x81 +#define RSL_MSG_TYPE_HSL_BSSGP 0x82 +#define RSL_MSG_TYPE_HSL_GPRS_TS_ALLOC 0x83 +#define RSL_MSG_TYPE_HSL_L1_PRIM 0x8a + +#define RSL_IE_HSL_BTS_SERNO 0xc0 +#define RSL_IE_HSL_TRAU_PARAMS 0xc1 +#define RSL_IE_HSL_TX_POWER 0xc4 +#define RSL_IE_HSL_BTS_VERSION 0xc5 +#define RSL_IE_HSL_DSP_VERSION 0xc6 +#define RSL_IE_HSL_FPGA_VERSION 0xc7 + static const value_string rsl_msg_type_vals[] = { /* 0 0 0 0 - - - - Radio Link Layer Management messages: */ { 0x01, "DATA REQuest" }, /* 8.3.1 */ @@ -438,6 +465,12 @@ { 0x77, "ip.access DLCX" }, { 0x78, "ip.access DLCX ACK" }, { 0x79, "ip.access DLCX NACK" }, + /* HSL */ + { 0x80, "HSL IDENTIFY" }, + { 0x81, "HSL CONNECT TRAU" }, + { 0x82, "HSL BSSGP" }, + { 0x83, "HSL GPRS TS ALLOC" }, + { 0x8a, "HSL TX SET POWER" }, { 0, NULL } }; @@ -578,6 +611,7 @@ Not used */ + /* ip.access */ { 0xe0, "SRTP Configuration" }, { 0xe1, "BSC Proxy UDP Port" }, { 0xe2, "BSC Multiplex Timeout" }, @@ -596,6 +630,13 @@ { 0xfc, "RTP Payload Type 2" }, { 0xfd, "RTP Multiplex" }, { 0xfe, "RTP Multiplex Identifier" }, + /* HSL */ + { 0xc0, "HSL Serial Number" }, + { 0xc1, "HSL TRAU Parameters" }, + { 0xc4, "HSL TX Power (dBm)" }, + { 0xc5, "HSL BTS SW Version" }, + { 0xc6, "HSL DSP SW Version" }, + { 0xc7, "HSL FPGA SW Version" }, { 0, NULL } }; @@ -3062,9 +3103,10 @@ guint8 msg_type; guint32 local_addr = 0; guint16 local_port = 0; + int old_visited; address src_addr; - msg_type = tvb_get_guint8(tvb, offset)&0x7f; + msg_type = tvb_get_guint8(tvb, offset)&0xff; offset++; #if 0 @@ -3203,20 +3245,62 @@ proto_tree_add_item(ie_tree, hf_rsl_cstat_avg_tx_dly, tvb, offset+24, 4, FALSE); break; + /* HSL */ + case RSL_IE_HSL_BTS_SERNO: + proto_tree_add_item(ie_tree, hf_rsl_hsl_bts_serno, tvb, + offset, len, FALSE); + col_append_fstr(pinfo->cinfo, COL_INFO, "SerNo %s ", + tvb_get_string(tvb, offset, len)); + break; + case RSL_IE_HSL_TRAU_PARAMS: + proto_tree_add_item(tree, hf_rsl_hsl_trau_remote_port, tvb, + offset+2, 2, TRUE); + local_port = tvb_get_letohs(tvb, offset+2); + proto_tree_add_item(tree, hf_rsl_hsl_trau_remote_ip, tvb, + offset+4, 4, FALSE); + local_addr = tvb_get_ipv4(tvb, offset+4); + break; + case RSL_IE_HSL_TX_POWER: + proto_tree_add_item(ie_tree, hf_rsl_hsl_tx_power, tvb, + offset, len, FALSE); + break; + case RSL_IE_HSL_BTS_VERSION: + proto_tree_add_item(ie_tree, hf_rsl_hsl_bts_ver, tvb, + offset, len, FALSE); + break; + case RSL_IE_HSL_DSP_VERSION: + proto_tree_add_item(ie_tree, hf_rsl_hsl_dsp_ver, tvb, + offset, len, FALSE); + break; + case RSL_IE_HSL_FPGA_VERSION: + proto_tree_add_item(ie_tree, hf_rsl_hsl_fpga_ver, tvb, + offset, len, FALSE); + break; + case RSL_IE_L3_INF: + if (msg_type == RSL_MSG_TYPE_HSL_BSSGP) { + tvbuff_t *next_tvb; + next_tvb = tvb_new_subset(tvb, offset, -1, len); + call_dissector(bssgp_handle, next_tvb, pinfo, tree); + } + break; } offset += len; } switch (msg_type) { case RSL_MSG_TYPE_IPAC_CRCX_ACK: + case RSL_MSG_TYPE_HSL_CONN_TRAU: /* Notify the RTP and RTCP dissectors about a new RTP stream */ src_addr.type = AT_IPv4; src_addr.len = 4; src_addr.data = (guint8 *)&local_addr; + old_visited = pinfo->fd->flags.visited; + pinfo->fd->flags.visited = 0; rtp_add_address(pinfo, &src_addr, local_port, 0, "GSM A-bis/IP", pinfo->fd->num, 0, NULL); rtcp_add_address(pinfo, &src_addr, local_port+1, 0, "GSM A-bis/IP", pinfo->fd->num); + pinfo->fd->flags.visited = old_visited; break; } return offset; @@ -3228,10 +3312,12 @@ guint8 msg_disc, msg_type; msg_disc = tvb_get_guint8(tvb, offset++) >> 1; - msg_type = tvb_get_guint8(tvb,offset)&0x7f; + msg_type = tvb_get_guint8(tvb,offset); proto_tree_add_item(tree, hf_rsl_msg_type, tvb, offset, 1, ENC_BIG_ENDIAN); - if (msg_disc == RSL_MSGDISC_IPACCESS) { + switch (msg_disc) { + case RSL_MSGDISC_HSL: + case RSL_MSGDISC_IPACCESS: offset = dissct_rsl_ipaccess_msg(tvb, pinfo, tree, offset); return offset; } @@ -3868,7 +3954,7 @@ col_set_str(pinfo->cinfo, COL_PROTOCOL, "RSL"); col_clear(pinfo->cinfo, COL_INFO); - msg_type = tvb_get_guint8(tvb,offset+1)&0x7f; + msg_type = tvb_get_guint8(tvb,offset+1); col_append_fstr(pinfo->cinfo, COL_INFO, "%s ",val_to_str(msg_type, rsl_msg_type_vals,"unknown %u")); @@ -3905,6 +3991,7 @@ gsm_a_ccch_handle = find_dissector("gsm_a_ccch"); gsm_a_dtap_handle = find_dissector("gsm_a_dtap"); + bssgp_handle = find_dissector("bssgp"); } /* Register the protocol with Wireshark */ @@ -3925,7 +4012,7 @@ }, { &hf_rsl_msg_type, { "Message type", "rsl.msg_type", - FT_UINT8, BASE_HEX_DEC, VALS(rsl_msg_type_vals), 0x7f, + FT_UINT8, BASE_HEX_DEC, VALS(rsl_msg_type_vals), 0xff, NULL, HFILL } }, { &hf_rsl_ie_id, @@ -4342,6 +4429,35 @@ { "Average Tx Delay", "rsl.ipacc.cstat.avg_tx_delay", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } }, + /* HSL */ + { &hf_rsl_hsl_bts_serno, + { "BTS Serial Number", "rsl.hsl.bts_serno", + FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL } + }, + { &hf_rsl_hsl_tx_power, + { "Transmit Power", "rsl.hsl.tx_power", + FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } + }, + { &hf_rsl_hsl_bts_ver, + { "BTS Version Number", "rsl.hsl.bts_ver", + FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL } + }, + { &hf_rsl_hsl_dsp_ver, + { "DSP Version Number", "rsl.hsl.dsp_ver", + FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL } + }, + { &hf_rsl_hsl_fpga_ver, + { "FPGA Version Number", "rsl.hsl.fpga_ver", + FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL } + }, + { &hf_rsl_hsl_trau_remote_port, + { "HSL TRAU Remote RTP Port", "rsl.hsl.trau.remote_port", + FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }, + }, + { &hf_rsl_hsl_trau_remote_ip, + { "HSL TRAU Remote IP Address", "rsl.hsl.trau.remote_ip", + FT_IPv4, BASE_NONE, NULL, 0x0, NULL, HFILL }, + }, }; static gint *ett[] = { &ett_rsl, @@ -4481,6 +4597,13 @@ RSL_ATT_TLVDEF(RSL_IE_IPAC_SPEECH_MODE, TLV_TYPE_TV, 0); RSL_ATT_TLVDEF(RSL_IE_IPAC_CONN_ID, TLV_TYPE_FIXED, 2); RSL_ATT_TLVDEF(RSL_IE_IPAC_RTP_PAYLOAD2,TLV_TYPE_TV, 0); + /* HSL */ + RSL_ATT_TLVDEF(RSL_IE_HSL_BTS_SERNO, TLV_TYPE_TLV, 0); + RSL_ATT_TLVDEF(RSL_IE_HSL_TRAU_PARAMS, TLV_TYPE_TLV, 0); + RSL_ATT_TLVDEF(RSL_IE_HSL_TX_POWER, TLV_TYPE_TV, 0); + RSL_ATT_TLVDEF(RSL_IE_HSL_BTS_VERSION, TLV_TYPE_TLV, 0); + RSL_ATT_TLVDEF(RSL_IE_HSL_DSP_VERSION, TLV_TYPE_TLV, 0); + RSL_ATT_TLVDEF(RSL_IE_HSL_FPGA_VERSION, TLV_TYPE_TLV, 0); /* Register the protocol name and description */ proto_rsl = proto_register_protocol("Radio Signalling Link (RSL)", openbsc-0.15.0/wireshark/0006-abis_oml-hsl.patch000066400000000000000000000024311265565154000212130ustar00rootroot00000000000000Index: wireshark/epan/dissectors/packet-gsm_abis_oml.c =================================================================== --- wireshark.orig/epan/dissectors/packet-gsm_abis_oml.c 2011-09-06 13:57:13.000000000 +0200 +++ wireshark/epan/dissectors/packet-gsm_abis_oml.c 2011-09-06 14:12:12.000000000 +0200 @@ -131,6 +131,7 @@ { ABIS_OM_MDISC_MMI, "MMI Transfer" }, { ABIS_OM_MDISC_TRAU, "TRAU O&M" }, { ABIS_OM_MDISC_MANUF, "Manufacturer specific" }, + { ABIS_OM_MDISC_FOM_HSL,"HSL Formatted O&M" }, }; /* TS 12.21 Chapter 8.1.1 */ @@ -1197,6 +1198,7 @@ switch (msg_disc) { case ABIS_OM_MDISC_FOM: + case ABIS_OM_MDISC_FOM_HSL: offset = dissect_oml_fom(tvb, pinfo, oml_tree, offset, ti); break; Index: wireshark/epan/dissectors/packet-gsm_abis_oml.h =================================================================== --- wireshark.orig/epan/dissectors/packet-gsm_abis_oml.h 2011-09-06 12:30:44.000000000 +0200 +++ wireshark/epan/dissectors/packet-gsm_abis_oml.h 2011-09-06 14:12:12.000000000 +0200 @@ -40,6 +40,7 @@ #define ABIS_OM_MDISC_MMI 0x40 #define ABIS_OM_MDISC_TRAU 0x20 #define ABIS_OM_MDISC_MANUF 0x10 +#define ABIS_OM_MDISC_FOM_HSL 0x81 #define ABIS_OM_PLACEMENT_ONLY 0x80 #define ABIS_OM_PLACEMENT_FIRST 0x40 #define ABIS_OM_PLACEMENT_MIDDLE 0x20 openbsc-0.15.0/wireshark/README000066400000000000000000000014571265565154000161250ustar00rootroot00000000000000These are patches to wireshark to add/enhance the GSM support. == Basic == The patches apply to SVN revision r38894 and were created using git format-patch. They can be either applied via the patch command or using git am on a git clone of wireshark. == Rebasing with git == It is the easiest to checkout the above revision, apply the patches and then use git rebase git-svn to get to the latest svn version. == patches == 0001-abis_oml.patch Add support for Abis OML 0002-ericsson_rbs2409.patch Add support for Ericsson Abis over IP 0003-lucent-hnb.patch Add support for Lucent HomeNodeB 0004-rsl-ipaccess.patch Add IPaccess RSL extensions 0005-rsl-hsl.patch Add support for HSL RSL extensions 0006-abis_oml-hsl.patch Add support for HSL OML