aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am4
-rw-r--r--src/gprs/.gitignore2
-rw-r--r--src/gprs/Makefile.am133
-rw-r--r--src/gprs/crc24.c67
-rw-r--r--src/gprs/gb_proxy.c1437
-rw-r--r--src/gprs/gb_proxy_main.c315
-rw-r--r--src/gprs/gb_proxy_patch.c458
-rw-r--r--src/gprs/gb_proxy_peer.c218
-rw-r--r--src/gprs/gb_proxy_tlli.c723
-rw-r--r--src/gprs/gb_proxy_vty.c852
-rw-r--r--src/gprs/gprs_gb_parse.c636
-rw-r--r--src/gprs/gprs_gmm.c2937
-rw-r--r--src/gprs/gprs_llc.c1132
-rw-r--r--src/gprs/gprs_llc_parse.c251
-rw-r--r--src/gprs/gprs_llc_vty.c116
-rw-r--r--src/gprs/gprs_llc_xid.c281
-rw-r--r--src/gprs/gprs_sgsn.c896
-rw-r--r--src/gprs/gprs_sndcp.c1258
-rw-r--r--src/gprs/gprs_sndcp_comp.c323
-rw-r--r--src/gprs/gprs_sndcp_dcomp.c358
-rw-r--r--src/gprs/gprs_sndcp_pcomp.c282
-rw-r--r--src/gprs/gprs_sndcp_vty.c71
-rw-r--r--src/gprs/gprs_sndcp_xid.c1822
-rw-r--r--src/gprs/gprs_subscriber.c922
-rw-r--r--src/gprs/gprs_utils.c274
-rw-r--r--src/gprs/gtphub.c2931
-rw-r--r--src/gprs/gtphub_ares.c220
-rw-r--r--src/gprs/gtphub_main.c357
-rw-r--r--src/gprs/gtphub_sock.c60
-rw-r--r--src/gprs/gtphub_vty.c613
-rw-r--r--src/gprs/osmo_sgsn.cfg23
-rw-r--r--src/gprs/sgsn_ares.c173
-rw-r--r--src/gprs/sgsn_auth.c312
-rw-r--r--src/gprs/sgsn_cdr.c258
-rw-r--r--src/gprs/sgsn_ctrl.c69
-rw-r--r--src/gprs/sgsn_libgtp.c870
-rw-r--r--src/gprs/sgsn_main.c472
-rw-r--r--src/gprs/sgsn_vty.c1340
-rw-r--r--src/gprs/slhc.c813
-rw-r--r--src/gprs/v42bis.c767
-rw-r--r--src/libbsc/abis_rsl.c3
-rw-r--r--src/libbsc/bsc_api.c3
-rw-r--r--src/libbsc/bsc_init.c1
-rw-r--r--src/libbsc/bsc_vty.c28
-rw-r--r--src/libbsc/chan_alloc.c1
-rw-r--r--src/libbsc/gsm_04_08_utils.c2
-rw-r--r--src/libbsc/handover_logic.c6
-rw-r--r--src/libbsc/net_init.c1
-rw-r--r--src/libbsc/paging.c1
-rw-r--r--src/libbsc/system_information.c4
-rw-r--r--src/libcommon-cs/common_cs.c3
-rw-r--r--src/libcommon/Makefile.am18
-rw-r--r--src/libcommon/gsm_data.c1
-rw-r--r--src/libcommon/gsm_subscriber_base.c27
-rw-r--r--src/libcommon/gsup_client.c347
-rw-r--r--src/libcommon/gsup_test_client.c298
-rw-r--r--src/libfilter/bsc_msg_filter.c2
-rw-r--r--src/libmsc/Makefile.am74
-rw-r--r--src/libmsc/a_iface.c578
-rw-r--r--src/libmsc/a_iface_bssap.c703
-rw-r--r--src/libmsc/auth.c42
-rw-r--r--src/libmsc/ctrl_commands.c88
-rw-r--r--src/libmsc/db.c851
-rw-r--r--src/libmsc/gsm_04_08.c3482
-rw-r--r--src/libmsc/gsm_04_11.c1074
-rw-r--r--src/libmsc/gsm_04_80.c155
-rw-r--r--src/libmsc/gsm_subscriber.c190
-rw-r--r--src/libmsc/iu_dummy.c93
-rw-r--r--src/libmsc/iucs.c189
-rw-r--r--src/libmsc/iucs_ranap.c104
-rw-r--r--src/libmsc/meas_feed.c168
-rw-r--r--src/libmsc/meas_feed.h12
-rw-r--r--src/libmsc/mncc.c107
-rw-r--r--src/libmsc/mncc_builtin.c383
-rw-r--r--src/libmsc/mncc_sock.c317
-rw-r--r--src/libmsc/msc_ifaces.c423
-rw-r--r--src/libmsc/msc_vty.c162
-rw-r--r--src/libmsc/osmo_msc.c387
-rw-r--r--src/libmsc/rrlp.c104
-rw-r--r--src/libmsc/silent_call.c165
-rw-r--r--src/libmsc/smpp_openbsc.c765
-rw-r--r--src/libmsc/smpp_smsc.c1030
-rw-r--r--src/libmsc/smpp_smsc.h166
-rw-r--r--src/libmsc/smpp_utils.c62
-rw-r--r--src/libmsc/smpp_vty.c612
-rw-r--r--src/libmsc/sms_queue.c578
-rw-r--r--src/libmsc/subscr_conn.c359
-rw-r--r--src/libmsc/transaction.c214
-rw-r--r--src/libmsc/ussd.c103
-rw-r--r--src/libmsc/vty_interface_layer3.c885
-rw-r--r--src/libvlr/Makefile.am19
-rw-r--r--src/libvlr/vlr.c1115
-rw-r--r--src/libvlr/vlr_access_req_fsm.c795
-rw-r--r--src/libvlr/vlr_access_req_fsm.h17
-rw-r--r--src/libvlr/vlr_auth_fsm.c605
-rw-r--r--src/libvlr/vlr_auth_fsm.h52
-rw-r--r--src/libvlr/vlr_core.h21
-rw-r--r--src/libvlr/vlr_lu_fsm.c1449
-rw-r--r--src/libvlr/vlr_lu_fsm.h18
-rw-r--r--src/osmo-bsc/Makefile.am1
-rw-r--r--src/osmo-bsc/osmo_bsc_api.c3
-rw-r--r--src/osmo-bsc/osmo_bsc_bssap.c18
-rw-r--r--src/osmo-bsc/osmo_bsc_filter.c2
-rw-r--r--src/osmo-bsc/osmo_bsc_main.c2
-rw-r--r--src/osmo-bsc_nat/Makefile.am4
-rw-r--r--src/osmo-bsc_nat/bsc_mgcp_utils.c4
-rw-r--r--src/osmo-bsc_nat/bsc_nat_ctrl.c1
-rw-r--r--src/osmo-bsc_nat/bsc_nat_rewrite.c2
-rw-r--r--src/osmo-bsc_nat/bsc_nat_vty.c4
-rw-r--r--src/osmo-bsc_nat/bsc_ussd.c3
-rw-r--r--src/osmo-msc/Makefile.am58
-rw-r--r--src/osmo-msc/msc_main.c584
112 files changed, 54 insertions, 45140 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 70e53ac3e..9a26a7b9e 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -22,9 +22,7 @@ AM_LDFLAGS = \
# Libraries
SUBDIRS = \
libcommon \
- libvlr \
libbsc \
- libmsc \
libtrau \
libfilter \
libcommon-cs \
@@ -32,10 +30,8 @@ SUBDIRS = \
# Programs
SUBDIRS += \
- osmo-msc \
utils \
ipaccess \
- gprs \
$(NULL)
# Conditional Programs
diff --git a/src/gprs/.gitignore b/src/gprs/.gitignore
deleted file mode 100644
index 7cfefbac2..000000000
--- a/src/gprs/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-gsn_restart
-osmo_*.cfg*
diff --git a/src/gprs/Makefile.am b/src/gprs/Makefile.am
deleted file mode 100644
index 39a4c12a7..000000000
--- a/src/gprs/Makefile.am
+++ /dev/null
@@ -1,133 +0,0 @@
-AM_CPPFLAGS = \
- $(all_includes) \
- -I$(top_srcdir)/include \
- -I$(top_builddir) \
- $(NULL)
-
-AM_CFLAGS = \
- -Wall \
- -fno-strict-aliasing \
- $(LIBOSMOCORE_CFLAGS) \
- $(LIBOSMOGSM_CFLAGS) \
- $(LIBOSMOVTY_CFLAGS) \
- $(LIBOSMOCTRL_CFLAGS) \
- $(LIBOSMOABIS_CFLAGS) \
- $(LIBOSMOGB_CFLAGS) \
- $(COVERAGE_CFLAGS) \
- $(LIBCARES_CFLAGS) \
- $(LIBCRYPTO_CFLAGS) \
- $(LIBGTP_CFLAGS) \
- $(NULL)
-if BUILD_IU
-AM_CFLAGS += \
- $(LIBASN1C_CFLAGS) \
- $(LIBOSMOSIGTRAN_CFLAGS) \
- $(LIBOSMORANAP_CFLAGS) \
- $(NULL)
-endif
-
-OSMO_LIBS = \
- $(LIBOSMOCORE_LIBS) \
- $(LIBOSMOABIS_LIBS) \
- $(LIBOSMOGSM_LIBS) \
- $(LIBOSMOVTY_LIBS) \
- $(LIBOSMOCTRL_LIBS) \
- $(LIBOSMOGB_LIBS) \
- $(LIBGTP_LIBS) \
- $(LIBOSMOSIGTRAN_LIBS) \
- $(NULL)
-
-bin_PROGRAMS = \
- osmo-gbproxy \
- $(NULL)
-if HAVE_LIBGTP
-if HAVE_LIBCARES
-bin_PROGRAMS += \
- osmo-sgsn \
- osmo-gtphub \
- $(NULL)
-endif
-endif
-
-osmo_gbproxy_SOURCES = \
- gb_proxy.c \
- gb_proxy_main.c \
- gb_proxy_vty.c \
- gb_proxy_patch.c \
- gb_proxy_tlli.c \
- gb_proxy_peer.c \
- gprs_gb_parse.c \
- gprs_llc_parse.c \
- crc24.c \
- gprs_utils.c \
- $(NULL)
-osmo_gbproxy_LDADD = \
- $(top_builddir)/src/libcommon/libcommon.a \
- $(OSMO_LIBS) \
- $(LIBCRYPTO_LIBS) \
- -lrt \
- $(NULL)
-
-osmo_sgsn_SOURCES = \
- gprs_gmm.c \
- gprs_sgsn.c \
- gprs_sndcp.c \
- gprs_sndcp_comp.c \
- gprs_sndcp_dcomp.c \
- gprs_sndcp_pcomp.c \
- gprs_sndcp_vty.c \
- gprs_sndcp_xid.c \
- sgsn_main.c \
- sgsn_vty.c \
- sgsn_libgtp.c \
- gprs_llc.c \
- gprs_llc_parse.c \
- gprs_llc_vty.c \
- crc24.c \
- sgsn_ctrl.c \
- sgsn_auth.c \
- gprs_subscriber.c \
- gprs_utils.c \
- sgsn_cdr.c \
- sgsn_ares.c \
- slhc.c \
- gprs_llc_xid.c \
- v42bis.c \
- $(NULL)
-osmo_sgsn_LDADD = \
- $(top_builddir)/src/libcommon/libcommon.a \
- $(OSMO_LIBS) \
- $(LIBOSMOABIS_LIBS) \
- $(LIBCARES_LIBS) \
- $(LIBCRYPTO_LIBS) \
- $(LIBGTP_LIBS) \
- -lrt \
- -lm \
- $(NULL)
-if BUILD_IU
-osmo_sgsn_LDADD += \
- $(LIBOSMOSIGTRAN_LIBS) \
- $(LIBOSMORANAP_LIBS) \
- $(LIBASN1C_LIBS) \
- $(NULL)
-endif
-
-osmo_gtphub_SOURCES = \
- gtphub_main.c \
- gtphub.c \
- gtphub_sock.c \
- gtphub_ares.c \
- gtphub_vty.c \
- sgsn_ares.c \
- gprs_utils.c \
- $(NULL)
-osmo_gtphub_LDADD = \
- $(top_builddir)/src/libcommon/libcommon.a \
- $(LIBOSMOCORE_LIBS) \
- $(LIBOSMOGSM_LIBS) \
- $(LIBOSMOVTY_LIBS) \
- $(LIBCARES_LIBS) \
- $(LIBGTP_LIBS) \
- $(LIBOSMOSIGTRAN_LIBS) \
- -lrt \
- $(NULL)
diff --git a/src/gprs/crc24.c b/src/gprs/crc24.c
deleted file mode 100644
index 1a420ed66..000000000
--- a/src/gprs/crc24.c
+++ /dev/null
@@ -1,67 +0,0 @@
-/* GPRS LLC CRC-24 Implementation */
-
-/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <openbsc/crc24.h>
-
-/* CRC24 table - FCS */
-static const uint32_t tbl_crc24[256] = {
- 0x00000000, 0x00d6a776, 0x00f64557, 0x0020e221, 0x00b78115, 0x00612663, 0x0041c442, 0x00976334,
- 0x00340991, 0x00e2aee7, 0x00c24cc6, 0x0014ebb0, 0x00838884, 0x00552ff2, 0x0075cdd3, 0x00a36aa5,
- 0x00681322, 0x00beb454, 0x009e5675, 0x0048f103, 0x00df9237, 0x00093541, 0x0029d760, 0x00ff7016,
- 0x005c1ab3, 0x008abdc5, 0x00aa5fe4, 0x007cf892, 0x00eb9ba6, 0x003d3cd0, 0x001ddef1, 0x00cb7987,
- 0x00d02644, 0x00068132, 0x00266313, 0x00f0c465, 0x0067a751, 0x00b10027, 0x0091e206, 0x00474570,
- 0x00e42fd5, 0x003288a3, 0x00126a82, 0x00c4cdf4, 0x0053aec0, 0x008509b6, 0x00a5eb97, 0x00734ce1,
- 0x00b83566, 0x006e9210, 0x004e7031, 0x0098d747, 0x000fb473, 0x00d91305, 0x00f9f124, 0x002f5652,
- 0x008c3cf7, 0x005a9b81, 0x007a79a0, 0x00acded6, 0x003bbde2, 0x00ed1a94, 0x00cdf8b5, 0x001b5fc3,
- 0x00fb4733, 0x002de045, 0x000d0264, 0x00dba512, 0x004cc626, 0x009a6150, 0x00ba8371, 0x006c2407,
- 0x00cf4ea2, 0x0019e9d4, 0x00390bf5, 0x00efac83, 0x0078cfb7, 0x00ae68c1, 0x008e8ae0, 0x00582d96,
- 0x00935411, 0x0045f367, 0x00651146, 0x00b3b630, 0x0024d504, 0x00f27272, 0x00d29053, 0x00043725,
- 0x00a75d80, 0x0071faf6, 0x005118d7, 0x0087bfa1, 0x0010dc95, 0x00c67be3, 0x00e699c2, 0x00303eb4,
- 0x002b6177, 0x00fdc601, 0x00dd2420, 0x000b8356, 0x009ce062, 0x004a4714, 0x006aa535, 0x00bc0243,
- 0x001f68e6, 0x00c9cf90, 0x00e92db1, 0x003f8ac7, 0x00a8e9f3, 0x007e4e85, 0x005eaca4, 0x00880bd2,
- 0x00437255, 0x0095d523, 0x00b53702, 0x00639074, 0x00f4f340, 0x00225436, 0x0002b617, 0x00d41161,
- 0x00777bc4, 0x00a1dcb2, 0x00813e93, 0x005799e5, 0x00c0fad1, 0x00165da7, 0x0036bf86, 0x00e018f0,
- 0x00ad85dd, 0x007b22ab, 0x005bc08a, 0x008d67fc, 0x001a04c8, 0x00cca3be, 0x00ec419f, 0x003ae6e9,
- 0x00998c4c, 0x004f2b3a, 0x006fc91b, 0x00b96e6d, 0x002e0d59, 0x00f8aa2f, 0x00d8480e, 0x000eef78,
- 0x00c596ff, 0x00133189, 0x0033d3a8, 0x00e574de, 0x007217ea, 0x00a4b09c, 0x008452bd, 0x0052f5cb,
- 0x00f19f6e, 0x00273818, 0x0007da39, 0x00d17d4f, 0x00461e7b, 0x0090b90d, 0x00b05b2c, 0x0066fc5a,
- 0x007da399, 0x00ab04ef, 0x008be6ce, 0x005d41b8, 0x00ca228c, 0x001c85fa, 0x003c67db, 0x00eac0ad,
- 0x0049aa08, 0x009f0d7e, 0x00bfef5f, 0x00694829, 0x00fe2b1d, 0x00288c6b, 0x00086e4a, 0x00dec93c,
- 0x0015b0bb, 0x00c317cd, 0x00e3f5ec, 0x0035529a, 0x00a231ae, 0x007496d8, 0x005474f9, 0x0082d38f,
- 0x0021b92a, 0x00f71e5c, 0x00d7fc7d, 0x00015b0b, 0x0096383f, 0x00409f49, 0x00607d68, 0x00b6da1e,
- 0x0056c2ee, 0x00806598, 0x00a087b9, 0x007620cf, 0x00e143fb, 0x0037e48d, 0x001706ac, 0x00c1a1da,
- 0x0062cb7f, 0x00b46c09, 0x00948e28, 0x0042295e, 0x00d54a6a, 0x0003ed1c, 0x00230f3d, 0x00f5a84b,
- 0x003ed1cc, 0x00e876ba, 0x00c8949b, 0x001e33ed, 0x008950d9, 0x005ff7af, 0x007f158e, 0x00a9b2f8,
- 0x000ad85d, 0x00dc7f2b, 0x00fc9d0a, 0x002a3a7c, 0x00bd5948, 0x006bfe3e, 0x004b1c1f, 0x009dbb69,
- 0x0086e4aa, 0x005043dc, 0x0070a1fd, 0x00a6068b, 0x003165bf, 0x00e7c2c9, 0x00c720e8, 0x0011879e,
- 0x00b2ed3b, 0x00644a4d, 0x0044a86c, 0x00920f1a, 0x00056c2e, 0x00d3cb58, 0x00f32979, 0x00258e0f,
- 0x00eef788, 0x003850fe, 0x0018b2df, 0x00ce15a9, 0x0059769d, 0x008fd1eb, 0x00af33ca, 0x007994bc,
- 0x00dafe19, 0x000c596f, 0x002cbb4e, 0x00fa1c38, 0x006d7f0c, 0x00bbd87a, 0x009b3a5b, 0x004d9d2d
-};
-
-#define INIT_CRC24 0xffffff
-
-uint32_t crc24_calc(uint32_t fcs, uint8_t *cp, unsigned int len)
-{
- while (len--)
- fcs = (fcs >> 8) ^ tbl_crc24[(fcs ^ *cp++) & 0xff];
- return fcs;
-}
diff --git a/src/gprs/gb_proxy.c b/src/gprs/gb_proxy.c
deleted file mode 100644
index d95139f8d..000000000
--- a/src/gprs/gb_proxy.c
+++ /dev/null
@@ -1,1437 +0,0 @@
-/* NS-over-IP proxy */
-
-/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
- * (C) 2010-2013 by On-Waves
- * (C) 2013 by Holger Hans Peter Freyther
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <unistd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <getopt.h>
-#include <errno.h>
-#include <sys/fcntl.h>
-#include <sys/stat.h>
-#include <arpa/inet.h>
-#include <time.h>
-
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/select.h>
-#include <osmocom/core/rate_ctr.h>
-#include <osmocom/core/stats.h>
-
-#include <osmocom/gprs/gprs_ns.h>
-#include <osmocom/gprs/gprs_bssgp.h>
-
-#include <osmocom/gsm/gsm_utils.h>
-
-#include <openbsc/signal.h>
-#include <openbsc/debug.h>
-#include <openbsc/gprs_gb_parse.h>
-#include <openbsc/gb_proxy.h>
-
-#include <openbsc/gprs_llc.h>
-#include <openbsc/gsm_04_08.h>
-#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
-#include <openbsc/gprs_utils.h>
-
-#include <openssl/rand.h>
-
-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,
- .class_id = OSMO_STATS_CLASS_GLOBAL,
-};
-
-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 {
- if (RAND_bytes((uint8_t *) &bss_ptmsi, sizeof(bss_ptmsi)) != 1) {
- bss_ptmsi = GSM_RESERVED_TMSI;
- break;
- }
-
- 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... */
- if (RAND_bytes((uint8_t *) &sgsn_tlli, sizeof(sgsn_tlli)) != 1) {
- sgsn_tlli = 0;
- break;
- }
-
- 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 int 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);
-
- rc = gbproxy_update_link_state_after(peer, link_info, now,
- &tmp_parse_ctx);
- if (rc == 1) {
- LOGP(DLLC, LOGL_NOTICE, "link_info deleted while flushing stored messages\n");
- msgb_free(stored_msg);
- return -1;
- }
-
- 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);
- }
-
- return 0;
-}
-
-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_name("GSM 04.08 ACQ IMSI");
- 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_name("GSM 04.08 DET ACC");
- 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 &&
- gsm48_hdr_pdisc(parse_ctx->g48_hdr) == GSM48_PDISC_MM_GPRS &&
- gsm48_hdr_msg_type(parse_ctx->g48_hdr) == GSM48_MT_GMM_ID_RESP;
-
- /* The IMSI is now available. If flushing the messages fails,
- * then link_info has been deleted and we should return
- * immediately. */
- if (gbproxy_flush_stored_messages(peer, msg, now, link_info,
- parse_ctx) < 0)
- return 0;
-
- 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;
- case GSM48_MT_GMM_DETACH_REQ:
- rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_DETACH_REQS]);
- break;
- case GSM48_MT_GMM_ATTACH_COMPL:
- rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_ATTACH_COMPLS]);
- break;
- case GSM48_MT_GMM_RA_UPD_REQ:
- rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_RA_UPD_REQS]);
- break;
- case GSM48_MT_GMM_RA_UPD_COMPL:
- rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_RA_UPD_COMPLS]);
- break;
- case GSM48_MT_GMM_STATUS:
- rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_GMM_STATUS_BSS]);
- break;
- case GSM48_MT_GSM_ACT_PDP_REQ:
- rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_PDP_ACT_REQS]);
- break;
- case GSM48_MT_GSM_DEACT_PDP_REQ:
- rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_PDP_DEACT_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_ACK:
- rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_ATTACH_ACKS]);
- break;
- case GSM48_MT_GMM_ATTACH_REJ:
- rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_ATTACH_REJS]);
- break;
- case GSM48_MT_GMM_DETACH_ACK:
- rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_DETACH_ACKS]);
- break;
- case GSM48_MT_GMM_RA_UPD_ACK:
- rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_RA_UPD_ACKS]);
- break;
- case GSM48_MT_GMM_RA_UPD_REJ:
- rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_RA_UPD_REJS]);
- break;
- case GSM48_MT_GMM_STATUS:
- rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_GMM_STATUS_SGSN]);
- break;
- case GSM48_MT_GSM_ACT_PDP_ACK:
- rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_PDP_ACT_ACKS]);
- break;
- case GSM48_MT_GSM_ACT_PDP_REJ:
- rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_PDP_ACT_REJS]);
- break;
- case GSM48_MT_GSM_DEACT_PDP_ACK:
- rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_PDP_DEACT_ACKS]);
- 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);
-
- return 0;
-}
diff --git a/src/gprs/gb_proxy_main.c b/src/gprs/gb_proxy_main.c
deleted file mode 100644
index 69a93b6f7..000000000
--- a/src/gprs/gb_proxy_main.c
+++ /dev/null
@@ -1,315 +0,0 @@
-/* NS-over-IP proxy */
-
-/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
- * (C) 2010 by On-Waves
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <unistd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <getopt.h>
-#include <errno.h>
-#include <signal.h>
-#include <sys/fcntl.h>
-#include <sys/stat.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-
-#include <osmocom/core/application.h>
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/select.h>
-#include <osmocom/core/rate_ctr.h>
-#include <osmocom/core/stats.h>
-
-#include <osmocom/gprs/gprs_ns.h>
-#include <osmocom/gprs/gprs_bssgp.h>
-
-#include <openbsc/signal.h>
-#include <openbsc/debug.h>
-#include <openbsc/vty.h>
-#include <openbsc/gb_proxy.h>
-
-#include <osmocom/vty/command.h>
-#include <osmocom/vty/telnet_interface.h>
-#include <osmocom/vty/logging.h>
-#include <osmocom/vty/stats.h>
-#include <osmocom/vty/ports.h>
-
-#include "../../bscconfig.h"
-
-#define _GNU_SOURCE
-#include <getopt.h>
-
-void *tall_bsc_ctx;
-
-const char *openbsc_copyright =
- "Copyright (C) 2010 Harald Welte and On-Waves\r\n"
- "License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
- "This is free software: you are free to change and redistribute it.\r\n"
- "There is NO WARRANTY, to the extent permitted by law.\r\n";
-
-static char *config_file = "osmo_gbproxy.cfg";
-struct gbproxy_config gbcfg = {0};
-static int daemonize = 0;
-
-/* Pointer to the SGSN peer */
-extern struct gbprox_peer *gbprox_peer_sgsn;
-
-/* call-back function for the NS protocol */
-static int proxy_ns_cb(enum gprs_ns_evt event, struct gprs_nsvc *nsvc,
- struct msgb *msg, uint16_t bvci)
-{
- int rc = 0;
-
- switch (event) {
- case GPRS_NS_EVT_UNIT_DATA:
- rc = gbprox_rcvmsg(&gbcfg, msg, nsvc->nsei, bvci, nsvc->nsvci);
- break;
- default:
- LOGP(DGPRS, LOGL_ERROR, "SGSN: Unknown event %u from NS\n", event);
- if (msg)
- msgb_free(msg);
- rc = -EIO;
- break;
- }
- return rc;
-}
-
-static void signal_handler(int signal)
-{
- fprintf(stdout, "signal %u received\n", signal);
-
- switch (signal) {
- case SIGINT:
- osmo_signal_dispatch(SS_L_GLOBAL, S_L_GLOBAL_SHUTDOWN, NULL);
- sleep(1);
- exit(0);
- break;
- case SIGABRT:
- /* in case of abort, we want to obtain a talloc report
- * and then return to the caller, who will abort the process */
- case SIGUSR1:
- talloc_report(tall_vty_ctx, stderr);
- talloc_report_full(tall_bsc_ctx, stderr);
- break;
- case SIGUSR2:
- talloc_report_full(tall_vty_ctx, stderr);
- break;
- default:
- break;
- }
-}
-
-static void print_usage()
-{
- printf("Usage: bsc_hack\n");
-}
-
-static void print_help()
-{
- printf(" Some useful help...\n");
- printf(" -h --help this text\n");
- printf(" -d option --debug=DNS:DGPRS,0:0 enable debugging\n");
- printf(" -D --daemonize Fork the process into a background daemon\n");
- printf(" -c --config-file filename The config file to use.\n");
- printf(" -s --disable-color\n");
- printf(" -T --timestamp Prefix every log line with a timestamp\n");
- printf(" -V --version. Print the version of OpenBSC.\n");
- printf(" -e --log-level number. Set a global loglevel.\n");
-}
-
-static void handle_options(int argc, char **argv)
-{
- while (1) {
- int option_index = 0, c;
- static struct option long_options[] = {
- { "help", 0, 0, 'h' },
- { "debug", 1, 0, 'd' },
- { "daemonize", 0, 0, 'D' },
- { "config-file", 1, 0, 'c' },
- { "disable-color", 0, 0, 's' },
- { "timestamp", 0, 0, 'T' },
- { "version", 0, 0, 'V' },
- { "log-level", 1, 0, 'e' },
- { 0, 0, 0, 0 }
- };
-
- c = getopt_long(argc, argv, "hd:Dc:sTVe:",
- long_options, &option_index);
- if (c == -1)
- break;
-
- switch (c) {
- case 'h':
- print_usage();
- print_help();
- exit(0);
- case 's':
- log_set_use_color(osmo_stderr_target, 0);
- break;
- case 'd':
- log_parse_category_mask(osmo_stderr_target, optarg);
- break;
- case 'D':
- daemonize = 1;
- break;
- case 'c':
- config_file = optarg;
- break;
- case 'T':
- log_set_print_timestamp(osmo_stderr_target, 1);
- break;
- case 'e':
- log_set_log_level(osmo_stderr_target, atoi(optarg));
- break;
- case 'V':
- print_version(1);
- exit(0);
- break;
- default:
- break;
- }
- }
-}
-
-extern int bsc_vty_go_parent(struct vty *vty);
-
-static struct vty_app_info vty_info = {
- .name = "OsmoGbProxy",
- .version = PACKAGE_VERSION,
- .go_parent_cb = bsc_vty_go_parent,
- .is_config_node = bsc_vty_is_config_node,
-};
-
-/* default categories */
-static struct log_info_cat gprs_categories[] = {
- [DGPRS] = {
- .name = "DGPRS",
- .description = "GPRS Packet Service",
- .enabled = 1, .loglevel = LOGL_DEBUG,
- },
- [DNS] = {
- .name = "DNS",
- .description = "GPRS Network Service (NS)",
- .enabled = 1, .loglevel = LOGL_INFO,
- },
- [DBSSGP] = {
- .name = "DBSSGP",
- .description = "GPRS BSS Gateway Protocol (BSSGP)",
- .enabled = 1, .loglevel = LOGL_DEBUG,
- },
-};
-
-static const struct log_info gprs_log_info = {
- .filter_fn = gprs_log_filter_fn,
- .cat = gprs_categories,
- .num_cat = ARRAY_SIZE(gprs_categories),
-};
-
-int main(int argc, char **argv)
-{
- struct gsm_network dummy_network;
- int rc;
-
- tall_bsc_ctx = talloc_named_const(NULL, 0, "nsip_proxy");
- msgb_talloc_ctx_init(tall_bsc_ctx, 0);
-
- signal(SIGINT, &signal_handler);
- signal(SIGABRT, &signal_handler);
- signal(SIGUSR1, &signal_handler);
- signal(SIGUSR2, &signal_handler);
- osmo_init_ignore_signals();
-
- osmo_init_logging(&gprs_log_info);
-
- vty_info.copyright = openbsc_copyright;
- vty_init(&vty_info);
- logging_vty_add_cmds(NULL);
- osmo_stats_vty_add_cmds(&gprs_log_info);
- gbproxy_vty_init();
-
- handle_options(argc, argv);
-
- rate_ctr_init(tall_bsc_ctx);
- osmo_stats_init(tall_bsc_ctx);
-
- bssgp_nsi = gprs_ns_instantiate(&proxy_ns_cb, tall_bsc_ctx);
- if (!bssgp_nsi) {
- LOGP(DGPRS, LOGL_ERROR, "Unable to instantiate NS\n");
- exit(1);
- }
- gbproxy_init_config(&gbcfg);
- gbcfg.nsi = bssgp_nsi;
- gprs_ns_vty_init(bssgp_nsi);
- gprs_ns_set_log_ss(DNS);
- bssgp_set_log_ss(DBSSGP);
- osmo_signal_register_handler(SS_L_NS, &gbprox_signal, &gbcfg);
-
- rc = gbproxy_parse_config(config_file, &gbcfg);
- if (rc < 0) {
- LOGP(DGPRS, LOGL_FATAL, "Cannot parse config file\n");
- exit(2);
- }
-
- /* start telnet after reading config for vty_get_bind_addr() */
- rc = telnet_init_dynif(tall_bsc_ctx, &dummy_network,
- vty_get_bind_addr(), OSMO_VTY_PORT_GBPROXY);
- if (rc < 0)
- exit(1);
-
- if (!gprs_nsvc_by_nsei(gbcfg.nsi, gbcfg.nsip_sgsn_nsei)) {
- LOGP(DGPRS, LOGL_FATAL, "You cannot proxy to NSEI %u "
- "without creating that NSEI before\n",
- gbcfg.nsip_sgsn_nsei);
- exit(2);
- }
-
- rc = gprs_ns_nsip_listen(bssgp_nsi);
- if (rc < 0) {
- LOGP(DGPRS, LOGL_FATAL, "Cannot bind/listen on NSIP socket\n");
- exit(2);
- }
-
- rc = gprs_ns_frgre_listen(bssgp_nsi);
- if (rc < 0) {
- LOGP(DGPRS, LOGL_FATAL, "Cannot bind/listen GRE "
- "socket. Do you have CAP_NET_RAW?\n");
- exit(2);
- }
-
- if (daemonize) {
- rc = osmo_daemonize();
- if (rc < 0) {
- perror("Error during daemonize");
- exit(1);
- }
- }
-
- /* Reset all the persistent NS-VCs that we've read from the config */
- gbprox_reset_persistent_nsvcs(bssgp_nsi);
-
- while (1) {
- rc = osmo_select_main(0);
- if (rc < 0)
- exit(3);
- }
-
- exit(0);
-}
diff --git a/src/gprs/gb_proxy_patch.c b/src/gprs/gb_proxy_patch.c
deleted file mode 100644
index 7bddc4494..000000000
--- a/src/gprs/gb_proxy_patch.c
+++ /dev/null
@@ -1,458 +0,0 @@
-/* Gb-proxy message patching */
-
-/* (C) 2014 by On-Waves
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <openbsc/gb_proxy.h>
-
-#include <openbsc/gprs_utils.h>
-#include <openbsc/gprs_gb_parse.h>
-
-#include <openbsc/gsm_data.h>
-#include <openbsc/debug.h>
-
-#include <osmocom/gprs/protocol/gsm_08_18.h>
-#include <osmocom/core/rate_ctr.h>
-
-/* 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;
-}
-
diff --git a/src/gprs/gb_proxy_peer.c b/src/gprs/gb_proxy_peer.c
deleted file mode 100644
index 5365ff0fa..000000000
--- a/src/gprs/gb_proxy_peer.c
+++ /dev/null
@@ -1,218 +0,0 @@
-/* Gb proxy peer handling */
-
-/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
- * (C) 2010-2013 by On-Waves
- * (C) 2013 by Holger Hans Peter Freyther
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <openbsc/gb_proxy.h>
-
-#include <openbsc/gsm_data.h>
-#include <openbsc/gsm_data_shared.h>
-#include <openbsc/debug.h>
-
-#include <osmocom/gprs/protocol/gsm_08_18.h>
-#include <osmocom/core/rate_ctr.h>
-#include <osmocom/core/stats.h>
-#include <osmocom/core/talloc.h>
-
-#include <string.h>
-
-static const struct rate_ctr_desc peer_ctr_description[] = {
- { "blocked", "BVC Block " },
- { "unblocked", "BVC Unblock " },
- { "dropped", "BVC blocked, dropped packet " },
- { "inv-nsei", "NSEI mismatch " },
- { "tx-err", "NS Transmission error " },
- { "raid-mod.bss", "RAID patched (BSS )" },
- { "raid-mod.sgsn", "RAID patched (SGSN)" },
- { "apn-mod.sgsn", "APN patched " },
- { "tlli-mod.bss", "TLLI patched (BSS )" },
- { "tlli-mod.sgsn", "TLLI patched (SGSN)" },
- { "ptmsi-mod.bss", "P-TMSI patched (BSS )" },
- { "ptmsi-mod.sgsn","P-TMSI patched (SGSN)" },
- { "mod-crypt-err", "Patch error: encrypted " },
- { "mod-err", "Patch error: other " },
- { "attach-reqs", "Attach Request count " },
- { "attach-rejs", "Attach Reject count " },
- { "attach-acks", "Attach Accept count " },
- { "attach-cpls", "Attach Completed count " },
- { "ra-upd-reqs", "RoutingArea Update Request count" },
- { "ra-upd-rejs", "RoutingArea Update Reject count " },
- { "ra-upd-acks", "RoutingArea Update Accept count " },
- { "ra-upd-cpls", "RoutingArea Update Compltd count" },
- { "gmm-status", "GMM Status count (BSS)" },
- { "gmm-status", "GMM Status count (SGSN)" },
- { "detach-reqs", "Detach Request count " },
- { "detach-acks", "Detach Accept count " },
- { "pdp-act-reqs", "PDP Activation Request count " },
- { "pdp-act-rejs", "PDP Activation Reject count " },
- { "pdp-act-acks", "PDP Activation Accept count " },
- { "pdp-deact-reqs","PDP Deactivation Request count " },
- { "pdp-deact-acks","PDP Deactivation Accept count " },
- { "tlli-unknown", "TLLI from SGSN unknown " },
- { "tlli-cache", "TLLI cache size " },
-};
-
-osmo_static_assert(ARRAY_SIZE(peer_ctr_description) == GBPROX_PEER_CTR_LAST, everything_described);
-
-static const struct rate_ctr_group_desc peer_ctrg_desc = {
- .group_name_prefix = "gbproxy.peer",
- .group_description = "GBProxy Peer Statistics",
- .num_ctr = ARRAY_SIZE(peer_ctr_description),
- .ctr_desc = peer_ctr_description,
- .class_id = OSMO_STATS_CLASS_PEER,
-};
-
-
-/* Find the gbprox_peer by its BVCI */
-struct gbproxy_peer *gbproxy_peer_by_bvci(struct gbproxy_config *cfg, uint16_t bvci)
-{
- struct gbproxy_peer *peer;
- llist_for_each_entry(peer, &cfg->bts_peers, list) {
- if (peer->bvci == bvci)
- return peer;
- }
- return NULL;
-}
-
-/* Find the gbprox_peer by its NSEI */
-struct gbproxy_peer *gbproxy_peer_by_nsei(struct gbproxy_config *cfg,
- uint16_t nsei)
-{
- struct gbproxy_peer *peer;
- llist_for_each_entry(peer, &cfg->bts_peers, list) {
- if (peer->nsei == nsei)
- return peer;
- }
- return NULL;
-}
-
-/* look-up a peer by its Routeing Area Identification (RAI) */
-struct gbproxy_peer *gbproxy_peer_by_rai(struct gbproxy_config *cfg,
- const uint8_t *ra)
-{
- struct gbproxy_peer *peer;
- llist_for_each_entry(peer, &cfg->bts_peers, list) {
- if (!memcmp(peer->ra, ra, 6))
- return peer;
- }
- return NULL;
-}
-
-/* look-up a peer by its Location Area Identification (LAI) */
-struct gbproxy_peer *gbproxy_peer_by_lai(struct gbproxy_config *cfg,
- const uint8_t *la)
-{
- struct gbproxy_peer *peer;
- llist_for_each_entry(peer, &cfg->bts_peers, list) {
- if (!memcmp(peer->ra, la, 5))
- return peer;
- }
- return NULL;
-}
-
-/* look-up a peer by its Location Area Code (LAC) */
-struct gbproxy_peer *gbproxy_peer_by_lac(struct gbproxy_config *cfg,
- const uint8_t *la)
-{
- struct gbproxy_peer *peer;
- llist_for_each_entry(peer, &cfg->bts_peers, list) {
- if (!memcmp(peer->ra + 3, la + 3, 2))
- return peer;
- }
- return NULL;
-}
-
-struct gbproxy_peer *gbproxy_peer_by_bssgp_tlv(struct gbproxy_config *cfg,
- struct tlv_parsed *tp)
-{
- if (TLVP_PRESENT(tp, BSSGP_IE_BVCI)) {
- uint16_t bvci;
-
- bvci = ntohs(tlvp_val16_unal(tp, BSSGP_IE_BVCI));
- if (bvci >= 2)
- return gbproxy_peer_by_bvci(cfg, bvci);
- }
-
- if (TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA)) {
- uint8_t *rai = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA);
- /* Only compare LAC part, since MCC/MNC are possibly patched.
- * Since the LAC of different BSS must be different when
- * MCC/MNC are patched, collisions shouldn't happen. */
- return gbproxy_peer_by_lac(cfg, rai);
- }
-
- if (TLVP_PRESENT(tp, BSSGP_IE_LOCATION_AREA)) {
- uint8_t *lai = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_LOCATION_AREA);
- return gbproxy_peer_by_lac(cfg, lai);
- }
-
- return NULL;
-}
-
-
-struct gbproxy_peer *gbproxy_peer_alloc(struct gbproxy_config *cfg, uint16_t bvci)
-{
- struct gbproxy_peer *peer;
-
- peer = talloc_zero(tall_bsc_ctx, struct gbproxy_peer);
- if (!peer)
- return NULL;
-
- peer->bvci = bvci;
- peer->ctrg = rate_ctr_group_alloc(peer, &peer_ctrg_desc, bvci);
- 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;
-}
-
diff --git a/src/gprs/gb_proxy_tlli.c b/src/gprs/gb_proxy_tlli.c
deleted file mode 100644
index 3b3b976a5..000000000
--- a/src/gprs/gb_proxy_tlli.c
+++ /dev/null
@@ -1,723 +0,0 @@
-/* Gb-proxy TLLI state handling */
-
-/* (C) 2014 by On-Waves
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <osmocom/gsm/gsm48.h>
-
-#include <openbsc/gb_proxy.h>
-
-#include <openbsc/gprs_utils.h>
-#include <openbsc/gprs_gb_parse.h>
-
-#include <openbsc/debug.h>
-
-#include <osmocom/gsm/gsm_utils.h>
-
-#include <osmocom/core/rate_ctr.h>
-#include <osmocom/core/talloc.h>
-
-struct gbproxy_link_info *gbproxy_link_info_by_tlli(struct gbproxy_peer *peer,
- uint32_t tlli)
-{
- struct gbproxy_link_info *link_info;
- struct gbproxy_patch_state *state = &peer->patch_state;
-
- if (!tlli)
- return NULL;
-
- llist_for_each_entry(link_info, &state->logical_links, list)
- if (link_info->tlli.current == tlli ||
- link_info->tlli.assigned == tlli)
- return link_info;
-
- return NULL;
-}
-
-struct gbproxy_link_info *gbproxy_link_info_by_ptmsi(
- struct gbproxy_peer *peer,
- uint32_t ptmsi)
-{
- struct gbproxy_link_info *link_info;
- struct gbproxy_patch_state *state = &peer->patch_state;
-
- if (ptmsi == GSM_RESERVED_TMSI)
- return NULL;
-
- llist_for_each_entry(link_info, &state->logical_links, list)
- if (link_info->tlli.ptmsi == ptmsi)
- return link_info;
-
- return NULL;
-}
-
-struct gbproxy_link_info *gbproxy_link_info_by_any_sgsn_tlli(
- struct gbproxy_peer *peer,
- uint32_t tlli)
-{
- struct gbproxy_link_info *link_info;
- struct gbproxy_patch_state *state = &peer->patch_state;
-
- if (!tlli)
- return NULL;
-
- /* Don't care about the NSEI */
- llist_for_each_entry(link_info, &state->logical_links, list)
- if (link_info->sgsn_tlli.current == tlli ||
- link_info->sgsn_tlli.assigned == tlli)
- return link_info;
-
- return NULL;
-}
-
-struct gbproxy_link_info *gbproxy_link_info_by_sgsn_tlli(
- struct gbproxy_peer *peer,
- uint32_t tlli, uint32_t sgsn_nsei)
-{
- struct gbproxy_link_info *link_info;
- struct gbproxy_patch_state *state = &peer->patch_state;
-
- if (!tlli)
- return NULL;
-
- llist_for_each_entry(link_info, &state->logical_links, list)
- if ((link_info->sgsn_tlli.current == tlli ||
- link_info->sgsn_tlli.assigned == tlli) &&
- link_info->sgsn_nsei == sgsn_nsei)
- return link_info;
-
- return NULL;
-}
-
-struct gbproxy_link_info *gbproxy_link_info_by_imsi(
- struct gbproxy_peer *peer,
- const uint8_t *imsi,
- size_t imsi_len)
-{
- struct gbproxy_link_info *link_info;
- struct gbproxy_patch_state *state = &peer->patch_state;
-
- if (!gprs_is_mi_imsi(imsi, imsi_len))
- return NULL;
-
- llist_for_each_entry(link_info, &state->logical_links, list) {
- if (link_info->imsi_len != imsi_len)
- continue;
- if (memcmp(link_info->imsi, imsi, imsi_len) != 0)
- continue;
-
- return link_info;
- }
-
- return NULL;
-}
-
-void gbproxy_link_info_discard_messages(struct gbproxy_link_info *link_info)
-{
- struct msgb *msg, *nxt;
-
- llist_for_each_entry_safe(msg, nxt, &link_info->stored_msgs, list) {
- llist_del(&msg->list);
- msgb_free(msg);
- }
-}
-
-void gbproxy_delete_link_info(struct gbproxy_peer *peer,
- struct gbproxy_link_info *link_info)
-{
- struct gbproxy_patch_state *state = &peer->patch_state;
-
- gbproxy_link_info_discard_messages(link_info);
-
- llist_del(&link_info->list);
- talloc_free(link_info);
- state->logical_link_count -= 1;
-
- peer->ctrg->ctr[GBPROX_PEER_CTR_TLLI_CACHE_SIZE].current =
- state->logical_link_count;
-}
-
-void gbproxy_delete_link_infos(struct gbproxy_peer *peer)
-{
- struct gbproxy_link_info *link_info, *nxt;
- struct gbproxy_patch_state *state = &peer->patch_state;
-
- llist_for_each_entry_safe(link_info, nxt, &state->logical_links, list)
- gbproxy_delete_link_info(peer, link_info);
-
- OSMO_ASSERT(state->logical_link_count == 0);
- OSMO_ASSERT(llist_empty(&state->logical_links));
-}
-
-void gbproxy_attach_link_info(struct gbproxy_peer *peer, time_t now,
- struct gbproxy_link_info *link_info)
-{
- struct gbproxy_patch_state *state = &peer->patch_state;
-
- link_info->timestamp = now;
- llist_add(&link_info->list, &state->logical_links);
- state->logical_link_count += 1;
-
- peer->ctrg->ctr[GBPROX_PEER_CTR_TLLI_CACHE_SIZE].current =
- state->logical_link_count;
-}
-
-int gbproxy_remove_stale_link_infos(struct gbproxy_peer *peer, time_t now)
-{
- struct gbproxy_patch_state *state = &peer->patch_state;
- int exceeded_max_len = 0;
- int deleted_count = 0;
- int check_for_age;
-
- if (peer->cfg->tlli_max_len > 0)
- exceeded_max_len =
- state->logical_link_count - peer->cfg->tlli_max_len;
-
- check_for_age = peer->cfg->tlli_max_age > 0;
-
- for (; exceeded_max_len > 0; exceeded_max_len--) {
- struct gbproxy_link_info *link_info;
- OSMO_ASSERT(!llist_empty(&state->logical_links));
- link_info = llist_entry(state->logical_links.prev,
- struct gbproxy_link_info,
- list);
- LOGP(DGPRS, LOGL_INFO,
- "Removing TLLI %08x from list "
- "(stale, length %d, max_len exceeded)\n",
- link_info->tlli.current, state->logical_link_count);
-
- gbproxy_delete_link_info(peer, link_info);
- deleted_count += 1;
- }
-
- while (check_for_age && !llist_empty(&state->logical_links)) {
- time_t age;
- struct gbproxy_link_info *link_info;
- link_info = llist_entry(state->logical_links.prev,
- struct gbproxy_link_info,
- list);
- age = now - link_info->timestamp;
- /* age < 0 only happens after system time jumps, discard entry */
- if (age <= peer->cfg->tlli_max_age && age >= 0) {
- check_for_age = 0;
- continue;
- }
-
- LOGP(DGPRS, LOGL_INFO,
- "Removing TLLI %08x from list "
- "(stale, age %d, max_age exceeded)\n",
- link_info->tlli.current, (int)age);
-
- gbproxy_delete_link_info(peer, link_info);
- deleted_count += 1;
- }
-
- return deleted_count;
-}
-
-struct gbproxy_link_info *gbproxy_link_info_alloc( struct gbproxy_peer *peer)
-{
- struct gbproxy_link_info *link_info;
-
- link_info = talloc_zero(peer, struct gbproxy_link_info);
- link_info->tlli.ptmsi = GSM_RESERVED_TMSI;
- link_info->sgsn_tlli.ptmsi = GSM_RESERVED_TMSI;
-
- link_info->vu_gen_tx_bss = GBPROXY_INIT_VU_GEN_TX;
-
- INIT_LLIST_HEAD(&link_info->stored_msgs);
-
- return link_info;
-}
-
-void gbproxy_detach_link_info(
- struct gbproxy_peer *peer,
- struct gbproxy_link_info *link_info)
-{
- struct gbproxy_patch_state *state = &peer->patch_state;
-
- llist_del(&link_info->list);
- OSMO_ASSERT(state->logical_link_count > 0);
- state->logical_link_count -= 1;
-
- peer->ctrg->ctr[GBPROX_PEER_CTR_TLLI_CACHE_SIZE].current =
- state->logical_link_count;
-}
-
-void gbproxy_update_link_info(struct gbproxy_link_info *link_info,
- const uint8_t *imsi, size_t imsi_len)
-{
- if (!gprs_is_mi_imsi(imsi, imsi_len))
- return;
-
- link_info->imsi_len = imsi_len;
- link_info->imsi =
- talloc_realloc_size(link_info, link_info->imsi, imsi_len);
- OSMO_ASSERT(link_info->imsi != NULL);
- memcpy(link_info->imsi, imsi, imsi_len);
-}
-
-void gbproxy_reassign_tlli(struct gbproxy_tlli_state *tlli_state,
- struct gbproxy_peer *peer, uint32_t new_tlli)
-{
- if (new_tlli == tlli_state->current)
- return;
-
- LOGP(DGPRS, LOGL_INFO,
- "The TLLI has been reassigned from %08x to %08x\n",
- tlli_state->current, new_tlli);
-
- /* Remember assigned TLLI */
- tlli_state->assigned = new_tlli;
- tlli_state->bss_validated = 0;
- tlli_state->net_validated = 0;
-}
-
-uint32_t gbproxy_map_tlli(uint32_t other_tlli,
- struct gbproxy_link_info *link_info, int to_bss)
-{
- uint32_t tlli = 0;
- struct gbproxy_tlli_state *src, *dst;
- if (to_bss) {
- src = &link_info->sgsn_tlli;
- dst = &link_info->tlli;
- } else {
- src = &link_info->tlli;
- dst = &link_info->sgsn_tlli;
- }
- if (src->current == other_tlli)
- tlli = dst->current;
- else if (src->assigned == other_tlli)
- tlli = dst->assigned;
-
- return tlli;
-}
-
-static void gbproxy_validate_tlli(struct gbproxy_tlli_state *tlli_state,
- uint32_t tlli, int to_bss)
-{
- LOGP(DGPRS, LOGL_DEBUG,
- "%s({current = %08x, assigned = %08x, net_vld = %d, bss_vld = %d}, %08x)\n",
- __func__, tlli_state->current, tlli_state->assigned,
- tlli_state->net_validated, tlli_state->bss_validated, tlli);
-
- if (!tlli_state->assigned || tlli_state->assigned != tlli)
- return;
-
- /* TODO: Is this ok? Check spec */
- if (gprs_tlli_type(tlli) != TLLI_LOCAL)
- return;
-
- /* See GSM 04.08, 4.7.1.5 */
- if (to_bss)
- tlli_state->net_validated = 1;
- else
- tlli_state->bss_validated = 1;
-
- if (!tlli_state->bss_validated || !tlli_state->net_validated)
- return;
-
- LOGP(DGPRS, LOGL_INFO,
- "The TLLI %08x has been validated (was %08x)\n",
- tlli_state->assigned, tlli_state->current);
-
- tlli_state->current = tlli;
- tlli_state->assigned = 0;
-}
-
-static void gbproxy_touch_link_info(struct gbproxy_peer *peer,
- struct gbproxy_link_info *link_info,
- time_t now)
-{
- gbproxy_detach_link_info(peer, link_info);
- gbproxy_attach_link_info(peer, now, link_info);
-}
-
-static int gbproxy_unregister_link_info(struct gbproxy_peer *peer,
- struct gbproxy_link_info *link_info)
-{
- if (!link_info)
- return 1;
-
- if (link_info->tlli.ptmsi == GSM_RESERVED_TMSI && !link_info->imsi_len) {
- LOGP(DGPRS, LOGL_INFO,
- "Removing TLLI %08x from list (P-TMSI or IMSI are not set)\n",
- link_info->tlli.current);
- gbproxy_delete_link_info(peer, link_info);
- return 1;
- }
-
- link_info->tlli.current = 0;
- link_info->tlli.assigned = 0;
- link_info->sgsn_tlli.current = 0;
- link_info->sgsn_tlli.assigned = 0;
-
- link_info->is_deregistered = 1;
-
- gbproxy_reset_link(link_info);
-
- return 0;
-}
-
-int gbproxy_imsi_matches(struct gbproxy_config *cfg,
- enum gbproxy_match_id match_id,
- struct gbproxy_link_info *link_info)
-{
- struct gbproxy_match *match;
- OSMO_ASSERT(match_id >= 0 && match_id < ARRAY_SIZE(cfg->matches));
-
- match = &cfg->matches[match_id];
- if (!match->enable)
- return 1;
-
- return link_info != NULL && link_info->is_matching[match_id];
-}
-
-void gbproxy_assign_imsi(struct gbproxy_peer *peer,
- struct gbproxy_link_info *link_info,
- struct gprs_gb_parse_context *parse_ctx)
-{
- int imsi_matches;
- struct gbproxy_link_info *other_link_info;
- enum gbproxy_match_id match_id;
-
- /* Make sure that there is a second entry with the same IMSI */
- other_link_info = gbproxy_link_info_by_imsi(
- peer, parse_ctx->imsi, parse_ctx->imsi_len);
-
- if (other_link_info && other_link_info != link_info) {
- char mi_buf[200];
- mi_buf[0] = '\0';
- gsm48_mi_to_string(mi_buf, sizeof(mi_buf),
- parse_ctx->imsi, parse_ctx->imsi_len);
- LOGP(DGPRS, LOGL_INFO,
- "Removing TLLI %08x from list (IMSI %s re-used)\n",
- other_link_info->tlli.current, mi_buf);
- gbproxy_delete_link_info(peer, other_link_info);
- }
-
- /* Update the IMSI field */
- gbproxy_update_link_info(link_info,
- parse_ctx->imsi, parse_ctx->imsi_len);
-
- /* Check, whether the IMSI matches */
- OSMO_ASSERT(ARRAY_SIZE(link_info->is_matching) ==
- ARRAY_SIZE(peer->cfg->matches));
- for (match_id = 0; match_id < ARRAY_SIZE(link_info->is_matching);
- ++match_id) {
- imsi_matches = gbproxy_check_imsi(
- &peer->cfg->matches[match_id],
- parse_ctx->imsi, parse_ctx->imsi_len);
- if (imsi_matches >= 0)
- link_info->is_matching[match_id] = imsi_matches;
- }
-}
-
-static int gbproxy_tlli_match(const struct gbproxy_tlli_state *a,
- const struct gbproxy_tlli_state *b)
-{
- if (a->current && a->current == b->current)
- return 1;
-
- if (a->assigned && a->assigned == b->assigned)
- return 1;
-
- if (a->ptmsi != GSM_RESERVED_TMSI && a->ptmsi == b->ptmsi)
- return 1;
-
- return 0;
-}
-
-static void gbproxy_remove_matching_link_infos(
- struct gbproxy_peer *peer, struct gbproxy_link_info *link_info)
-{
- struct gbproxy_link_info *info, *nxt;
- struct gbproxy_patch_state *state = &peer->patch_state;
-
- /* Make sure that there is no second entry with the same P-TMSI or TLLI */
- llist_for_each_entry_safe(info, nxt, &state->logical_links, list) {
- if (info == link_info)
- continue;
-
- if (!gbproxy_tlli_match(&link_info->tlli, &info->tlli) &&
- (link_info->sgsn_nsei != info->sgsn_nsei ||
- !gbproxy_tlli_match(&link_info->sgsn_tlli, &info->sgsn_tlli)))
- continue;
-
- LOGP(DGPRS, LOGL_INFO,
- "Removing TLLI %08x from list (P-TMSI/TLLI re-used)\n",
- info->tlli.current);
- gbproxy_delete_link_info(peer, info);
- }
-}
-
-static struct gbproxy_link_info *gbproxy_get_link_info_ul(
- struct gbproxy_peer *peer,
- int *tlli_is_valid,
- struct gprs_gb_parse_context *parse_ctx)
-{
- struct gbproxy_link_info *link_info = NULL;
-
- if (parse_ctx->tlli_enc) {
- link_info = gbproxy_link_info_by_tlli(peer, parse_ctx->tlli);
-
- if (link_info) {
- *tlli_is_valid = 1;
- return link_info;
- }
- }
-
- *tlli_is_valid = 0;
-
- if (!link_info && parse_ctx->imsi) {
- link_info = gbproxy_link_info_by_imsi(
- peer, parse_ctx->imsi, parse_ctx->imsi_len);
- }
-
- if (!link_info && parse_ctx->ptmsi_enc && !parse_ctx->old_raid_is_foreign) {
- uint32_t bss_ptmsi;
- gprs_parse_tmsi(parse_ctx->ptmsi_enc, &bss_ptmsi);
- link_info = gbproxy_link_info_by_ptmsi(peer, bss_ptmsi);
- }
-
- if (!link_info)
- return NULL;
-
- link_info->is_deregistered = 0;
-
- return link_info;
-}
-
-struct gbproxy_link_info *gbproxy_update_link_state_ul(
- struct gbproxy_peer *peer,
- time_t now,
- struct gprs_gb_parse_context *parse_ctx)
-{
- struct gbproxy_link_info *link_info;
- int tlli_is_valid;
-
- link_info = gbproxy_get_link_info_ul(peer, &tlli_is_valid, parse_ctx);
-
- if (parse_ctx->tlli_enc && parse_ctx->llc) {
- uint32_t sgsn_tlli;
-
- if (!link_info) {
- LOGP(DGPRS, LOGL_INFO, "Adding TLLI %08x to list\n",
- parse_ctx->tlli);
- link_info = gbproxy_link_info_alloc(peer);
- gbproxy_attach_link_info(peer, now, link_info);
-
- /* Setup TLLIs */
- sgsn_tlli = gbproxy_make_sgsn_tlli(peer, link_info,
- parse_ctx->tlli);
- link_info->sgsn_tlli.current = sgsn_tlli;
- link_info->tlli.current = parse_ctx->tlli;
- } else if (!tlli_is_valid) {
- /* New TLLI (info found by IMSI or P-TMSI) */
- link_info->tlli.current = parse_ctx->tlli;
- link_info->tlli.assigned = 0;
- link_info->sgsn_tlli.current =
- gbproxy_make_sgsn_tlli(peer, link_info,
- parse_ctx->tlli);
- link_info->sgsn_tlli.assigned = 0;
- gbproxy_touch_link_info(peer, link_info, now);
- } else {
- sgsn_tlli = gbproxy_map_tlli(parse_ctx->tlli, link_info, 0);
- if (!sgsn_tlli)
- sgsn_tlli = gbproxy_make_sgsn_tlli(peer, link_info,
- parse_ctx->tlli);
-
- gbproxy_validate_tlli(&link_info->tlli,
- parse_ctx->tlli, 0);
- gbproxy_validate_tlli(&link_info->sgsn_tlli,
- sgsn_tlli, 0);
- gbproxy_touch_link_info(peer, link_info, now);
- }
- } else if (link_info) {
- gbproxy_touch_link_info(peer, link_info, now);
- }
-
- if (parse_ctx->imsi && link_info && link_info->imsi_len == 0)
- gbproxy_assign_imsi(peer, link_info, parse_ctx);
-
- return link_info;
-}
-
-static struct gbproxy_link_info *gbproxy_get_link_info_dl(
- struct gbproxy_peer *peer,
- struct gprs_gb_parse_context *parse_ctx)
-{
- struct gbproxy_link_info *link_info = NULL;
-
- /* Which key to use depends on its availability only, if that fails, do
- * not retry it with another key (e.g. IMSI). */
- if (parse_ctx->tlli_enc)
- link_info = gbproxy_link_info_by_sgsn_tlli(peer, parse_ctx->tlli,
- parse_ctx->peer_nsei);
-
- /* TODO: Get link_info by (SGSN) P-TMSI if that is available (see
- * GSM 08.18, 7.2) instead of using the IMSI as key. */
- else if (parse_ctx->imsi)
- link_info = gbproxy_link_info_by_imsi(
- peer, parse_ctx->imsi, parse_ctx->imsi_len);
-
- if (link_info)
- link_info->is_deregistered = 0;
-
- return link_info;
-}
-
-struct gbproxy_link_info *gbproxy_update_link_state_dl(
- struct gbproxy_peer *peer,
- time_t now,
- struct gprs_gb_parse_context *parse_ctx)
-{
- struct gbproxy_link_info *link_info = NULL;
-
- link_info = gbproxy_get_link_info_dl(peer, parse_ctx);
-
- if (parse_ctx->tlli_enc && parse_ctx->new_ptmsi_enc && link_info) {
- /* A new P-TMSI has been signalled in the message,
- * register new TLLI */
- uint32_t new_sgsn_ptmsi;
- uint32_t new_bss_ptmsi = GSM_RESERVED_TMSI;
- gprs_parse_tmsi(parse_ctx->new_ptmsi_enc, &new_sgsn_ptmsi);
-
- if (link_info->sgsn_tlli.ptmsi == new_sgsn_ptmsi)
- new_bss_ptmsi = link_info->tlli.ptmsi;
-
- if (new_bss_ptmsi == GSM_RESERVED_TMSI)
- new_bss_ptmsi = gbproxy_make_bss_ptmsi(peer, new_sgsn_ptmsi);
-
- LOGP(DGPRS, LOGL_INFO,
- "Got new PTMSI %08x from SGSN, using %08x for BSS\n",
- new_sgsn_ptmsi, new_bss_ptmsi);
- /* Setup PTMSIs */
- link_info->sgsn_tlli.ptmsi = new_sgsn_ptmsi;
- link_info->tlli.ptmsi = new_bss_ptmsi;
- } else if (parse_ctx->tlli_enc && parse_ctx->new_ptmsi_enc && !link_info &&
- !peer->cfg->patch_ptmsi) {
- /* A new P-TMSI has been signalled in the message with an unknown
- * TLLI, create a new link_info */
- /* TODO: Add a test case for this branch */
- uint32_t new_ptmsi;
- gprs_parse_tmsi(parse_ctx->new_ptmsi_enc, &new_ptmsi);
-
- LOGP(DGPRS, LOGL_INFO,
- "Adding TLLI %08x to list (SGSN, new P-TMSI is %08x)\n",
- parse_ctx->tlli, new_ptmsi);
-
- link_info = gbproxy_link_info_alloc(peer);
- link_info->sgsn_tlli.current = parse_ctx->tlli;
- link_info->tlli.current = parse_ctx->tlli;
- link_info->sgsn_tlli.ptmsi = new_ptmsi;
- link_info->tlli.ptmsi = new_ptmsi;
- gbproxy_attach_link_info(peer, now, link_info);
- } else if (parse_ctx->tlli_enc && parse_ctx->llc && !link_info &&
- !peer->cfg->patch_ptmsi) {
- /* Unknown SGSN TLLI, create a new link_info */
- uint32_t new_ptmsi;
- link_info = gbproxy_link_info_alloc(peer);
- LOGP(DGPRS, LOGL_INFO, "Adding TLLI %08x to list (SGSN)\n",
- parse_ctx->tlli);
-
- gbproxy_attach_link_info(peer, now, link_info);
-
- /* Setup TLLIs */
- link_info->sgsn_tlli.current = parse_ctx->tlli;
- link_info->tlli.current = parse_ctx->tlli;
-
- if (!parse_ctx->new_ptmsi_enc)
- return link_info;
- /* A new P-TMSI has been signalled in the message */
-
- gprs_parse_tmsi(parse_ctx->new_ptmsi_enc, &new_ptmsi);
- LOGP(DGPRS, LOGL_INFO,
- "Assigning new P-TMSI %08x\n", new_ptmsi);
- /* Setup P-TMSIs */
- link_info->sgsn_tlli.ptmsi = new_ptmsi;
- link_info->tlli.ptmsi = new_ptmsi;
- } else if (parse_ctx->tlli_enc && parse_ctx->llc && link_info) {
- uint32_t bss_tlli = gbproxy_map_tlli(parse_ctx->tlli,
- link_info, 1);
- gbproxy_validate_tlli(&link_info->sgsn_tlli, parse_ctx->tlli, 1);
- gbproxy_validate_tlli(&link_info->tlli, bss_tlli, 1);
- gbproxy_touch_link_info(peer, link_info, now);
- } else if (link_info) {
- gbproxy_touch_link_info(peer, link_info, now);
- }
-
- if (parse_ctx->imsi && link_info && link_info->imsi_len == 0)
- gbproxy_assign_imsi(peer, link_info, parse_ctx);
-
- return link_info;
-}
-
-int gbproxy_update_link_state_after(
- struct gbproxy_peer *peer,
- struct gbproxy_link_info *link_info,
- time_t now,
- struct gprs_gb_parse_context *parse_ctx)
-{
- int rc = 0;
- if (parse_ctx->invalidate_tlli && link_info) {
- int keep_info =
- peer->cfg->keep_link_infos == GBPROX_KEEP_ALWAYS ||
- (peer->cfg->keep_link_infos == GBPROX_KEEP_REATTACH &&
- parse_ctx->await_reattach) ||
- (peer->cfg->keep_link_infos == GBPROX_KEEP_IDENTIFIED &&
- link_info->imsi_len > 0);
- if (keep_info) {
- LOGP(DGPRS, LOGL_INFO, "Unregistering TLLI %08x\n",
- link_info->tlli.current);
- rc = gbproxy_unregister_link_info(peer, link_info);
- } else {
- LOGP(DGPRS, LOGL_INFO, "Removing TLLI %08x from list\n",
- link_info->tlli.current);
- gbproxy_delete_link_info(peer, link_info);
- rc = 1;
- }
- } else if (parse_ctx->to_bss && parse_ctx->tlli_enc &&
- parse_ctx->new_ptmsi_enc && link_info) {
- /* A new PTMSI has been signaled in the message,
- * register new TLLI */
- uint32_t new_sgsn_ptmsi = link_info->sgsn_tlli.ptmsi;
- uint32_t new_bss_ptmsi = link_info->tlli.ptmsi;
- uint32_t new_sgsn_tlli;
- uint32_t new_bss_tlli = 0;
-
- new_sgsn_tlli = gprs_tmsi2tlli(new_sgsn_ptmsi, TLLI_LOCAL);
- if (new_bss_ptmsi != GSM_RESERVED_TMSI)
- new_bss_tlli = gprs_tmsi2tlli(new_bss_ptmsi, TLLI_LOCAL);
- LOGP(DGPRS, LOGL_INFO,
- "Assigning new TLLI %08x to SGSN, %08x to BSS\n",
- new_sgsn_tlli, new_bss_tlli);
-
- gbproxy_reassign_tlli(&link_info->sgsn_tlli,
- peer, new_sgsn_tlli);
- gbproxy_reassign_tlli(&link_info->tlli,
- peer, new_bss_tlli);
- gbproxy_remove_matching_link_infos(peer, link_info);
- }
-
- gbproxy_remove_stale_link_infos(peer, now);
-
- return rc;
-}
-
-
diff --git a/src/gprs/gb_proxy_vty.c b/src/gprs/gb_proxy_vty.c
deleted file mode 100644
index 933b6b010..000000000
--- a/src/gprs/gb_proxy_vty.c
+++ /dev/null
@@ -1,852 +0,0 @@
-/*
- * (C) 2010 by Harald Welte <laforge@gnumonks.org>
- * (C) 2010 by On-Waves
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <string.h>
-#include <time.h>
-
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/rate_ctr.h>
-
-#include <openbsc/gsm_04_08.h>
-#include <osmocom/gprs/gprs_ns.h>
-
-#include <openbsc/debug.h>
-#include <openbsc/gb_proxy.h>
-#include <openbsc/gprs_utils.h>
-#include <openbsc/vty.h>
-
-#include <osmocom/vty/command.h>
-#include <osmocom/vty/vty.h>
-#include <osmocom/vty/misc.h>
-
-static struct gbproxy_config *g_cfg = NULL;
-
-/*
- * vty code for mgcp below
- */
-static struct cmd_node gbproxy_node = {
- GBPROXY_NODE,
- "%s(config-gbproxy)# ",
- 1,
-};
-
-static const struct value_string keep_modes[] = {
- {GBPROX_KEEP_NEVER, "never"},
- {GBPROX_KEEP_REATTACH, "re-attach"},
- {GBPROX_KEEP_IDENTIFIED, "identified"},
- {GBPROX_KEEP_ALWAYS, "always"},
- {0, NULL}
-};
-
-static const struct value_string match_ids[] = {
- {GBPROX_MATCH_PATCHING, "patching"},
- {GBPROX_MATCH_ROUTING, "routing"},
- {0, NULL}
-};
-
-static void gbprox_vty_print_peer(struct vty *vty, struct gbproxy_peer *peer)
-{
- struct gprs_ra_id raid;
- gsm48_parse_ra(&raid, peer->ra);
-
- vty_out(vty, "NSEI %5u, PTP-BVCI %5u, "
- "RAI %u-%u-%u-%u",
- peer->nsei, peer->bvci,
- raid.mcc, raid.mnc, raid.lac, raid.rac);
- if (peer->blocked)
- vty_out(vty, " [BVC-BLOCKED]");
-
- vty_out(vty, "%s", VTY_NEWLINE);
-}
-
-static int config_write_gbproxy(struct vty *vty)
-{
- enum gbproxy_match_id match_id;
-
- vty_out(vty, "gbproxy%s", VTY_NEWLINE);
-
- vty_out(vty, " sgsn nsei %u%s", g_cfg->nsip_sgsn_nsei,
- VTY_NEWLINE);
-
- if (g_cfg->core_mcc > 0)
- vty_out(vty, " core-mobile-country-code %d%s",
- g_cfg->core_mcc, VTY_NEWLINE);
- if (g_cfg->core_mnc > 0)
- vty_out(vty, " core-mobile-network-code %d%s",
- g_cfg->core_mnc, VTY_NEWLINE);
-
- for (match_id = 0; match_id < ARRAY_SIZE(g_cfg->matches); ++match_id) {
- struct gbproxy_match *match = &g_cfg->matches[match_id];
- if (match->re_str)
- vty_out(vty, " match-imsi %s %s%s",
- get_value_string(match_ids, match_id),
- match->re_str, VTY_NEWLINE);
- }
-
- if (g_cfg->core_apn != NULL) {
- if (g_cfg->core_apn_size > 0) {
- char str[500] = {0};
- vty_out(vty, " core-access-point-name %s%s",
- 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;
-}
-
diff --git a/src/gprs/gprs_gb_parse.c b/src/gprs/gprs_gb_parse.c
deleted file mode 100644
index d5a122bda..000000000
--- a/src/gprs/gprs_gb_parse.c
+++ /dev/null
@@ -1,636 +0,0 @@
-/* GPRS Gb message parser */
-
-/* (C) 2014 by On-Waves
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <osmocom/gsm/gsm48.h>
-#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
-
-#include <openbsc/gprs_gb_parse.h>
-
-#include <openbsc/gprs_utils.h>
-
-#include <openbsc/debug.h>
-
-#include <osmocom/gprs/gprs_bssgp.h>
-
-static int gprs_gb_parse_gmm_attach_req(uint8_t *data, size_t data_len,
- struct gprs_gb_parse_context *parse_ctx)
-{
- uint8_t *value;
- size_t value_len;
-
- parse_ctx->llc_msg_name = "ATTACH_REQ";
-
- /* Skip MS network capability */
- if (osmo_shift_lv(&data, &data_len, NULL, &value_len) <= 0 ||
- value_len < 1 || value_len > 8)
- /* invalid */
- return 0;
-
- /* Skip Attach type */
- /* Skip Ciphering key sequence number */
- /* Skip DRX parameter */
- osmo_shift_v_fixed(&data, &data_len, 3, NULL);
-
- /* Get Mobile identity */
- if (osmo_shift_lv(&data, &data_len, &value, &value_len) <= 0 ||
- value_len < 5 || value_len > 8)
- /* invalid */
- return 0;
-
- if (gprs_is_mi_tmsi(value, value_len)) {
- parse_ctx->ptmsi_enc = value + 1;
- } else if (gprs_is_mi_imsi(value, value_len)) {
- parse_ctx->imsi = value;
- parse_ctx->imsi_len = value_len;
- }
-
- if (osmo_shift_v_fixed(&data, &data_len, 6, &value) <= 0)
- return 0;
-
- parse_ctx->old_raid_enc = value;
-
- return 1;
-}
-
-static int gprs_gb_parse_gmm_attach_ack(uint8_t *data, size_t data_len,
- struct gprs_gb_parse_context *parse_ctx)
-{
- uint8_t *value;
- size_t value_len;
-
- parse_ctx->llc_msg_name = "ATTACH_ACK";
-
- /* Skip Attach result */
- /* Skip Force to standby */
- /* Skip Periodic RA update timer */
- /* Skip Radio priority for SMS */
- /* Skip Spare half octet */
- osmo_shift_v_fixed(&data, &data_len, 3, NULL);
-
- if (osmo_shift_v_fixed(&data, &data_len, 6, &value) <= 0)
- return 0;
-
- parse_ctx->raid_enc = value;
-
- /* Skip P-TMSI signature (P-TMSI signature, opt, TV, length 4) */
- osmo_match_shift_tv_fixed(&data, &data_len, GSM48_IE_GMM_PTMSI_SIG, 3, NULL);
-
- /* Skip Negotiated READY timer value (GPRS timer, opt, TV, length 2) */
- osmo_match_shift_tv_fixed(&data, &data_len, GSM48_IE_GMM_TIMER_READY, 1, NULL);
-
- /* Allocated P-TMSI (Mobile identity, opt, TLV, length 7) */
- if (osmo_match_shift_tlv(&data, &data_len, GSM48_IE_GMM_ALLOC_PTMSI,
- &value, &value_len) > 0 &&
- gprs_is_mi_tmsi(value, value_len))
- parse_ctx->new_ptmsi_enc = value + 1;
- return 1;
-}
-
-static int gprs_gb_parse_gmm_attach_rej(uint8_t *data, size_t data_len,
- struct gprs_gb_parse_context *parse_ctx)
-{
- uint8_t *value;
-
- parse_ctx->llc_msg_name = "ATTACH_REJ";
-
- /* GMM cause */
- if (osmo_shift_v_fixed(&data, &data_len, 1, &value) <= 0)
- return 0;
-
- parse_ctx->invalidate_tlli = 1;
-
- return 1;
-}
-
-
-static int gprs_gb_parse_gmm_detach_req(uint8_t *data, size_t data_len,
- struct gprs_gb_parse_context *parse_ctx)
-{
- uint8_t *value;
- size_t value_len;
- int detach_type;
- int power_off;
-
- parse_ctx->llc_msg_name = "DETACH_REQ";
-
- /* Skip spare half octet */
- /* Get Detach type */
- if (osmo_shift_v_fixed(&data, &data_len, 1, &value) <= 0)
- /* invalid */
- return 0;
-
- detach_type = *value & 0x07;
- power_off = *value & 0x08 ? 1 : 0;
-
- if (parse_ctx->to_bss) {
- /* Network originated */
- if (detach_type == GPRS_DET_T_MT_REATT_REQ)
- parse_ctx->await_reattach = 1;
- } else {
- /* Mobile originated */
-
- if (power_off)
- parse_ctx->invalidate_tlli = 1;
-
- /* Get P-TMSI (Mobile identity), see GSM 24.008, 9.4.5.2 */
- if (osmo_match_shift_tlv(&data, &data_len,
- GSM48_IE_GMM_ALLOC_PTMSI, &value, &value_len) > 0)
- {
- if (gprs_is_mi_tmsi(value, value_len))
- parse_ctx->ptmsi_enc = value + 1;
- }
- }
-
- return 1;
-}
-
-static int gprs_gb_parse_gmm_ra_upd_req(uint8_t *data, size_t data_len,
- struct gprs_gb_parse_context *parse_ctx)
-{
- uint8_t *value;
-
- parse_ctx->llc_msg_name = "RA_UPD_REQ";
-
- /* Skip Update type */
- /* Skip GPRS ciphering key sequence number */
- osmo_shift_v_fixed(&data, &data_len, 1, NULL);
-
- if (osmo_shift_v_fixed(&data, &data_len, 6, &value) <= 0)
- return 0;
-
- parse_ctx->old_raid_enc = value;
-
- return 1;
-}
-
-static int gprs_gb_parse_gmm_ra_upd_rej(uint8_t *data, size_t data_len,
- struct gprs_gb_parse_context *parse_ctx)
-{
- uint8_t *value;
- uint8_t cause;
- int force_standby;
-
- parse_ctx->llc_msg_name = "RA_UPD_REJ";
-
- /* GMM cause */
- if (osmo_shift_v_fixed(&data, &data_len, 1, &value) <= 0)
- return 0;
-
- cause = value[0];
-
- /* Force to standby, 1/2 */
- /* spare bits, 1/2 */
- if (osmo_shift_v_fixed(&data, &data_len, 1, &value) <= 0)
- return 0;
-
- force_standby = (value[0] & 0x07) == 0x01;
-
- if (cause == GMM_CAUSE_IMPL_DETACHED && !force_standby)
- parse_ctx->await_reattach = 1;
-
- parse_ctx->invalidate_tlli = 1;
-
- return 1;
-}
-
-static int gprs_gb_parse_gmm_ra_upd_ack(uint8_t *data, size_t data_len,
- struct gprs_gb_parse_context *parse_ctx)
-{
- uint8_t *value;
- size_t value_len;
-
- parse_ctx->llc_msg_name = "RA_UPD_ACK";
-
- /* Skip Force to standby */
- /* Skip Update result */
- /* Skip Periodic RA update timer */
- osmo_shift_v_fixed(&data, &data_len, 2, NULL);
-
- if (osmo_shift_v_fixed(&data, &data_len, 6, &value) <= 0)
- return 0;
-
- parse_ctx->raid_enc = value;
-
- /* Skip P-TMSI signature (P-TMSI signature, opt, TV, length 4) */
- osmo_match_shift_tv_fixed(&data, &data_len, GSM48_IE_GMM_PTMSI_SIG, 3, NULL);
-
- /* Allocated P-TMSI (Mobile identity, opt, TLV, length 7) */
- if (osmo_match_shift_tlv(&data, &data_len, GSM48_IE_GMM_ALLOC_PTMSI,
- &value, &value_len) > 0 &&
- gprs_is_mi_tmsi(value, value_len))
- parse_ctx->new_ptmsi_enc = value + 1;
-
- return 1;
-}
-
-static int gprs_gb_parse_gmm_ptmsi_reall_cmd(uint8_t *data, size_t data_len,
- struct gprs_gb_parse_context *parse_ctx)
-{
- uint8_t *value;
- size_t value_len;
-
- parse_ctx->llc_msg_name = "PTMSI_REALL_CMD";
-
- LOGP(DLLC, LOGL_NOTICE,
- "Got P-TMSI Reallocation Command which is not covered by unit tests yet.\n");
-
- /* Allocated P-TMSI */
- if (osmo_shift_lv(&data, &data_len, &value, &value_len) > 0 &&
- gprs_is_mi_tmsi(value, value_len))
- parse_ctx->new_ptmsi_enc = value + 1;
-
- if (osmo_shift_v_fixed(&data, &data_len, 6, &value) <= 0)
- return 0;
-
- parse_ctx->raid_enc = value;
-
- return 1;
-}
-
-static int gprs_gb_parse_gmm_id_resp(uint8_t *data, size_t data_len,
- struct gprs_gb_parse_context *parse_ctx)
-{
- uint8_t *value;
- size_t value_len;
-
- parse_ctx->llc_msg_name = "ID_RESP";
-
- /* Mobile identity, Mobile identity 10.5.1.4, M LV 2-10 */
- if (osmo_shift_lv(&data, &data_len, &value, &value_len) <= 0 ||
- value_len < 1 || value_len > 9)
- /* invalid */
- return 0;
-
- if (gprs_is_mi_tmsi(value, value_len)) {
- parse_ctx->ptmsi_enc = value + 1;
- } else if (gprs_is_mi_imsi(value, value_len)) {
- parse_ctx->imsi = value;
- parse_ctx->imsi_len = value_len;
- }
-
- return 1;
-}
-
-static int gprs_gb_parse_gsm_act_pdp_req(uint8_t *data, size_t data_len,
- struct gprs_gb_parse_context *parse_ctx)
-{
- ssize_t old_len;
- uint8_t *value;
- size_t value_len;
-
- parse_ctx->llc_msg_name = "ACT_PDP_REQ";
-
- /* Skip Requested NSAPI */
- /* Skip Requested LLC SAPI */
- osmo_shift_v_fixed(&data, &data_len, 2, NULL);
-
- /* Skip Requested QoS (support 04.08 and 24.008) */
- if (osmo_shift_lv(&data, &data_len, NULL, &value_len) <= 0 ||
- value_len < 4 || value_len > 14)
- /* invalid */
- return 0;
-
- /* Skip Requested PDP address */
- if (osmo_shift_lv(&data, &data_len, NULL, &value_len) <= 0 ||
- value_len < 2 || value_len > 18)
- /* invalid */
- return 0;
-
- /* Access point name */
- old_len = osmo_match_shift_tlv(&data, &data_len,
- GSM48_IE_GSM_APN, &value, &value_len);
-
- if (old_len > 0 && value_len >=1 && value_len <= 100) {
- parse_ctx->apn_ie = data - old_len;
- parse_ctx->apn_ie_len = old_len;
- }
-
- return 1;
-}
-
-int gprs_gb_parse_dtap(uint8_t *data, size_t data_len,
- struct gprs_gb_parse_context *parse_ctx)
-{
- struct gsm48_hdr *g48h;
- uint8_t pdisc;
- uint8_t msg_type;
-
- if (osmo_shift_v_fixed(&data, &data_len, sizeof(*g48h), (uint8_t **)&g48h) <= 0)
- return 0;
-
- parse_ctx->g48_hdr = g48h;
-
- pdisc = gsm48_hdr_pdisc(g48h);
- if (pdisc != GSM48_PDISC_MM_GPRS && pdisc != GSM48_PDISC_SM_GPRS)
- return 1;
-
- msg_type = gsm48_hdr_msg_type(g48h);
- switch (msg_type) {
- case GSM48_MT_GMM_ATTACH_REQ:
- return gprs_gb_parse_gmm_attach_req(data, data_len, parse_ctx);
-
- case GSM48_MT_GMM_ATTACH_REJ:
- return gprs_gb_parse_gmm_attach_rej(data, data_len, parse_ctx);
-
- case GSM48_MT_GMM_ATTACH_ACK:
- return gprs_gb_parse_gmm_attach_ack(data, data_len, parse_ctx);
-
- case GSM48_MT_GMM_RA_UPD_REQ:
- return gprs_gb_parse_gmm_ra_upd_req(data, data_len, parse_ctx);
-
- case GSM48_MT_GMM_RA_UPD_REJ:
- return gprs_gb_parse_gmm_ra_upd_rej(data, data_len, parse_ctx);
-
- case GSM48_MT_GMM_RA_UPD_ACK:
- return gprs_gb_parse_gmm_ra_upd_ack(data, data_len, parse_ctx);
-
- case GSM48_MT_GMM_PTMSI_REALL_CMD:
- return gprs_gb_parse_gmm_ptmsi_reall_cmd(data, data_len, parse_ctx);
-
- case GSM48_MT_GSM_ACT_PDP_REQ:
- return gprs_gb_parse_gsm_act_pdp_req(data, data_len, parse_ctx);
-
- case GSM48_MT_GMM_ID_RESP:
- return gprs_gb_parse_gmm_id_resp(data, data_len, parse_ctx);
-
- case GSM48_MT_GMM_DETACH_REQ:
- return gprs_gb_parse_gmm_detach_req(data, data_len, parse_ctx);
-
- case GSM48_MT_GMM_DETACH_ACK:
- parse_ctx->llc_msg_name = "DETACH_ACK";
- parse_ctx->invalidate_tlli = 1;
- break;
-
- default:
- LOGP(DLLC, LOGL_NOTICE,
- "Unhandled GSM 04.08 message type %s for protocol discriminator %s.\n",
- get_value_string(gprs_msgt_gmm_names, msg_type), get_value_string(gsm48_pdisc_names, pdisc));
- break;
- };
-
- return 1;
-}
-
-int gprs_gb_parse_llc(uint8_t *llc, size_t llc_len,
- struct gprs_gb_parse_context *parse_ctx)
-{
- struct gprs_llc_hdr_parsed *ghp = &parse_ctx->llc_hdr_parsed;
- int rc;
- int fcs;
-
- /* parse LLC */
- rc = gprs_llc_hdr_parse(ghp, llc, llc_len);
- gprs_llc_hdr_dump(ghp, NULL);
- if (rc != 0) {
- LOGP(DLLC, LOGL_NOTICE, "Error during LLC header parsing\n");
- return 0;
- }
-
- fcs = gprs_llc_fcs(llc, ghp->crc_length);
- LOGP(DLLC, LOGL_DEBUG, "Got LLC message, CRC: %06x (computed %06x)\n",
- ghp->fcs, fcs);
-
- if (!ghp->data)
- return 0;
-
- if (ghp->sapi != GPRS_SAPI_GMM)
- return 1;
-
- if (ghp->cmd != GPRS_LLC_UI)
- return 1;
-
- if (ghp->is_encrypted) {
- parse_ctx->need_decryption = 1;
- return 0;
- }
-
- return gprs_gb_parse_dtap(ghp->data, ghp->data_len, parse_ctx);
-}
-
-int gprs_gb_parse_bssgp(uint8_t *bssgp, size_t bssgp_len,
- struct gprs_gb_parse_context *parse_ctx)
-{
- struct bssgp_normal_hdr *bgph;
- struct bssgp_ud_hdr *budh = NULL;
- struct tlv_parsed *tp = &parse_ctx->bssgp_tp;
- uint8_t pdu_type;
- uint8_t *data;
- size_t data_len;
- int rc;
-
- if (bssgp_len < sizeof(struct bssgp_normal_hdr))
- return 0;
-
- bgph = (struct bssgp_normal_hdr *)bssgp;
- pdu_type = bgph->pdu_type;
-
- if (pdu_type == BSSGP_PDUT_UL_UNITDATA ||
- pdu_type == BSSGP_PDUT_DL_UNITDATA) {
- if (bssgp_len < sizeof(struct bssgp_ud_hdr))
- return 0;
- budh = (struct bssgp_ud_hdr *)bssgp;
- bgph = NULL;
- data = budh->data;
- data_len = bssgp_len - sizeof(*budh);
- } else {
- data = bgph->data;
- data_len = bssgp_len - sizeof(*bgph);
- }
-
- parse_ctx->pdu_type = pdu_type;
- parse_ctx->bud_hdr = budh;
- parse_ctx->bgp_hdr = bgph;
- parse_ctx->bssgp_data = data;
- parse_ctx->bssgp_data_len = data_len;
-
- if (bssgp_tlv_parse(tp, data, data_len) < 0)
- return 0;
-
- if (budh)
- parse_ctx->tlli_enc = (uint8_t *)&budh->tlli;
-
- if (TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA))
- parse_ctx->bssgp_raid_enc = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA);
-
- if (TLVP_PRESENT(tp, BSSGP_IE_CELL_ID))
- parse_ctx->bssgp_raid_enc = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_CELL_ID);
-
- if (TLVP_PRESENT(tp, BSSGP_IE_IMSI)) {
- parse_ctx->imsi = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_IMSI);
- parse_ctx->imsi_len = TLVP_LEN(tp, BSSGP_IE_IMSI);
- }
-
- if (TLVP_PRESENT(tp, BSSGP_IE_TLLI)) {
- if (parse_ctx->tlli_enc)
- /* This is TLLI old, don't confuse it with TLLI current */
- parse_ctx->old_tlli_enc = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_TLLI);
- else
- parse_ctx->tlli_enc = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_TLLI);
- }
-
- if (TLVP_PRESENT(tp, BSSGP_IE_TMSI) && pdu_type == BSSGP_PDUT_PAGING_PS)
- parse_ctx->bssgp_ptmsi_enc = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_TMSI);
-
- if (TLVP_PRESENT(tp, BSSGP_IE_LLC_PDU)) {
- uint8_t *llc = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_LLC_PDU);
- size_t llc_len = TLVP_LEN(tp, BSSGP_IE_LLC_PDU);
-
- rc = gprs_gb_parse_llc(llc, llc_len, parse_ctx);
- if (!rc)
- return 0;
-
- parse_ctx->llc = llc;
- parse_ctx->llc_len = llc_len;
- }
-
- if (parse_ctx->tlli_enc) {
- uint32_t tmp_tlli;
- memcpy(&tmp_tlli, parse_ctx->tlli_enc, sizeof(tmp_tlli));
- parse_ctx->tlli = ntohl(tmp_tlli);
- }
-
- if (parse_ctx->bssgp_raid_enc && parse_ctx->old_raid_enc &&
- memcmp(parse_ctx->bssgp_raid_enc, parse_ctx->old_raid_enc, 6) != 0)
- parse_ctx->old_raid_is_foreign = 1;
-
- return 1;
-}
-
-void gprs_gb_log_parse_context(int log_level,
- struct gprs_gb_parse_context *parse_ctx,
- const char *default_msg_name)
-{
- const char *msg_name;
- const char *sep = "";
-
- if (!parse_ctx->tlli_enc &&
- !parse_ctx->ptmsi_enc &&
- !parse_ctx->new_ptmsi_enc &&
- !parse_ctx->bssgp_ptmsi_enc &&
- !parse_ctx->imsi)
- return;
-
- msg_name = gprs_gb_message_name(parse_ctx, default_msg_name);
-
- if (parse_ctx->llc_msg_name)
- msg_name = parse_ctx->llc_msg_name;
-
- LOGP(DGPRS, log_level, "%s: Got", msg_name);
-
- if (parse_ctx->tlli_enc) {
- LOGPC(DGPRS, log_level, "%s TLLI %08x", sep, parse_ctx->tlli);
- sep = ",";
- }
-
- if (parse_ctx->old_tlli_enc) {
- LOGPC(DGPRS, log_level, "%s old TLLI %02x%02x%02x%02x", sep,
- parse_ctx->old_tlli_enc[0],
- parse_ctx->old_tlli_enc[1],
- parse_ctx->old_tlli_enc[2],
- parse_ctx->old_tlli_enc[3]);
- sep = ",";
- }
-
- if (parse_ctx->bssgp_raid_enc) {
- struct gprs_ra_id raid;
- gsm48_parse_ra(&raid, parse_ctx->bssgp_raid_enc);
- LOGPC(DGPRS, log_level, "%s BSSGP RAID %u-%u-%u-%u", sep,
- raid.mcc, raid.mnc, raid.lac, raid.rac);
- sep = ",";
- }
-
- if (parse_ctx->raid_enc) {
- struct gprs_ra_id raid;
- gsm48_parse_ra(&raid, parse_ctx->raid_enc);
- LOGPC(DGPRS, log_level, "%s RAID %u-%u-%u-%u", sep,
- raid.mcc, raid.mnc, raid.lac, raid.rac);
- sep = ",";
- }
-
- if (parse_ctx->old_raid_enc) {
- struct gprs_ra_id raid;
- gsm48_parse_ra(&raid, parse_ctx->old_raid_enc);
- LOGPC(DGPRS, log_level, "%s old RAID %u-%u-%u-%u", sep,
- raid.mcc, raid.mnc, raid.lac, raid.rac);
- sep = ",";
- }
-
- if (parse_ctx->bssgp_ptmsi_enc) {
- uint32_t ptmsi = GSM_RESERVED_TMSI;
- gprs_parse_tmsi(parse_ctx->bssgp_ptmsi_enc, &ptmsi);
- LOGPC(DGPRS, log_level, "%s BSSGP PTMSI %08x", sep, ptmsi);
- sep = ",";
- }
-
- if (parse_ctx->ptmsi_enc) {
- uint32_t ptmsi = GSM_RESERVED_TMSI;
- gprs_parse_tmsi(parse_ctx->ptmsi_enc, &ptmsi);
- LOGPC(DGPRS, log_level, "%s PTMSI %08x", sep, ptmsi);
- sep = ",";
- }
-
- if (parse_ctx->new_ptmsi_enc) {
- uint32_t new_ptmsi = GSM_RESERVED_TMSI;
- gprs_parse_tmsi(parse_ctx->new_ptmsi_enc, &new_ptmsi);
- LOGPC(DGPRS, log_level, "%s new PTMSI %08x", sep, new_ptmsi);
- sep = ",";
- }
-
- if (parse_ctx->imsi) {
- char mi_buf[200];
- mi_buf[0] = '\0';
- gsm48_mi_to_string(mi_buf, sizeof(mi_buf),
- parse_ctx->imsi, parse_ctx->imsi_len);
- LOGPC(DGPRS, log_level, "%s IMSI %s",
- sep, mi_buf);
- sep = ",";
- }
- if (parse_ctx->invalidate_tlli) {
- LOGPC(DGPRS, log_level, "%s invalidate", sep);
- sep = ",";
- }
- if (parse_ctx->await_reattach) {
- LOGPC(DGPRS, log_level, "%s re-attach", sep);
- sep = ",";
- }
-
- LOGPC(DGPRS, log_level, "\n");
-}
-
-const char *gprs_gb_message_name(const struct gprs_gb_parse_context *parse_ctx,
- const char *default_msg_name)
-{
- if (parse_ctx->llc_msg_name)
- return parse_ctx->llc_msg_name;
-
- if (parse_ctx->g48_hdr)
- return "GMM";
-
- if (parse_ctx->llc)
- return "LLC";
-
- if (parse_ctx->bud_hdr)
- return "BSSGP-UNITDATA";
-
- if (parse_ctx->bgp_hdr)
- return "BSSGP";
-
- return "unknown";
-}
diff --git a/src/gprs/gprs_gmm.c b/src/gprs/gprs_gmm.c
deleted file mode 100644
index 032137f0b..000000000
--- a/src/gprs/gprs_gmm.c
+++ /dev/null
@@ -1,2937 +0,0 @@
-/* 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 <laforge@gnumonks.org>
- * (C) 2010 by On-Waves
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdint.h>
-#include <errno.h>
-#include <stdbool.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <netdb.h>
-
-#include <openssl/rand.h>
-
-#include "bscconfig.h"
-
-#include <openbsc/db.h>
-#include <osmocom/core/msgb.h>
-#include <osmocom/gsm/tlv.h>
-#include <osmocom/gsm/gsm_utils.h>
-#include <osmocom/core/signal.h>
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/rate_ctr.h>
-#include <osmocom/core/utils.h>
-#include <osmocom/crypt/auth.h>
-#include <osmocom/gsm/apn.h>
-#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
-
-#include <osmocom/gprs/gprs_bssgp.h>
-
-#ifdef BUILD_IU
-#include <osmocom/ranap/ranap_ies_defs.h>
-#include <osmocom/ranap/ranap_msg_factory.h>
-#include <osmocom/ranap/iu_client.h>
-#endif
-
-#include <openbsc/debug.h>
-#include <openbsc/gsm_data.h>
-#include <openbsc/gsm_subscriber.h>
-#include <openbsc/gsm_04_08.h>
-#include <openbsc/paging.h>
-#include <openbsc/transaction.h>
-#include <openbsc/gprs_llc.h>
-#include <openbsc/gprs_sgsn.h>
-#include <openbsc/gprs_gmm.h>
-#include <openbsc/gprs_utils.h>
-#include <openbsc/gprs_subscriber.h>
-#include <openbsc/sgsn.h>
-#include <openbsc/signal.h>
-#include <openbsc/gprs_sndcp.h>
-
-#include <pdp.h>
-
-#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_AUTH_RES_EXT] = { TLV_TYPE_TLV, 0 },
- [GSM48_IE_GMM_AUTH_FAIL_PAR] = { TLV_TYPE_TLV, 0 },
- [GSM48_IE_GMM_IMEISV] = { TLV_TYPE_TLV, 0 },
- [GSM48_IE_GMM_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 const struct value_string gprs_pmm_state_names[] = {
- { PMM_DETACHED, "PMM DETACH" },
- { PMM_CONNECTED, "PMM CONNECTED" },
- { PMM_IDLE, "PMM IDLE" },
- { MM_IDLE, "MM IDLE" },
- { MM_READY, "MM READY" },
- { MM_STANDBY, "MM STANDBY" },
- { 0, NULL }
-};
-
-static int gsm48_gmm_authorize(struct sgsn_mm_ctx *ctx);
-
-static void mmctx_change_gtpu_endpoints_to_sgsn(struct sgsn_mm_ctx *mm_ctx)
-{
- struct sgsn_pdp_ctx *pdp;
- llist_for_each_entry(pdp, &mm_ctx->pdp_list, list) {
- sgsn_pdp_upd_gtp_u(pdp,
- &sgsn->cfg.gtp_listenaddr.sin_addr,
- sizeof(sgsn->cfg.gtp_listenaddr.sin_addr));
- }
-}
-
-void mmctx_set_pmm_state(struct sgsn_mm_ctx *ctx, enum gprs_pmm_state state)
-{
- if (ctx->ran_type != MM_CTX_T_UTRAN_Iu)
- return;
-
- if (ctx->pmm_state == state)
- return;
-
- LOGMMCTXP(LOGL_INFO, ctx, "Changing PMM state from %s to %s\n",
- get_value_string(gprs_pmm_state_names, ctx->pmm_state),
- get_value_string(gprs_pmm_state_names, state));
-
- switch (state) {
- case PMM_IDLE:
- /* TODO: start RA Upd timer */
- mmctx_change_gtpu_endpoints_to_sgsn(ctx);
- break;
- case PMM_CONNECTED:
- break;
- default:
- break;
- }
-
- ctx->pmm_state = state;
-}
-
-void mmctx_set_mm_state(struct sgsn_mm_ctx *ctx, enum gprs_pmm_state state)
-{
- if (ctx->ran_type != MM_CTX_T_GERAN_Gb)
- return;
-
- if (ctx->pmm_state == state)
- return;
-
- LOGMMCTXP(LOGL_INFO, ctx, "Changing MM state from %s to %s\n",
- get_value_string(gprs_pmm_state_names, ctx->pmm_state),
- get_value_string(gprs_pmm_state_names, state));
-
- ctx->pmm_state = state;
-}
-
-#ifdef BUILD_IU
-int sgsn_ranap_rab_ass_resp(struct sgsn_mm_ctx *ctx, RANAP_RAB_SetupOrModifiedItemIEs_t *setup_ies);
-int sgsn_ranap_iu_event(struct ranap_ue_conn_ctx *ctx, enum ranap_iu_event_type type, void *data)
-{
- struct sgsn_mm_ctx *mm;
- int rc = -1;
-
- mm = sgsn_mm_ctx_by_ue_ctx(ctx);
-
-#define REQUIRE_MM \
- if (!mm) { \
- LOGP(DRANAP, LOGL_NOTICE, "Cannot find mm ctx for IU event %d\n", type); \
- return rc; \
- }
-
- switch (type) {
- case RANAP_IU_EVENT_RAB_ASSIGN:
- REQUIRE_MM
- rc = sgsn_ranap_rab_ass_resp(mm, (RANAP_RAB_SetupOrModifiedItemIEs_t *)data);
- break;
- case RANAP_IU_EVENT_IU_RELEASE:
- /* fall thru */
- case RANAP_IU_EVENT_LINK_INVALIDATED:
- /* Clean up ranap_ue_conn_ctx here */
- if (mm)
- LOGMMCTXP(LOGL_INFO, mm, "IU release for imsi %s\n", mm->imsi);
- else
- LOGMMCTXP(LOGL_INFO, mm, "IU release for UE conn 0x%x\n",
- ctx->conn_id);
- if (mm && mm->pmm_state == PMM_CONNECTED)
- mmctx_set_pmm_state(mm, PMM_IDLE);
- rc = 0;
- break;
- case RANAP_IU_EVENT_SECURITY_MODE_COMPLETE:
- REQUIRE_MM
- /* Continue authentication here */
- mm->iu.ue_ctx->integrity_active = 1;
- rc = gsm48_gmm_authorize(mm);
- break;
- default:
- LOGP(DRANAP, LOGL_NOTICE, "Unknown event received: %i\n", type);
- rc = -1;
- break;
- }
- return rc;
-}
-#endif
-
-
-/* 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 ? */
- osmo_timer_setup(&mm->timer, mmctx_timer_cb, mm);
- 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.
- * For param encryptable, see 3GPP TS 24.008 § 4.7.1.2 and
- * gsm48_hdr_gmm_cipherable(). Pass false for not cipherable messages. */
-static int gsm48_gmm_sendmsg(struct msgb *msg, int command,
- struct sgsn_mm_ctx *mm, bool encryptable)
-{
- if (mm) {
- rate_ctr_inc(&mm->ctrg->ctr[GMM_CTR_PKTS_SIG_OUT]);
-#ifdef BUILD_IU
- if (mm->ran_type == MM_CTX_T_UTRAN_Iu)
- return ranap_iu_tx(msg, GPRS_SAPI_GMM);
-#endif
- }
-
-#ifdef BUILD_IU
- /* In Iu mode, msg->dst contains the ranap_ue_conn_ctx pointer, in Gb mode
- * dst is empty. */
- /* FIXME: have a more explicit indicator for Iu messages */
- if (msg->dst)
- return ranap_iu_tx(msg, GPRS_SAPI_GMM);
-#endif
-
- /* caller needs to provide TLLI, BVCI and NSEI */
- return gprs_llc_tx_ui(msg, GPRS_SAPI_GMM, command, mm, encryptable);
-}
-
-/* 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);
- msg->dst = old->dst;
-}
-
-/* Store BVCI/NSEI in MM context */
-static void msgid2mmctx(struct sgsn_mm_ctx *mm, const struct msgb *msg)
-{
- mm->gb.bvci = msgb_bvci(msg);
- mm->gb.nsei = msgb_nsei(msg);
- /* In case a Iu connection is reconnected we need to update the ue ctx */
- mm->iu.ue_ctx = msg->dst;
- if (mm->ran_type == MM_CTX_T_UTRAN_Iu
- && mm->iu.ue_ctx) {
-#ifdef BUILD_IU
- mm->iu.ue_ctx->rab_assign_addr_enc =
- sgsn->cfg.iu.rab_assign_addr_enc;
-#endif
- }
-}
-
-/* Store BVCI/NSEI in MM context */
-static void mmctx2msgid(struct msgb *msg, const struct sgsn_mm_ctx *mm)
-{
- msgb_tlli(msg) = mm->gb.tlli;
- msgb_bvci(msg) = mm->gb.bvci;
- msgb_nsei(msg) = mm->gb.nsei;
- msg->dst = mm->iu.ue_ctx;
-}
-
-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->gmm_state = GMM_DEREGISTERED;
- mmctx_set_pmm_state(ctx, PMM_DETACHED);
- mmctx_set_pmm_state(ctx, MM_IDLE);
-
- 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, true);
-}
-
-static int gsm48_tx_gmm_status(struct sgsn_mm_ctx *mmctx, uint8_t cause)
-{
- struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 GMM STATUS");
-
- 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_name("GSM 04.08 SM STATUS");
-
- 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, true);
-}
-
-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_name("GSM 04.08 DET REQ");
-
- 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_name("GSM 04.08 DET OLD");
-
- 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_name("GSM 04.08 ATT ACK");
- 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);
- rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_GPRS_ATTACH_ACKED]);
-
- 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, true);
-}
-
-/* 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));
- rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_GPRS_ATTACH_REJECTED]);
-
- 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, false);
-}
-static int gsm48_tx_gmm_att_rej_oldmsg(const struct msgb *old_msg,
- uint8_t gmm_cause)
-{
- struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 ATT REJ OLD");
- 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_name("GSM 04.08 ATT REJ");
- 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);
- rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_GPRS_DETACH_ACKED]);
-
- 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, true);
-}
-
-static int gsm48_tx_gmm_det_ack(struct sgsn_mm_ctx *mm, uint8_t force_stby)
-{
- struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 DET ACK");
-
- 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_name("GSM 04.08 DET ACK OLD");
-
- 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_name("GSM 04.08 ID REQ");
- 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, false);
-}
-
-/* determine if the MS/UE supports R99 or later */
-static bool mmctx_is_r99(const struct sgsn_mm_ctx *mm)
-{
- if (mm->ms_network_capa.len < 1)
- return false;
- if (mm->ms_network_capa.buf[0] & 0x01)
- return true;
- return false;
-}
-
-/* 3GPP TS 24.008 Section 9.4.9: Authentication and Ciphering Request */
-static int gsm48_tx_gmm_auth_ciph_req(struct sgsn_mm_ctx *mm,
- const struct osmo_auth_vector *vec,
- uint8_t key_seq, bool force_standby)
-{
- struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 AUTH CIPH REQ");
- struct gsm48_hdr *gh;
- struct gsm48_auth_ciph_req *acreq;
- uint8_t *m_rand, *m_cksn, rbyte;
-
- LOGMMCTXP(LOGL_INFO, mm, "<- GPRS AUTH AND CIPHERING REQ (rand = %s",
- osmo_hexdump(vec->rand, sizeof(vec->rand)));
- if (mmctx_is_r99(mm) && vec
- && (vec->auth_types & OSMO_AUTH_TYPE_UMTS)) {
- LOGPC(DMM, LOGL_INFO, ", autn = %s)\n",
- osmo_hexdump(vec->autn, sizeof(vec->autn)));
- } else
- LOGPC(DMM, LOGL_INFO, ")\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_REQ;
-
- acreq = (struct gsm48_auth_ciph_req *) msgb_put(msg, sizeof(*acreq));
- acreq->ciph_alg = mm->ciph_algo & 0xf;
- /* § 10.5.5.10: */
- acreq->imeisv_req = 0x1;
- /* § 10.5.5.7: */
- acreq->force_stby = force_standby;
- /* 3GPP TS 24.008 § 10.5.5.19: */
- if (RAND_bytes(&rbyte, 1) != 1) {
- LOGP(DMM, LOGL_NOTICE, "RAND_bytes failed for A&C ref, falling "
- "back to rand()\n");
- acreq->ac_ref_nr = rand();
- } else
- acreq->ac_ref_nr = rbyte;
- mm->ac_ref_nr_used = acreq->ac_ref_nr;
-
- /* Only if authentication is requested we need to set RAND + CKSN */
- if (vec) {
- m_rand = msgb_put(msg, sizeof(vec->rand) + 1);
- m_rand[0] = GSM48_IE_GMM_AUTH_RAND;
- memcpy(m_rand + 1, vec->rand, sizeof(vec->rand));
-
- /* § 10.5.1.2: */
- m_cksn = msgb_put(msg, 1);
- m_cksn[0] = (GSM48_IE_GMM_CIPH_CKSN << 4) | (key_seq & 0x07);
-
- /* A Release99 or higher MS/UE must be able to handle
- * the optional AUTN IE. If a classic GSM SIM is
- * inserted, it will simply ignore AUTN and just use
- * RAND */
- if (mmctx_is_r99(mm) &&
- (vec->auth_types & OSMO_AUTH_TYPE_UMTS)) {
- msgb_tlv_put(msg, GSM48_IE_GMM_AUTN,
- sizeof(vec->autn), vec->autn);
- }
- }
-
- return gsm48_gmm_sendmsg(msg, 1, mm, false);
-}
-
-/* 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_name("GSM 04.08 AUTH CIPH REJ");
- 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, false);
-}
-
-/* check if the received authentication response matches */
-static bool check_auth_resp(struct sgsn_mm_ctx *ctx,
- bool is_utran,
- const struct osmo_auth_vector *vec,
- const uint8_t *res, uint8_t res_len)
-{
- const uint8_t *expect_res;
- uint8_t expect_res_len;
- enum osmo_sub_auth_type expect_type;
- const char *expect_str;
-
- if (!vec)
- return true; /* really!? */
-
- /* On UTRAN (3G) we always expect UMTS AKA. On GERAN (2G) we sent AUTN
- * and expect UMTS AKA if there is R99 capability and our vector
- * supports UMTS AKA, otherwise we expect GSM AKA. */
- if (is_utran
- || (mmctx_is_r99(ctx) && (vec->auth_types & OSMO_AUTH_TYPE_UMTS))) {
- expect_type = OSMO_AUTH_TYPE_UMTS;
- expect_str = "UMTS RES";
- expect_res = vec->res;
- expect_res_len = vec->res_len;
- } else {
- expect_type = OSMO_AUTH_TYPE_GSM;
- expect_str = "GSM SRES";
- expect_res = vec->sres;
- expect_res_len = sizeof(vec->sres);
- }
-
- if (!(vec->auth_types & expect_type)) {
- LOGMMCTXP(LOGL_ERROR, ctx, "Auth error: auth vector does"
- " not provide the expected auth type:"
- " expected %s = 0x%x, auth_types are 0x%x\n",
- expect_str, expect_type, vec->auth_types);
- return false;
- }
-
- if (!res)
- goto auth_mismatch;
-
- if (res_len != expect_res_len)
- goto auth_mismatch;
-
- if (memcmp(res, expect_res, res_len) != 0)
- goto auth_mismatch;
-
- /* Authorized! */
- return true;
-
-auth_mismatch:
- LOGMMCTXP(LOGL_ERROR, ctx, "Auth mismatch: expected %s = %s\n",
- expect_str, osmo_hexdump_nospc(expect_res, expect_res_len));
- return false;
-}
-
-/* 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;
- const char *res_name = "(no response)";
- uint8_t res[16];
- uint8_t res_len;
- 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;
- }
-
- if (acr->ac_ref_nr != ctx->ac_ref_nr_used) {
- LOGMMCTXP(LOGL_NOTICE, ctx, "Reference mismatch for Auth & Ciph"
- " Response: %u received, %u expected\n",
- acr->ac_ref_nr, ctx->ac_ref_nr_used);
- 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);
-
- if (!TLVP_PRESENT(&tp, GSM48_IE_GMM_AUTH_SRES) ||
- !TLVP_PRESENT(&tp, GSM48_IE_GMM_IMEISV) ||
- TLVP_LEN(&tp,GSM48_IE_GMM_AUTH_SRES) != 4) {
- /* TODO: missing mandatory IE, return STATUS or REJ? */
- LOGMMCTXP(LOGL_ERROR, ctx, "Missing mandantory IE\n");
- return -EINVAL;
- }
-
- /* Start with the good old 4-byte SRES */
- memcpy(res, TLVP_VAL(&tp, GSM48_IE_GMM_AUTH_SRES), 4);
- res_len = 4;
- res_name = "GSM SRES";
-
- /* Append extended RES as part of UMTS AKA, if any */
- if (TLVP_PRESENT(&tp, GSM48_IE_GMM_AUTH_RES_EXT)) {
- unsigned int l = TLVP_LEN(&tp, GSM48_IE_GMM_AUTH_RES_EXT);
- if (l > sizeof(res)-4)
- l = sizeof(res)-4;
- memcpy(res+4, TLVP_VAL(&tp, GSM48_IE_GMM_AUTH_RES_EXT), l);
- res_len += l;
- res_name = "UMTS RES";
- }
-
- at = &ctx->auth_triplet;
-
- LOGMMCTXP(LOGL_DEBUG, ctx, "checking auth: received %s = %s\n",
- res_name, osmo_hexdump(res, res_len));
- rc = check_auth_resp(ctx, false, &at->vec, res, res_len);
- if (!rc) {
- rc = gsm48_tx_gmm_auth_ciph_rej(ctx);
- mm_ctx_cleanup_free(ctx, "GPRS AUTH AND CIPH REJECT");
- return rc;
- }
-
- ctx->is_authenticated = 1;
-
- if (ctx->ran_type == MM_CTX_T_UTRAN_Iu)
- ctx->iu.new_key = 1;
-
- /* FIXME: enable LLC cipheirng */
-
- /* Check if we can let the mobile station enter */
- return gsm48_gmm_authorize(ctx);
-}
-
-/* Section 9.4.10: Authentication and Ciphering Failure */
-static int gsm48_rx_gmm_auth_ciph_fail(struct sgsn_mm_ctx *ctx,
- struct msgb *msg)
-{
- struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg);
- struct tlv_parsed tp;
- const uint8_t gmm_cause = gh->data[0];
- const uint8_t *auts;
- int rc;
-
- LOGMMCTXP(LOGL_INFO, ctx, "-> GPRS AUTH AND CIPH FAILURE (cause = %s)\n",
- get_value_string(gsm48_gmm_cause_names, gmm_cause));
-
- tlv_parse(&tp, &gsm48_gmm_att_tlvdef, gh->data+1, msg->len - 1, 0, 0);
-
- /* Only if GMM cause is present and the AUTS is provided, we can
- * start re-sync procedure */
- if (gmm_cause == GMM_CAUSE_SYNC_FAIL &&
- TLVP_PRESENT(&tp, GSM48_IE_GMM_AUTH_FAIL_PAR)) {
- if (TLVP_LEN(&tp, GSM48_IE_GMM_AUTH_FAIL_PAR) != 14) {
- LOGMMCTXP(LOGL_ERROR, ctx, "AUTS IE has wrong size:"
- " expected %d, got %u\n", 14,
- TLVP_LEN(&tp, GSM48_IE_GMM_AUTH_FAIL_PAR));
- return -EINVAL;
- }
- auts = TLVP_VAL(&tp, GSM48_IE_GMM_AUTH_FAIL_PAR);
-
- LOGMMCTXP(LOGL_INFO, ctx,
- "R99 AUTHENTICATION SYNCH (AUTS = %s)\n",
- osmo_hexdump_nospc(auts, 14));
-
- /* make sure we'll refresh the auth_triplet in
- * sgsn_auth_update() */
- ctx->auth_triplet.key_seq = GSM_KEY_SEQ_INVAL;
-
- /* make sure we'll retry authentication after the resync */
- ctx->auth_state = SGSN_AUTH_UMTS_RESYNC;
-
- /* Send AUTS to HLR and wait for new Auth Info Result */
- rc = gprs_subscr_request_auth_info(ctx, auts,
- ctx->auth_triplet.vec.rand);
- if (!rc)
- return 0;
- /* on error, fall through to send a reject */
- LOGMMCTXP(LOGL_ERROR, ctx,
- "Sending AUTS to HLR failed (rc = %d)\n", rc);
- }
-
- LOGMMCTXP(LOGL_NOTICE, ctx, "Authentication failed\n");
- rc = gsm48_tx_gmm_auth_ciph_rej(ctx);
- mm_ctx_cleanup_free(ctx, "GPRS AUTH FAILURE");
- return rc;
-}
-
-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] = '+';
- osmo_strlcpy(&ctx->msisdn[1], called.number,
- sizeof(ctx->msisdn));
- } else {
- osmo_strlcpy(ctx->msisdn, called.number, sizeof(ctx->msisdn));
- }
-}
-
-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;
- }
-
- osmo_strlcpy(ctx->hlr, called.number, sizeof(ctx->hlr));
-}
-
-#ifdef BUILD_IU
-/* Chapter 9.4.21: Service accept */
-static int gsm48_tx_gmm_service_ack(struct sgsn_mm_ctx *mm)
-{
- struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 SERVICE ACK");
- struct gsm48_hdr *gh;
-
- LOGMMCTXP(LOGL_INFO, mm, "<- GPRS SERVICE ACCEPT (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_SERVICE_ACK;
-
- /* Optional: PDP context status */
- /* Optional: MBMS context status */
-
- return gsm48_gmm_sendmsg(msg, 0, mm, false);
-}
-#endif
-
-/* Chapter 9.4.22: Service reject */
-static int _tx_gmm_service_rej(struct msgb *msg, uint8_t gmm_cause,
- const struct sgsn_mm_ctx *mm)
-{
- struct gsm48_hdr *gh;
-
- LOGMMCTXP(LOGL_NOTICE, mm, "<- GPRS SERVICE 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_SERVICE_REJ;
- gh->data[0] = gmm_cause;
-
- return gsm48_gmm_sendmsg(msg, 0, NULL, true);
-}
-static int gsm48_tx_gmm_service_rej_oldmsg(const struct msgb *old_msg,
- uint8_t gmm_cause)
-{
- struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 SERVICE REJ OLD");
- gmm_copy_id(msg, old_msg);
- return _tx_gmm_service_rej(msg, gmm_cause, NULL);
-}
-#if 0
--- currently unused --
-static int gsm48_tx_gmm_service_rej(struct sgsn_mm_ctx *mm,
- uint8_t gmm_cause)
-{
- struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 SERVICE REJ");
- mmctx2msgid(msg, mm);
- return _tx_gmm_service_rej(msg, gmm_cause, mm);
-}
-#endif
-
-static int gsm48_tx_gmm_ra_upd_ack(struct sgsn_mm_ctx *mm);
-
-#ifdef BUILD_IU
-/* Send RAB activation requests for all PDP contexts */
-void activate_pdp_rabs(struct sgsn_mm_ctx *ctx)
-{
- struct sgsn_pdp_ctx *pdp;
- if (ctx->ran_type != MM_CTX_T_UTRAN_Iu)
- return;
- llist_for_each_entry(pdp, &ctx->pdp_list, list) {
- iu_rab_act_ps(pdp->nsapi, pdp);
- }
-}
-#endif
-
-/* Check if we can already authorize a subscriber */
-static int gsm48_gmm_authorize(struct sgsn_mm_ctx *ctx)
-{
-#ifdef BUILD_IU
- int rc;
-#endif
-#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->vec, at->key_seq,
- false);
- }
-
- 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 */
-#ifdef BUILD_IU
- if (ctx->ran_type == MM_CTX_T_UTRAN_Iu && !ctx->iu.ue_ctx->integrity_active) {
- rc = ranap_iu_tx_sec_mode_cmd(ctx->iu.ue_ctx, &ctx->auth_triplet.vec, 0, ctx->iu.new_key);
- ctx->iu.new_key = 0;
- return rc;
- }
-#endif
-
- switch (ctx->pending_req) {
- case 0:
- LOGMMCTXP(LOGL_INFO, ctx,
- "no pending request, authorization completed\n");
- break;
- case GSM48_MT_GMM_ATTACH_REQ:
- ctx->pending_req = 0;
-
- 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->gmm_state = GMM_REGISTERED_NORMAL;
-#endif
-
- return gsm48_tx_gmm_att_ack(ctx);
-#ifdef BUILD_IU
- case GSM48_MT_GMM_SERVICE_REQ:
- ctx->pending_req = 0;
- mmctx_set_pmm_state(ctx, PMM_CONNECTED);
- rc = gsm48_tx_gmm_service_ack(ctx);
-
- if (ctx->iu.service.type != GPRS_SERVICE_T_SIGNALLING)
- activate_pdp_rabs(ctx);
-
- return rc;
-#endif
- case GSM48_MT_GMM_RA_UPD_REQ:
- ctx->pending_req = 0;
- /* Send RA UPDATE ACCEPT */
- return gsm48_tx_gmm_ra_upd_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->gmm_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->gmm_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");
- }
- }
- osmo_strlcpy(ctx->imsi, mi_string, sizeof(ctx->imsi));
- break;
- case GSM_MI_TYPE_IMEI:
- osmo_strlcpy(ctx->imei, mi_string, sizeof(ctx->imei));
- 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 = 0;
- enum gsm48_gmm_cause reject_cause;
- int rc;
-
- LOGMMCTXP(LOGL_INFO, ctx, "-> GMM ATTACH REQUEST ");
- rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_GPRS_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. */
-
- /* In Iu mode, msg->dst contains the ranap_ue_conn_ctx pointer, in Gb mode
- * dst is empty. */
- /* FIXME: have a more explicit indicator for Iu messages */
- if (!msg->dst) {
- /* Gb mode */
- cid = bssgp_parse_cell_id(&ra_id, msgb_bcid(msg));
- } else {
-#ifdef BUILD_IU
- ra_id = ((struct ranap_ue_conn_ctx*)msg->dst)->ra_id;
-#else
- LOGMMCTXP(LOGL_ERROR, ctx, "Cannot handle Iu Attach Request, built without Iu support\n");
- return -ENOTSUP;
-#endif
- }
-
- /* 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;
-
- /* TODO: In iu mode - handle follow-on request */
-
- /* aTTACH Type 10.5.5.2 */
- att_type = *cur++ & 0x07;
-
- /* 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
- if (msg->dst)
- ctx = sgsn_mm_ctx_alloc_iu(msg->dst);
- else
- ctx = sgsn_mm_ctx_alloc_gb(0, &ra_id);
- if (!ctx) {
- reject_cause = GMM_CAUSE_NET_FAIL;
- goto rejected;
- }
- osmo_strlcpy(ctx->imsi, mi_string, sizeof(ctx->imsi));
-#endif
- }
- if (ctx->ran_type == MM_CTX_T_GERAN_Gb) {
- ctx->gb.tlli = msgb_tlli(msg);
- ctx->gb.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 */
- if (msg->dst)
- ctx = sgsn_mm_ctx_alloc_iu(msg->dst);
- else
- ctx = sgsn_mm_ctx_alloc_gb(msgb_tlli(msg), &ra_id);
- ctx->p_tmsi = tmsi;
- }
- if (ctx->ran_type == MM_CTX_T_GERAN_Gb) {
- ctx->gb.tlli = msgb_tlli(msg);
- ctx->gb.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;
- if (ctx->ran_type == MM_CTX_T_GERAN_Gb)
- ctx->gb.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);
- if (!gprs_ms_net_cap_gea_supported(ctx->ms_network_capa.buf, msnc_len,
- ctx->ciph_algo)) {
- reject_cause = GMM_CAUSE_PROTO_ERR_UNSPEC;
- LOGMMCTXP(LOGL_NOTICE, ctx, "Rejecting ATTACH REQUEST with MI "
- "type %s because MS do not support required %s "
- "encryption\n", gsm48_mi_type_name(mi_type),
- get_value_string(gprs_cipher_names,ctx->ciph_algo));
- goto rejected;
- }
-#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->gmm_state != GMM_COMMON_PROC_INIT) {
- ctx->p_tmsi_old = ctx->p_tmsi;
- ctx->p_tmsi = sgsn_alloc_ptmsi();
- }
- ctx->gmm_state = GMM_COMMON_PROC_INIT;
-#endif
-
- if (ctx->ran_type == MM_CTX_T_GERAN_Gb) {
- /* Even if there is no P-TMSI allocated, the MS will
- * switch from foreign TLLI to local TLLI */
- ctx->gb.tlli_new = gprs_tmsi2tlli(ctx->p_tmsi, TLLI_LOCAL);
-
- /* Inform LLC layer about new TLLI but keep old active */
- if (ctx->is_authenticated)
- gprs_llme_copy_key(ctx, ctx->gb.llme);
-
- gprs_llgmm_assign(ctx->gb.llme, ctx->gb.tlli, ctx->gb.tlli_new);
- }
-
- 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
- gprs_llgmm_unassign(llme);
-
- 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 */
- rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_GPRS_DETACH_REQUEST]);
- 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_name("GSM 04.08 UPD ACK");
- struct gsm48_hdr *gh;
- struct gsm48_ra_upd_ack *rua;
- uint8_t *mid;
-
- rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_GPRS_ROUTING_AREA_ACKED]);
- 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, true);
-}
-
-/* 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_name("GSM 04.08 RA UPD REJ");
- struct gsm48_hdr *gh;
-
- LOGP(DMM, LOGL_NOTICE, "<- ROUTING AREA UPDATE REJECT\n");
- rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_GPRS_ROUTING_AREA_REJECT]);
-
- 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, false);
-}
-
-static void process_ms_ctx_status(struct sgsn_mm_ctx *mmctx,
- const uint8_t *pdp_status)
-{
- struct sgsn_pdp_ctx *pdp, *pdp2;
- /* 24.008 4.7.5.1.3: If the PDP context status information element is
- * included in ROUTING AREA UPDATE REQUEST message, then the network
- * shall deactivate all those PDP contexts locally (without peer to
- * peer signalling between the MS and the network), which are not in SM
- * state PDP-INACTIVE on network side but are indicated by the MS as
- * being in state PDP-INACTIVE. */
-
- llist_for_each_entry_safe(pdp, pdp2, &mmctx->pdp_list, list) {
- 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 = GMM_CAUSE_PROTO_ERR_UNSPEC;
- int rc;
-
- /* TODO: In iu mode - handle follow-on request */
-
- /* Update Type 10.5.5.18 */
- upd_type = *cur++ & 0x07;
-
- rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_GPRS_ROUTING_AREA_REQUEST]);
- 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;
- }
-
- if (!mmctx) {
- /* BSSGP doesn't give us an mmctx */
-
- /* TODO: Check if there is an MM CTX with old_ra_id and
- * the P-TMSI (if given, reguired for UMTS) or as last resort
- * if the TLLI matches foreign_tlli (P-TMSI). Note that this
- * is an optimization to avoid the RA reject (impl detached)
- * below, which will cause a new attach cycle. */
- /* Look-up the MM context based on old RA-ID and TLLI */
- /* In Iu mode, msg->dst contains the ranap_ue_conn_ctx pointer, in Gb
- * mode dst is empty. */
- /* FIXME: have a more explicit indicator for Iu messages */
- if (!msg->dst) {
- mmctx = sgsn_mm_ctx_by_tlli_and_ptmsi(msgb_tlli(msg), &old_ra_id);
- } else if (TLVP_PRESENT(&tp, GSM48_IE_GMM_ALLOC_PTMSI)) {
-#ifdef BUILD_IU
- /* In Iu mode search only for ptmsi */
- char mi_string[GSM48_MI_SIZE];
- uint8_t mi_len = TLVP_LEN(&tp, GSM48_IE_GMM_ALLOC_PTMSI);
- uint8_t *mi = TLVP_VAL(&tp, GSM48_IE_GMM_ALLOC_PTMSI);
- uint8_t mi_type = *mi & GSM_MI_TYPE_MASK;
- uint32_t tmsi;
-
- gsm48_mi_to_string(mi_string, sizeof(mi_string), mi, mi_len);
-
- if (mi_type == GSM_MI_TYPE_TMSI) {
- memcpy(&tmsi, mi+1, 4);
- tmsi = ntohl(tmsi);
- mmctx = sgsn_mm_ctx_by_ptmsi(tmsi);
- }
-#else
- goto rejected;
-#endif
- }
- if (mmctx) {
- LOGMMCTXP(LOGL_INFO, mmctx,
- "Looked up by matching TLLI and P_TMSI. "
- "BSSGP TLLI: %08x, P-TMSI: %08x (%08x), "
- "TLLI: %08x (%08x), RA: %d-%d-%d-%d\n",
- msgb_tlli(msg),
- mmctx->p_tmsi, mmctx->p_tmsi_old,
- mmctx->gb.tlli, mmctx->gb.tlli_new,
- mmctx->ra.mcc, mmctx->ra.mnc,
- mmctx->ra.lac, mmctx->ra.rac);
-
- mmctx->gmm_state = GMM_COMMON_PROC_INIT;
- }
- } else if (!gprs_ra_id_equals(&mmctx->ra, &old_ra_id) ||
- mmctx->gmm_state == GMM_DEREGISTERED)
- {
- /* We cannot use the mmctx */
- LOGMMCTXP(LOGL_INFO, mmctx,
- "The MM context cannot be used, RA: %d-%d-%d-%d\n",
- mmctx->ra.mcc, mmctx->ra.mnc,
- mmctx->ra.lac, mmctx->ra.rac);
- mmctx = NULL;
- }
-
- if (!mmctx) {
- if (llme) {
- /* 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 */
- if (mmctx->ran_type == MM_CTX_T_GERAN_Gb) {
- bssgp_parse_cell_id(&mmctx->ra, msgb_bcid(msg));
- /* Update the MM context with the new (i.e. foreign) TLLI */
- mmctx->gb.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->gmm_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->gmm_state = GMM_COMMON_PROC_INIT;
-#else
- /* Make sure we are NORMAL (i.e. not SUSPENDED anymore) */
- mmctx->gmm_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
- if (mmctx->ran_type == MM_CTX_T_GERAN_Gb) {
- /* Even if there is no P-TMSI allocated, the MS will switch from
- * foreign TLLI to local TLLI */
- mmctx->gb.tlli_new = gprs_tmsi2tlli(mmctx->p_tmsi, TLLI_LOCAL);
-
- /* Inform LLC layer about new TLLI but keep old active */
- gprs_llgmm_assign(mmctx->gb.llme, mmctx->gb.tlli,
- mmctx->gb.tlli_new);
- }
-
- /* 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. In Iu, the RA upd request can be called from
- * a new Iu connection, so we might need to re-authenticate the
- * connection as well as turn on integrity protection. */
- mmctx->pending_req = GSM48_MT_GMM_RA_UPD_REQ;
- return gsm48_gmm_authorize(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 {
- if (llme)
- gprs_llgmm_unassign(llme);
- }
-
- return rc;
-}
-
-/* 3GPP TS 24.008 Section 9.4.20 Service request.
- * In Iu, a UE in PMM-IDLE mode can use GSM48_MT_GMM_SERVICE_REQ to switch back
- * to PMM-CONNECTED mode. */
-static int gsm48_rx_gmm_service_req(struct sgsn_mm_ctx *ctx, struct msgb *msg)
-{
- struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg);
- uint8_t *cur = gh->data, *mi;
- uint8_t ciph_seq_nr, service_type, mi_len, mi_type;
- uint32_t tmsi;
- struct tlv_parsed tp;
- char mi_string[GSM48_MI_SIZE];
- enum gsm48_gmm_cause reject_cause;
- int rc;
-
- LOGMMCTXP(LOGL_INFO, ctx, "-> GMM SERVICE REQUEST ");
-
- /* This message is only valid in Iu mode */
- if (!msg->dst) {
- LOGPC(DMM, LOGL_INFO, "Invalid if not in Iu mode\n");
- return -1;
- }
-
- /* Skip Ciphering key sequence number 10.5.1.2 */
- ciph_seq_nr = *cur & 0x07;
-
- /* Service type 10.5.5.20 */
- service_type = (*cur++ >> 4) & 0x07;
-
- /* 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_service_t_strs, service_type));
-
- LOGPC(DMM, LOGL_INFO, "\n");
-
- /* Optional: PDP context status, MBMS context status, Uplink data status, Device properties */
- tlv_parse(&tp, &gsm48_gmm_att_tlvdef, cur, (msg->data + msg->len) - cur, 0, 0);
-
- 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) {
- /* FIXME: We need to have a context for service request? */
- reject_cause = GMM_CAUSE_NET_FAIL;
- goto rejected;
- }
- 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) {
- /* FIXME: We need to have a context for service request? */
- reject_cause = GMM_CAUSE_NET_FAIL;
- goto rejected;
- }
- msgid2mmctx(ctx, msg);
- break;
- default:
- LOGMMCTXP(LOGL_NOTICE, ctx, "Rejecting SERVICE REQUEST with "
- "MI type %s\n", gsm48_mi_type_name(mi_type));
- reject_cause = GMM_CAUSE_MS_ID_NOT_DERIVED;
- goto rejected;
- }
-
- ctx->gmm_state = GMM_COMMON_PROC_INIT;
-
- ctx->iu.service.type = service_type;
-
- /* TODO: Handle those only in case of accept? */
- /* 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(ctx, pdp_status);
- }
-
-
- ctx->pending_req = GSM48_MT_GMM_SERVICE_REQ;
- return gsm48_gmm_authorize(ctx);
-
-err_inval:
- LOGPC(DMM, LOGL_INFO, "\n");
- reject_cause = GMM_CAUSE_SEM_INCORR_MSG;
-
-rejected:
- /* Send SERVICE REJECT */
- LOGMMCTXP(LOGL_NOTICE, ctx,
- "Rejecting Service Request with cause '%s' (%d)\n",
- get_value_string(gsm48_gmm_cause_names, reject_cause), reject_cause);
- rc = gsm48_tx_gmm_service_rej_oldmsg(msg, reject_cause);
-
- 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, bool drop_cipherable)
-{
- 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 (drop_cipherable && gsm48_hdr_gmm_cipherable(gh)) {
- LOGMMCTXP(LOGL_NOTICE, mmctx, "Dropping cleartext GMM %s which "
- "is expected to be encrypted for TLLI 0x%08x\n",
- get_value_string(gprs_msgt_gmm_names, gh->msg_type),
- llme->tlli);
- return -EBADMSG;
- }
-
- if (llme && !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_unassign(llme);
- return 0;
- }
-
- /* Don't reply or establish a LLME on DETACH_ACK */
- if (gh->msg_type == GSM48_MT_GMM_DETACH_ACK)
- return gprs_llgmm_unassign(llme);
-
- 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_unassign(llme);
- return rc;
- }
-
- /* Force the MS to re-attach */
- rc = gsm0408_gprs_force_reattach_oldmsg(msg, llme);
-
- /* TLLI unassignment */
- gprs_llgmm_unassign(llme);
- return rc;
- }
-
- /*
- * For a few messages, mmctx may be NULL. For most, we want to ensure a
- * non-NULL mmctx. At the same time, we want to keep the message
- * validity check intact, so that all message types appear in the
- * switch statement and the default case thus means "unknown message".
- * If we split the switch in two parts to check non-NULL halfway, the
- * unknown-message check breaks, or we'd need to duplicate the switch
- * cases in both parts. Just keep one large switch and add some gotos.
- */
- 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;
- case GSM48_MT_GMM_SERVICE_REQ:
- rc = gsm48_rx_gmm_service_req(mmctx, msg);
- break;
- /* For all the following types mmctx can not be NULL */
- case GSM48_MT_GMM_ID_RESP:
- if (!mmctx)
- goto null_mmctx;
- rc = gsm48_rx_gmm_id_resp(mmctx, msg);
- break;
- case GSM48_MT_GMM_STATUS:
- if (!mmctx)
- goto null_mmctx;
- rc = gsm48_rx_gmm_status(mmctx, msg);
- break;
- case GSM48_MT_GMM_DETACH_REQ:
- if (!mmctx)
- goto null_mmctx;
- rc = gsm48_rx_gmm_det_req(mmctx, msg);
- break;
- case GSM48_MT_GMM_DETACH_ACK:
- if (!mmctx)
- goto null_mmctx;
- LOGMMCTXP(LOGL_INFO, mmctx, "-> DETACH ACK\n");
- mm_ctx_cleanup_free(mmctx, "GPRS DETACH ACK");
- rc = 0;
- break;
- case GSM48_MT_GMM_ATTACH_COMPL:
- if (!mmctx)
- goto null_mmctx;
- /* 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;
- if (mmctx->ran_type == MM_CTX_T_GERAN_Gb) {
- /* Unassign the old TLLI */
- mmctx->gb.tlli = mmctx->gb.tlli_new;
- gprs_llme_copy_key(mmctx, mmctx->gb.llme);
- gprs_llgmm_assign(mmctx->gb.llme, 0xffffffff,
- mmctx->gb.tlli_new);
- }
- mmctx->gmm_state = GMM_REGISTERED_NORMAL;
- mmctx_set_pmm_state(mmctx, PMM_CONNECTED);
- mmctx_set_mm_state(mmctx, MM_READY);
- 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:
- if (!mmctx)
- goto null_mmctx;
- /* 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;
- if (mmctx->ran_type == MM_CTX_T_GERAN_Gb) {
- /* Unassign the old TLLI */
- mmctx->gb.tlli = mmctx->gb.tlli_new;
- gprs_llgmm_assign(mmctx->gb.llme, 0xffffffff,
- mmctx->gb.tlli_new);
- }
- mmctx->gmm_state = GMM_REGISTERED_NORMAL;
- mmctx_set_pmm_state(mmctx, PMM_CONNECTED);
- mmctx_set_mm_state(mmctx, MM_READY);
- 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:
- if (!mmctx)
- goto null_mmctx;
- 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;
- if (mmctx->ran_type == MM_CTX_T_GERAN_Gb) {
- /* Unassign the old TLLI */
- mmctx->gb.tlli = mmctx->gb.tlli_new;
- //gprs_llgmm_assign(mmctx->gb.llme, 0xffffffff, mmctx->gb.tlli_new, GPRS_ALGO_GEA0, NULL);
- }
- rc = 0;
- break;
- case GSM48_MT_GMM_AUTH_CIPH_RESP:
- if (!mmctx)
- goto null_mmctx;
- rc = gsm48_rx_gmm_auth_ciph_resp(mmctx, msg);
- break;
- case GSM48_MT_GMM_AUTH_CIPH_FAIL:
- rc = gsm48_rx_gmm_auth_ciph_fail(mmctx, msg);
- break;
- default:
- LOGMMCTXP(LOGL_NOTICE, mmctx, "Unknown GSM 04.08 GMM msg type 0x%02x\n",
- gh->msg_type);
- if (mmctx)
- rc = gsm48_tx_gmm_status(mmctx, GMM_CAUSE_MSGT_NOTEXIST_NOTIMPL);
- else
- rc = -EINVAL;
- break;
- }
-
- return rc;
-
-null_mmctx:
- LOGP(DMM, LOGL_ERROR,
- "Received GSM 04.08 message type 0x%02x,"
- " but no MM context available\n",
- gh->msg_type);
- return -EINVAL;
-}
-
-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->vec, at->key_seq, false);
- 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))
- LOGPDPCTXP(LOGL_ERROR, pdp, "Starting PDP 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 ? */
- osmo_timer_setup(&pdp->timer, pdpctx_timer_cb, pdp);
- osmo_timer_schedule(&pdp->timer, seconds, 0);
-}
-
-static void pdpctx_timer_stop(struct sgsn_pdp_ctx *pdp, unsigned int T)
-{
- if (pdp->T != T)
- LOGPDPCTXP(LOGL_ERROR, pdp, "Stopping PDP timer %u but "
- "%u is running\n", T, pdp->T);
- osmo_timer_del(&pdp->timer);
-}
-
-#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: Activate PDP Context Accept */
-int gsm48_tx_gsm_act_pdp_acc(struct sgsn_pdp_ctx *pdp)
-{
- struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 PDP ACC");
- struct gsm48_hdr *gh;
- uint8_t transaction_id = pdp->ti ^ 0x8; /* flip */
-
- LOGPDPCTXP(LOGL_INFO, pdp, "<- ACTIVATE PDP CONTEXT ACK\n");
- rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_PDP_ACTIVATE_ACCEPT]);
-
- 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, true);
-}
-
-/* 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_name("GSM 04.08 PDP REJ");
- struct gsm48_hdr *gh;
- uint8_t transaction_id = tid ^ 0x8; /* flip */
-
- LOGMMCTXP(LOGL_NOTICE, mm, "<- ACTIVATE PDP CONTEXT REJ(cause=%u)\n", cause);
- rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_PDP_ACTIVATE_REJECT]);
-
- 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, true);
-}
-
-/* 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_name("GSM 04.08 PDP DET REQ");
- struct gsm48_hdr *gh;
- uint8_t transaction_id = tid ^ 0x8; /* flip */
-
- LOGMMCTXP(LOGL_INFO, mm, "<- DEACTIVATE PDP CONTEXT REQ\n");
- rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_PDP_DL_DEACTIVATE_REQUEST]);
-
- 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, true);
-}
-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_name("GSM 04.08 PDP DET ACC");
- struct gsm48_hdr *gh;
- uint8_t transaction_id = tid ^ 0x8; /* flip */
-
- LOGMMCTXP(LOGL_INFO, mm, "<- DEACTIVATE PDP CONTEXT ACK\n");
- rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_PDP_DL_DEACTIVATE_ACCEPT]);
-
- 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, true);
-}
-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->orig_msg);
- 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->orig_msg);
- 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->orig_msg);
- talloc_free(lookup);
-}
-
-static int do_act_pdp_req(struct sgsn_mm_ctx *mmctx, struct msgb *msg, bool *delete)
-{
- 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 = gsm48_hdr_trans_id(gh);
- 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;
- struct gprs_llc_lle *lle;
-
- 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) */
- rc = gsm48_tx_gsm_act_pdp_acc(pdp);
- if (rc < 0)
- return rc;
-
- if (pdp->mm->ran_type == MM_CTX_T_GERAN_Gb) {
- /* Also re-transmit the SNDCP XID message */
- lle = &pdp->mm->gb.llme->lle[pdp->sapi];
- rc = sndcp_sn_xid_req(lle,pdp->nsapi);
- if (rc < 0)
- return rc;
- }
-
- return 0;
- }
-
- /* 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;
- }
- *delete = 0;
-
- 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)
-{
- bool delete = 1;
- struct msgb *msg;
- int rc;
-
- rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_PDP_ACTIVATE_REQUEST]);
-
- /*
- * 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 = gsm48_hdr_trans_id(gh);
-
- 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, &delete);
- if (delete)
- 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 = gsm48_hdr_trans_id(gh);
- 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]));
- rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_PDP_UL_DEACTIVATE_REQUEST]);
-
- 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 = gsm48_hdr_trans_id(gh);
- struct sgsn_pdp_ctx *pdp;
-
- LOGMMCTXP(LOGL_INFO, mm, "-> DEACTIVATE PDP CONTEXT ACK\n");
- rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_PDP_UL_DEACTIVATE_ACCEPT]);
-
- 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;
- }
- /* stop timer 3395 */
- pdpctx_timer_stop(pdp, 3395);
- 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);
- 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 gsm0408_gprs_force_reattach_oldmsg(msg, llme);
- }
-
- 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,
- struct gprs_llc_llme *llme)
-{
- int rc;
- if (llme)
- gprs_llgmm_reset_oldmsg(msg, GPRS_SAPI_GMM, llme);
-
- 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;
- if (mmctx->ran_type == MM_CTX_T_GERAN_Gb)
- gprs_llgmm_reset(mmctx->gb.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 from Iu */
-int gsm0408_gprs_rcvmsg_iu(struct msgb *msg, struct gprs_ra_id *ra_id,
- uint16_t *sai)
-{
- struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg);
- uint8_t pdisc = gsm48_hdr_pdisc(gh);
- struct sgsn_mm_ctx *mmctx;
- int rc = -EINVAL;
-
- mmctx = sgsn_mm_ctx_by_ue_ctx(msg->dst);
- if (mmctx) {
- rate_ctr_inc(&mmctx->ctrg->ctr[GMM_CTR_PKTS_SIG_IN]);
- if (ra_id)
- memcpy(&mmctx->ra, ra_id, sizeof(mmctx->ra));
- }
-
- /* MMCTX can be NULL */
-
- switch (pdisc) {
- case GSM48_PDISC_MM_GPRS:
- rc = gsm0408_rcv_gmm(mmctx, msg, NULL, false);
-#warning "set drop_cipherable arg for gsm0408_rcv_gmm() from IuPS?"
- break;
- case GSM48_PDISC_SM_GPRS:
- rc = gsm0408_rcv_gsm(mmctx, msg, NULL);
- break;
- default:
- LOGMMCTXP(LOGL_NOTICE, mmctx,
- "Unknown GSM 04.08 discriminator 0x%02x: %s\n",
- pdisc, osmo_hexdump((uint8_t *)gh, msgb_l3len(msg)));
- /* FIXME: return status message */
- break;
- }
-
- /* MMCTX can be invalid */
-
- return rc;
-}
-
-/* Main entry point for incoming 04.08 GPRS messages from Gb */
-int gsm0408_gprs_rcvmsg_gb(struct msgb *msg, struct gprs_llc_llme *llme,
- bool drop_cipherable)
-{
- struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg);
- uint8_t pdisc = gsm48_hdr_pdisc(gh);
- struct sgsn_mm_ctx *mmctx;
- struct gprs_ra_id ra_id;
- 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->gb.llme = llme;
- }
-
- /* MMCTX can be NULL */
-
- switch (pdisc) {
- case GSM48_PDISC_MM_GPRS:
- rc = gsm0408_rcv_gmm(mmctx, msg, llme, drop_cipherable);
- 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->gmm_state != GMM_REGISTERED_NORMAL &&
- mmctx->gmm_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->gmm_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->gmm_state != GMM_REGISTERED_NORMAL &&
- mmctx->gmm_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->gmm_state = GMM_REGISTERED_NORMAL;
- return 0;
-}
-
-#ifdef BUILD_IU
-int iu_rab_act_ps(uint8_t rab_id, struct sgsn_pdp_ctx *pdp)
-{
- struct msgb *msg;
- struct sgsn_mm_ctx *mm = pdp->mm;
- struct ranap_ue_conn_ctx *uectx;
- uint32_t ggsn_ip;
- bool use_x213_nsap;
-
- uectx = mm->iu.ue_ctx;
- use_x213_nsap = (uectx->rab_assign_addr_enc == RANAP_NSAP_ADDR_ENC_X213);
-
- /* Get the IP address for ggsn user plane */
- memcpy(&ggsn_ip, pdp->lib->gsnru.v, pdp->lib->gsnru.l);
- ggsn_ip = htonl(ggsn_ip);
-
- LOGP(DRANAP, LOGL_DEBUG, "Assigning RAB: rab_id=%d, ggsn_ip=%x,"
- " teid_gn=%x, use_x213_nsap=%d\n",
- rab_id, ggsn_ip, pdp->lib->teid_gn, use_x213_nsap);
-
- msg = ranap_new_msg_rab_assign_data(rab_id, ggsn_ip,
- pdp->lib->teid_gn, use_x213_nsap);
- msg->l2h = msg->data;
- return ranap_iu_rab_act(uectx, msg);
-}
-#endif
diff --git a/src/gprs/gprs_llc.c b/src/gprs/gprs_llc.c
deleted file mode 100644
index 2be663f98..000000000
--- a/src/gprs/gprs_llc.c
+++ /dev/null
@@ -1,1132 +0,0 @@
-/* GPRS LLC protocol implementation as per 3GPP TS 04.64 */
-
-/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <errno.h>
-#include <stdint.h>
-#include <stdbool.h>
-
-#include <openssl/rand.h>
-
-#include <osmocom/core/msgb.h>
-#include <osmocom/core/linuxlist.h>
-#include <osmocom/core/timer.h>
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/rate_ctr.h>
-#include <osmocom/gprs/gprs_bssgp.h>
-
-#include <openbsc/gsm_data.h>
-#include <openbsc/debug.h>
-#include <openbsc/gprs_sgsn.h>
-#include <openbsc/gprs_gmm.h>
-#include <openbsc/gprs_llc.h>
-#include <openbsc/crc24.h>
-#include <openbsc/sgsn.h>
-#include <openbsc/gsm_subscriber.h>
-#include <openbsc/gprs_llc_xid.h>
-#include <openbsc/gprs_sndcp_comp.h>
-#include <openbsc/gprs_sndcp.h>
-
-static struct gprs_llc_llme *llme_alloc(uint32_t tlli);
-static int gprs_llc_tx_xid(struct gprs_llc_lle *lle, struct msgb *msg,
- int command);
-static int gprs_llc_tx_u(struct msgb *msg, uint8_t sapi,
- int command, enum gprs_llc_u_cmd u_cmd, int pf_bit);
-
-/* BEGIN XID RELATED */
-
-/* Generate XID message */
-static int gprs_llc_generate_xid(uint8_t *bytes, int bytes_len,
- struct gprs_llc_xid_field *l3_xid_field,
- struct gprs_llc_llme *llme)
-{
- /* Note: Called by gprs_ll_xid_req() */
-
- LLIST_HEAD(xid_fields);
-
- struct gprs_llc_xid_field xid_version;
- struct gprs_llc_xid_field xid_n201u;
- struct gprs_llc_xid_field xid_n201i;
-
- xid_version.type = GPRS_LLC_XID_T_VERSION;
- xid_version.data = (uint8_t *) "\x00";
- xid_version.data_len = 1;
-
- xid_n201u.type = GPRS_LLC_XID_T_N201_U;
- xid_n201u.data = (uint8_t *) "\x05\xf0";
- xid_n201u.data_len = 2;
-
- xid_n201i.type = GPRS_LLC_XID_T_N201_I;
- xid_n201i.data = (uint8_t *) "\x05\xf0";
- xid_n201i.data_len = 2;
-
- /* Add locally managed XID Fields */
- llist_add(&xid_version.list, &xid_fields);
- llist_add(&xid_n201u.list, &xid_fields);
- llist_add(&xid_n201i.list, &xid_fields);
-
- /* Append layer 3 XID field (if present) */
- if (l3_xid_field) {
- /* Enforce layer 3 XID type (just to be sure) */
- l3_xid_field->type = GPRS_LLC_XID_T_L3_PAR;
-
- /* Add Layer 3 XID field to the list */
- llist_add(&l3_xid_field->list, &xid_fields);
- }
-
- /* Store generated XID for later reference */
- talloc_free(llme->xid);
- llme->xid = gprs_llc_copy_xid(llme, &xid_fields);
-
- return gprs_llc_compile_xid(bytes, bytes_len, &xid_fields);
-}
-
-/* Generate XID message that will cause the GMM to reset */
-static int gprs_llc_generate_xid_for_gmm_reset(uint8_t *bytes,
- int bytes_len, uint32_t iov_ui,
- struct gprs_llc_llme *llme)
-{
- /* Called by gprs_llgmm_reset() and
- * gprs_llgmm_reset_oldmsg() */
-
- LLIST_HEAD(xid_fields);
-
- struct gprs_llc_xid_field xid_reset;
- struct gprs_llc_xid_field xid_iovui;
-
- /* First XID component must be RESET */
- xid_reset.type = GPRS_LLC_XID_T_RESET;
- xid_reset.data = NULL;
- xid_reset.data_len = 0;
-
- /* Add new IOV-UI */
- xid_iovui.type = GPRS_LLC_XID_T_IOV_UI;
- xid_iovui.data = (uint8_t *) & iov_ui;
- xid_iovui.data_len = 4;
-
- /* Add locally managed XID Fields */
- llist_add(&xid_iovui.list, &xid_fields);
- llist_add(&xid_reset.list, &xid_fields);
-
- /* Store generated XID for later reference */
- talloc_free(llme->xid);
- llme->xid = gprs_llc_copy_xid(llme, &xid_fields);
-
- return gprs_llc_compile_xid(bytes, bytes_len, &xid_fields);
-}
-
-/* Process an incoming XID confirmation */
-static int gprs_llc_process_xid_conf(uint8_t *bytes, int bytes_len,
- struct gprs_llc_lle *lle)
-{
- /* Note: This function handles the response of a network originated
- * XID-Request. There XID messages reflected by the MS are analyzed
- * and processed here. The caller is called by rx_llc_xid(). */
-
- struct llist_head *xid_fields;
- struct gprs_llc_xid_field *xid_field;
- struct gprs_llc_xid_field *xid_field_request;
- struct gprs_llc_xid_field *xid_field_request_l3 = NULL;
-
- /* Pick layer3 XID from the XID request we have sent last */
- if (lle->llme->xid) {
- llist_for_each_entry(xid_field_request, lle->llme->xid, list) {
- if (xid_field_request->type == GPRS_LLC_XID_T_L3_PAR)
- xid_field_request_l3 = xid_field_request;
- }
- }
-
- /* Parse and analyze XID-Response */
- xid_fields = gprs_llc_parse_xid(NULL, bytes, bytes_len);
-
- if (xid_fields) {
-
- gprs_llc_dump_xid_fields(xid_fields, LOGL_DEBUG);
- llist_for_each_entry(xid_field, xid_fields, list) {
-
- /* Forward SNDCP-XID fields to Layer 3 (SNDCP) */
- if (xid_field->type == GPRS_LLC_XID_T_L3_PAR &&
- xid_field_request_l3) {
- sndcp_sn_xid_conf(xid_field,
- xid_field_request_l3, lle);
- }
-
- /* Process LLC-XID fields: */
- else {
-
- /* FIXME: Do something more useful with the
- * echoed XID-Information. Currently we
- * just ignore the response completely and
- * by doing so we blindly accept any changes
- * the MS might have done to the our XID
- * inquiry. There is a remainig risk of
- * malfunction! */
- LOGP(DLLC, LOGL_NOTICE,
- "Ignoring XID-Field: XID: type %s, data_len=%d, data=%s\n",
- get_value_string(gprs_llc_xid_type_names,
- xid_field->type),
- xid_field->data_len,
- osmo_hexdump_nospc(xid_field->data,
- xid_field->data_len));
- }
- }
- talloc_free(xid_fields);
- }
-
- /* Flush pending XID fields */
- talloc_free(lle->llme->xid);
- lle->llme->xid = NULL;
-
- return 0;
-}
-
-/* Process an incoming XID indication and generate an appropiate response */
-static int gprs_llc_process_xid_ind(uint8_t *bytes_request,
- int bytes_request_len,
- uint8_t *bytes_response,
- int bytes_response_maxlen,
- struct gprs_llc_lle *lle)
-{
- /* Note: This function computes the response that is sent back to the
- * MS when a mobile originated XID is received. The function is
- * called by rx_llc_xid() */
-
- int rc = -EINVAL;
-
- struct llist_head *xid_fields;
- struct llist_head *xid_fields_response;
-
- struct gprs_llc_xid_field *xid_field;
- struct gprs_llc_xid_field *xid_field_response;
-
- /* Parse and analyze XID-Request */
- xid_fields =
- gprs_llc_parse_xid(lle->llme, bytes_request, bytes_request_len);
- if (xid_fields) {
- xid_fields_response = talloc_zero(lle->llme, struct llist_head);
- INIT_LLIST_HEAD(xid_fields_response);
- gprs_llc_dump_xid_fields(xid_fields, LOGL_DEBUG);
-
- /* Process LLC-XID fields: */
- llist_for_each_entry(xid_field, xid_fields, list) {
-
- if (xid_field->type != GPRS_LLC_XID_T_L3_PAR) {
- /* FIXME: Check the incoming XID parameters for
- * for validity. Currently we just blindly
- * accept all XID fields by just echoing them.
- * There is a remaining risk of malfunction
- * when a MS submits values which defer from
- * the default! */
- LOGP(DLLC, LOGL_NOTICE,
- "Echoing XID-Field: XID: type %s, data_len=%d, data=%s\n",
- get_value_string(gprs_llc_xid_type_names,
- xid_field->type),
- xid_field->data_len,
- osmo_hexdump_nospc(xid_field->data,
- xid_field->data_len));
- xid_field_response =
- gprs_llc_dup_xid_field
- (lle->llme, xid_field);
- llist_add(&xid_field_response->list,
- xid_fields_response);
- }
- }
-
- /* Forward SNDCP-XID fields to Layer 3 (SNDCP) */
- llist_for_each_entry(xid_field, xid_fields, list) {
- if (xid_field->type == GPRS_LLC_XID_T_L3_PAR) {
-
- xid_field_response =
- talloc_zero(lle->llme,
- struct gprs_llc_xid_field);
- rc = sndcp_sn_xid_ind(xid_field,
- xid_field_response, lle);
- if (rc == 0)
- llist_add(&xid_field_response->list,
- xid_fields_response);
- else
- talloc_free(xid_field_response);
- }
- }
-
- rc = gprs_llc_compile_xid(bytes_response,
- bytes_response_maxlen,
- xid_fields_response);
- talloc_free(xid_fields_response);
- talloc_free(xid_fields);
- }
-
- return rc;
-}
-
-/* Dispatch XID indications and responses comming from the MS */
-static void rx_llc_xid(struct gprs_llc_lle *lle,
- struct gprs_llc_hdr_parsed *gph)
-{
- uint8_t response[1024];
- int response_len;
-
- /* FIXME: 8.5.3.3: check if XID is invalid */
- if (gph->is_cmd) {
- LOGP(DLLC, LOGL_NOTICE,
- "Received XID indication from MS.\n");
-
- struct msgb *resp;
- uint8_t *xid;
- resp = msgb_alloc_headroom(4096, 1024, "LLC_XID");
-
- response_len =
- gprs_llc_process_xid_ind(gph->data, gph->data_len,
- response, sizeof(response),
- lle);
- if (response_len < 0) {
- LOGP(DLLC, LOGL_ERROR,
- "invalid XID indication received!\n");
- } else {
- xid = msgb_put(resp, response_len);
- memcpy(xid, response, response_len);
- }
- gprs_llc_tx_xid(lle, resp, 0);
- } else {
- LOGP(DLLC, LOGL_NOTICE,
- "Received XID confirmation from MS.\n");
- gprs_llc_process_xid_conf(gph->data, gph->data_len, lle);
- /* FIXME: if we had sent a XID reset, send
- * LLGMM-RESET.conf to GMM */
- }
-}
-
-/* Set of LL-XID negotiation (See also: TS 101 351, Section 7.2.2.4) */
-int gprs_ll_xid_req(struct gprs_llc_lle *lle,
- struct gprs_llc_xid_field *l3_xid_field)
-{
- /* Note: This functions is calle from gprs_sndcp.c */
-
- uint8_t xid_bytes[1024];;
- int xid_bytes_len;
- uint8_t *xid;
- struct msgb *msg;
- const char *ftype;
-
- /* Generate XID */
- xid_bytes_len =
- gprs_llc_generate_xid(xid_bytes, sizeof(xid_bytes),
- l3_xid_field, lle->llme);
-
- /* Only perform XID sending if the XID message contains something */
- if (xid_bytes_len > 0) {
- /* Transmit XID bytes */
- msg = msgb_alloc_headroom(4096, 1024, "LLC_XID");
- xid = msgb_put(msg, xid_bytes_len);
- memcpy(xid, xid_bytes, xid_bytes_len);
- if (l3_xid_field)
- ftype = get_value_string(gprs_llc_xid_type_names,
- l3_xid_field->type);
- else
- ftype = "NULL";
- LOGP(DLLC, LOGL_NOTICE, "Sending XID type %s (%d bytes) request"
- " to MS...\n", ftype, xid_bytes_len);
- gprs_llc_tx_xid(lle, msg, 1);
- } else {
- LOGP(DLLC, LOGL_ERROR,
- "XID-Message generation failed, XID not sent!\n");
- return -EINVAL;
- }
-
- return 0;
-}
-/* END XID RELATED */
-
-
-
-
-/* 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 from 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->gb.llme->tlli
- || msgb_tlli(msg) == mmctx->gb.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;
-
- 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, 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;
- llme->cksn = GSM_KEY_SEQ_INVAL;
-
- for (i = 0; i < ARRAY_SIZE(llme->lle); i++)
- lle_init(llme, i);
-
- llist_add(&llme->list, &gprs_llc_llmes);
-
- llme->comp.proto = gprs_sndcp_comp_alloc(llme);
- llme->comp.data = gprs_sndcp_comp_alloc(llme);
-
- return llme;
-}
-
-static void llme_free(struct gprs_llc_llme *llme)
-{
- gprs_sndcp_comp_free(llme->comp.proto);
- gprs_sndcp_comp_free(llme->comp.data);
- talloc_free(llme->xid);
- 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) */
-
- rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_LLC_DL_PACKETS]);
- rate_ctr_add(&sgsn->rate_ctrs->ctr[CTR_LLC_DL_BYTES], msg->len);
-
- /* 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);
-}
-
-/* encrypt information field + FCS, if needed! */
-static int apply_gea(struct gprs_llc_lle *lle, uint16_t crypt_len, uint16_t nu,
- uint32_t oc, uint8_t sapi, uint8_t *fcs, uint8_t *data)
-{
- uint8_t cipher_out[GSM0464_CIPH_MAX_BLOCK];
-
- if (lle->llme->algo == GPRS_ALGO_GEA0)
- return -EINVAL;
-
- /* Compute the 'Input' Paraemeter */
- uint32_t fcs_calc, iv = gprs_cipher_gen_input_ui(lle->llme->iov_ui, sapi,
- nu, oc);
- /* Compute gamma that we need to XOR with the data */
- int r = gprs_cipher_run(cipher_out, crypt_len, lle->llme->algo,
- lle->llme->kc, iv,
- fcs ? GPRS_CIPH_SGSN2MS : GPRS_CIPH_MS2SGSN);
- if (r < 0) {
- LOGP(DLLC, LOGL_ERROR, "Error producing %s gamma for UI "
- "frame: %d\n", get_value_string(gprs_cipher_names,
- lle->llme->algo), r);
- return -ENOMSG;
- }
-
- if (fcs) {
- /* Mark frame as encrypted and update FCS */
- data[2] |= 0x02;
- fcs_calc = gprs_llc_fcs(data, fcs - data);
- fcs[0] = fcs_calc & 0xff;
- fcs[1] = (fcs_calc >> 8) & 0xff;
- fcs[2] = (fcs_calc >> 16) & 0xff;
- data += 3;
- }
-
- /* XOR the cipher output with the data */
- for (r = 0; r < crypt_len; r++)
- *(data + r) ^= cipher_out[r];
-
- return 0;
-}
-
-/* Transmit a UI frame over the given SAPI:
- 'encryptable' indicates whether particular message can be encrypted according
- to 3GPP TS 24.008 § 4.7.1.2
- */
-int gprs_llc_tx_ui(struct msgb *msg, uint8_t sapi, int command,
- struct sgsn_mm_ctx *mmctx, bool encryptable)
-{
- 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;
- }
-
- gprs_llme_copy_key(mmctx, lle->llme);
-
- /* 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;
-
- if (lle->llme->algo != GPRS_ALGO_GEA0 && encryptable) {
- int rc = apply_gea(lle, fcs - llch, nu, oc, sapi, fcs, llch);
- if (rc < 0) {
- msgb_free(msg);
- return rc;
- }
- }
-
- rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_LLC_DL_PACKETS]);
- rate_ctr_add(&sgsn->rate_ctrs->ctr[CTR_LLC_DL_BYTES], msg->len);
-
- /* Identifiers passed down: (BVCI, NSEI) */
-
- /* Send BSSGP-DL-UNITDATA.req */
- return _bssgp_tx_dl_ud(msg, mmctx);
-}
-
-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 = NULL;
- bool drop_cipherable = false;
- 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));
- 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 gsm0408_gprs_force_reattach_oldmsg(msg, NULL);
- default:
- break;
- }
- return 0;
- }
- gprs_llc_hdr_dump(&llhp, lle);
- /* reset age computation */
- lle->llme->age_timestamp = GPRS_LLME_RESET_AGE;
-
- /* decrypt information field + FCS, if needed! */
- if (llhp.is_encrypted) {
- if (lle->llme->algo != GPRS_ALGO_GEA0) {
- rc = apply_gea(lle, llhp.data_len + 3, llhp.seq_tx,
- lle->oc_ui_recv, lle->sapi, NULL,
- llhp.data);
- if (rc < 0)
- return rc;
- llhp.fcs = *(llhp.data + llhp.data_len);
- llhp.fcs |= *(llhp.data + llhp.data_len + 1) << 8;
- llhp.fcs |= *(llhp.data + llhp.data_len + 2) << 16;
- } else {
- LOGP(DLLC, LOGL_NOTICE, "encrypted frame for LLC that "
- "has no KC/Algo! Dropping.\n");
- return 0;
- }
- } else {
- if (lle->llme->algo != GPRS_ALGO_GEA0 &&
- lle->llme->cksn != GSM_KEY_SEQ_INVAL)
- drop_cipherable = true;
- }
-
- /* 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;
-
- rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_LLC_UL_PACKETS]);
- rate_ctr_add(&sgsn->rate_ctrs->ctr[CTR_LLC_UL_BYTES], msg->len);
-
- /* 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_gb(msg, lle->llme,
- drop_cipherable);
- 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;
-}
-
-/* Propagate crypto parameters MM -> LLME */
-void gprs_llme_copy_key(struct sgsn_mm_ctx *mm, struct gprs_llc_llme *llme)
-{
- if (!mm)
- return;
- if (mm->ciph_algo != GPRS_ALGO_GEA0) {
- llme->algo = mm->ciph_algo;
- if (llme->cksn != mm->auth_triplet.key_seq &&
- mm->auth_triplet.key_seq != GSM_KEY_SEQ_INVAL) {
- memcpy(llme->kc, mm->auth_triplet.vec.kc,
- gprs_cipher_key_length(mm->ciph_algo));
- llme->cksn = mm->auth_triplet.key_seq;
- }
- } else
- llme->cksn = GSM_KEY_SEQ_INVAL;
-}
-
-/* 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)
-{
- unsigned int i;
-
- 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;
-}
-
-/* TLLI unassignment */
-int gprs_llgmm_unassign(struct gprs_llc_llme *llme)
-{
- return gprs_llgmm_assign(llme, llme->tlli, 0xffffffff);
-}
-
-/* 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");
- struct gprs_llc_lle *lle = &llme->lle[1];
- uint8_t xid_bytes[1024];
- int xid_bytes_len;
- uint8_t *xid;
-
- LOGP(DLLC, LOGL_NOTICE, "LLGM Reset\n");
- if (RAND_bytes((uint8_t *) &llme->iov_ui, 4) != 1) {
- LOGP(DLLC, LOGL_NOTICE, "RAND_bytes failed for LLC XID reset, "
- "falling back to rand()\n");
- llme->iov_ui = rand();
- }
-
- /* Generate XID message */
- xid_bytes_len = gprs_llc_generate_xid_for_gmm_reset(xid_bytes,
- sizeof(xid_bytes),llme->iov_ui,llme);
- if (xid_bytes_len < 0)
- return -EINVAL;
- xid = msgb_put(msg, xid_bytes_len);
- memcpy(xid, xid_bytes, xid_bytes_len);
-
- /* 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 gprs_llc_llme *llme)
-{
- struct msgb *msg = msgb_alloc_headroom(4096, 1024, "LLC_XID");
- uint8_t xid_bytes[1024];
- int xid_bytes_len;
- uint8_t *xid;
-
- LOGP(DLLC, LOGL_NOTICE, "LLGM Reset\n");
- if (RAND_bytes((uint8_t *) &llme->iov_ui, 4) != 1) {
- LOGP(DLLC, LOGL_NOTICE, "RAND_bytes failed for LLC XID reset, "
- "falling back to rand()\n");
- llme->iov_ui = rand();
- }
-
- /* Generate XID message */
- xid_bytes_len = gprs_llc_generate_xid_for_gmm_reset(xid_bytes,
- sizeof(xid_bytes),llme->iov_ui,llme);
- if (xid_bytes_len < 0)
- return -EINVAL;
- xid = msgb_put(msg, xid_bytes_len);
- memcpy(xid, xid_bytes, xid_bytes_len);
-
- /* 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);
-}
diff --git a/src/gprs/gprs_llc_parse.c b/src/gprs/gprs_llc_parse.c
deleted file mode 100644
index a5a7a7122..000000000
--- a/src/gprs/gprs_llc_parse.c
+++ /dev/null
@@ -1,251 +0,0 @@
-/* GPRS LLC protocol implementation as per 3GPP TS 04.64 */
-
-/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <errno.h>
-#include <stdint.h>
-
-#include <osmocom/core/msgb.h>
-#include <osmocom/core/linuxlist.h>
-#include <osmocom/core/timer.h>
-#include <osmocom/core/talloc.h>
-#include <osmocom/gprs/gprs_bssgp.h>
-
-#include <openbsc/gsm_data.h>
-#include <openbsc/debug.h>
-#include <openbsc/gprs_sgsn.h>
-#include <openbsc/gprs_gmm.h>
-#include <openbsc/gprs_llc.h>
-#include <openbsc/crc24.h>
-
-static const struct value_string llc_cmd_strs[] = {
- { GPRS_LLC_NULL, "NULL" },
- { GPRS_LLC_RR, "RR" },
- { GPRS_LLC_ACK, "ACK" },
- { GPRS_LLC_RNR, "RNR" },
- { GPRS_LLC_SACK, "SACK" },
- { GPRS_LLC_DM, "DM" },
- { GPRS_LLC_DISC, "DISC" },
- { GPRS_LLC_UA, "UA" },
- { GPRS_LLC_SABM, "SABM" },
- { GPRS_LLC_FRMR, "FRMR" },
- { GPRS_LLC_XID, "XID" },
- { GPRS_LLC_UI, "UI" },
- { 0, NULL }
-};
-
-#define LLC_ALLOC_SIZE 16384
-#define UI_HDR_LEN 3
-#define N202 4
-#define CRC24_LENGTH 3
-
-int gprs_llc_fcs(uint8_t *data, unsigned int len)
-{
- uint32_t fcs_calc;
-
- fcs_calc = crc24_calc(INIT_CRC24, data, len);
- fcs_calc = ~fcs_calc;
- fcs_calc &= 0xffffff;
-
- return fcs_calc;
-}
-
-void gprs_llc_hdr_dump(struct gprs_llc_hdr_parsed *gph, struct gprs_llc_lle *lle)
-{
- const char *gea;
- uint32_t iov_ui = 0;
- if (lle) {
- gea = get_value_string(gprs_cipher_names, lle->llme->algo);
- iov_ui = lle->llme->iov_ui;
- } else
- gea = "GEA?";
- DEBUGP(DLLC, "LLC SAPI=%u %c %c %c %s IOV-UI=0x%06x FCS=0x%06x ",
- gph->sapi, gph->is_cmd ? 'C' : 'R', gph->ack_req ? 'A' : ' ',
- gph->is_encrypted ? 'E' : 'U',
- gea, iov_ui, gph->fcs);
-
- if (gph->cmd)
- DEBUGPC(DLLC, "CMD=%s ", get_value_string(llc_cmd_strs, gph->cmd));
-
- if (gph->data)
- DEBUGPC(DLLC, "DATA ");
-
- DEBUGPC(DLLC, "\n");
-}
-
-/* parse a GPRS LLC header, also check for invalid frames */
-int gprs_llc_hdr_parse(struct gprs_llc_hdr_parsed *ghp,
- uint8_t *llc_hdr, int len)
-{
- uint8_t *ctrl = llc_hdr+1;
-
- if (len <= CRC24_LENGTH)
- return -EIO;
-
- ghp->crc_length = len - CRC24_LENGTH;
-
- ghp->ack_req = 0;
-
- /* Section 5.5: FCS */
- ghp->fcs = *(llc_hdr + len - 3);
- ghp->fcs |= *(llc_hdr + len - 2) << 8;
- ghp->fcs |= *(llc_hdr + len - 1) << 16;
-
- /* Section 6.2.1: invalid PD field */
- if (llc_hdr[0] & 0x80)
- return -EIO;
-
- /* This only works for the MS->SGSN direction */
- if (llc_hdr[0] & 0x40)
- ghp->is_cmd = 0;
- else
- ghp->is_cmd = 1;
-
- ghp->sapi = llc_hdr[0] & 0xf;
-
- /* Section 6.2.3: check for reserved SAPI */
- switch (ghp->sapi) {
- case 0:
- case 4:
- case 6:
- case 0xa:
- case 0xc:
- case 0xd:
- case 0xf:
- return -EINVAL;
- }
-
- if ((ctrl[0] & 0x80) == 0) {
- /* I (Information transfer + Supervisory) format */
- uint8_t k;
-
- ghp->data = ctrl + 3;
-
- if (ctrl[0] & 0x40)
- ghp->ack_req = 1;
-
- ghp->seq_tx = (ctrl[0] & 0x1f) << 4;
- ghp->seq_tx |= (ctrl[1] >> 4);
-
- ghp->seq_rx = (ctrl[1] & 0x7) << 6;
- ghp->seq_rx |= (ctrl[2] >> 2);
-
- switch (ctrl[2] & 0x03) {
- case 0:
- ghp->cmd = GPRS_LLC_RR;
- break;
- case 1:
- ghp->cmd = GPRS_LLC_ACK;
- break;
- case 2:
- ghp->cmd = GPRS_LLC_RNR;
- break;
- case 3:
- ghp->cmd = GPRS_LLC_SACK;
- k = ctrl[3] & 0x1f;
- ghp->data += 1 + k;
- break;
- }
- ghp->data_len = (llc_hdr + len - 3) - ghp->data;
- } else if ((ctrl[0] & 0xc0) == 0x80) {
- /* S (Supervisory) format */
- ghp->data = NULL;
- ghp->data_len = 0;
-
- if (ctrl[0] & 0x20)
- ghp->ack_req = 1;
- ghp->seq_rx = (ctrl[0] & 0x7) << 6;
- ghp->seq_rx |= (ctrl[1] >> 2);
-
- switch (ctrl[1] & 0x03) {
- case 0:
- ghp->cmd = GPRS_LLC_RR;
- break;
- case 1:
- ghp->cmd = GPRS_LLC_ACK;
- break;
- case 2:
- ghp->cmd = GPRS_LLC_RNR;
- break;
- case 3:
- ghp->cmd = GPRS_LLC_SACK;
- break;
- }
- } else if ((ctrl[0] & 0xe0) == 0xc0) {
- /* UI (Unconfirmed Inforamtion) format */
- ghp->cmd = GPRS_LLC_UI;
- ghp->data = ctrl + 2;
- ghp->data_len = (llc_hdr + len - 3) - ghp->data;
-
- ghp->seq_tx = (ctrl[0] & 0x7) << 6;
- ghp->seq_tx |= (ctrl[1] >> 2);
- if (ctrl[1] & 0x02) {
- ghp->is_encrypted = 1;
- /* FIXME: encryption */
- }
- if (ctrl[1] & 0x01) {
- /* FCS over hdr + all inf fields */
- } else {
- /* FCS over hdr + N202 octets (4) */
- if (ghp->crc_length > UI_HDR_LEN + N202)
- ghp->crc_length = UI_HDR_LEN + N202;
- }
- } else {
- /* U (Unnumbered) format: 1 1 1 P/F M4 M3 M2 M1 */
- ghp->data = NULL;
- ghp->data_len = 0;
-
- switch (ctrl[0] & 0xf) {
- case GPRS_LLC_U_NULL_CMD:
- ghp->cmd = GPRS_LLC_NULL;
- break;
- case GPRS_LLC_U_DM_RESP:
- ghp->cmd = GPRS_LLC_DM;
- break;
- case GPRS_LLC_U_DISC_CMD:
- ghp->cmd = GPRS_LLC_DISC;
- break;
- case GPRS_LLC_U_UA_RESP:
- ghp->cmd = GPRS_LLC_UA;
- break;
- case GPRS_LLC_U_SABM_CMD:
- ghp->cmd = GPRS_LLC_SABM;
- break;
- case GPRS_LLC_U_FRMR_RESP:
- ghp->cmd = GPRS_LLC_FRMR;
- break;
- case GPRS_LLC_U_XID:
- ghp->cmd = GPRS_LLC_XID;
- ghp->data = ctrl + 1;
- ghp->data_len = (llc_hdr + len - 3) - ghp->data;
- break;
- default:
- return -EIO;
- }
- }
-
- /* FIXME: parse sack frame */
- if (ghp->cmd == GPRS_LLC_SACK) {
- LOGP(DLLC, LOGL_NOTICE, "Unsupported SACK frame\n");
- return -EIO;
- }
-
- return 0;
-}
diff --git a/src/gprs/gprs_llc_vty.c b/src/gprs/gprs_llc_vty.c
deleted file mode 100644
index bf34e9782..000000000
--- a/src/gprs/gprs_llc_vty.c
+++ /dev/null
@@ -1,116 +0,0 @@
-/* VTY interface for our GPRS LLC implementation */
-
-/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <stdlib.h>
-#include <unistd.h>
-#include <errno.h>
-#include <stdint.h>
-#include <time.h>
-
-#include <arpa/inet.h>
-
-#include <openbsc/gsm_data.h>
-#include <osmocom/core/msgb.h>
-#include <osmocom/gsm/tlv.h>
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/select.h>
-#include <osmocom/core/rate_ctr.h>
-#include <openbsc/debug.h>
-#include <openbsc/signal.h>
-#include <openbsc/gprs_llc.h>
-
-#include <osmocom/vty/vty.h>
-#include <osmocom/vty/command.h>
-
-struct value_string gprs_llc_state_strs[] = {
- { GPRS_LLES_UNASSIGNED, "TLLI Unassigned" },
- { GPRS_LLES_ASSIGNED_ADM, "TLLI Assigned" },
- { GPRS_LLES_LOCAL_EST, "Local Establishment" },
- { GPRS_LLES_REMOTE_EST, "Remote Establishment" },
- { GPRS_LLES_ABM, "Asynchronous Balanced Mode" },
- { GPRS_LLES_LOCAL_REL, "Local Release" },
- { GPRS_LLES_TIMER_REC, "Timer Recovery" },
- { 0, NULL }
-};
-
-static void vty_dump_lle(struct vty *vty, struct gprs_llc_lle *lle)
-{
- struct gprs_llc_params *par = &lle->params;
- vty_out(vty, " SAPI %2u State %s VUsend=%u, VUrecv=%u", lle->sapi,
- get_value_string(gprs_llc_state_strs, lle->state),
- lle->vu_send, lle->vu_recv);
- vty_out(vty, " Vsent=%u Vack=%u Vrecv=%u, RetransCtr=%u%s",
- lle->v_sent, lle->v_ack, lle->v_recv,
- lle->retrans_ctr, VTY_NEWLINE);
- vty_out(vty, " T200=%u, N200=%u, N201-U=%u, N201-I=%u, mD=%u, "
- "mU=%u, kD=%u, kU=%u%s", par->t200_201, par->n200,
- par->n201_u, par->n201_i, par->mD, par->mU, par->kD,
- par->kU, VTY_NEWLINE);
-}
-
-static uint8_t valid_sapis[] = { 1, 2, 3, 5, 7, 8, 9, 11 };
-
-static void vty_dump_llme(struct vty *vty, struct gprs_llc_llme *llme)
-{
- unsigned int i;
- struct timespec now_tp = {0};
- clock_gettime(CLOCK_MONOTONIC, &now_tp);
-
- vty_out(vty, "TLLI %08x (Old TLLI %08x) BVCI=%u NSEI=%u %s: "
- "IOV-UI=0x%06x CKSN=%d Age=%d: State %s%s", llme->tlli,
- llme->old_tlli, llme->bvci, llme->nsei,
- get_value_string(gprs_cipher_names, llme->algo), llme->iov_ui,
- llme->cksn, llme->age_timestamp == GPRS_LLME_RESET_AGE ? 0 :
- (int)(now_tp.tv_sec - (time_t)llme->age_timestamp),
- get_value_string(gprs_llc_state_strs, llme->state), VTY_NEWLINE);
-
- for (i = 0; i < ARRAY_SIZE(valid_sapis); i++) {
- struct gprs_llc_lle *lle;
- uint8_t sapi = valid_sapis[i];
-
- if (sapi >= ARRAY_SIZE(llme->lle))
- continue;
-
- lle = &llme->lle[sapi];
- vty_dump_lle(vty, lle);
- }
-}
-
-
-DEFUN(show_llc, show_llc_cmd,
- "show llc",
- SHOW_STR "Display information about the LLC protocol")
-{
- struct gprs_llc_llme *llme;
-
- vty_out(vty, "State of LLC Entities%s", VTY_NEWLINE);
- llist_for_each_entry(llme, &gprs_llc_llmes, list) {
- vty_dump_llme(vty, llme);
- }
- return CMD_SUCCESS;
-}
-
-int gprs_llc_vty_init(void)
-{
- install_element_ve(&show_llc_cmd);
-
- return 0;
-}
diff --git a/src/gprs/gprs_llc_xid.c b/src/gprs/gprs_llc_xid.c
deleted file mode 100644
index fe631715a..000000000
--- a/src/gprs/gprs_llc_xid.c
+++ /dev/null
@@ -1,281 +0,0 @@
-/* GPRS LLC XID field encoding/decoding as per 3GPP TS 44.064 */
-
-/* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
- * All Rights Reserved
- *
- * Author: Philipp Maier
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <stdio.h>
-#include <string.h>
-#include <stdint.h>
-#include <errno.h>
-
-#include <osmocom/core/utils.h>
-#include <osmocom/core/linuxlist.h>
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/msgb.h>
-#include <osmocom/core/talloc.h>
-
-#include <openbsc/debug.h>
-#include <openbsc/gprs_llc.h>
-#include <openbsc/sgsn.h>
-#include <openbsc/gprs_llc_xid.h>
-
-const struct value_string gprs_llc_xid_type_names[] = {
- { GPRS_LLC_XID_T_VERSION, "VERSION"},
- { GPRS_LLC_XID_T_IOV_UI, "IOV_UI"},
- { GPRS_LLC_XID_T_IOV_I, "IOV_I"},
- { GPRS_LLC_XID_T_T200, "T200"},
- { GPRS_LLC_XID_T_N200, "N200"},
- { GPRS_LLC_XID_T_N201_U, "N201_"},
- { GPRS_LLC_XID_T_N201_I, "N201_I"},
- { GPRS_LLC_XID_T_mD, "mD"},
- { GPRS_LLC_XID_T_mU, "mU"},
- { GPRS_LLC_XID_T_kD, "kD"},
- { GPRS_LLC_XID_T_kU, "kU"},
- { GPRS_LLC_XID_T_L3_PAR, "L3_PAR"},
- { GPRS_LLC_XID_T_RESET, "RESET"},
- { 0, NULL },
-};
-
-/* Parse XID parameter field */
-static int decode_xid_field(struct gprs_llc_xid_field *xid_field,
- const uint8_t *src, uint8_t src_len)
-{
- uint8_t xl;
- uint8_t type;
- uint8_t len;
- int src_counter = 0;
-
- /* Exit immediately if it is clear that no
- * parseable data is present */
- if (src_len < 1 || !src)
- return -EINVAL;
-
- /* Extract header info */
- xl = (*src >> 7) & 1;
- type = (*src >> 2) & 0x1F;
-
- /* Extract length field */
- len = (*src) & 0x3;
- src++;
- src_counter++;
- if (xl) {
- if (src_len < 2)
- return -EINVAL;
- len = (len << 6) & 0xC0;
- len |= ((*src) >> 2) & 0x3F;
- src++;
- src_counter++;
- }
-
- /* Fill out struct */
- xid_field->type = type;
- xid_field->data_len = len;
- if (len > 0) {
- if (src_len < src_counter + len)
- return -EINVAL;
- xid_field->data =
- talloc_memdup(xid_field,src,xid_field->data_len);
- } else
- xid_field->data = NULL;
-
- /* Return consumed length */
- return src_counter + len;
-}
-
-/* Encode XID parameter field */
-static int encode_xid_field(uint8_t *dst, int dst_maxlen,
- const struct gprs_llc_xid_field *xid_field)
-{
- int xl = 0;
-
- /* When the length does not fit into 2 bits,
- * we need extended length fields */
- if (xid_field->data_len > 3)
- xl = 1;
-
- /* Exit immediately if it is clear that no
- * encoding result can be stored */
- if (dst_maxlen < xid_field->data_len + 1 + xl)
- return -EINVAL;
-
- /* There are only 5 bits reserved for the type, exit on exceed */
- if (xid_field->type > 31)
- return -EINVAL;
-
- /* Encode header */
- memset(dst, 0, dst_maxlen);
- if (xl)
- dst[0] |= 0x80;
- dst[0] |= (((xid_field->type) & 0x1F) << 2);
-
- if (xl) {
- dst[0] |= (((xid_field->data_len) >> 6) & 0x03);
- dst[1] = ((xid_field->data_len) << 2) & 0xFC;
- } else
- dst[0] |= ((xid_field->data_len) & 0x03);
-
- /* Append payload data */
- if (xid_field->data && xid_field->data_len)
- memcpy(dst + 1 + xl, xid_field->data, xid_field->data_len);
-
- /* Return generated length */
- return xid_field->data_len + 1 + xl;
-}
-
-/* Transform a list with XID fields into a XID message (dst) */
-int gprs_llc_compile_xid(uint8_t *dst, int dst_maxlen,
- const struct llist_head *xid_fields)
-{
- struct gprs_llc_xid_field *xid_field;
- int rc;
- int byte_counter = 0;
-
- OSMO_ASSERT(xid_fields);
- OSMO_ASSERT(dst);
-
- llist_for_each_entry_reverse(xid_field, xid_fields, list) {
- /* Encode XID-Field */
- rc = encode_xid_field(dst, dst_maxlen, xid_field);
- if (rc < 0)
- return -EINVAL;
-
- /* Advance pointer and lower maxlen for the
- * next encoding round */
- dst += rc;
- byte_counter += rc;
- dst_maxlen -= rc;
- }
-
- /* Return generated length */
- return byte_counter;
-}
-
-/* Transform a XID message (dst) into a list of XID fields */
-struct llist_head *gprs_llc_parse_xid(const void *ctx, const uint8_t *src,
- int src_len)
-{
- struct gprs_llc_xid_field *xid_field;
- struct llist_head *xid_fields;
-
- int rc;
- int max_loops = src_len;
-
- OSMO_ASSERT(src);
-
- xid_fields = talloc_zero(ctx, struct llist_head);
- INIT_LLIST_HEAD(xid_fields);
-
- while (1) {
- /* Bail in case decode_xid_field() constantly returns zero */
- if (max_loops <= 0) {
- talloc_free(xid_fields);
- return NULL;
- }
-
- /* Decode XID field */
- xid_field = talloc_zero(xid_fields, struct gprs_llc_xid_field);
- rc = decode_xid_field(xid_field, src, src_len);
-
- /* Immediately stop on error */
- if (rc < 0) {
- talloc_free(xid_fields);
- return NULL;
- }
-
- /* Add parsed XID field to list */
- llist_add(&xid_field->list, xid_fields);
-
- /* Advance pointer and lower dst_len for the next
- * decoding round */
- src += rc;
- src_len -= rc;
-
- /* We are (scuccessfully) done when no further byes are left */
- if (src_len == 0)
- return xid_fields;
-
- max_loops--;
- }
-}
-
-/* Create a duplicate of an XID-Field */
-struct gprs_llc_xid_field *gprs_llc_dup_xid_field(const void *ctx, const struct
- gprs_llc_xid_field
- *xid_field)
-{
- struct gprs_llc_xid_field *dup;
-
- OSMO_ASSERT(xid_field);
-
- /* Create a copy of the XID field in memory */
- dup = talloc_memdup(ctx, xid_field, sizeof(*xid_field));
- dup->data = talloc_memdup(ctx, xid_field->data, xid_field->data_len);
-
- /* Unlink duplicate from source list */
- INIT_LLIST_HEAD(&dup->list);
-
- return dup;
-}
-
-/* Copy an llist with xid fields */
-struct llist_head *gprs_llc_copy_xid(const void *ctx,
- const struct llist_head *xid_fields)
-{
- struct gprs_llc_xid_field *xid_field;
- struct llist_head *xid_fields_copy;
-
- OSMO_ASSERT(xid_fields);
-
- xid_fields_copy = talloc_zero(ctx, struct llist_head);
- INIT_LLIST_HEAD(xid_fields_copy);
-
- /* Create duplicates and add them to the target list */
- llist_for_each_entry(xid_field, xid_fields, list) {
- llist_add(&gprs_llc_dup_xid_field(ctx, xid_field)->list,
- xid_fields_copy);
- }
-
- return xid_fields_copy;
-}
-
-/* Dump a list with XID fields (Debug) */
-void gprs_llc_dump_xid_fields(const struct llist_head *xid_fields,
- unsigned int logl)
-{
- struct gprs_llc_xid_field *xid_field;
-
- OSMO_ASSERT(xid_fields);
-
- llist_for_each_entry(xid_field, xid_fields, list) {
- if (xid_field->data_len) {
- OSMO_ASSERT(xid_field->data);
- LOGP(DLLC, logl,
- "XID: type %s, data_len=%d, data=%s\n",
- get_value_string(gprs_llc_xid_type_names,
- xid_field->type),
- xid_field->data_len,
- osmo_hexdump_nospc(xid_field->data,
- xid_field->data_len));
- } else {
- LOGP(DLLC, logl,
- "XID: type=%d, data_len=%d, data=NULL\n",
- xid_field->type, xid_field->data_len);
- }
- }
-}
diff --git a/src/gprs/gprs_sgsn.c b/src/gprs/gprs_sgsn.c
deleted file mode 100644
index 340fcb08d..000000000
--- a/src/gprs/gprs_sgsn.c
+++ /dev/null
@@ -1,896 +0,0 @@
-/* GPRS SGSN functionality */
-
-/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <stdint.h>
-
-#include <osmocom/core/linuxlist.h>
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/timer.h>
-#include <osmocom/core/rate_ctr.h>
-#include <osmocom/core/stats.h>
-#include <osmocom/core/backtrace.h>
-#include <osmocom/gprs/gprs_ns.h>
-#include <osmocom/gprs/gprs_bssgp.h>
-#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
-#include <osmocom/ranap/iu_client.h>
-
-#include <openbsc/gprs_subscriber.h>
-#include <openbsc/debug.h>
-#include <openbsc/gprs_sgsn.h>
-#include <openbsc/sgsn.h>
-#include <openbsc/gprs_gmm.h>
-#include <openbsc/gprs_utils.h>
-#include <openbsc/signal.h>
-#include "openbsc/gprs_llc.h"
-
-#include <pdp.h>
-
-#include <time.h>
-
-#include <openssl/rand.h>
-
-#define GPRS_LLME_CHECK_TICK 30
-
-extern struct sgsn_instance *sgsn;
-
-LLIST_HEAD(sgsn_mm_ctxts);
-LLIST_HEAD(sgsn_ggsn_ctxts);
-LLIST_HEAD(sgsn_apn_ctxts);
-LLIST_HEAD(sgsn_pdp_ctxts);
-
-static const struct rate_ctr_desc mmctx_ctr_description[] = {
- { "sign.packets.in", "Signalling Messages ( In)" },
- { "sign.packets.out", "Signalling Messages (Out)" },
- { "udata.packets.in", "User Data Messages ( In)" },
- { "udata.packets.out", "User Data Messages (Out)" },
- { "udata.bytes.in", "User Data Bytes ( In)" },
- { "udata.bytes.out", "User Data Bytes (Out)" },
- { "pdp_ctx_act", "PDP Context Activations " },
- { "suspend", "SUSPEND Count " },
- { "paging.ps", "Paging Packet Switched " },
- { "paging.cs", "Paging Circuit Switched " },
- { "ra_update", "Routing Area Update " },
-};
-
-static const struct rate_ctr_group_desc mmctx_ctrg_desc = {
- .group_name_prefix = "sgsn.mmctx",
- .group_description = "SGSN MM Context Statistics",
- .num_ctr = ARRAY_SIZE(mmctx_ctr_description),
- .ctr_desc = mmctx_ctr_description,
- .class_id = OSMO_STATS_CLASS_SUBSCRIBER,
-};
-
-static const struct rate_ctr_desc pdpctx_ctr_description[] = {
- { "udata.packets.in", "User Data Messages ( In)" },
- { "udata.packets.out", "User Data Messages (Out)" },
- { "udata.bytes.in", "User Data Bytes ( In)" },
- { "udata.bytes.out", "User Data Bytes (Out)" },
-};
-
-static const struct rate_ctr_group_desc pdpctx_ctrg_desc = {
- .group_name_prefix = "sgsn.pdpctx",
- .group_description = "SGSN PDP Context Statistics",
- .num_ctr = ARRAY_SIZE(pdpctx_ctr_description),
- .ctr_desc = pdpctx_ctr_description,
- .class_id = OSMO_STATS_CLASS_SUBSCRIBER,
-};
-
-static const struct rate_ctr_desc sgsn_ctr_description[] = {
- { "llc.dl_bytes", "Count sent LLC bytes before giving it to the bssgp layer" },
- { "llc.ul_bytes", "Count sucessful received LLC bytes (encrypt & fcs correct)" },
- { "llc.dl_packets", "Count sucessful sent LLC packets before giving it to the bssgp layer" },
- { "llc.ul_packets", "Count sucessful received LLC packets (encrypt & fcs correct)" },
- { "gprs.attach_requested", "Received attach requests" },
- { "gprs.attach_accepted", "Sent attach accepts" },
- { "gprs.attach_rejected", "Sent attach rejects" },
- { "gprs.detach_requested", "Received detach requests" },
- { "gprs.detach_acked", "Sent detach acks" },
- { "gprs.routing_area_requested", "Received routing area requests" },
- { "gprs.routing_area_requested", "Sent routing area acks" },
- { "gprs.routing_area_requested", "Sent routing area rejects" },
- { "pdp.activate_requested", "Received activate requests" },
- { "pdp.activate_rejected", "Sent activate rejects" },
- { "pdp.activate_accepted", "Sent activate accepts" },
- { "pdp.request_activated", "unused" },
- { "pdp.request_activate_rejected", "unused" },
- { "pdp.modify_requested", "unused" },
- { "pdp.modify_accepted", "unused" },
- { "pdp.dl_deactivate_requested", "Sent deactivate requests" },
- { "pdp.dl_deactivate_accepted", "Sent deactivate accepted" },
- { "pdp.ul_deactivate_requested", "Received deactivate requests" },
- { "pdp.ul_deactivate_accepted", "Received deactivate accepts" },
-};
-
-static const struct rate_ctr_group_desc sgsn_ctrg_desc = {
- "sgsn",
- "SGSN Overall Statistics",
- OSMO_STATS_CLASS_GLOBAL,
- ARRAY_SIZE(sgsn_ctr_description),
- sgsn_ctr_description,
-};
-
-void sgsn_rate_ctr_init() {
- sgsn->rate_ctrs = rate_ctr_group_alloc(tall_bsc_ctx, &sgsn_ctrg_desc, 0);
-}
-
-/* look-up an SGSN MM context based on Iu UE context (struct ue_conn_ctx)*/
-struct sgsn_mm_ctx *sgsn_mm_ctx_by_ue_ctx(const void *uectx)
-{
- struct sgsn_mm_ctx *ctx;
-
- llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) {
- if (ctx->ran_type == MM_CTX_T_UTRAN_Iu
- && uectx == ctx->iu.ue_ctx)
- return ctx;
- }
-
- return NULL;
-}
-
-/* look-up a SGSN MM context based on TLLI + RAI */
-struct sgsn_mm_ctx *sgsn_mm_ctx_by_tlli(uint32_t tlli,
- const struct gprs_ra_id *raid)
-{
- struct sgsn_mm_ctx *ctx;
-
- llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) {
- if ((tlli == ctx->gb.tlli || tlli == ctx->gb.tlli_new) &&
- gprs_ra_id_equals(raid, &ctx->ra))
- return ctx;
- }
-
- return NULL;
-}
-
-struct sgsn_mm_ctx *sgsn_mm_ctx_by_tlli_and_ptmsi(uint32_t tlli,
- const struct gprs_ra_id *raid)
-{
- struct sgsn_mm_ctx *ctx;
- int tlli_type;
-
- /* TODO: Also check the P_TMSI signature to be safe. That signature
- * should be different (at least with a sufficiently high probability)
- * after SGSN restarts and for multiple SGSN instances.
- */
-
- tlli_type = gprs_tlli_type(tlli);
- if (tlli_type != TLLI_FOREIGN && tlli_type != TLLI_LOCAL)
- return NULL;
-
- llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) {
- if ((gprs_tmsi2tlli(ctx->p_tmsi, tlli_type) == tlli ||
- gprs_tmsi2tlli(ctx->p_tmsi_old, tlli_type) == tlli) &&
- gprs_ra_id_equals(raid, &ctx->ra))
- return ctx;
- }
-
- return NULL;
-}
-
-struct sgsn_mm_ctx *sgsn_mm_ctx_by_ptmsi(uint32_t p_tmsi)
-{
- struct sgsn_mm_ctx *ctx;
-
- llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) {
- if (p_tmsi == ctx->p_tmsi ||
- (ctx->p_tmsi_old && ctx->p_tmsi_old == p_tmsi))
- return ctx;
- }
- return NULL;
-}
-
-struct sgsn_mm_ctx *sgsn_mm_ctx_by_imsi(const char *imsi)
-{
- struct sgsn_mm_ctx *ctx;
-
- llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) {
- if (!strcmp(imsi, ctx->imsi))
- return ctx;
- }
- return NULL;
-
-}
-
-/* Allocate a new SGSN MM context for GERAN_Gb */
-struct sgsn_mm_ctx *sgsn_mm_ctx_alloc_gb(uint32_t tlli,
- const struct gprs_ra_id *raid)
-{
- struct sgsn_mm_ctx *ctx;
-
- ctx = talloc_zero(tall_bsc_ctx, struct sgsn_mm_ctx);
- if (!ctx)
- return NULL;
-
- memcpy(&ctx->ra, raid, sizeof(ctx->ra));
- ctx->ran_type = MM_CTX_T_GERAN_Gb;
- ctx->gb.tlli = tlli;
- ctx->gmm_state = GMM_DEREGISTERED;
- ctx->pmm_state = MM_IDLE;
- ctx->auth_triplet.key_seq = GSM_KEY_SEQ_INVAL;
- ctx->ciph_algo = sgsn->cfg.cipher;
- LOGMMCTXP(LOGL_DEBUG, ctx, "Allocated with %s cipher.\n",
- get_value_string(gprs_cipher_names, ctx->ciph_algo));
- ctx->ctrg = rate_ctr_group_alloc(ctx, &mmctx_ctrg_desc, tlli);
- INIT_LLIST_HEAD(&ctx->pdp_list);
-
- llist_add(&ctx->list, &sgsn_mm_ctxts);
-
- return ctx;
-}
-
-/* Allocate a new SGSN MM context */
-struct sgsn_mm_ctx *sgsn_mm_ctx_alloc_iu(void *uectx)
-{
- struct sgsn_mm_ctx *ctx;
-
- ctx = talloc_zero(tall_bsc_ctx, struct sgsn_mm_ctx);
- if (!ctx)
- return NULL;
-
- ctx->ran_type = MM_CTX_T_UTRAN_Iu;
- ctx->iu.ue_ctx = uectx;
- ctx->iu.ue_ctx->rab_assign_addr_enc = sgsn->cfg.iu.rab_assign_addr_enc;
- ctx->iu.new_key = 1;
- ctx->gmm_state = GMM_DEREGISTERED;
- ctx->pmm_state = PMM_DETACHED;
- ctx->auth_triplet.key_seq = GSM_KEY_SEQ_INVAL;
- ctx->ctrg = rate_ctr_group_alloc(ctx, &mmctx_ctrg_desc, 0);
-
- /* Need to get RAID from IU conn */
- ctx->ra = ctx->iu.ue_ctx->ra_id;
-
- INIT_LLIST_HEAD(&ctx->pdp_list);
-
- llist_add(&ctx->list, &sgsn_mm_ctxts);
-
- return ctx;
-}
-
-
-/* this is a hard _free_ function, it doesn't clean up the PDP contexts
- * in libgtp! */
-static void sgsn_mm_ctx_free(struct sgsn_mm_ctx *mm)
-{
- struct sgsn_pdp_ctx *pdp, *pdp2;
-
- /* Unlink from global list of MM contexts */
- llist_del(&mm->list);
-
- /* Free all PDP contexts */
- llist_for_each_entry_safe(pdp, pdp2, &mm->pdp_list, list)
- sgsn_pdp_ctx_free(pdp);
-
- rate_ctr_group_free(mm->ctrg);
-
- talloc_free(mm);
-}
-
-void sgsn_mm_ctx_cleanup_free(struct sgsn_mm_ctx *mm)
-{
- struct gprs_llc_llme *llme = NULL;
- uint32_t tlli = mm->gb.tlli;
- struct sgsn_pdp_ctx *pdp, *pdp2;
- struct sgsn_signal_data sig_data;
-
- if (mm->ran_type == MM_CTX_T_GERAN_Gb)
- llme = mm->gb.llme;
- else
- OSMO_ASSERT(mm->gb.llme == NULL);
-
- /* Forget about ongoing look-ups */
- if (mm->ggsn_lookup) {
- LOGMMCTXP(LOGL_NOTICE, mm,
- "Cleaning mmctx with on-going query.\n");
- mm->ggsn_lookup->mmctx = NULL;
- mm->ggsn_lookup = NULL;
- }
-
- /* delete all existing PDP contexts for this MS */
- llist_for_each_entry_safe(pdp, pdp2, &mm->pdp_list, list) {
- LOGMMCTXP(LOGL_NOTICE, mm,
- "Dropping PDP context for NSAPI=%u\n", pdp->nsapi);
- sgsn_pdp_ctx_terminate(pdp);
- }
-
- if (osmo_timer_pending(&mm->timer)) {
- LOGMMCTXP(LOGL_INFO, mm, "Cancelling MM timer %u\n", mm->T);
- osmo_timer_del(&mm->timer);
- }
-
- memset(&sig_data, 0, sizeof(sig_data));
- sig_data.mm = mm;
- osmo_signal_dispatch(SS_SGSN, S_SGSN_MM_FREE, &sig_data);
-
-
- /* Detach from subscriber which is possibly freed then */
- if (mm->subscr) {
- struct gprs_subscr *subscr = gprs_subscr_get(mm->subscr);
- gprs_subscr_cleanup(subscr);
- gprs_subscr_put(subscr);
- }
-
- sgsn_mm_ctx_free(mm);
- mm = NULL;
-
- if (llme) {
- /* TLLI unassignment, must be called after sgsn_mm_ctx_free */
- gprs_llgmm_assign(llme, tlli, 0xffffffff);
- }
-}
-
-
-/* look up PDP context by MM context and NSAPI */
-struct sgsn_pdp_ctx *sgsn_pdp_ctx_by_nsapi(const struct sgsn_mm_ctx *mm,
- uint8_t nsapi)
-{
- struct sgsn_pdp_ctx *pdp;
-
- llist_for_each_entry(pdp, &mm->pdp_list, list) {
- if (pdp->nsapi == nsapi)
- return pdp;
- }
- return NULL;
-}
-
-/* look up PDP context by MM context and transaction ID */
-struct sgsn_pdp_ctx *sgsn_pdp_ctx_by_tid(const struct sgsn_mm_ctx *mm,
- uint8_t tid)
-{
- struct sgsn_pdp_ctx *pdp;
-
- llist_for_each_entry(pdp, &mm->pdp_list, list) {
- if (pdp->ti == tid)
- return pdp;
- }
- return NULL;
-}
-
-/* you don't want to use this directly, call sgsn_create_pdp_ctx() */
-struct sgsn_pdp_ctx *sgsn_pdp_ctx_alloc(struct sgsn_mm_ctx *mm,
- uint8_t nsapi)
-{
- struct sgsn_pdp_ctx *pdp;
-
- pdp = sgsn_pdp_ctx_by_nsapi(mm, nsapi);
- if (pdp)
- return NULL;
-
- pdp = talloc_zero(tall_bsc_ctx, struct sgsn_pdp_ctx);
- if (!pdp)
- return NULL;
-
- pdp->mm = mm;
- pdp->nsapi = nsapi;
- pdp->ctrg = rate_ctr_group_alloc(pdp, &pdpctx_ctrg_desc, nsapi);
- llist_add(&pdp->list, &mm->pdp_list);
- llist_add(&pdp->g_list, &sgsn_pdp_ctxts);
-
- return pdp;
-}
-
-/*
- * This function will not trigger any GSM DEACT PDP ACK messages, so you
- * probably want to call sgsn_delete_pdp_ctx() instead if the connection
- * isn't detached already.
- */
-void sgsn_pdp_ctx_terminate(struct sgsn_pdp_ctx *pdp)
-{
- struct sgsn_signal_data sig_data;
-
- OSMO_ASSERT(pdp->mm != NULL);
-
- /* There might still be pending callbacks in libgtp. So the parts of
- * this object relevant to GTP need to remain intact in this case. */
-
- LOGPDPCTXP(LOGL_INFO, pdp, "Forcing release of PDP context\n");
-
- if (pdp->mm->ran_type == MM_CTX_T_GERAN_Gb) {
- /* Force the deactivation of the SNDCP layer */
- sndcp_sm_deactivate_ind(&pdp->mm->gb.llme->lle[pdp->sapi], pdp->nsapi);
- }
-
- memset(&sig_data, 0, sizeof(sig_data));
- sig_data.pdp = pdp;
- osmo_signal_dispatch(SS_SGSN, S_SGSN_PDP_TERMINATE, &sig_data);
-
- /* Detach from MM context */
- llist_del(&pdp->list);
- pdp->mm = NULL;
-
- sgsn_delete_pdp_ctx(pdp);
-}
-
-/*
- * Don't call this function directly unless you know what you are doing.
- * In normal conditions use sgsn_delete_pdp_ctx and in unspecified or
- * implementation dependent abnormal ones sgsn_pdp_ctx_terminate.
- */
-void sgsn_pdp_ctx_free(struct sgsn_pdp_ctx *pdp)
-{
- struct sgsn_signal_data sig_data;
-
- memset(&sig_data, 0, sizeof(sig_data));
- sig_data.pdp = pdp;
- osmo_signal_dispatch(SS_SGSN, S_SGSN_PDP_FREE, &sig_data);
-
- rate_ctr_group_free(pdp->ctrg);
- if (pdp->mm)
- llist_del(&pdp->list);
- llist_del(&pdp->g_list);
-
- /* _if_ we still have a library handle, at least set it to NULL
- * to avoid any dereferences of the now-deleted PDP context from
- * sgsn_libgtp:cb_data_ind() */
- if (pdp->lib) {
- struct pdp_t *lib = pdp->lib;
- LOGPDPCTXP(LOGL_NOTICE, pdp, "freeing PDP context that still "
- "has a libgtp handle attached to it, this shouldn't "
- "happen!\n");
- osmo_generate_backtrace();
- lib->priv = NULL;
- }
-
- if (pdp->destroy_ggsn)
- sgsn_ggsn_ctx_free(pdp->ggsn);
- talloc_free(pdp);
-}
-
-/* GGSN contexts */
-
-struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_alloc(uint32_t id)
-{
- struct sgsn_ggsn_ctx *ggc;
-
- ggc = talloc_zero(tall_bsc_ctx, struct sgsn_ggsn_ctx);
- if (!ggc)
- return NULL;
-
- ggc->id = id;
- ggc->gtp_version = 1;
- ggc->remote_restart_ctr = -1;
- /* if we are called from config file parse, this gsn doesn't exist yet */
- ggc->gsn = sgsn->gsn;
- llist_add(&ggc->list, &sgsn_ggsn_ctxts);
-
- return ggc;
-}
-
-void sgsn_ggsn_ctx_free(struct sgsn_ggsn_ctx *ggc)
-{
- llist_del(&ggc->list);
- talloc_free(ggc);
-}
-
-struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_by_id(uint32_t id)
-{
- struct sgsn_ggsn_ctx *ggc;
-
- llist_for_each_entry(ggc, &sgsn_ggsn_ctxts, list) {
- if (id == ggc->id)
- return ggc;
- }
- return NULL;
-}
-
-struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_by_addr(struct in_addr *addr)
-{
- struct sgsn_ggsn_ctx *ggc;
-
- llist_for_each_entry(ggc, &sgsn_ggsn_ctxts, list) {
- if (!memcmp(addr, &ggc->remote_addr, sizeof(*addr)))
- return ggc;
- }
- return NULL;
-}
-
-
-struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_find_alloc(uint32_t id)
-{
- struct sgsn_ggsn_ctx *ggc;
-
- ggc = sgsn_ggsn_ctx_by_id(id);
- if (!ggc)
- ggc = sgsn_ggsn_ctx_alloc(id);
- return ggc;
-}
-
-/* APN contexts */
-
-static struct apn_ctx *sgsn_apn_ctx_alloc(const char *ap_name, const char *imsi_prefix)
-{
- struct apn_ctx *actx;
-
- actx = talloc_zero(tall_bsc_ctx, struct apn_ctx);
- if (!actx)
- return NULL;
- actx->name = talloc_strdup(actx, ap_name);
- actx->imsi_prefix = talloc_strdup(actx, imsi_prefix);
-
- llist_add_tail(&actx->list, &sgsn_apn_ctxts);
-
- return actx;
-}
-
-void sgsn_apn_ctx_free(struct apn_ctx *actx)
-{
- llist_del(&actx->list);
- talloc_free(actx);
-}
-
-struct apn_ctx *sgsn_apn_ctx_match(const char *name, const char *imsi)
-{
- struct apn_ctx *actx;
- struct apn_ctx *found_actx = NULL;
- size_t imsi_prio = 0;
- size_t name_prio = 0;
- size_t name_req_len = strlen(name);
-
- llist_for_each_entry(actx, &sgsn_apn_ctxts, list) {
- size_t name_ref_len, imsi_ref_len;
- const char *name_ref_start, *name_match_start;
-
- imsi_ref_len = strlen(actx->imsi_prefix);
- if (strncmp(actx->imsi_prefix, imsi, imsi_ref_len) != 0)
- continue;
-
- if (imsi_ref_len < imsi_prio)
- continue;
-
- /* IMSI matches */
-
- name_ref_start = &actx->name[0];
- if (name_ref_start[0] == '*') {
- /* Suffix match */
- name_ref_start += 1;
- name_ref_len = strlen(name_ref_start);
- if (name_ref_len > name_req_len)
- continue;
- } else {
- name_ref_len = strlen(name_ref_start);
- if (name_ref_len != name_req_len)
- continue;
- }
-
- name_match_start = name + (name_req_len - name_ref_len);
- if (strcasecmp(name_match_start, name_ref_start) != 0)
- continue;
-
- /* IMSI and name match */
-
- if (imsi_ref_len == imsi_prio && name_ref_len < name_prio)
- /* Lower priority, skip */
- continue;
-
- imsi_prio = imsi_ref_len;
- name_prio = name_ref_len;
- found_actx = actx;
- }
- return found_actx;
-}
-
-struct apn_ctx *sgsn_apn_ctx_by_name(const char *name, const char *imsi_prefix)
-{
- struct apn_ctx *actx;
-
- llist_for_each_entry(actx, &sgsn_apn_ctxts, list) {
- if (strcasecmp(name, actx->name) == 0 &&
- strcasecmp(imsi_prefix, actx->imsi_prefix) == 0)
- return actx;
- }
- return NULL;
-}
-
-struct apn_ctx *sgsn_apn_ctx_find_alloc(const char *name, const char *imsi_prefix)
-{
- struct apn_ctx *actx;
-
- actx = sgsn_apn_ctx_by_name(name, imsi_prefix);
- if (!actx)
- actx = sgsn_apn_ctx_alloc(name, imsi_prefix);
-
- return actx;
-}
-
-uint32_t sgsn_alloc_ptmsi(void)
-{
- struct sgsn_mm_ctx *mm;
- uint32_t ptmsi = 0xdeadbeef;
- int max_retries = 100;
-
-restart:
- if (RAND_bytes((uint8_t *) &ptmsi, sizeof(ptmsi)) != 1)
- goto failed;
-
- /* Enforce that the 2 MSB are set without loosing the distance between
- * identical values. Since rand() has no duplicate values within a
- * period (because the size of the state is the same like the size of
- * the random value), this leads to a distance of period/4 when the
- * distribution of the 2 MSB is uniform. This approach fails with a
- * probability of (3/4)^max_retries, only 1% of the approaches will
- * need more than 16 numbers (even distribution assumed).
- *
- * Alternatively, a freeze list could be used if another PRNG is used
- * or when this approach proves to be not sufficient.
- */
- if (ptmsi >= 0xC0000000) {
- if (!max_retries--)
- goto failed;
- goto restart;
- }
- ptmsi |= 0xC0000000;
-
- if (ptmsi == GSM_RESERVED_TMSI) {
- if (!max_retries--)
- goto failed;
- goto restart;
- }
-
- llist_for_each_entry(mm, &sgsn_mm_ctxts, list) {
- if (mm->p_tmsi == ptmsi) {
- if (!max_retries--)
- goto failed;
- goto restart;
- }
- }
-
- return ptmsi;
-
-failed:
- LOGP(DGPRS, LOGL_ERROR, "Failed to allocate a P-TMSI\n");
- return GSM_RESERVED_TMSI;
-}
-
-static void drop_one_pdp(struct sgsn_pdp_ctx *pdp)
-{
- if (pdp->mm->gmm_state == GMM_REGISTERED_NORMAL)
- gsm48_tx_gsm_deact_pdp_req(pdp, GSM_CAUSE_NET_FAIL);
- else {
- /* FIXME: GPRS paging in case MS is SUSPENDED */
- LOGPDPCTXP(LOGL_NOTICE, pdp, "Hard-dropping PDP ctx due to GGSN "
- "recovery\n");
- /* FIXME: how to tell this to libgtp? */
- sgsn_pdp_ctx_free(pdp);
- }
-}
-
-/* High-level function to be called in case a GGSN has disappeared or
- * otherwise lost state (recovery procedure) */
-int drop_all_pdp_for_ggsn(struct sgsn_ggsn_ctx *ggsn)
-{
- struct sgsn_mm_ctx *mm;
- int num = 0;
-
- llist_for_each_entry(mm, &sgsn_mm_ctxts, list) {
- struct sgsn_pdp_ctx *pdp;
- llist_for_each_entry(pdp, &mm->pdp_list, list) {
- if (pdp->ggsn == ggsn) {
- drop_one_pdp(pdp);
- num++;
- }
- }
- }
-
- return num;
-}
-
-void sgsn_update_subscriber_data(struct sgsn_mm_ctx *mmctx)
-{
- OSMO_ASSERT(mmctx != NULL);
- LOGMMCTXP(LOGL_INFO, mmctx, "Subscriber data update\n");
-
- sgsn_auth_update(mmctx);
-}
-
-static void insert_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->gb.llme) {
- gsm0408_gprs_access_cancelled(mmctx, SGSN_ERROR_CAUSE_NONE);
- return;
- }
- }
-
- /* No MM context found */
- LOGP(DGPRS, LOGL_INFO, "Deleting orphaned LLME, TLLI 0x%08x\n",
- llme->tlli);
- gprs_llgmm_unassign(llme);
-}
-
-static void sgsn_llme_check_cb(void *data_)
-{
- struct gprs_llc_llme *llme, *llme_tmp;
- struct timespec now_tp;
- time_t now, age;
- time_t max_age = gprs_max_time_to_idle();
-
- int rc;
-
- rc = clock_gettime(CLOCK_MONOTONIC, &now_tp);
- OSMO_ASSERT(rc >= 0);
- now = now_tp.tv_sec;
-
- LOGP(DGPRS, LOGL_DEBUG,
- "Checking for inactive LLMEs, time = %u\n", (unsigned)now);
-
- llist_for_each_entry_safe(llme, llme_tmp, &gprs_llc_llmes, list) {
- if (llme->age_timestamp == GPRS_LLME_RESET_AGE)
- llme->age_timestamp = now;
-
- age = now - llme->age_timestamp;
-
- if (age > max_age || age < 0) {
- LOGP(DGPRS, LOGL_INFO,
- "Inactivity timeout for TLLI 0x%08x, age %d\n",
- llme->tlli, (int)age);
- sgsn_llme_cleanup_free(llme);
- }
- }
-
- osmo_timer_schedule(&sgsn->llme_timer, GPRS_LLME_CHECK_TICK, 0);
-}
-
-void sgsn_inst_init()
-{
- osmo_timer_setup(&sgsn->llme_timer, sgsn_llme_check_cb, NULL);
- osmo_timer_schedule(&sgsn->llme_timer, GPRS_LLME_CHECK_TICK, 0);
-}
-
diff --git a/src/gprs/gprs_sndcp.c b/src/gprs/gprs_sndcp.c
deleted file mode 100644
index a18998f9e..000000000
--- a/src/gprs/gprs_sndcp.c
+++ /dev/null
@@ -1,1258 +0,0 @@
-/* GPRS SNDCP protocol implementation as per 3GPP TS 04.65 */
-
-/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
- * (C) 2010 by On-Waves
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <errno.h>
-#include <stdint.h>
-#include <stdbool.h>
-
-#include <osmocom/core/msgb.h>
-#include <osmocom/core/linuxlist.h>
-#include <osmocom/core/timer.h>
-#include <osmocom/core/talloc.h>
-#include <osmocom/gprs/gprs_bssgp.h>
-
-#include <openbsc/gsm_data.h>
-#include <openbsc/debug.h>
-#include <openbsc/gprs_llc.h>
-#include <openbsc/sgsn.h>
-#include <openbsc/gprs_sndcp.h>
-#include <openbsc/gprs_llc_xid.h>
-#include <openbsc/gprs_sndcp_xid.h>
-#include <openbsc/gprs_sndcp_pcomp.h>
-#include <openbsc/gprs_sndcp_dcomp.h>
-#include <openbsc/gprs_sndcp_comp.h>
-
-#define DEBUG_IP_PACKETS 0 /* 0=Disabled, 1=Enabled */
-
-#if DEBUG_IP_PACKETS == 1
-/* Calculate TCP/IP checksum */
-static uint16_t calc_ip_csum(uint8_t *data, int len)
-{
- int i;
- uint32_t accumulator = 0;
- uint16_t *pointer = (uint16_t *) data;
-
- for (i = len; i > 1; i -= 2) {
- accumulator += *pointer;
- pointer++;
- }
-
- if (len % 2)
- accumulator += *pointer;
-
- accumulator = (accumulator & 0xffff) + ((accumulator >> 16) & 0xffff);
- accumulator += (accumulator >> 16) & 0xffff;
- return (~accumulator);
-}
-
-/* Calculate TCP/IP checksum */
-static uint16_t calc_tcpip_csum(const void *ctx, uint8_t *packet, int len)
-{
- uint8_t *buf;
- uint16_t csum;
-
- buf = talloc_zero_size(ctx, len);
- memset(buf, 0, len);
- memcpy(buf, packet + 12, 8);
- buf[9] = packet[9];
- buf[11] = (len - 20) & 0xFF;
- buf[10] = (len - 20) >> 8 & 0xFF;
- memcpy(buf + 12, packet + 20, len - 20);
- csum = calc_ip_csum(buf, len - 20 + 12);
- talloc_free(buf);
- return csum;
-}
-
-/* Show some ip packet details */
-static void debug_ip_packet(uint8_t *data, int len, int dir, char *info)
-{
- uint8_t tcp_flags;
- char flags_debugmsg[256];
- int len_short;
- static unsigned int packet_count = 0;
- static unsigned int tcp_csum_err_count = 0;
- static unsigned int ip_csum_err_count = 0;
-
- packet_count++;
-
- if (len > 80)
- len_short = 80;
- else
- len_short = len;
-
- if (dir)
- DEBUGP(DSNDCP, "%s: MS => SGSN: %s\n", info,
- osmo_hexdump_nospc(data, len_short));
- else
- DEBUGP(DSNDCP, "%s: MS <= SGSN: %s\n", info,
- osmo_hexdump_nospc(data, len_short));
-
- DEBUGP(DSNDCP, "%s: Length.: %d\n", info, len);
- DEBUGP(DSNDCP, "%s: NO.: %d\n", info, packet_count);
-
- if (len < 20) {
- DEBUGP(DSNDCP, "%s: Error: Short IP packet!\n", info);
- return;
- }
-
- if (calc_ip_csum(data, 20) != 0) {
- DEBUGP(DSNDCP, "%s: Bad IP-Header checksum!\n", info);
- ip_csum_err_count++;
- } else
- DEBUGP(DSNDCP, "%s: IP-Header checksum ok.\n", info);
-
- if (data[9] == 0x06) {
- if (len < 40) {
- DEBUGP(DSNDCP, "%s: Error: Short TCP packet!\n", info);
- return;
- }
-
- DEBUGP(DSNDCP, "%s: Protocol type: TCP\n", info);
- tcp_flags = data[33];
-
- if (calc_tcpip_csum(NULL, data, len) != 0) {
- DEBUGP(DSNDCP, "%s: Bad TCP checksum!\n", info);
- tcp_csum_err_count++;
- } else
- DEBUGP(DSNDCP, "%s: TCP checksum ok.\n", info);
-
- memset(flags_debugmsg, 0, sizeof(flags_debugmsg));
- if (tcp_flags & 1)
- strcat(flags_debugmsg, "FIN ");
- if (tcp_flags & 2)
- strcat(flags_debugmsg, "SYN ");
- if (tcp_flags & 4)
- strcat(flags_debugmsg, "RST ");
- if (tcp_flags & 8)
- strcat(flags_debugmsg, "PSH ");
- if (tcp_flags & 16)
- strcat(flags_debugmsg, "ACK ");
- if (tcp_flags & 32)
- strcat(flags_debugmsg, "URG ");
- DEBUGP(DSNDCP, "%s: FLAGS: %s\n", info, flags_debugmsg);
- } else if (data[9] == 0x11) {
- DEBUGP(DSNDCP, "%s: Protocol type: UDP\n", info);
- } else {
- DEBUGP(DSNDCP, "%s: Protocol type: (%02x)\n", info, data[9]);
- }
-
- DEBUGP(DSNDCP, "%s: IP-Header checksum errors: %d\n", info,
- ip_csum_err_count);
- DEBUGP(DSNDCP, "%s: TCP-Checksum errors: %d\n", info,
- tcp_csum_err_count);
-}
-#endif
-
-/* 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);
-
-/* Check if any compression parameters are set in the sgsn configuration */
-static inline int any_pcomp_or_dcomp_active(struct sgsn_instance *sgsn) {
- if (sgsn->cfg.pcomp_rfc1144.active || sgsn->cfg.pcomp_rfc1144.passive ||
- sgsn->cfg.dcomp_v42bis.active || sgsn->cfg.dcomp_v42bis.passive)
- return true;
- else
- return false;
-}
-
-/* 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;
- int npdu_len;
- int rc;
- uint8_t *expnd = NULL;
-
- 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);
- }
-
- npdu_len = sne->defrag.tot_len;
-
- /* 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() */
-
- /* Decompress packet */
-#if DEBUG_IP_PACKETS == 1
- DEBUGP(DSNDCP, " \n");
- DEBUGP(DSNDCP, ":::::::::::::::::::::::::::::::::::::::::::::::::::\n");
- DEBUGP(DSNDCP, "===================================================\n");
-#endif
- if (any_pcomp_or_dcomp_active(sgsn)) {
-
- expnd = talloc_zero_size(msg, npdu_len * MAX_DATADECOMPR_FAC +
- MAX_HDRDECOMPR_INCR);
- memcpy(expnd, npdu, npdu_len);
-
- /* Apply data decompression */
- rc = gprs_sndcp_dcomp_expand(expnd, npdu_len, sne->defrag.dcomp,
- sne->defrag.data);
- if (rc < 0) {
- LOGP(DSNDCP, LOGL_ERROR,
- "Data decompression failed!\n");
- talloc_free(expnd);
- return -EIO;
- }
-
- /* Apply header decompression */
- rc = gprs_sndcp_pcomp_expand(expnd, rc, sne->defrag.pcomp,
- sne->defrag.proto);
- if (rc < 0) {
- LOGP(DSNDCP, LOGL_ERROR,
- "TCP/IP Header decompression failed!\n");
- talloc_free(expnd);
- return -EIO;
- }
-
- /* Modify npu length, expnd is handed directly handed
- * over to gsn_rx_sndcp_ud_ind(), see below */
- npdu_len = rc;
- } else
- expnd = npdu;
-#if DEBUG_IP_PACKETS == 1
- debug_ip_packet(expnd, npdu_len, 1, "defrag_segments()");
- DEBUGP(DSNDCP, "===================================================\n");
- DEBUGP(DSNDCP, ":::::::::::::::::::::::::::::::::::::::::::::::::::\n");
- DEBUGP(DSNDCP, " \n");
-#endif
-
- /* Hand off packet to gtp */
- rc = sgsn_rx_sndcp_ud_ind(&sne->ra_id, sne->lle->llme->tlli,
- sne->nsapi, msg, npdu_len, expnd);
-
- if (any_pcomp_or_dcomp_active(sgsn))
- talloc_free(expnd);
-
- return rc;
-}
-
-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,
- uint8_t pcomp, uint8_t dcomp)
-{
- 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 = pcomp;
- scomph->dcomp = dcomp;
- }
-
- /* 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, true);
- /* 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;
- uint8_t pcomp = 0;
- uint8_t dcomp = 0;
- int rc;
-
- /* Identifiers from UP: (TLLI, SAPI) + (BVCI, NSEI) */
-
- /* Compress packet */
-#if DEBUG_IP_PACKETS == 1
- DEBUGP(DSNDCP, " \n");
- DEBUGP(DSNDCP, ":::::::::::::::::::::::::::::::::::::::::::::::::::\n");
- DEBUGP(DSNDCP, "===================================================\n");
- debug_ip_packet(msg->data, msg->len, 0, "sndcp_initdata_req()");
-#endif
- if (any_pcomp_or_dcomp_active(sgsn)) {
-
- /* Apply header compression */
- rc = gprs_sndcp_pcomp_compress(msg->data, msg->len, &pcomp,
- lle->llme->comp.proto, nsapi);
- if (rc < 0) {
- LOGP(DSNDCP, LOGL_ERROR,
- "TCP/IP Header compression failed!\n");
- return -EIO;
- }
-
- /* Fixup pointer locations and sizes in message buffer to match
- * the new, compressed buffer size */
- msgb_get(msg, msg->len);
- msgb_put(msg, rc);
-
- /* Apply data compression */
- rc = gprs_sndcp_dcomp_compress(msg->data, msg->len, &dcomp,
- lle->llme->comp.data, nsapi);
- if (rc < 0) {
- LOGP(DSNDCP, LOGL_ERROR, "Data compression failed!\n");
- return -EIO;
- }
-
- /* Fixup pointer locations and sizes in message buffer to match
- * the new, compressed buffer size */
- msgb_get(msg, msg->len);
- msgb_put(msg, rc);
- }
-#if DEBUG_IP_PACKETS == 1
- DEBUGP(DSNDCP, "===================================================\n");
- DEBUGP(DSNDCP, ":::::::::::::::::::::::::::::::::::::::::::::::::::\n");
- DEBUGP(DSNDCP, " \n");
-#endif
-
- 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,pcomp,dcomp);
- 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 = pcomp;
- scomph->dcomp = dcomp;
-
- /* 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, true);
-}
-
-/* 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;
- int rc;
- uint8_t *expnd = NULL;
-
- 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));
-
- if (scomph) {
- sne->defrag.pcomp = scomph->pcomp;
- sne->defrag.dcomp = scomph->dcomp;
- sne->defrag.proto = lle->llme->comp.proto;
- sne->defrag.data = lle->llme->comp.data;
- }
-
- /* 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);
-
- npdu_num = (suh->npdu_high << 8) | suh->npdu_low;
- npdu = (uint8_t *)suh + sizeof(*suh);
- npdu_len = (msg->data + msg->len) - npdu - 3; /* -3 'removes' the FCS */
-
- 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() */
-
- /* Decompress packet */
-#if DEBUG_IP_PACKETS == 1
- DEBUGP(DSNDCP, " \n");
- DEBUGP(DSNDCP, ":::::::::::::::::::::::::::::::::::::::::::::::::::\n");
- DEBUGP(DSNDCP, "===================================================\n");
-#endif
- if (any_pcomp_or_dcomp_active(sgsn)) {
-
- expnd = talloc_zero_size(msg, npdu_len * MAX_DATADECOMPR_FAC +
- MAX_HDRDECOMPR_INCR);
- memcpy(expnd, npdu, npdu_len);
-
- /* Apply data decompression */
- rc = gprs_sndcp_dcomp_expand(expnd, npdu_len, sne->defrag.dcomp,
- sne->defrag.data);
- if (rc < 0) {
- LOGP(DSNDCP, LOGL_ERROR,
- "Data decompression failed!\n");
- talloc_free(expnd);
- return -EIO;
- }
-
- /* Apply header decompression */
- rc = gprs_sndcp_pcomp_expand(expnd, rc, sne->defrag.pcomp,
- sne->defrag.proto);
- if (rc < 0) {
- LOGP(DSNDCP, LOGL_ERROR,
- "TCP/IP Header decompression failed!\n");
- talloc_free(expnd);
- return -EIO;
- }
-
- /* Modify npu length, expnd is handed directly handed
- * over to gsn_rx_sndcp_ud_ind(), see below */
- npdu_len = rc;
- } else
- expnd = npdu;
-#if DEBUG_IP_PACKETS == 1
- debug_ip_packet(expnd, npdu_len, 1, "sndcp_llunitdata_ind()");
- DEBUGP(DSNDCP, "===================================================\n");
- DEBUGP(DSNDCP, ":::::::::::::::::::::::::::::::::::::::::::::::::::\n");
- DEBUGP(DSNDCP, " \n");
-#endif
-
- /* Hand off packet to gtp */
- rc = sgsn_rx_sndcp_ud_ind(&sne->ra_id, lle->llme->tlli,
- sne->nsapi, msg, npdu_len, expnd);
-
- if (any_pcomp_or_dcomp_active(sgsn))
- talloc_free(expnd);
-
- return rc;
-}
-
-#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
-
-/* Generate SNDCP-XID message */
-static int gprs_llc_gen_sndcp_xid(uint8_t *bytes, int bytes_len, uint8_t nsapi)
-{
- int entity = 0;
- LLIST_HEAD(comp_fields);
- struct gprs_sndcp_pcomp_rfc1144_params rfc1144_params;
- struct gprs_sndcp_comp_field rfc1144_comp_field;
- struct gprs_sndcp_dcomp_v42bis_params v42bis_params;
- struct gprs_sndcp_comp_field v42bis_comp_field;
-
- memset(&rfc1144_comp_field, 0, sizeof(struct gprs_sndcp_comp_field));
- memset(&v42bis_comp_field, 0, sizeof(struct gprs_sndcp_comp_field));
-
- /* Setup rfc1144 */
- if (sgsn->cfg.pcomp_rfc1144.active) {
- rfc1144_params.nsapi[0] = nsapi;
- rfc1144_params.nsapi_len = 1;
- rfc1144_params.s01 = sgsn->cfg.pcomp_rfc1144.s01;
- rfc1144_comp_field.p = 1;
- rfc1144_comp_field.entity = entity;
- rfc1144_comp_field.algo = RFC_1144;
- rfc1144_comp_field.comp[RFC1144_PCOMP1] = 1;
- rfc1144_comp_field.comp[RFC1144_PCOMP2] = 2;
- rfc1144_comp_field.comp_len = RFC1144_PCOMP_NUM;
- rfc1144_comp_field.rfc1144_params = &rfc1144_params;
- entity++;
- llist_add(&rfc1144_comp_field.list, &comp_fields);
- }
-
- /* Setup V.42bis */
- if (sgsn->cfg.dcomp_v42bis.active) {
- v42bis_params.nsapi[0] = nsapi;
- v42bis_params.nsapi_len = 1;
- v42bis_params.p0 = sgsn->cfg.dcomp_v42bis.p0;
- v42bis_params.p1 = sgsn->cfg.dcomp_v42bis.p1;
- v42bis_params.p2 = sgsn->cfg.dcomp_v42bis.p2;
- v42bis_comp_field.p = 1;
- v42bis_comp_field.entity = entity;
- v42bis_comp_field.algo = V42BIS;
- v42bis_comp_field.comp[V42BIS_DCOMP1] = 1;
- v42bis_comp_field.comp_len = V42BIS_DCOMP_NUM;
- v42bis_comp_field.v42bis_params = &v42bis_params;
- entity++;
- llist_add(&v42bis_comp_field.list, &comp_fields);
- }
-
- /* Do not attempt to compile anything if there is no data in the list */
- if (llist_empty(&comp_fields))
- return 0;
-
- /* Compile bytestream */
- return gprs_sndcp_compile_xid(bytes, bytes_len, &comp_fields,
- DEFAULT_SNDCP_VERSION);
-}
-
-/* Set of SNDCP-XID bnegotiation (See also: TS 144 065,
- * Section 6.8 XID parameter negotiation) */
-int sndcp_sn_xid_req(struct gprs_llc_lle *lle, uint8_t nsapi)
-{
- /* Note: The specification requires the SNDCP-User to set of an
- * SNDCP xid request. See also 3GPP TS 44.065, 6.8 XID parameter
- * negotiation, Figure 11: SNDCP XID negotiation procedure. In
- * our case the SNDCP-User is sgsn_libgtp.c, which calls
- * sndcp_sn_xid_req directly. */
-
- uint8_t l3params[1024];
- int xid_len;
- struct gprs_llc_xid_field xid_field_request;
-
- /* Wipe off all compression entities and their states to
- * get rid of possible leftovers from a previous session */
- gprs_sndcp_comp_free(lle->llme->comp.proto);
- gprs_sndcp_comp_free(lle->llme->comp.data);
- lle->llme->comp.proto = gprs_sndcp_comp_alloc(lle->llme);
- lle->llme->comp.data = gprs_sndcp_comp_alloc(lle->llme);
- talloc_free(lle->llme->xid);
- lle->llme->xid = NULL;
-
- /* Generate compression parameter bytestream */
- xid_len = gprs_llc_gen_sndcp_xid(l3params, sizeof(l3params), nsapi);
-
- /* Send XID with the SNDCP-XID bytetsream included */
- if (xid_len > 0) {
- xid_field_request.type = GPRS_LLC_XID_T_L3_PAR;
- xid_field_request.data = l3params;
- xid_field_request.data_len = xid_len;
- return gprs_ll_xid_req(lle, &xid_field_request);
- }
-
- /* When bytestream can not be generated, proceed without SNDCP-XID */
- return gprs_ll_xid_req(lle, NULL);
-
-}
-
-/* Handle header compression entites */
-static int handle_pcomp_entities(struct gprs_sndcp_comp_field *comp_field,
- struct gprs_llc_lle *lle)
-{
- /* Note: This functions also transforms the comp_field into its
- * echo form (strips comp values, resets propose bit etc...)
- * the processed comp_fields can then be sent back as XID-
- * Response without further modification. */
-
- /* Delete propose bit */
- comp_field->p = 0;
-
- /* Process proposed parameters */
- switch (comp_field->algo) {
- case RFC_1144:
- if (sgsn->cfg.pcomp_rfc1144.passive
- && comp_field->rfc1144_params->nsapi_len > 0) {
- DEBUGP(DSNDCP,
- "Accepting RFC1144 header compression...\n");
- gprs_sndcp_comp_add(lle->llme, lle->llme->comp.proto,
- comp_field);
- } else {
- DEBUGP(DSNDCP,
- "Rejecting RFC1144 header compression...\n");
- gprs_sndcp_comp_delete(lle->llme->comp.proto,
- comp_field->entity);
- comp_field->rfc1144_params->nsapi_len = 0;
- }
- break;
- case RFC_2507:
- /* RFC 2507 is not yet supported,
- * so we set applicable nsapis to zero */
- DEBUGP(DSNDCP, "Rejecting RFC2507 header compression...\n");
- comp_field->rfc2507_params->nsapi_len = 0;
- gprs_sndcp_comp_delete(lle->llme->comp.proto,
- comp_field->entity);
- break;
- case ROHC:
- /* ROHC is not yet supported,
- * so we set applicable nsapis to zero */
- DEBUGP(DSNDCP, "Rejecting ROHC header compression...\n");
- comp_field->rohc_params->nsapi_len = 0;
- gprs_sndcp_comp_delete(lle->llme->comp.proto,
- comp_field->entity);
- break;
- }
-
- return 0;
-}
-
-/* Hanle data compression entites */
-static int handle_dcomp_entities(struct gprs_sndcp_comp_field *comp_field,
- struct gprs_llc_lle *lle)
-{
- /* See note in handle_pcomp_entities() */
-
- /* Delete propose bit */
- comp_field->p = 0;
-
- /* Process proposed parameters */
- switch (comp_field->algo) {
- case V42BIS:
- if (sgsn->cfg.dcomp_v42bis.passive &&
- comp_field->v42bis_params->nsapi_len > 0) {
- DEBUGP(DSNDCP,
- "Accepting V.42bis data compression...\n");
- gprs_sndcp_comp_add(lle->llme, lle->llme->comp.data,
- comp_field);
- } else {
- LOGP(DSNDCP, LOGL_DEBUG,
- "Rejecting V.42bis data compression...\n");
- gprs_sndcp_comp_delete(lle->llme->comp.data,
- comp_field->entity);
- comp_field->v42bis_params->nsapi_len = 0;
- }
- break;
- case V44:
- /* V44 is not yet supported,
- * so we set applicable nsapis to zero */
- DEBUGP(DSNDCP, "Rejecting V.44 data compression...\n");
- comp_field->v44_params->nsapi_len = 0;
- gprs_sndcp_comp_delete(lle->llme->comp.data,
- comp_field->entity);
- break;
- }
-
- return 0;
-
-}
-
-/* Process SNDCP-XID indication
- * (See also: TS 144 065, Section 6.8 XID parameter negotiation) */
-int sndcp_sn_xid_ind(struct gprs_llc_xid_field *xid_field_indication,
- struct gprs_llc_xid_field *xid_field_response,
- struct gprs_llc_lle *lle)
-{
- /* Note: This function computes the SNDCP-XID response that is sent
- * back to the ms when a ms originated XID is received. The
- * Input XID fields are directly processed and the result is directly
- * handed back. */
-
- int rc;
- int compclass;
- int version;
-
- struct llist_head *comp_fields;
- struct gprs_sndcp_comp_field *comp_field;
-
- OSMO_ASSERT(xid_field_indication);
- OSMO_ASSERT(xid_field_response);
- OSMO_ASSERT(lle);
-
- /* Parse SNDCP-CID XID-Field */
- comp_fields = gprs_sndcp_parse_xid(&version, lle->llme,
- xid_field_indication->data,
- xid_field_indication->data_len,
- NULL);
- if (!comp_fields)
- return -EINVAL;
-
- /* Handle compression entites */
- DEBUGP(DSNDCP, "SNDCP-XID-IND (ms):\n");
- gprs_sndcp_dump_comp_fields(comp_fields, LOGL_DEBUG);
-
- llist_for_each_entry(comp_field, comp_fields, list) {
- compclass = gprs_sndcp_get_compression_class(comp_field);
- if (compclass == SNDCP_XID_PROTOCOL_COMPRESSION)
- rc = handle_pcomp_entities(comp_field, lle);
- else if (compclass == SNDCP_XID_DATA_COMPRESSION)
- rc = handle_dcomp_entities(comp_field, lle);
- else {
- gprs_sndcp_comp_delete(lle->llme->comp.proto,
- comp_field->entity);
- gprs_sndcp_comp_delete(lle->llme->comp.data,
- comp_field->entity);
- rc = 0;
- }
-
- if (rc < 0) {
- talloc_free(comp_fields);
- return -EINVAL;
- }
- }
-
- DEBUGP(DSNDCP, "SNDCP-XID-RES (sgsn):\n");
- gprs_sndcp_dump_comp_fields(comp_fields, LOGL_DEBUG);
-
- /* Reserve some memory to store the modified SNDCP-XID bytes */
- xid_field_response->data =
- talloc_zero_size(lle->llme, xid_field_indication->data_len);
-
- /* Set Type flag for response */
- xid_field_response->type = GPRS_LLC_XID_T_L3_PAR;
-
- /* Compile modified SNDCP-XID bytes */
- rc = gprs_sndcp_compile_xid(xid_field_response->data,
- xid_field_indication->data_len,
- comp_fields, 0);
-
- if (rc > 0)
- xid_field_response->data_len = rc;
- else {
- talloc_free(xid_field_response->data);
- xid_field_response->data = NULL;
- xid_field_response->data_len = 0;
- return -EINVAL;
- }
-
- talloc_free(comp_fields);
-
- return 0;
-}
-
-/* Process SNDCP-XID indication
- * (See also: TS 144 065, Section 6.8 XID parameter negotiation) */
-int sndcp_sn_xid_conf(struct gprs_llc_xid_field *xid_field_conf,
- struct gprs_llc_xid_field *xid_field_request,
- struct gprs_llc_lle *lle)
-{
- /* Note: This function handles an incomming SNDCP-XID confirmiation.
- * Since the confirmation fields may lack important parameters we
- * will reconstruct these missing fields using the original request
- * we have sent. After that we will create (or delete) the
- * compression entites */
-
- struct llist_head *comp_fields_req;
- struct llist_head *comp_fields_conf;
- struct gprs_sndcp_comp_field *comp_field;
- int rc;
- int compclass;
-
- /* We need both, the confirmation that is sent back by the ms,
- * and the original request we have sent. If one of this is missing
- * we can not process the confirmation, the caller must check if
- * request and confirmation fields are available. */
- OSMO_ASSERT(xid_field_conf);
- OSMO_ASSERT(xid_field_request);
-
- /* Parse SNDCP-CID XID-Field */
- comp_fields_req = gprs_sndcp_parse_xid(NULL, lle->llme,
- xid_field_request->data,
- xid_field_request->data_len,
- NULL);
- if (!comp_fields_req)
- return -EINVAL;
-
- DEBUGP(DSNDCP, "SNDCP-XID-REQ (sgsn):\n");
- gprs_sndcp_dump_comp_fields(comp_fields_req, LOGL_DEBUG);
-
- /* Parse SNDCP-CID XID-Field */
- comp_fields_conf = gprs_sndcp_parse_xid(NULL, lle->llme,
- xid_field_conf->data,
- xid_field_conf->data_len,
- comp_fields_req);
- if (!comp_fields_conf)
- return -EINVAL;
-
- DEBUGP(DSNDCP, "SNDCP-XID-CONF (ms):\n");
- gprs_sndcp_dump_comp_fields(comp_fields_conf, LOGL_DEBUG);
-
- /* Handle compression entites */
- llist_for_each_entry(comp_field, comp_fields_conf, list) {
- compclass = gprs_sndcp_get_compression_class(comp_field);
- if (compclass == SNDCP_XID_PROTOCOL_COMPRESSION)
- rc = handle_pcomp_entities(comp_field, lle);
- else if (compclass == SNDCP_XID_DATA_COMPRESSION)
- rc = handle_dcomp_entities(comp_field, lle);
- else {
- gprs_sndcp_comp_delete(lle->llme->comp.proto,
- comp_field->entity);
- gprs_sndcp_comp_delete(lle->llme->comp.data,
- comp_field->entity);
- rc = 0;
- }
-
- if (rc < 0) {
- talloc_free(comp_fields_req);
- talloc_free(comp_fields_conf);
- return -EINVAL;
- }
- }
-
- talloc_free(comp_fields_req);
- talloc_free(comp_fields_conf);
-
- return 0;
-}
diff --git a/src/gprs/gprs_sndcp_comp.c b/src/gprs/gprs_sndcp_comp.c
deleted file mode 100644
index a12c39aa6..000000000
--- a/src/gprs/gprs_sndcp_comp.c
+++ /dev/null
@@ -1,323 +0,0 @@
-/* GPRS SNDCP header compression entity management tools */
-
-/* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
- * All Rights Reserved
- *
- * Author: Philipp Maier
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <stdio.h>
-#include <string.h>
-#include <stdint.h>
-#include <math.h>
-#include <errno.h>
-#include <stdbool.h>
-
-#include <osmocom/core/linuxlist.h>
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/utils.h>
-
-#include <openbsc/debug.h>
-#include <openbsc/gprs_sndcp_xid.h>
-#include <openbsc/gprs_sndcp_comp.h>
-#include <openbsc/gprs_sndcp_pcomp.h>
-#include <openbsc/gprs_sndcp_dcomp.h>
-
-/* Create a new compression entity from a XID-Field */
-static struct gprs_sndcp_comp *gprs_sndcp_comp_create(const void *ctx,
- const struct
- gprs_sndcp_comp_field
- *comp_field)
-{
- struct gprs_sndcp_comp *comp_entity;
- comp_entity = talloc_zero(ctx, struct gprs_sndcp_comp);
-
- /* Copy relevant information from the SNDCP-XID field */
- comp_entity->entity = comp_field->entity;
- comp_entity->comp_len = comp_field->comp_len;
- memcpy(comp_entity->comp, comp_field->comp, sizeof(comp_entity->comp));
-
- if (comp_field->rfc1144_params) {
- comp_entity->nsapi_len = comp_field->rfc1144_params->nsapi_len;
- memcpy(comp_entity->nsapi,
- comp_field->rfc1144_params->nsapi,
- sizeof(comp_entity->nsapi));
- } else if (comp_field->rfc2507_params) {
- comp_entity->nsapi_len = comp_field->rfc2507_params->nsapi_len;
- memcpy(comp_entity->nsapi,
- comp_field->rfc2507_params->nsapi,
- sizeof(comp_entity->nsapi));
- } else if (comp_field->rohc_params) {
- comp_entity->nsapi_len = comp_field->rohc_params->nsapi_len;
- memcpy(comp_entity->nsapi, comp_field->rohc_params->nsapi,
- sizeof(comp_entity->nsapi));
- } else if (comp_field->v42bis_params) {
- comp_entity->nsapi_len = comp_field->v42bis_params->nsapi_len;
- memcpy(comp_entity->nsapi,
- comp_field->v42bis_params->nsapi,
- sizeof(comp_entity->nsapi));
- } else if (comp_field->v44_params) {
- comp_entity->nsapi_len = comp_field->v44_params->nsapi_len;
- memcpy(comp_entity->nsapi,
- comp_field->v44_params->nsapi,
- sizeof(comp_entity->nsapi));
- } else {
- /* The caller is expected to check carefully if the all
- * data fields required for compression entity creation
- * are present. Otherwise we blow an assertion here */
- OSMO_ASSERT(false);
- }
- comp_entity->algo = comp_field->algo;
-
- /* Check if an NSAPI is selected, if not, it does not make sense
- * to create the compression entity, since the caller should
- * have checked the presence of the NSAPI, we blow an assertion
- * in case of missing NSAPIs */
- OSMO_ASSERT(comp_entity->nsapi_len > 0);
-
- /* Determine of which class our compression entity will be
- * (Protocol or Data compresson ?) */
- comp_entity->compclass = gprs_sndcp_get_compression_class(comp_field);
-
- OSMO_ASSERT(comp_entity->compclass != -1);
-
- /* Create an algorithm specific compression context */
- if (comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION) {
- if (gprs_sndcp_pcomp_init(ctx, comp_entity, comp_field) != 0) {
- talloc_free(comp_entity);
- comp_entity = NULL;
- }
- } else {
- if (gprs_sndcp_dcomp_init(ctx, comp_entity, comp_field) != 0) {
- talloc_free(comp_entity);
- comp_entity = NULL;
- }
- }
-
- /* Bail on failure */
- if (comp_entity == NULL) {
- LOGP(DSNDCP, LOGL_ERROR,
- "Compression entity creation failed!\n");
- return NULL;
- }
-
- /* Display info message */
- if (comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION) {
- LOGP(DSNDCP, LOGL_INFO,
- "New header compression entity (%d) created.\n",
- comp_entity->entity);
- } else {
- LOGP(DSNDCP, LOGL_INFO,
- "New data compression entity (%d) created.\n",
- comp_entity->entity);
- }
-
- return comp_entity;
-}
-
-/* Allocate a compression enitiy list */
-struct llist_head *gprs_sndcp_comp_alloc(const void *ctx)
-{
- struct llist_head *lh;
-
- lh = talloc_zero(ctx, struct llist_head);
- INIT_LLIST_HEAD(lh);
-
- return lh;
-}
-
-/* Free a compression entitiy list */
-void gprs_sndcp_comp_free(struct llist_head *comp_entities)
-{
- struct gprs_sndcp_comp *comp_entity;
-
- /* We expect the caller to take care of allocating a
- * compression entity list properly. Attempting to
- * free a non existing list clearly points out
- * a malfunction. */
- OSMO_ASSERT(comp_entities);
-
- llist_for_each_entry(comp_entity, comp_entities, list) {
- /* Free compression entity */
- if (comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION) {
- LOGP(DSNDCP, LOGL_INFO,
- "Deleting header compression entity %d ...\n",
- comp_entity->entity);
- gprs_sndcp_pcomp_term(comp_entity);
- } else {
- LOGP(DSNDCP, LOGL_INFO,
- "Deleting data compression entity %d ...\n",
- comp_entity->entity);
- gprs_sndcp_dcomp_term(comp_entity);
- }
- }
-
- talloc_free(comp_entities);
-}
-
-/* Delete a compression entity */
-void gprs_sndcp_comp_delete(struct llist_head *comp_entities,
- unsigned int entity)
-{
- struct gprs_sndcp_comp *comp_entity;
- struct gprs_sndcp_comp *comp_entity_to_delete = NULL;
-
- OSMO_ASSERT(comp_entities);
-
- llist_for_each_entry(comp_entity, comp_entities, list) {
- if (comp_entity->entity == entity) {
- comp_entity_to_delete = comp_entity;
- break;
- }
- }
-
- if (!comp_entity_to_delete)
- return;
-
- if (comp_entity_to_delete->compclass == SNDCP_XID_PROTOCOL_COMPRESSION) {
- LOGP(DSNDCP, LOGL_INFO,
- "Deleting header compression entity %d ...\n",
- comp_entity_to_delete->entity);
- gprs_sndcp_pcomp_term(comp_entity_to_delete);
- } else {
- LOGP(DSNDCP, LOGL_INFO,
- "Deleting data compression entity %d ...\n",
- comp_entity_to_delete->entity);
- }
-
- /* Delete compression entity */
- llist_del(&comp_entity_to_delete->list);
- talloc_free(comp_entity_to_delete);
-}
-
-/* Create and Add a new compression entity
- * (returns a pointer to the compression entity that has just been created) */
-struct gprs_sndcp_comp *gprs_sndcp_comp_add(const void *ctx,
- struct llist_head *comp_entities,
- const struct gprs_sndcp_comp_field
- *comp_field)
-{
- struct gprs_sndcp_comp *comp_entity;
-
- OSMO_ASSERT(comp_entities);
- OSMO_ASSERT(comp_field);
-
- /* Just to be sure, if the entity is already in
- * the list it will be deleted now */
- gprs_sndcp_comp_delete(comp_entities, comp_field->entity);
-
- /* Create and add a new entity to the list */
- comp_entity = gprs_sndcp_comp_create(ctx, comp_field);
-
- if (!comp_entity)
- return NULL;
-
- llist_add(&comp_entity->list, comp_entities);
- return comp_entity;
-}
-
-/* Find which compression entity handles the specified pcomp/dcomp */
-struct gprs_sndcp_comp *gprs_sndcp_comp_by_comp(const struct llist_head
- *comp_entities, uint8_t comp)
-{
- struct gprs_sndcp_comp *comp_entity;
- int i;
-
- OSMO_ASSERT(comp_entities);
-
- llist_for_each_entry(comp_entity, comp_entities, list) {
- for (i = 0; i < comp_entity->comp_len; i++) {
- if (comp_entity->comp[i] == comp)
- return comp_entity;
- }
- }
-
- LOGP(DSNDCP, LOGL_ERROR,
- "Could not find a matching compression entity for given pcomp/dcomp value %d.\n",
- comp);
- return NULL;
-}
-
-/* Find which compression entity handles the specified nsapi */
-struct gprs_sndcp_comp *gprs_sndcp_comp_by_nsapi(const struct llist_head
- *comp_entities, uint8_t nsapi)
-{
- struct gprs_sndcp_comp *comp_entity;
- int i;
-
- OSMO_ASSERT(comp_entities);
-
- llist_for_each_entry(comp_entity, comp_entities, list) {
- for (i = 0; i < comp_entity->nsapi_len; i++) {
- if (comp_entity->nsapi[i] == nsapi)
- return comp_entity;
- }
- }
-
- return NULL;
-}
-
-/* Find a comp_index for a given pcomp/dcomp value */
-uint8_t gprs_sndcp_comp_get_idx(const struct gprs_sndcp_comp *comp_entity,
- uint8_t comp)
-{
- /* Note: This function returns a normalized version of the comp value,
- * which matches up with the position of the comp field. Since comp=0
- * is reserved for "no compression", the index value starts counting
- * at one. The return value is the PCOMPn/DCOMPn value one can find
- * in the Specification (see e.g. 3GPP TS 44.065, 6.5.3.2, Table 7) */
-
- int i;
- OSMO_ASSERT(comp_entity);
-
- /* A pcomp/dcomp value of zero is reserved for "no comproession",
- * So we just bail and return zero in this case */
- if (comp == 0)
- return 0;
-
- /* Look in the pcomp/dcomp list for the index */
- for (i = 0; i < comp_entity->comp_len; i++) {
- if (comp_entity->comp[i] == comp)
- return i + 1;
- }
-
- LOGP(DSNDCP, LOGL_ERROR,
- "Could not find a matching comp_index for given pcomp/dcomp value %d\n",
- comp);
- return 0;
-}
-
-/* Find a pcomp/dcomp value for a given comp_index */
-uint8_t gprs_sndcp_comp_get_comp(const struct gprs_sndcp_comp *comp_entity,
- uint8_t comp_index)
-{
- OSMO_ASSERT(comp_entity);
-
- /* A comp_index of zero translates to zero right away. */
- if (comp_index == 0)
- return 0;
-
- if (comp_index > comp_entity->comp_len) {
- LOGP(DSNDCP, LOGL_ERROR,
- "Could not find a matching pcomp/dcomp value for given comp_index value %d.\n",
- comp_index);
- return 0;
- }
-
- /* Look in the pcomp/dcomp list for the comp_index, see
- * note in gprs_sndcp_comp_get_idx() */
- return comp_entity->comp[comp_index - 1];
-}
diff --git a/src/gprs/gprs_sndcp_dcomp.c b/src/gprs/gprs_sndcp_dcomp.c
deleted file mode 100644
index b0f95b486..000000000
--- a/src/gprs/gprs_sndcp_dcomp.c
+++ /dev/null
@@ -1,358 +0,0 @@
-/* GPRS SNDCP data compression handler */
-
-/* (C) 2016 by Sysmocom s.f.m.c. GmbH
- * All Rights Reserved
- *
- * Author: Philipp Maier
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <stdio.h>
-#include <string.h>
-#include <stdint.h>
-#include <math.h>
-#include <errno.h>
-#include <stdbool.h>
-
-#include <osmocom/core/utils.h>
-#include <osmocom/core/msgb.h>
-#include <osmocom/core/linuxlist.h>
-#include <osmocom/core/talloc.h>
-#include <osmocom/gsm/tlv.h>
-
-#include <openbsc/gprs_llc.h>
-#include <openbsc/sgsn.h>
-#include <openbsc/gprs_sndcp_xid.h>
-#include <openbsc/v42bis.h>
-#include <openbsc/v42bis_private.h>
-#include <openbsc/debug.h>
-#include <openbsc/gprs_sndcp_comp.h>
-#include <openbsc/gprs_sndcp_dcomp.h>
-
-/* A struct to capture the output data of compressor and decompressor */
-struct v42bis_output_buffer {
- uint8_t *buf;
- uint8_t *buf_pointer;
- int len;
-};
-
-/* Handler to capture the output data from the compressor */
-void tx_v42bis_frame_handler(void *user_data, const uint8_t *pkt, int len)
-{
- struct v42bis_output_buffer *output_buffer =
- (struct v42bis_output_buffer *)user_data;
- memcpy(output_buffer->buf_pointer, pkt, len);
- output_buffer->buf_pointer += len;
- output_buffer->len += len;
- return;
-}
-
-/* Handler to capture the output data from the decompressor */
-void rx_v42bis_data_handler(void *user_data, const uint8_t *buf, int len)
-{
- struct v42bis_output_buffer *output_buffer =
- (struct v42bis_output_buffer *)user_data;
- memcpy(output_buffer->buf_pointer, buf, len);
- output_buffer->buf_pointer += len;
- output_buffer->len += len;
- return;
-}
-
-/* Initalize data compression */
-int gprs_sndcp_dcomp_init(const void *ctx, struct gprs_sndcp_comp *comp_entity,
- const struct gprs_sndcp_comp_field *comp_field)
-{
- /* Note: This function is automatically called from
- * gprs_sndcp_comp.c when a new data compression
- * entity is created by gprs_sndcp.c */
-
- OSMO_ASSERT(comp_entity);
- OSMO_ASSERT(comp_field);
-
- if (comp_entity->compclass == SNDCP_XID_DATA_COMPRESSION
- && comp_entity->algo == V42BIS) {
- OSMO_ASSERT(comp_field->v42bis_params);
- comp_entity->state =
- v42bis_init(ctx, NULL, comp_field->v42bis_params->p0,
- comp_field->v42bis_params->p1,
- comp_field->v42bis_params->p2,
- &tx_v42bis_frame_handler, NULL,
- V42BIS_MAX_OUTPUT_LENGTH,
- &rx_v42bis_data_handler, NULL,
- V42BIS_MAX_OUTPUT_LENGTH);
- LOGP(DSNDCP, LOGL_INFO,
- "V.42bis data compression initalized.\n");
- return 0;
- }
-
- /* Just in case someone tries to initalize an unknown or unsupported
- * data compresson. Since everything is checked during the SNDCP
- * negotiation process, this should never happen! */
- OSMO_ASSERT(false);
-}
-
-/* Terminate data compression */
-void gprs_sndcp_dcomp_term(struct gprs_sndcp_comp *comp_entity)
-{
- /* Note: This function is automatically called from
- * gprs_sndcp_comp.c when a data compression
- * entity is deleted by gprs_sndcp.c */
-
- OSMO_ASSERT(comp_entity);
-
- if (comp_entity->compclass == SNDCP_XID_DATA_COMPRESSION
- && comp_entity->algo == V42BIS) {
- if (comp_entity->state) {
- v42bis_free((v42bis_state_t *) comp_entity->state);
- comp_entity->state = NULL;
- }
- LOGP(DSNDCP, LOGL_INFO,
- "V.42bis data compression terminated.\n");
- return;
- }
-
- /* Just in case someone tries to terminate an unknown or unsupported
- * data compresson. Since everything is checked during the SNDCP
- * negotiation process, this should never happen! */
- OSMO_ASSERT(false);
-}
-
-/* Perform a full reset of the V.42bis compression state */
-static void v42bis_reset(v42bis_state_t *comp)
-{
- /* This function performs a complete reset of the V.42bis compression
- * state by reinitalizing the state withe the previously negotiated
- * parameters. */
-
- int p0, p1, p2;
- p0 = comp->decompress.v42bis_parm_p0 | comp->compress.v42bis_parm_p0;
- p1 = comp->decompress.v42bis_parm_n2;
- p2 = comp->decompress.v42bis_parm_n7;
-
- DEBUGP(DSNDCP, "Resetting compression state: %p, p0=%d, p1=%d, p2=%d\n",
- comp, p0, p1, p2);
-
- v42bis_init(NULL, comp, p0, p1, p2, &tx_v42bis_frame_handler, NULL,
- V42BIS_MAX_OUTPUT_LENGTH, &rx_v42bis_data_handler, NULL,
- V42BIS_MAX_OUTPUT_LENGTH);
-}
-
-/* Compress a packet using V.42bis data compression */
-static int v42bis_compress_unitdata(uint8_t *pcomp_index, uint8_t *data,
- unsigned int len, v42bis_state_t *comp)
-{
- /* Note: This implementation may only be used to compress SN_UNITDATA
- * packets, since it resets the compression state for each NPDU. */
-
- uint8_t *data_o;
- int rc;
- int skip = 0;
- struct v42bis_output_buffer compressed_data;
-
- /* Don't bother with short packets */
- if (len < MIN_COMPR_PAYLOAD)
- skip = 1;
-
- /* Skip if compression is not enabled for TX direction */
- if (!comp->compress.v42bis_parm_p0)
- skip = 1;
-
- /* Skip compression */
- if (skip) {
- *pcomp_index = 0;
- return len;
- }
-
- /* Reset V.42bis compression state */
- v42bis_reset(comp);
-
- /* Run compressor */
- data_o = talloc_zero_size(comp, len * MAX_DATADECOMPR_FAC);
- compressed_data.buf = data_o;
- compressed_data.buf_pointer = data_o;
- compressed_data.len = 0;
- comp->compress.user_data = (&compressed_data);
- rc = v42bis_compress(comp, data, len);
- if (rc < 0) {
- LOGP(DSNDCP, LOGL_ERROR,
- "Data compression failed, skipping...\n");
- skip = 1;
- }
- rc = v42bis_compress_flush(comp);
- if (rc < 0) {
- LOGP(DSNDCP, LOGL_ERROR,
- "Data compression failed, skipping...\n");
- skip = 1;
- }
-
- /* The compressor might yield negative compression gain, in
- * this case, we just decide to send the packat as normal,
- * uncompressed payload => skip compresssion */
- if (compressed_data.len >= len) {
- LOGP(DSNDCP, LOGL_ERROR,
- "Data compression ineffective, skipping...\n");
- skip = 1;
- }
-
- /* Skip compression */
- if (skip) {
- *pcomp_index = 0;
- talloc_free(data_o);
- return len;
- }
-
- *pcomp_index = 1;
- memcpy(data, data_o, compressed_data.len);
- talloc_free(data_o);
-
- return compressed_data.len;
-}
-
-/* Expand a packet using V.42bis data compression */
-static int v42bis_expand_unitdata(uint8_t *data, unsigned int len,
- uint8_t pcomp_index, v42bis_state_t *comp)
-{
- /* Note: This implementation may only be used to compress SN_UNITDATA
- * packets, since it resets the compression state for each NPDU. */
-
- int rc;
- struct v42bis_output_buffer uncompressed_data;
- uint8_t *data_i;
-
- /* Skip when the packet is marked as uncompressed */
- if (pcomp_index == 0) {
- return len;
- }
-
- /* Reset V.42bis compression state */
- v42bis_reset(comp);
-
- /* Decompress packet */
- data_i = talloc_zero_size(comp, len);
- memcpy(data_i, data, len);
- uncompressed_data.buf = data;
- uncompressed_data.buf_pointer = data;
- uncompressed_data.len = 0;
- comp->decompress.user_data = (&uncompressed_data);
- rc = v42bis_decompress(comp, data_i, len);
- talloc_free(data_i);
- if (rc < 0)
- return -EINVAL;
- rc = v42bis_decompress_flush(comp);
- if (rc < 0)
- return -EINVAL;
-
- return uncompressed_data.len;
-}
-
-/* Expand packet */
-int gprs_sndcp_dcomp_expand(uint8_t *data, unsigned int len, uint8_t pcomp,
- const struct llist_head *comp_entities)
-{
- int rc;
- uint8_t pcomp_index = 0;
- struct gprs_sndcp_comp *comp_entity;
-
- OSMO_ASSERT(data);
- OSMO_ASSERT(comp_entities);
-
- LOGP(DSNDCP, LOGL_DEBUG,
- "Data compression entity list: comp_entities=%p\n", comp_entities);
-
- LOGP(DSNDCP, LOGL_DEBUG, "Data compression mode: dcomp=%d\n", pcomp);
-
- /* Skip on pcomp=0 */
- if (pcomp == 0) {
- return len;
- }
-
- /* Find out which compression entity handles the data */
- comp_entity = gprs_sndcp_comp_by_comp(comp_entities, pcomp);
-
- /* Skip compression if no suitable compression entity can be found */
- if (!comp_entity) {
- return len;
- }
-
- /* Note: Only data compression entities may appear in
- * data compression context */
- OSMO_ASSERT(comp_entity->compclass == SNDCP_XID_DATA_COMPRESSION);
-
- /* Note: Currently V42BIS is the only compression method we
- * support, so the only allowed algorithm is V42BIS */
- OSMO_ASSERT(comp_entity->algo == V42BIS);
-
- /* Find pcomp_index */
- pcomp_index = gprs_sndcp_comp_get_idx(comp_entity, pcomp);
-
- /* Run decompression algo */
- rc = v42bis_expand_unitdata(data, len, pcomp_index, comp_entity->state);
-
- LOGP(DSNDCP, LOGL_DEBUG,
- "Data expansion done, old length=%d, new length=%d, entity=%p\n",
- len, rc, comp_entity);
-
- return rc;
-}
-
-/* Compress packet */
-int gprs_sndcp_dcomp_compress(uint8_t *data, unsigned int len, uint8_t *pcomp,
- const struct llist_head *comp_entities,
- uint8_t nsapi)
-{
- int rc;
- uint8_t pcomp_index = 0;
- struct gprs_sndcp_comp *comp_entity;
-
- OSMO_ASSERT(data);
- OSMO_ASSERT(pcomp);
- OSMO_ASSERT(comp_entities);
-
- LOGP(DSNDCP, LOGL_DEBUG,
- "Data compression entity list: comp_entities=%p\n", comp_entities);
-
- /* Find out which compression entity handles the data */
- comp_entity = gprs_sndcp_comp_by_nsapi(comp_entities, nsapi);
-
- /* Skip compression if no suitable compression entity can be found */
- if (!comp_entity) {
- *pcomp = 0;
- return len;
- }
-
- /* Note: Only data compression entities may appear in
- * data compression context */
- OSMO_ASSERT(comp_entity->compclass == SNDCP_XID_DATA_COMPRESSION);
-
- /* Note: Currently V42BIS is the only compression method we
- * support, so the only allowed algorithm is V42BIS */
- OSMO_ASSERT(comp_entity->algo == V42BIS);
-
- /* Run compression algo */
- rc = v42bis_compress_unitdata(&pcomp_index, data, len,
- comp_entity->state);
-
- /* Find pcomp value */
- *pcomp = gprs_sndcp_comp_get_comp(comp_entity, pcomp_index);
-
- LOGP(DSNDCP, LOGL_DEBUG, "Data compression mode: dcomp=%d\n", *pcomp);
-
- LOGP(DSNDCP, LOGL_DEBUG,
- "Data compression done, old length=%d, new length=%d, entity=%p\n",
- len, rc, comp_entity);
-
- return rc;
-}
diff --git a/src/gprs/gprs_sndcp_pcomp.c b/src/gprs/gprs_sndcp_pcomp.c
deleted file mode 100644
index a2236c3b1..000000000
--- a/src/gprs/gprs_sndcp_pcomp.c
+++ /dev/null
@@ -1,282 +0,0 @@
-/* GPRS SNDCP header compression handler */
-
-/* (C) 2016 by Sysmocom s.f.m.c. GmbH
- * All Rights Reserved
- *
- * Author: Philipp Maier
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <stdio.h>
-#include <string.h>
-#include <stdint.h>
-#include <math.h>
-#include <errno.h>
-#include <stdbool.h>
-
-#include <osmocom/core/utils.h>
-#include <osmocom/core/msgb.h>
-#include <osmocom/core/linuxlist.h>
-#include <osmocom/core/talloc.h>
-#include <osmocom/gsm/tlv.h>
-
-#include <openbsc/gprs_llc.h>
-#include <openbsc/sgsn.h>
-#include <openbsc/gprs_sndcp_xid.h>
-#include <openbsc/slhc.h>
-#include <openbsc/debug.h>
-#include <openbsc/gprs_sndcp_comp.h>
-#include <openbsc/gprs_sndcp_pcomp.h>
-
-/* Initalize header compression */
-int gprs_sndcp_pcomp_init(const void *ctx, struct gprs_sndcp_comp *comp_entity,
- const struct gprs_sndcp_comp_field *comp_field)
-{
- /* Note: This function is automatically called from
- * gprs_sndcp_comp.c when a new header compression
- * entity is created by gprs_sndcp.c */
-
- OSMO_ASSERT(comp_entity);
- OSMO_ASSERT(comp_field);
-
- if (comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION
- && comp_entity->algo == RFC_1144) {
- OSMO_ASSERT(comp_field->rfc1144_params);
- comp_entity->state =
- slhc_init(ctx, comp_field->rfc1144_params->s01 + 1,
- comp_field->rfc1144_params->s01 + 1);
- LOGP(DSNDCP, LOGL_INFO,
- "RFC1144 header compression initalized.\n");
- return 0;
- }
-
- /* Just in case someone tries to initalize an unknown or unsupported
- * header compresson. Since everything is checked during the SNDCP
- * negotiation process, this should never happen! */
- OSMO_ASSERT(false);
-}
-
-/* Terminate header compression */
-void gprs_sndcp_pcomp_term(struct gprs_sndcp_comp *comp_entity)
-{
- /* Note: This function is automatically called from
- * gprs_sndcp_comp.c when a header compression
- * entity is deleted by gprs_sndcp.c */
-
- OSMO_ASSERT(comp_entity);
-
- if (comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION
- && comp_entity->algo == RFC_1144) {
- if (comp_entity->state) {
- slhc_free((struct slcompress *)comp_entity->state);
- comp_entity->state = NULL;
- }
- LOGP(DSNDCP, LOGL_INFO,
- "RFC1144 header compression terminated.\n");
- return;
- }
-
- /* Just in case someone tries to terminate an unknown or unsupported
- * data compresson. Since everything is checked during the SNDCP
- * negotiation process, this should never happen! */
- OSMO_ASSERT(false);
-}
-
-/* Compress a packet using Van Jacobson RFC1144 header compression */
-static int rfc1144_compress(uint8_t *pcomp_index, uint8_t *data,
- unsigned int len, struct slcompress *comp)
-{
- uint8_t *comp_ptr;
- int compr_len;
- uint8_t *data_o;
-
- /* Create a working copy of the incoming data */
- data_o = talloc_zero_size(comp, len);
- memcpy(data_o, data, len);
-
- /* Run compressor */
- compr_len = slhc_compress(comp, data, len, data_o, &comp_ptr, 0);
-
- /* Generate pcomp_index */
- if (data_o[0] & SL_TYPE_COMPRESSED_TCP) {
- *pcomp_index = 2;
- data_o[0] &= ~SL_TYPE_COMPRESSED_TCP;
- memcpy(data, data_o, compr_len);
- } else if ((data_o[0] & SL_TYPE_UNCOMPRESSED_TCP) ==
- SL_TYPE_UNCOMPRESSED_TCP) {
- *pcomp_index = 1;
- data_o[0] &= 0x4F;
- memcpy(data, data_o, compr_len);
- } else
- *pcomp_index = 0;
-
- talloc_free(data_o);
- return compr_len;
-}
-
-/* Expand a packet using Van Jacobson RFC1144 header compression */
-static int rfc1144_expand(uint8_t *data, unsigned int len, uint8_t pcomp_index,
- struct slcompress *comp)
-{
- int data_decompressed_len;
- int type;
-
- /* Note: this function should never be called with pcomp_index=0,
- * since this condition is already filtered
- * out by gprs_sndcp_pcomp_expand() */
-
- /* Determine the data type by the PCOMP index */
- switch (pcomp_index) {
- case 0:
- type = SL_TYPE_IP;
- break;
- case 1:
- type = SL_TYPE_UNCOMPRESSED_TCP;
- break;
- case 2:
- type = SL_TYPE_COMPRESSED_TCP;
- break;
- default:
- LOGP(DSNDCP, LOGL_ERROR,
- "rfc1144_expand() Invalid pcomp_index value (%d) detected, assuming no compression!\n",
- pcomp_index);
- type = SL_TYPE_IP;
- break;
- }
-
- /* Restore the original version nibble on
- * marked uncompressed packets */
- if (type == SL_TYPE_UNCOMPRESSED_TCP) {
- /* Just in case the phone tags uncompressed tcp-data
- * (normally this is handled by pcomp so there is
- * no need for tagging the data) */
- data[0] &= 0x4F;
- data_decompressed_len = slhc_remember(comp, data, len);
- return data_decompressed_len;
- }
-
- /* Uncompress compressed packets */
- else if (type == SL_TYPE_COMPRESSED_TCP) {
- data_decompressed_len = slhc_uncompress(comp, data, len);
- return data_decompressed_len;
- }
-
- /* Regular or unknown packets will not be touched */
- return len;
-}
-
-/* Expand packet header */
-int gprs_sndcp_pcomp_expand(uint8_t *data, unsigned int len, uint8_t pcomp,
- const struct llist_head *comp_entities)
-{
- int rc;
- uint8_t pcomp_index = 0;
- struct gprs_sndcp_comp *comp_entity;
-
- OSMO_ASSERT(data);
- OSMO_ASSERT(comp_entities);
-
- LOGP(DSNDCP, LOGL_DEBUG,
- "Header compression entity list: comp_entities=%p\n",
- comp_entities);
-
- LOGP(DSNDCP, LOGL_DEBUG, "Header compression mode: pcomp=%d\n", pcomp);
-
- /* Skip on pcomp=0 */
- if (pcomp == 0) {
- return len;
- }
-
- /* Find out which compression entity handles the data */
- comp_entity = gprs_sndcp_comp_by_comp(comp_entities, pcomp);
-
- /* Skip compression if no suitable compression entity can be found */
- if (!comp_entity) {
- return len;
- }
-
- /* Note: Only protocol compression entities may appear in
- * protocol compression context */
- OSMO_ASSERT(comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION);
-
- /* Note: Currently RFC1144 is the only compression method we
- * support, so the only allowed algorithm is RFC1144 */
- OSMO_ASSERT(comp_entity->algo == RFC_1144);
-
- /* Find pcomp_index */
- pcomp_index = gprs_sndcp_comp_get_idx(comp_entity, pcomp);
-
- /* Run decompression algo */
- rc = rfc1144_expand(data, len, pcomp_index, comp_entity->state);
- slhc_i_status(comp_entity->state);
- slhc_o_status(comp_entity->state);
-
- LOGP(DSNDCP, LOGL_DEBUG,
- "Header expansion done, old length=%d, new length=%d, entity=%p\n",
- len, rc, comp_entity);
-
- return rc;
-}
-
-/* Compress packet header */
-int gprs_sndcp_pcomp_compress(uint8_t *data, unsigned int len, uint8_t *pcomp,
- const struct llist_head *comp_entities,
- uint8_t nsapi)
-{
- int rc;
- uint8_t pcomp_index = 0;
- struct gprs_sndcp_comp *comp_entity;
-
- OSMO_ASSERT(data);
- OSMO_ASSERT(pcomp);
- OSMO_ASSERT(comp_entities);
-
- LOGP(DSNDCP, LOGL_DEBUG,
- "Header compression entity list: comp_entities=%p\n",
- comp_entities);
-
- /* Find out which compression entity handles the data */
- comp_entity = gprs_sndcp_comp_by_nsapi(comp_entities, nsapi);
-
- /* Skip compression if no suitable compression entity can be found */
- if (!comp_entity) {
- *pcomp = 0;
- return len;
- }
-
- /* Note: Only protocol compression entities may appear in
- * protocol compression context */
- OSMO_ASSERT(comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION);
-
- /* Note: Currently RFC1144 is the only compression method we
- * support, so the only allowed algorithm is RFC1144 */
- OSMO_ASSERT(comp_entity->algo == RFC_1144);
-
- /* Run compression algo */
- rc = rfc1144_compress(&pcomp_index, data, len, comp_entity->state);
- slhc_i_status(comp_entity->state);
- slhc_o_status(comp_entity->state);
-
- /* Find pcomp value */
- *pcomp = gprs_sndcp_comp_get_comp(comp_entity, pcomp_index);
-
- LOGP(DSNDCP, LOGL_DEBUG, "Header compression mode: pcomp=%d\n", *pcomp);
-
- LOGP(DSNDCP, LOGL_DEBUG,
- "Header compression done, old length=%d, new length=%d, entity=%p\n",
- len, rc, comp_entity);
- return rc;
-}
diff --git a/src/gprs/gprs_sndcp_vty.c b/src/gprs/gprs_sndcp_vty.c
deleted file mode 100644
index 430881fc8..000000000
--- a/src/gprs/gprs_sndcp_vty.c
+++ /dev/null
@@ -1,71 +0,0 @@
-/* VTY interface for our GPRS SNDCP implementation */
-
-/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <stdlib.h>
-#include <unistd.h>
-#include <errno.h>
-#include <stdint.h>
-
-#include <arpa/inet.h>
-
-#include <openbsc/gsm_data.h>
-#include <osmocom/core/msgb.h>
-#include <osmocom/gsm/tlv.h>
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/select.h>
-#include <osmocom/core/rate_ctr.h>
-#include <openbsc/debug.h>
-#include <openbsc/signal.h>
-#include <openbsc/gprs_llc.h>
-#include <openbsc/gprs_sndcp.h>
-
-#include <osmocom/vty/vty.h>
-#include <osmocom/vty/command.h>
-
-static void vty_dump_sne(struct vty *vty, struct gprs_sndcp_entity *sne)
-{
- vty_out(vty, " TLLI %08x SAPI=%u NSAPI=%u:%s",
- sne->lle->llme->tlli, sne->lle->sapi, sne->nsapi, VTY_NEWLINE);
- vty_out(vty, " Defrag: npdu=%u highest_seg=%u seg_have=0x%08x tot_len=%u%s",
- sne->defrag.npdu, sne->defrag.highest_seg, sne->defrag.seg_have,
- sne->defrag.tot_len, VTY_NEWLINE);
-}
-
-
-DEFUN(show_sndcp, show_sndcp_cmd,
- "show sndcp",
- SHOW_STR "Display information about the SNDCP protocol")
-{
- struct gprs_sndcp_entity *sne;
-
- vty_out(vty, "State of SNDCP Entities%s", VTY_NEWLINE);
- llist_for_each_entry(sne, &gprs_sndcp_entities, list)
- vty_dump_sne(vty, sne);
-
- return CMD_SUCCESS;
-}
-
-int gprs_sndcp_vty_init(void)
-{
- install_element_ve(&show_sndcp_cmd);
-
- return 0;
-}
diff --git a/src/gprs/gprs_sndcp_xid.c b/src/gprs/gprs_sndcp_xid.c
deleted file mode 100644
index dfea5febc..000000000
--- a/src/gprs/gprs_sndcp_xid.c
+++ /dev/null
@@ -1,1822 +0,0 @@
-/* GPRS SNDCP XID field encoding/decoding as per 3GPP TS 44.065 */
-
-/* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
- * All Rights Reserved
- *
- * Author: Philipp Maier
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <stdio.h>
-#include <string.h>
-#include <stdint.h>
-#include <math.h>
-#include <errno.h>
-
-#include <osmocom/core/utils.h>
-#include <osmocom/core/msgb.h>
-#include <osmocom/core/linuxlist.h>
-#include <osmocom/core/talloc.h>
-#include <osmocom/gsm/tlv.h>
-
-#include <openbsc/debug.h>
-#include <openbsc/gprs_llc.h>
-#include <openbsc/sgsn.h>
-#include <openbsc/gprs_sndcp_xid.h>
-
-/* When the propose bit in an SNDCP-XID compression field is set to zero,
- * the algorithm identifier is stripped. The algoritm parameters are specific
- * for each algorithms. The following struct is used to pass the information
- * about the referenced algorithm to the parser. */
-struct entity_algo_table {
- unsigned int entity; /* see also: 6.5.1.1.3 and 6.6.1.1.3 */
- unsigned int algo; /* see also: 6.5.1.1.4 and 6.6.1.1.4 */
- unsigned int compclass; /* Can be either SNDCP_XID_DATA_COMPRESSION or
- SNDCP_XID_PROTOCOL_COMPRESSION */
-};
-
-/* FUNCTIONS RELATED TO SNDCP-XID ENCODING */
-
-/* Encode applicable sapis (works the same in all three compression schemes) */
-static int encode_pcomp_applicable_sapis(uint8_t *dst,
- const uint8_t *nsapis,
- uint8_t nsapis_len)
-{
- /* NOTE: Buffer *dst needs offer at 2 bytes
- * of space to store the generation results */
-
- uint16_t blob;
- uint8_t nsapi;
- int i;
-
- /* Bail if number of possible nsapis exceeds valid range
- * (Only 11 nsapis possible for PDP-Contexts) */
- OSMO_ASSERT(nsapis_len <= 11);
-
- /* Encode applicable SAPIs */
- blob = 0;
- for (i = 0; i < nsapis_len; i++) {
- nsapi = nsapis[i];
- /* Only NSAPI 5 to 15 are applicable for user traffic (PDP-
- * contexts). Only for these NSAPIs SNDCP-XID parameters
- * can apply. See also 3GPP TS 44.065, 5.1 Service primitives */
- OSMO_ASSERT(nsapi >= 5 && nsapi <= 15);
- blob |= (1 << nsapi);
- }
-
- /* Store result */
- *dst = (blob >> 8) & 0xFF;
- dst++;
- *dst = blob & 0xFF;
-
- return 2;
-}
-
-/* Encode rfc1144 parameter field
- * (see also: 3GPP TS 44.065, 6.5.2.1, Table 5) */
-static int encode_pcomp_rfc1144_params(uint8_t *dst, unsigned int dst_maxlen,
- const struct
- gprs_sndcp_pcomp_rfc1144_params *params)
-{
- /* NOTE: Buffer *dst should offer at least 3 bytes
- * of space to store the generation results */
-
- int dst_counter = 0;
- int rc;
-
- OSMO_ASSERT(dst_maxlen >= 3);
-
- /* Zero out buffer */
- memset(dst, 0, dst_maxlen);
-
- /* Encode applicable SAPIs */
- rc = encode_pcomp_applicable_sapis(dst, params->nsapi,
- params->nsapi_len);
- dst += rc;
- dst_counter += rc;
-
- /* Encode s01 (see also: 3GPP TS 44.065, 6.5.2.1, Table 5) */
- OSMO_ASSERT(params->s01 >= 0);
- OSMO_ASSERT(params->s01 <= 255);
- *dst = params->s01;
- dst++;
- dst_counter++;
-
- /* Return generated length */
- return dst_counter;
-}
-
-/*
- * Encode rfc2507 parameter field
- * (see also: 3GPP TS 44.065, 6.5.3.1, Table 6)
- */
-static int encode_pcomp_rfc2507_params(uint8_t *dst, unsigned int dst_maxlen,
- const struct
- gprs_sndcp_pcomp_rfc2507_params *params)
-{
- /* NOTE: Buffer *dst should offer at least 3 bytes
- * of space to store the generation results */
-
- int dst_counter = 0;
- int rc;
-
- OSMO_ASSERT(dst_maxlen >= 9);
-
- /* Zero out buffer */
- memset(dst, 0, dst_maxlen);
-
- /* Encode applicable SAPIs */
- rc = encode_pcomp_applicable_sapis(dst, params->nsapi,
- params->nsapi_len);
- dst += rc;
- dst_counter += rc;
-
- /* Encode F_MAX_PERIOD (see also: 3GPP TS 44.065, 6.5.3.1, Table 6) */
- OSMO_ASSERT(params->f_max_period >= 1);
- OSMO_ASSERT(params->f_max_period <= 65535);
- *dst = (params->f_max_period >> 8) & 0xFF;
- dst++;
- dst_counter++;
- *dst = (params->f_max_period) & 0xFF;
- dst++;
- dst_counter++;
-
- /* Encode F_MAX_TIME (see also: 3GPP TS 44.065, 6.5.3.1, Table 6) */
- OSMO_ASSERT(params->f_max_time >= 1);
- OSMO_ASSERT(params->f_max_time <= 255);
- *dst = params->f_max_time;
- dst++;
- dst_counter++;
-
- /* Encode MAX_HEADER (see also: 3GPP TS 44.065, 6.5.3.1, Table 6) */
- OSMO_ASSERT(params->max_header >= 60);
- OSMO_ASSERT(params->max_header <= 255);
- *dst = params->max_header;
- dst++;
- dst_counter++;
-
- /* Encode TCP_SPACE (see also: 3GPP TS 44.065, 6.5.3.1, Table 6) */
- OSMO_ASSERT(params->tcp_space >= 3);
- OSMO_ASSERT(params->tcp_space <= 255);
- *dst = params->tcp_space;
- dst++;
- dst_counter++;
-
- /* Encode NON_TCP_SPACE (see also: 3GPP TS 44.065, 6.5.3.1, Table 6) */
- OSMO_ASSERT(params->non_tcp_space >= 3);
- OSMO_ASSERT(params->non_tcp_space <= 65535);
- *dst = (params->non_tcp_space >> 8) & 0xFF;
- dst++;
- dst_counter++;
- *dst = (params->non_tcp_space) & 0xFF;
- dst++;
- dst_counter++;
-
- /* Return generated length */
- return dst_counter;
-}
-
-/* Encode ROHC parameter field
- * (see also: 3GPP TS 44.065, 6.5.4.1, Table 10) */
-static int encode_pcomp_rohc_params(uint8_t *dst, unsigned int dst_maxlen,
- const struct gprs_sndcp_pcomp_rohc_params
- *params)
-{
- /* NOTE: Buffer *dst should offer at least 36
- * (2 * 16 Profiles + 2 * 3 Parameter) bytes
- * of memory space to store generation results */
-
- int i;
- int dst_counter = 0;
- int rc;
-
- OSMO_ASSERT(dst_maxlen >= 38);
-
- /* Bail if number of ROHC profiles exceeds limit
- * (ROHC supports only a maximum of 16 different profiles) */
- OSMO_ASSERT(params->profile_len <= 16);
-
- /* Zero out buffer */
- memset(dst, 0, dst_maxlen);
-
- /* Encode applicable SAPIs */
- rc = encode_pcomp_applicable_sapis(dst, params->nsapi,
- params->nsapi_len);
- dst += rc;
- dst_counter += rc;
-
- /* Encode MAX_CID (see also: 3GPP TS 44.065, 6.5.4.1, Table 10) */
- OSMO_ASSERT(params->max_cid >= 0);
- OSMO_ASSERT(params->max_cid <= 16383);
- *dst = (params->max_cid >> 8) & 0xFF;
- dst++;
- *dst = params->max_cid & 0xFF;
- dst++;
- dst_counter += 2;
-
- /* Encode MAX_HEADER (see also: 3GPP TS 44.065, 6.5.4.1, Table 10) */
- OSMO_ASSERT(params->max_header >= 60);
- OSMO_ASSERT(params->max_header <= 255);
- *dst = (params->max_header >> 8) & 0xFF;
- dst++;
- *dst = params->max_header & 0xFF;
- dst++;
- dst_counter += 2;
-
- /* Encode ROHC Profiles (see also: 3GPP TS 44.065, 6.5.4.1, Table 10) */
- for (i = 0; i < params->profile_len; i++) {
- *dst = (params->profile[i] >> 8) & 0xFF;
- dst++;
- *dst = params->profile[i] & 0xFF;
- dst++;
- dst_counter += 2;
- }
-
- /* Return generated length */
- return dst_counter;
-}
-
-/* Encode V.42bis parameter field
- * (see also: 3GPP TS 44.065, 6.6.2.1, Table 7a) */
-static int encode_dcomp_v42bis_params(uint8_t *dst, unsigned int dst_maxlen,
- const struct
- gprs_sndcp_dcomp_v42bis_params *params)
-{
- /* NOTE: Buffer *dst should offer at least 6 bytes
- * of space to store the generation results */
-
- int dst_counter = 0;
- int rc;
-
- OSMO_ASSERT(dst_maxlen >= 6);
-
- /* Zero out buffer */
- memset(dst, 0, dst_maxlen);
-
- /* Encode applicable SAPIs */
- rc = encode_pcomp_applicable_sapis(dst, params->nsapi,
- params->nsapi_len);
- dst += rc;
- dst_counter += rc;
-
- /* Encode P0 (see also: 3GPP TS 44.065, 6.6.2.1, Table 7a) */
- OSMO_ASSERT(params->p0 >= 0);
- OSMO_ASSERT(params->p0 <= 3);
- *dst = params->p0 & 0x03;
- dst++;
- dst_counter++;
-
- /* Encode P1 (see also: 3GPP TS 44.065, 6.6.2.1, Table 7a) */
- OSMO_ASSERT(params->p1 >= 512);
- OSMO_ASSERT(params->p1 <= 65535);
- *dst = (params->p1 >> 8) & 0xFF;
- dst++;
- *dst = params->p1 & 0xFF;
- dst++;
- dst_counter += 2;
-
- /* Encode P2 (see also: 3GPP TS 44.065, 6.6.2.1, Table 7a) */
- OSMO_ASSERT(params->p2 >= 6);
- OSMO_ASSERT(params->p2 <= 250);
- *dst = params->p2;
- dst++;
- dst_counter++;
-
- /* Return generated length */
- return dst_counter;
-}
-
-/* Encode V44 parameter field
- * (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */
-static int encode_dcomp_v44_params(uint8_t *dst, unsigned int dst_maxlen,
- const struct gprs_sndcp_dcomp_v44_params
- *params)
-{
- /* NOTE: Buffer *dst should offer at least 12 bytes
- * of space to store the generation results */
-
- int dst_counter = 0;
- int rc;
-
- OSMO_ASSERT(dst_maxlen >= 12);
-
- /* Zero out buffer */
- memset(dst, 0, dst_maxlen);
-
- /* Encode applicable SAPIs */
- rc = encode_pcomp_applicable_sapis(dst, params->nsapi,
- params->nsapi_len);
- dst += rc;
- dst_counter += rc;
-
- /* Encode C0 (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */
- OSMO_ASSERT(params->c0 == 0x80 || params->c0 == 0xC0);
- *dst = params->c0 & 0xC0;
- dst++;
- dst_counter++;
-
- /* Encode P0 (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */
- OSMO_ASSERT(params->p0 >= 0);
- OSMO_ASSERT(params->p0 <= 3);
- *dst = params->p0 & 0x03;
- dst++;
- dst_counter++;
-
- /* Encode P1T (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */
- OSMO_ASSERT(params->p1t >= 256);
- OSMO_ASSERT(params->p1t <= 65535);
- *dst = (params->p1t >> 8) & 0xFF;
- dst++;
- *dst = params->p1t & 0xFF;
- dst++;
- dst_counter += 2;
-
- /* Encode P1R (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */
- OSMO_ASSERT(params->p1r >= 256);
- OSMO_ASSERT(params->p1r <= 65535);
- *dst = (params->p1r >> 8) & 0xFF;
- dst++;
- *dst = params->p1r & 0xFF;
- dst++;
- dst_counter += 2;
-
- /* Encode P3T (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */
- OSMO_ASSERT(params->p3t >= 0);
- OSMO_ASSERT(params->p3t <= 65535);
- OSMO_ASSERT(params->p3t >= 2 * params->p1t);
- *dst = (params->p3t >> 8) & 0xFF;
- dst++;
- *dst = params->p3t & 0xFF;
- dst++;
- dst_counter += 2;
-
- /* Encode P3R (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */
- OSMO_ASSERT(params->p3r >= 0);
- OSMO_ASSERT(params->p3r <= 65535);
- OSMO_ASSERT(params->p3r >= 2 * params->p1r);
- *dst = (params->p3r >> 8) & 0xFF;
- dst++;
- *dst = params->p3r & 0xFF;
- dst++;
- dst_counter += 2;
-
- /* Return generated length */
- return dst_counter;
-}
-
-/* Encode data or protocol control information compression field
- * (see also: 3GPP TS 44.065, 6.6.1.1, Figure 9 and
- * 3GPP TS 44.065, 6.5.1.1, Figure 7) */
-static int encode_comp_field(uint8_t *dst, unsigned int dst_maxlen,
- const struct gprs_sndcp_comp_field *comp_field)
-{
- int dst_counter = 0;
- int len;
- int expected_length;
- int i;
-
- uint8_t payload_bytes[256];
- int payload_bytes_len = -1;
-
- /* If possible, try do encode payload bytes first */
- if (comp_field->rfc1144_params) {
- payload_bytes_len =
- encode_pcomp_rfc1144_params(payload_bytes,
- sizeof(payload_bytes),
- comp_field->rfc1144_params);
- } else if (comp_field->rfc2507_params) {
- payload_bytes_len =
- encode_pcomp_rfc2507_params(payload_bytes,
- sizeof(payload_bytes),
- comp_field->rfc2507_params);
- } else if (comp_field->rohc_params) {
- payload_bytes_len =
- encode_pcomp_rohc_params(payload_bytes,
- sizeof(payload_bytes),
- comp_field->rohc_params);
- } else if (comp_field->v42bis_params) {
- payload_bytes_len =
- encode_dcomp_v42bis_params(payload_bytes,
- sizeof(payload_bytes),
- comp_field->v42bis_params);
- } else if (comp_field->v44_params) {
- payload_bytes_len =
- encode_dcomp_v44_params(payload_bytes,
- sizeof(payload_bytes),
- comp_field->v44_params);
- } else
- OSMO_ASSERT(false);
-
- /* Bail immediately if payload byte generation failed */
- OSMO_ASSERT(payload_bytes_len >= 0);
-
- /* Bail if comp_len is out of bounds */
- OSMO_ASSERT(comp_field->comp_len <= sizeof(comp_field->comp));
-
- /* Calculate length field of the data block */
- if (comp_field->p) {
- len =
- payload_bytes_len +
- ceil((double)(comp_field->comp_len) / 2.0);
- expected_length = len + 3;
- } else {
- len = payload_bytes_len;
- expected_length = len + 2;
- }
-
- /* Bail immediately if no sufficient memory space is supplied */
- OSMO_ASSERT(dst_maxlen >= expected_length);
-
- /* Check if the entity number is within bounds */
- OSMO_ASSERT(comp_field->entity <= 0x1f);
-
- /* Check if the algorithm number is within bounds */
- OSMO_ASSERT(comp_field->algo >= 0 || comp_field->algo <= 0x1f);
-
- /* Zero out buffer */
- memset(dst, 0, dst_maxlen);
-
- /* Encode Propose bit */
- if (comp_field->p)
- *dst |= (1 << 7);
-
- /* Encode entity number */
- *dst |= comp_field->entity & 0x1F;
- dst++;
- dst_counter++;
-
- /* Encode algorithm number */
- if (comp_field->p) {
- *dst |= comp_field->algo & 0x1F;
- dst++;
- dst_counter++;
- }
-
- /* Encode length field */
- *dst |= len & 0xFF;
- dst++;
- dst_counter++;
-
- /* Encode PCOMP/DCOMP values */
- if (comp_field->p) {
- for (i = 0; i < comp_field->comp_len; i++) {
- /* Check if submitted PCOMP/DCOMP
- values are within bounds */
- if (comp_field->comp[i] > 0x0F)
- return -EINVAL;
-
- if (i & 1) {
- *dst |= comp_field->comp[i] & 0x0F;
- dst++;
- dst_counter++;
- } else
- *dst |= (comp_field->comp[i] << 4) & 0xF0;
- }
-
- if (i & 1) {
- dst++;
- dst_counter++;
- }
- }
-
- /* Append payload bytes */
- memcpy(dst, payload_bytes, payload_bytes_len);
- dst_counter += payload_bytes_len;
-
- /* Return generated length */
- return dst_counter;
-}
-
-/* Find out to which compression class the specified comp-field belongs
- * (header compression or data compression?) */
-int gprs_sndcp_get_compression_class(const struct gprs_sndcp_comp_field
- *comp_field)
-{
- OSMO_ASSERT(comp_field);
-
- if (comp_field->rfc1144_params)
- return SNDCP_XID_PROTOCOL_COMPRESSION;
- else if (comp_field->rfc2507_params)
- return SNDCP_XID_PROTOCOL_COMPRESSION;
- else if (comp_field->rohc_params)
- return SNDCP_XID_PROTOCOL_COMPRESSION;
- else if (comp_field->v42bis_params)
- return SNDCP_XID_DATA_COMPRESSION;
- else if (comp_field->v44_params)
- return SNDCP_XID_DATA_COMPRESSION;
- else
- return -EINVAL;
-}
-
-/* Convert all compression fields to bytstreams */
-static int gprs_sndcp_pack_fields(const struct llist_head *comp_fields,
- uint8_t *dst,
- unsigned int dst_maxlen, int class)
-{
- struct gprs_sndcp_comp_field *comp_field;
- int byte_counter = 0;
- int rc;
-
- llist_for_each_entry_reverse(comp_field, comp_fields, list) {
- if (class == gprs_sndcp_get_compression_class(comp_field)) {
- rc = encode_comp_field(dst + byte_counter,
- dst_maxlen - byte_counter,
- comp_field);
-
- /* When input data is correct, there is
- * no reason for the encoder to fail! */
- OSMO_ASSERT(rc >= 0);
-
- byte_counter += rc;
- }
- }
-
- /* Return generated length */
- return byte_counter;
-}
-
-/* Transform a list with compression fields into an SNDCP-XID message (dst) */
-int gprs_sndcp_compile_xid(uint8_t *dst, unsigned int dst_maxlen,
- const struct llist_head *comp_fields, int version)
-{
- int rc;
- int byte_counter = 0;
- uint8_t comp_bytes[512];
- uint8_t xid_version_number[1];
-
- OSMO_ASSERT(comp_fields);
- OSMO_ASSERT(dst);
- OSMO_ASSERT(dst_maxlen >= 2 + sizeof(xid_version_number));
-
- /* Prepend header with version number */
- if (version >= 0) {
- xid_version_number[0] = (uint8_t) (version & 0xff);
- dst =
- tlv_put(dst, SNDCP_XID_VERSION_NUMBER,
- sizeof(xid_version_number), xid_version_number);
- byte_counter += (sizeof(xid_version_number) + 2);
- }
-
- /* Stop if there is no compression fields supplied */
- if (llist_empty(comp_fields))
- return byte_counter;
-
- /* Add data compression fields */
- rc = gprs_sndcp_pack_fields(comp_fields, comp_bytes,
- sizeof(comp_bytes),
- SNDCP_XID_DATA_COMPRESSION);
- OSMO_ASSERT(rc >= 0);
-
- if (rc > 0) {
- dst = tlv_put(dst, SNDCP_XID_DATA_COMPRESSION, rc, comp_bytes);
- byte_counter += rc + 2;
- }
-
- /* Add header compression fields */
- rc = gprs_sndcp_pack_fields(comp_fields, comp_bytes,
- sizeof(comp_bytes),
- SNDCP_XID_PROTOCOL_COMPRESSION);
- OSMO_ASSERT(rc >= 0);
-
- if (rc > 0) {
- dst = tlv_put(dst, SNDCP_XID_PROTOCOL_COMPRESSION, rc,
- comp_bytes);
- byte_counter += rc + 2;
- }
-
- /* Return generated length */
- return byte_counter;
-}
-
-/* FUNCTIONS RELATED TO SNDCP-XID DECODING */
-
-/* Decode applicable sapis (works the same in all three compression schemes) */
-static int decode_pcomp_applicable_sapis(uint8_t *nsapis,
- uint8_t *nsapis_len,
- const uint8_t *src,
- unsigned int src_len)
-{
- uint16_t blob;
- int i;
- int nsapi_len = 0;
-
- /* Exit immediately if no result can be stored */
- if (!nsapis)
- return -EINVAL;
-
- /* Exit immediately if not enough input data is available */
- if (src_len < 2)
- return -EINVAL;
-
- /* Read bitmask */
- blob = *src;
- blob = (blob << 8) & 0xFF00;
- src++;
- blob |= (*src) & 0xFF;
- blob = (blob >> 5);
-
- /* Decode applicable SAPIs */
- for (i = 0; i < 15; i++) {
- if ((blob >> i) & 1) {
- nsapis[nsapi_len] = i + 5;
- nsapi_len++;
- }
- }
-
- /* Return consumed length */
- *nsapis_len = nsapi_len;
- return 2;
-}
-
-/* Decode 16 bit field */
-static int decode_pcomp_16_bit_field(int *value_int, uint16_t * value_uint16,
- const uint8_t *src,
- unsigned int src_len,
- int value_min, int value_max)
-{
- uint16_t blob;
-
- /* Reset values to zero (just to be sure) */
- if (value_int)
- *value_int = -1;
- if (value_uint16)
- *value_uint16 = 0;
-
- /* Exit if not enough src are available */
- if (src_len < 2)
- return -EINVAL;
-
- /* Decode bit value */
- blob = *src;
- blob = (blob << 8) & 0xFF00;
- src++;
- blob |= *src;
-
- /* Check if parsed value is within bounds */
- if (blob < value_min)
- return -EINVAL;
- if (blob > value_max)
- return -EINVAL;
-
- /* Hand back results to the caller */
- if (value_int)
- *value_int = blob;
- if (value_uint16)
- *value_uint16 = blob;
-
- /* Return consumed length */
- return 2;
-}
-
-/* Decode 8 bit field */
-static int decode_pcomp_8_bit_field(int *value_int, uint8_t *value_uint8,
- const uint8_t *src,
- unsigned int src_len,
- int value_min, int value_max)
-{
- uint8_t blob;
-
- /* Reset values to invalid (just to be sure) */
- if (value_int)
- *value_int = -1;
- if (value_uint8)
- *value_uint8 = 0;
-
- /* Exit if not enough src are available */
- if (src_len < 1)
- return -EINVAL;
-
- /* Decode bit value */
- blob = *src;
-
- /* Check if parsed value is within bounds */
- if (blob < value_min)
- return -EINVAL;
- if (blob > value_max)
- return -EINVAL;
-
- /* Hand back results to the caller */
- if (value_int)
- *value_int = blob;
- if (value_uint8)
- *value_uint8 = blob;
-
- /* Return consumed length */
- return 1;
-}
-
-/* Decode rfc1144 parameter field see also: 3GPP TS 44.065, 6.5.2.1, Table 5) */
-static int decode_pcomp_rfc1144_params(struct gprs_sndcp_pcomp_rfc1144_params
- *params, const uint8_t *src,
- unsigned int src_len)
-{
- int rc;
- int byte_counter = 0;
-
- /* Mark all optional parameters invalid by default */
- params->s01 = -1;
-
- /* Decode applicable SAPIs */
- rc = decode_pcomp_applicable_sapis(params->nsapi, &params->nsapi_len,
- src, src_len);
- if (rc > 0) {
- byte_counter += rc;
- src += rc;
- } else
- return byte_counter;
-
- /* Decode parameter S0 -1
- * (see also: 3GPP TS 44.065, 6.5.2.1, Table 5) */
- rc = decode_pcomp_8_bit_field(&params->s01, NULL, src,
- src_len - byte_counter, 0, 255);
- if (rc <= 0)
- return byte_counter;
- byte_counter += rc;
- src += rc;
-
- /* Return consumed length */
- return byte_counter;
-}
-
-/* Decode rfc2507 parameter field
- * (see also: 3GPP TS 44.065, 6.5.3.1, Table 6) */
-static int decode_pcomp_rfc2507_params(struct gprs_sndcp_pcomp_rfc2507_params
- *params, const uint8_t *src,
- unsigned int src_len)
-{
- int rc;
- int byte_counter = 0;
-
- /* Mark all optional parameters invalid by default */
- params->f_max_period = -1;
- params->f_max_time = -1;
- params->max_header = -1;
- params->tcp_space = -1;
- params->non_tcp_space = -1;
-
- /* Decode applicable SAPIs */
- rc = decode_pcomp_applicable_sapis(params->nsapi, &params->nsapi_len,
- src, src_len);
- if (rc > 0) {
- byte_counter += rc;
- src += rc;
- } else
- return byte_counter;
-
- /* Decode F_MAX_PERIOD (see also: 3GPP TS 44.065, 6.5.3.1, Table 6) */
- rc = decode_pcomp_16_bit_field(&params->f_max_period, NULL, src,
- src_len - byte_counter, 1, 65535);
- if (rc <= 0)
- return byte_counter;
- byte_counter += rc;
- src += rc;
-
- /* Decode F_MAX_TIME (see also: 3GPP TS 44.065, 6.5.3.1, Table 6) */
- rc = decode_pcomp_8_bit_field(&params->f_max_time, NULL, src,
- src_len - byte_counter, 1, 255);
- if (rc <= 0)
- return byte_counter;
- byte_counter += rc;
- src += rc;
-
- /* Decode MAX_HEADER (see also: 3GPP TS 44.065, 6.5.3.1, Table 6) */
- rc = decode_pcomp_8_bit_field(&params->max_header, NULL, src,
- src_len - byte_counter, 60, 255);
- if (rc <= 0)
- return byte_counter;
- byte_counter += rc;
- src += rc;
-
- /* Decode TCP_SPACE (see also: 3GPP TS 44.065, 6.5.3.1, Table 6) */
- rc = decode_pcomp_8_bit_field(&params->tcp_space, NULL, src,
- src_len - byte_counter, 3, 255);
- if (rc <= 0)
- return byte_counter;
- byte_counter += rc;
- src += rc;
-
- /* Decode NON_TCP_SPACE (see also: 3GPP TS 44.065, 6.5.3.1, Table 6) */
- rc = decode_pcomp_16_bit_field(&params->non_tcp_space, NULL, src,
- src_len - byte_counter, 3, 65535);
- if (rc <= 0)
- return byte_counter;
- byte_counter += rc;
- src += rc;
-
- /* Return consumed length */
- return byte_counter;
-}
-
-/* Decode ROHC parameter field (see also: 3GPP TS 44.065, 6.5.4.1, Table 10) */
-static int decode_pcomp_rohc_params(struct gprs_sndcp_pcomp_rohc_params *params,
- const uint8_t *src, unsigned int src_len)
-{
- int rc;
- int byte_counter = 0;
- int i;
-
- /* Mark all optional parameters invalid by default */
- params->max_cid = -1;
- params->max_header = -1;
-
- /* Decode applicable SAPIs */
- rc = decode_pcomp_applicable_sapis(params->nsapi, &params->nsapi_len,
- src, src_len);
- if (rc <= 0)
- return byte_counter;
- byte_counter += rc;
- src += rc;
-
- /* Decode MAX_CID (see also: 3GPP TS 44.065, 6.5.4.1, Table 10) */
- rc = decode_pcomp_16_bit_field(&params->max_cid, NULL, src,
- src_len - byte_counter, 0, 16383);
- if (rc <= 0)
- return byte_counter;
- byte_counter += rc;
- src += rc;
-
- /* Decode MAX_HEADER (see also: 3GPP TS 44.065, 6.5.4.1, Table 10) */
- rc = decode_pcomp_16_bit_field(&params->max_header, NULL, src,
- src_len - byte_counter, 60, 255);
- if (rc <= 0)
- return byte_counter;
- byte_counter += rc;
- src += rc;
-
- /* Decode Profiles (see also: 3GPP TS 44.065, 6.5.4.1, Table 10) */
- for (i = 0; i < 16; i++) {
- params->profile_len = 0;
- rc = decode_pcomp_16_bit_field(NULL, &params->profile[i], src,
- src_len - byte_counter, 0,
- 65535);
- if (rc <= 0)
- return byte_counter;
- byte_counter += rc;
- src += rc;
- params->profile_len = i + 1;
- }
-
- /* Return consumed length */
- return byte_counter;
-}
-
-/* Decode V.42bis parameter field
- * (see also: 3GPP TS 44.065, 6.6.2.1, Table 7a) */
-static int decode_dcomp_v42bis_params(struct gprs_sndcp_dcomp_v42bis_params
- *params, const uint8_t *src,
- unsigned int src_len)
-{
- int rc;
- int byte_counter = 0;
-
- /* Mark all optional parameters invalid by default */
- params->p0 = -1;
- params->p1 = -1;
- params->p2 = -1;
-
- /* Decode applicable SAPIs */
- rc = decode_pcomp_applicable_sapis(params->nsapi, &params->nsapi_len,
- src, src_len);
- if (rc > 0) {
- byte_counter += rc;
- src += rc;
- } else
- return byte_counter;
-
- /* Decode P0 (see also: 3GPP TS 44.065, 6.6.2.1, Table 7a) */
- rc = decode_pcomp_8_bit_field(&params->p0, NULL, src,
- src_len - byte_counter, 0, 3);
- if (rc <= 0)
- return byte_counter;
- byte_counter += rc;
- src += rc;
-
- /* Decode P1 (see also: 3GPP TS 44.065, 6.6.2.1, Table 7a) */
- rc = decode_pcomp_16_bit_field(&params->p1, NULL, src,
- src_len - byte_counter, 512, 65535);
- if (rc <= 0)
- return byte_counter;
- byte_counter += rc;
- src += rc;
-
- /* Decode P2 (see also: 3GPP TS 44.065, 6.6.2.1, Table 7a) */
- rc = decode_pcomp_8_bit_field(&params->p2, NULL, src,
- src_len - byte_counter, 6, 250);
- if (rc <= 0)
- return byte_counter;
- byte_counter += rc;
- src += rc;
-
- /* Return consumed length */
- return byte_counter;
-}
-
-/* Decode V44 parameter field (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */
-static int decode_dcomp_v44_params(struct gprs_sndcp_dcomp_v44_params *params,
- const uint8_t *src, unsigned int src_len)
-{
- int rc;
- int byte_counter = 0;
-
- /* Mark all optional parameters invalid by default */
- params->c0 = -1;
- params->p0 = -1;
- params->p1t = -1;
- params->p1r = -1;
- params->p3t = -1;
- params->p3r = -1;
-
- /* Decode applicable SAPIs */
- rc = decode_pcomp_applicable_sapis(params->nsapi, &params->nsapi_len,
- src, src_len);
- if (rc > 0) {
- byte_counter += rc;
- src += rc;
- } else
- return byte_counter;
-
- /* Decode C0 (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */
- rc = decode_pcomp_8_bit_field(&params->c0, NULL, src,
- src_len - byte_counter, 0, 255);
- if (rc <= 0)
- return byte_counter;
- if ((params->c0 != 0x80) && (params->c0 != 0xC0))
- return -EINVAL;
- byte_counter += rc;
- src += rc;
-
- /* Decode P0 (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */
- rc = decode_pcomp_8_bit_field(&params->p0, NULL, src,
- src_len - byte_counter, 0, 3);
- if (rc <= 0)
- return byte_counter;
- byte_counter += rc;
- src += rc;
-
- /* Decode P1T (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */
- rc = decode_pcomp_16_bit_field(&params->p1t, NULL, src,
- src_len - byte_counter, 265, 65535);
- if (rc <= 0)
- return byte_counter;
- byte_counter += rc;
- src += rc;
-
- /* Decode P1R (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */
- rc = decode_pcomp_16_bit_field(&params->p1r, NULL, src,
- src_len - byte_counter, 265, 65535);
- if (rc <= 0)
- return byte_counter;
- byte_counter += rc;
- src += rc;
-
- /* Decode P3T (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */
- rc = decode_pcomp_16_bit_field(&params->p3t, NULL, src,
- src_len - byte_counter, 265, 65535);
- if (rc <= 0)
- return byte_counter;
- if (params->p3t < 2 * params->p1t)
- return -EINVAL;
- byte_counter += rc;
- src += rc;
-
- /* Decode P3R (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */
- rc = decode_pcomp_16_bit_field(&params->p3r, NULL, src,
- src_len - byte_counter, 265, 65535);
- if (rc <= 0)
- return byte_counter;
- if (params->p3r < 2 * params->p1r)
- return -EINVAL;
- byte_counter += rc;
- src += rc;
-
- /* Return consumed length */
- return byte_counter;
-}
-
-/* Lookup algorithm identfier by entity ID */
-static int lookup_algorithm_identifier(int entity, const struct
- entity_algo_table
- *lt, unsigned int lt_len, int compclass)
-{
- int i;
-
- if (!lt)
- return -1;
-
- for (i = 0; i < lt_len; i++) {
- if ((lt[i].entity == entity)
- && (lt[i].compclass == compclass))
- return lt[i].algo;
- }
-
- return -1;
-}
-
-/* Helper function for decode_comp_field(), decodes
- * numeric pcomp/dcomp values */
-static int decode_comp_values(struct gprs_sndcp_comp_field *comp_field,
- const uint8_t *src, int compclass)
-{
- int src_counter = 0;
- int i;
-
- if (comp_field->p) {
- /* Determine the number of expected PCOMP/DCOMP values */
- if (compclass == SNDCP_XID_PROTOCOL_COMPRESSION) {
- /* For protocol compression */
- switch (comp_field->algo) {
- case RFC_1144:
- comp_field->comp_len = RFC1144_PCOMP_NUM;
- break;
- case RFC_2507:
- comp_field->comp_len = RFC2507_PCOMP_NUM;
- break;
- case ROHC:
- comp_field->comp_len = ROHC_PCOMP_NUM;
- break;
-
- /* Exit if the algorithem type encodes
- something unknown / unspecified */
- default:
- return -EINVAL;
- }
- } else {
- /* For data compression */
- switch (comp_field->algo) {
- case V42BIS:
- comp_field->comp_len = V42BIS_DCOMP_NUM;
- break;
- case V44:
- comp_field->comp_len = V44_DCOMP_NUM;
- break;
-
- /* Exit if the algorithem type encodes
- something unknown / unspecified */
- default:
- return -EINVAL;
- }
- }
-
- for (i = 0; i < comp_field->comp_len; i++) {
- if (i & 1) {
- comp_field->comp[i] = (*src) & 0x0F;
- src++;
- src_counter++;
- } else
- comp_field->comp[i] = ((*src) >> 4) & 0x0F;
- }
-
- if (i & 1) {
- src++;
- src_counter++;
- }
- }
-
- return src_counter;
-}
-
-/* Helper function for decode_comp_field(), decodes the parameters
- * which are protocol compression specific */
-static int decode_pcomp_params(struct gprs_sndcp_comp_field *comp_field,
- const uint8_t *src, int src_len)
-{
- int rc;
-
- switch (comp_field->algo) {
- case RFC_1144:
- comp_field->rfc1144_params = talloc_zero(comp_field, struct
- gprs_sndcp_pcomp_rfc1144_params);
- rc = decode_pcomp_rfc1144_params(comp_field->rfc1144_params,
- src, src_len);
- if (rc < 0)
- talloc_free(comp_field->rfc1144_params);
- break;
- case RFC_2507:
- comp_field->rfc2507_params = talloc_zero(comp_field, struct
- gprs_sndcp_pcomp_rfc2507_params);
- rc = decode_pcomp_rfc2507_params(comp_field->rfc2507_params,
- src, src_len);
- if (rc < 0)
- talloc_free(comp_field->rfc1144_params);
- break;
- case ROHC:
- comp_field->rohc_params = talloc_zero(comp_field, struct
- gprs_sndcp_pcomp_rohc_params);
- rc = decode_pcomp_rohc_params(comp_field->rohc_params, src,
- src_len);
- if (rc < 0)
- talloc_free(comp_field->rohc_params);
- break;
-
- /* If no suitable decoder is detected,
- leave the remaining bytes undecoded */
- default:
- rc = src_len;
- }
-
- if (rc < 0) {
- comp_field->rfc1144_params = NULL;
- comp_field->rfc2507_params = NULL;
- comp_field->rohc_params = NULL;
- }
-
- return rc;
-}
-
-/* Helper function for decode_comp_field(), decodes the parameters
- * which are data compression specific */
-static int decode_dcomp_params(struct gprs_sndcp_comp_field *comp_field,
- const uint8_t *src, int src_len)
-{
- int rc;
-
- switch (comp_field->algo) {
- case V42BIS:
- comp_field->v42bis_params = talloc_zero(comp_field, struct
- gprs_sndcp_dcomp_v42bis_params);
- rc = decode_dcomp_v42bis_params(comp_field->v42bis_params, src,
- src_len);
- if (rc < 0)
- talloc_free(comp_field->v42bis_params);
- break;
- case V44:
- comp_field->v44_params = talloc_zero(comp_field, struct
- gprs_sndcp_dcomp_v44_params);
- rc = decode_dcomp_v44_params(comp_field->v44_params, src,
- src_len);
- if (rc < 0)
- talloc_free(comp_field->v44_params);
- break;
-
- /* If no suitable decoder is detected,
- * leave the remaining bytes undecoded */
- default:
- rc = src_len;
- }
-
- if (rc < 0) {
- comp_field->v42bis_params = NULL;
- comp_field->v44_params = NULL;
- }
-
- return rc;
-}
-
-/* Decode data or protocol control information compression field
- * (see also: 3GPP TS 44.065, 6.6.1.1, Figure 9 and
- * 3GPP TS 44.065, 6.5.1.1, Figure 7) */
-static int decode_comp_field(struct gprs_sndcp_comp_field *comp_field,
- const uint8_t *src, unsigned int src_len,
- const struct entity_algo_table *lt,
- unsigned int lt_len, int compclass)
-{
- int src_counter = 0;
- unsigned int len;
- int rc;
-
- OSMO_ASSERT(comp_field);
-
- /* Exit immediately if it is clear that no
- parseable data is present */
- if (src_len < 1 || !src)
- return -EINVAL;
-
- /* Zero out target struct */
- memset(comp_field, 0, sizeof(struct gprs_sndcp_comp_field));
-
- /* Decode Propose bit and Entity number */
- if ((*src) & 0x80)
- comp_field->p = 1;
- comp_field->entity = (*src) & 0x1F;
- src_counter++;
- src++;
-
- /* Decode algorithm number (if present) */
- if (comp_field->p) {
- comp_field->algo = (*src) & 0x1F;
- src_counter++;
- src++;
- }
- /* Alternatively take the information from the lookup table */
- else
- comp_field->algo =
- lookup_algorithm_identifier(comp_field->entity, lt,
- lt_len, compclass);
-
- /* Decode length field */
- len = *src;
- src_counter++;
- src++;
-
- /* Decode PCOMP/DCOMP values */
- rc = decode_comp_values(comp_field, src, compclass);
- if (rc < 0)
- return -EINVAL;
- src_counter += rc;
- src += rc;
- len -= rc;
-
- /* Decode algorithm specific payload data */
- if (compclass == SNDCP_XID_PROTOCOL_COMPRESSION)
- rc = decode_pcomp_params(comp_field, src, len);
- else if (compclass == SNDCP_XID_DATA_COMPRESSION)
- rc = decode_dcomp_params(comp_field, src, len);
- else
- return -EINVAL;
-
- if (rc >= 0)
- src_counter += rc;
- else
- return -EINVAL;
-
- /* Return consumed length */
- return src_counter;
-}
-
-/* Helper function for gprs_sndcp_decode_xid() to decode XID blocks */
-static int decode_xid_block(struct llist_head *comp_fields, uint8_t tag,
- uint16_t tag_len, const uint8_t *val,
- const struct entity_algo_table *lt,
- unsigned int lt_len)
-{
- struct gprs_sndcp_comp_field *comp_field;
- int byte_counter = 0;
- int comp_field_count = 0;
- int rc;
-
- byte_counter = 0;
- do {
- /* Bail if more than the maximum number of
- comp_fields is generated */
- if (comp_field_count > MAX_ENTITIES * 2) {
- return -EINVAL;
- }
-
- /* Parse and add comp_field */
- comp_field =
- talloc_zero(comp_fields, struct gprs_sndcp_comp_field);
-
- rc = decode_comp_field(comp_field, val + byte_counter,
- tag_len - byte_counter, lt, lt_len, tag);
-
- if (rc < 0) {
- talloc_free(comp_field);
- return -EINVAL;
- }
-
- byte_counter += rc;
- llist_add(&comp_field->list, comp_fields);
- comp_field_count++;
- }
- while (tag_len - byte_counter > 0);
-
- return byte_counter;
-}
-
-/* Transform an SNDCP-XID message (src) into a list of SNDCP-XID fields */
-static int gprs_sndcp_decode_xid(int *version, struct llist_head *comp_fields,
- const uint8_t *src, unsigned int src_len,
- const struct entity_algo_table *lt,
- unsigned int lt_len)
-{
- int src_pos = 0;
- uint8_t tag;
- uint16_t tag_len;
- const uint8_t *val;
- int byte_counter = 0;
- int rc;
- int tlv_count = 0;
-
- /* Preset version value as invalid */
- if (version)
- *version = -1;
-
- /* Valid TLV-Tag and types */
- static const struct tlv_definition sndcp_xid_def = {
- .def = {
- [SNDCP_XID_VERSION_NUMBER] = {TLV_TYPE_TLV,},
- [SNDCP_XID_DATA_COMPRESSION] = {TLV_TYPE_TLV,},
- [SNDCP_XID_PROTOCOL_COMPRESSION] = {TLV_TYPE_TLV,},
- },
- };
-
- /* Parse TLV-Encoded SNDCP-XID message and defer payload
- to the apporpiate sub-parser functions */
- while (1) {
-
- /* Bail if an the maximum number of TLV fields
- * have been parsed */
- if (tlv_count >= 3) {
- talloc_free(comp_fields);
- return -EINVAL;
- }
-
- /* Parse TLV field */
- rc = tlv_parse_one(&tag, &tag_len, &val, &sndcp_xid_def,
- src + src_pos, src_len - src_pos);
- if (rc > 0)
- src_pos += rc;
- else {
- talloc_free(comp_fields);
- return -EINVAL;
- }
-
- /* Decode sndcp xid version number */
- if (version && tag == SNDCP_XID_VERSION_NUMBER)
- *version = val[0];
-
- /* Decode compression parameters */
- if ((tag == SNDCP_XID_PROTOCOL_COMPRESSION)
- || (tag == SNDCP_XID_DATA_COMPRESSION)) {
- rc = decode_xid_block(comp_fields, tag, tag_len, val,
- lt, lt_len);
-
- if (rc < 0) {
- talloc_free(comp_fields);
- return -EINVAL;
- } else
- byte_counter += rc;
- }
-
- /* Stop when no further TLV elements can be expected */
- if (src_len - src_pos <= 2)
- break;
-
- tlv_count++;
- }
-
- return 0;
-}
-
-/* Fill up lookutable from a list with comression entitiy fields */
-static int gprs_sndcp_fill_table(struct
- entity_algo_table *lt,
- unsigned int lt_len,
- const struct llist_head *comp_fields)
-{
- struct gprs_sndcp_comp_field *comp_field;
- int i = 0;
- int rc;
-
- if (!comp_fields)
- return -EINVAL;
- if (!lt)
- return -EINVAL;
-
- memset(lt, 0, sizeof(*lt));
-
- llist_for_each_entry(comp_field, comp_fields, list) {
- if (comp_field->algo >= 0) {
- lt[i].entity = comp_field->entity;
- lt[i].algo = comp_field->algo;
- rc = gprs_sndcp_get_compression_class(comp_field);
-
- if (rc < 0) {
- memset(lt, 0, sizeof(*lt));
- return -EINVAL;
- }
-
- lt[i].compclass = rc;
- i++;
- }
- }
-
- return i;
-}
-
-/* Complete comp field params
- * (if a param (dst) is not valid, it will be copied from source (src) */
-static int complete_comp_field_params(struct gprs_sndcp_comp_field
- *comp_field_dst, const struct
- gprs_sndcp_comp_field *comp_field_src)
-{
- if (comp_field_dst->algo < 0)
- return -EINVAL;
-
- if (comp_field_dst->rfc1144_params && comp_field_src->rfc1144_params) {
- if (comp_field_dst->rfc1144_params->s01 < 0) {
- comp_field_dst->rfc1144_params->s01 =
- comp_field_src->rfc1144_params->s01;
- }
- return 0;
- }
-
- if (comp_field_dst->rfc2507_params && comp_field_src->rfc2507_params) {
-
- if (comp_field_dst->rfc2507_params->f_max_period < 0) {
- comp_field_dst->rfc2507_params->f_max_period =
- comp_field_src->rfc2507_params->f_max_period;
- }
- if (comp_field_dst->rfc2507_params->f_max_time < 0) {
- comp_field_dst->rfc2507_params->f_max_time =
- comp_field_src->rfc2507_params->f_max_time;
- }
- if (comp_field_dst->rfc2507_params->max_header < 0) {
- comp_field_dst->rfc2507_params->max_header =
- comp_field_src->rfc2507_params->max_header;
- }
- if (comp_field_dst->rfc2507_params->tcp_space < 0) {
- comp_field_dst->rfc2507_params->tcp_space =
- comp_field_src->rfc2507_params->tcp_space;
- }
- if (comp_field_dst->rfc2507_params->non_tcp_space < 0) {
- comp_field_dst->rfc2507_params->non_tcp_space =
- comp_field_src->rfc2507_params->non_tcp_space;
- }
- return 0;
- }
-
- if (comp_field_dst->rohc_params && comp_field_src->rohc_params) {
- if (comp_field_dst->rohc_params->max_cid < 0) {
- comp_field_dst->rohc_params->max_cid =
- comp_field_src->rohc_params->max_cid;
- }
- if (comp_field_dst->rohc_params->max_header < 0) {
- comp_field_dst->rohc_params->max_header =
- comp_field_src->rohc_params->max_header;
- }
- if (comp_field_dst->rohc_params->profile_len > 0) {
- memcpy(comp_field_dst->rohc_params->profile,
- comp_field_src->rohc_params->profile,
- sizeof(comp_field_dst->rohc_params->profile));
- comp_field_dst->rohc_params->profile_len =
- comp_field_src->rohc_params->profile_len;
- }
-
- return 0;
- }
-
- if (comp_field_dst->v42bis_params && comp_field_src->v42bis_params) {
- if (comp_field_dst->v42bis_params->p0 < 0) {
- comp_field_dst->v42bis_params->p0 =
- comp_field_src->v42bis_params->p0;
- }
- if (comp_field_dst->v42bis_params->p1 < 0) {
- comp_field_dst->v42bis_params->p1 =
- comp_field_src->v42bis_params->p1;
- }
- if (comp_field_dst->v42bis_params->p2 < 0) {
- comp_field_dst->v42bis_params->p2 =
- comp_field_src->v42bis_params->p2;
- }
- return 0;
- }
-
- if (comp_field_dst->v44_params && comp_field_src->v44_params) {
- if (comp_field_dst->v44_params->c0 < 0) {
- comp_field_dst->v44_params->c0 =
- comp_field_src->v44_params->c0;
- }
- if (comp_field_dst->v44_params->p0 < 0) {
- comp_field_dst->v44_params->p0 =
- comp_field_src->v44_params->p0;
- }
- if (comp_field_dst->v44_params->p1t < 0) {
- comp_field_dst->v44_params->p1t =
- comp_field_src->v44_params->p1t;
- }
- if (comp_field_dst->v44_params->p1r < 0) {
- comp_field_dst->v44_params->p1r =
- comp_field_src->v44_params->p1r;
- }
- if (comp_field_dst->v44_params->p3t < 0) {
- comp_field_dst->v44_params->p3t =
- comp_field_src->v44_params->p3t;
- }
- if (comp_field_dst->v44_params->p3r < 0) {
- comp_field_dst->v44_params->p3r =
- comp_field_src->v44_params->p3r;
- }
- return 0;
- }
-
- /* There should be at least exist one param set
- * in the destination struct, otherwise something
- * must be wrong! */
- return -EINVAL;
-}
-
-/* Complete missing parameters in a comp_field */
-static int gprs_sndcp_complete_comp_field(struct gprs_sndcp_comp_field
- *comp_field, const struct llist_head
- *comp_fields)
-{
- struct gprs_sndcp_comp_field *comp_field_src;
- int rc = 0;
-
- llist_for_each_entry(comp_field_src, comp_fields, list) {
- if (comp_field_src->entity == comp_field->entity) {
-
- /* Complete header fields */
- if (comp_field_src->comp_len > 0) {
- memcpy(comp_field->comp,
- comp_field_src->comp,
- sizeof(comp_field_src->comp));
- comp_field->comp_len = comp_field_src->comp_len;
- }
-
- /* Complete parameter fields */
- rc = complete_comp_field_params(comp_field,
- comp_field_src);
- }
- }
-
- return rc;
-}
-
-/* Complete missing parameters of all comp_field in a list */
-static int gprs_sndcp_complete_comp_fields(struct llist_head
- *comp_fields_incomplete,
- const struct llist_head *comp_fields)
-{
- struct gprs_sndcp_comp_field *comp_field_incomplete;
- int rc;
-
- llist_for_each_entry(comp_field_incomplete, comp_fields_incomplete,
- list) {
-
- rc = gprs_sndcp_complete_comp_field(comp_field_incomplete,
- comp_fields);
- if (rc < 0)
- return -EINVAL;
-
- }
-
- return 0;
-}
-
-/* Transform an SNDCP-XID message (src) into a list of SNDCP-XID fields */
-struct llist_head *gprs_sndcp_parse_xid(int *version,
- const void *ctx,
- const uint8_t *src,
- unsigned int src_len,
- const struct llist_head
- *comp_fields_req)
-{
- int rc;
- int lt_len;
- struct llist_head *comp_fields;
- struct entity_algo_table lt[MAX_ENTITIES * 2];
-
- /* In case of a zero length field, just exit */
- if (src_len == 0)
- return NULL;
-
- /* We should go any further if we have a field length greater
- * zero and a null pointer as buffer! */
- OSMO_ASSERT(src);
-
- comp_fields = talloc_zero(ctx, struct llist_head);
- INIT_LLIST_HEAD(comp_fields);
-
- if (comp_fields_req) {
- /* Generate lookup table */
- lt_len =
- gprs_sndcp_fill_table(lt, MAX_ENTITIES * 2,
- comp_fields_req);
- if (lt_len < 0) {
- talloc_free(comp_fields);
- return NULL;
- }
-
- /* Parse SNDCP-CID XID-Field */
- rc = gprs_sndcp_decode_xid(version, comp_fields, src, src_len,
- lt, lt_len);
- if (rc < 0) {
- talloc_free(comp_fields);
- return NULL;
- }
-
- rc = gprs_sndcp_complete_comp_fields(comp_fields,
- comp_fields_req);
- if (rc < 0) {
- talloc_free(comp_fields);
- return NULL;
- }
-
- } else {
- /* Parse SNDCP-CID XID-Field */
- rc = gprs_sndcp_decode_xid(version, comp_fields, src, src_len,
- NULL, 0);
- if (rc < 0) {
- talloc_free(comp_fields);
- return NULL;
- }
- }
-
- return comp_fields;
-}
-
-/* Helper for gprs_sndcp_dump_comp_fields(),
- * dumps protocol compression parameters */
-static void dump_pcomp_params(const struct gprs_sndcp_comp_field
- *comp_field, unsigned int logl)
-{
- int i;
-
- switch (comp_field->algo) {
- case RFC_1144:
- if (comp_field->rfc1144_params == NULL) {
- LOGP(DSNDCP, logl,
- " gprs_sndcp_pcomp_rfc1144_params=NULL\n");
- break;
- }
- LOGP(DSNDCP, logl, " gprs_sndcp_pcomp_rfc1144_params {\n");
- LOGP(DSNDCP, logl,
- " nsapi_len=%d;\n",
- comp_field->rfc1144_params->nsapi_len);
- if (comp_field->rfc1144_params->nsapi_len == 0)
- LOGP(DSNDCP, logl, " nsapi[] = NULL;\n");
- for (i = 0; i < comp_field->rfc1144_params->nsapi_len; i++) {
- LOGP(DSNDCP, logl,
- " nsapi[%d]=%d;\n", i,
- comp_field->rfc1144_params->nsapi[i]);
- }
- LOGP(DSNDCP, logl, " s01=%d;\n",
- comp_field->rfc1144_params->s01);
- LOGP(DSNDCP, logl, " }\n");
- break;
- case RFC_2507:
- if (comp_field->rfc2507_params == NULL) {
- LOGP(DSNDCP, logl,
- " gprs_sndcp_pcomp_rfc2507_params=NULL\n");
- break;
- }
- LOGP(DSNDCP, logl, " gprs_sndcp_pcomp_rfc2507_params {\n");
- LOGP(DSNDCP, logl,
- " nsapi_len=%d;\n",
- comp_field->rfc2507_params->nsapi_len);
- if (comp_field->rfc2507_params->nsapi_len == 0)
- LOGP(DSNDCP, logl, " nsapi[] = NULL;\n");
- for (i = 0; i < comp_field->rfc2507_params->nsapi_len; i++) {
- LOGP(DSNDCP, logl,
- " nsapi[%d]=%d;\n", i,
- comp_field->rfc2507_params->nsapi[i]);
- }
- LOGP(DSNDCP, logl,
- " f_max_period=%d;\n",
- comp_field->rfc2507_params->f_max_period);
- LOGP(DSNDCP, logl,
- " f_max_time=%d;\n",
- comp_field->rfc2507_params->f_max_time);
- LOGP(DSNDCP, logl,
- " max_header=%d;\n",
- comp_field->rfc2507_params->max_header);
- LOGP(DSNDCP, logl,
- " tcp_space=%d;\n",
- comp_field->rfc2507_params->tcp_space);
- LOGP(DSNDCP, logl,
- " non_tcp_space=%d;\n",
- comp_field->rfc2507_params->non_tcp_space);
- LOGP(DSNDCP, logl, " }\n");
- break;
- case ROHC:
- if (comp_field->rohc_params == NULL) {
- LOGP(DSNDCP, logl,
- " gprs_sndcp_pcomp_rohc_params=NULL\n");
- break;
- }
- LOGP(DSNDCP, logl, " gprs_sndcp_pcomp_rohc_params {\n");
- LOGP(DSNDCP, logl,
- " nsapi_len=%d;\n",
- comp_field->rohc_params->nsapi_len);
- if (comp_field->rohc_params->nsapi_len == 0)
- LOGP(DSNDCP, logl, " nsapi[] = NULL;\n");
- for (i = 0; i < comp_field->rohc_params->nsapi_len; i++) {
- LOGP(DSNDCP, logl,
- " nsapi[%d]=%d;\n", i,
- comp_field->rohc_params->nsapi[i]);
- }
- LOGP(DSNDCP, logl,
- " max_cid=%d;\n", comp_field->rohc_params->max_cid);
- LOGP(DSNDCP, logl,
- " max_header=%d;\n",
- comp_field->rohc_params->max_header);
- LOGP(DSNDCP, logl,
- " profile_len=%d;\n",
- comp_field->rohc_params->profile_len);
- if (comp_field->rohc_params->profile_len == 0)
- LOGP(DSNDCP, logl, " profile[] = NULL;\n");
- for (i = 0; i < comp_field->rohc_params->profile_len; i++)
- LOGP(DSNDCP, logl,
- " profile[%d]=%04x;\n",
- i, comp_field->rohc_params->profile[i]);
- LOGP(DSNDCP, logl, " }\n");
- break;
- }
-
-}
-
-/* Helper for gprs_sndcp_dump_comp_fields(),
- * data protocol compression parameters */
-static void dump_dcomp_params(const struct gprs_sndcp_comp_field
- *comp_field, unsigned int logl)
-{
- int i;
-
- switch (comp_field->algo) {
- case V42BIS:
- if (comp_field->v42bis_params == NULL) {
- LOGP(DSNDCP, logl,
- " gprs_sndcp_dcomp_v42bis_params=NULL\n");
- break;
- }
- LOGP(DSNDCP, logl, " gprs_sndcp_dcomp_v42bis_params {\n");
- LOGP(DSNDCP, logl,
- " nsapi_len=%d;\n",
- comp_field->v42bis_params->nsapi_len);
- if (comp_field->v42bis_params->nsapi_len == 0)
- LOGP(DSNDCP, logl, " nsapi[] = NULL;\n");
- for (i = 0; i < comp_field->v42bis_params->nsapi_len; i++)
- LOGP(DSNDCP, logl,
- " nsapi[%d]=%d;\n", i,
- comp_field->v42bis_params->nsapi[i]);
- LOGP(DSNDCP, logl, " p0=%d;\n",
- comp_field->v42bis_params->p0);
- LOGP(DSNDCP, logl, " p1=%d;\n",
- comp_field->v42bis_params->p1);
- LOGP(DSNDCP, logl, " p2=%d;\n",
- comp_field->v42bis_params->p2);
- LOGP(DSNDCP, logl, " }\n");
- break;
- case V44:
- if (comp_field->v44_params == NULL) {
- LOGP(DSNDCP, logl,
- " gprs_sndcp_dcomp_v44_params=NULL\n");
- break;
- }
- LOGP(DSNDCP, logl, " gprs_sndcp_dcomp_v44_params {\n");
- LOGP(DSNDCP, logl,
- " nsapi_len=%d;\n",
- comp_field->v44_params->nsapi_len);
- if (comp_field->v44_params->nsapi_len == 0)
- LOGP(DSNDCP, logl, " nsapi[] = NULL;\n");
- for (i = 0; i < comp_field->v44_params->nsapi_len; i++) {
- LOGP(DSNDCP, logl,
- " nsapi[%d]=%d;\n", i,
- comp_field->v44_params->nsapi[i]);
- }
- LOGP(DSNDCP, logl, " c0=%d;\n",
- comp_field->v44_params->c0);
- LOGP(DSNDCP, logl, " p0=%d;\n",
- comp_field->v44_params->p0);
- LOGP(DSNDCP, logl, " p1t=%d;\n",
- comp_field->v44_params->p1t);
- LOGP(DSNDCP, logl, " p1r=%d;\n",
- comp_field->v44_params->p1r);
- LOGP(DSNDCP, logl, " p3t=%d;\n",
- comp_field->v44_params->p3t);
- LOGP(DSNDCP, logl, " p3r=%d;\n",
- comp_field->v44_params->p3r);
- LOGP(DSNDCP, logl, " }\n");
- break;
- }
-}
-
-/* Dump a list with SNDCP-XID fields (Debug) */
-void gprs_sndcp_dump_comp_fields(const struct llist_head *comp_fields,
- unsigned int logl)
-{
- struct gprs_sndcp_comp_field *comp_field;
- int i;
- int compclass;
-
- OSMO_ASSERT(comp_fields);
-
- llist_for_each_entry(comp_field, comp_fields, list) {
- LOGP(DSNDCP, logl, "SNDCP-XID:\n");
- LOGP(DSNDCP, logl, "struct gprs_sndcp_comp_field {\n");
- LOGP(DSNDCP, logl, " entity=%d;\n", comp_field->entity);
- LOGP(DSNDCP, logl, " algo=%d;\n", comp_field->algo);
- LOGP(DSNDCP, logl, " comp_len=%d;\n", comp_field->comp_len);
- if (comp_field->comp_len == 0)
- LOGP(DSNDCP, logl, " comp[] = NULL;\n");
- for (i = 0; i < comp_field->comp_len; i++) {
- LOGP(DSNDCP, logl, " comp[%d]=%d;\n", i,
- comp_field->comp[i]);
- }
-
- compclass = gprs_sndcp_get_compression_class(comp_field);
-
- if (compclass == SNDCP_XID_PROTOCOL_COMPRESSION) {
- dump_pcomp_params(comp_field, logl);
- } else if (compclass == SNDCP_XID_DATA_COMPRESSION) {
- dump_dcomp_params(comp_field, logl);
- }
-
- LOGP(DSNDCP, logl, "}\n");
- }
-
-}
diff --git a/src/gprs/gprs_subscriber.c b/src/gprs/gprs_subscriber.c
deleted file mode 100644
index 176583b6f..000000000
--- a/src/gprs/gprs_subscriber.c
+++ /dev/null
@@ -1,922 +0,0 @@
-/* MS subscriber data handling */
-
-/* (C) 2014 by sysmocom s.f.m.c. GmbH
- * (C) 2015 by Holger Hans Peter Freyther
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
-#include <osmocom/gsm/gsup.h>
-#include <osmocom/core/utils.h>
-#include <osmocom/core/logging.h>
-#include <openbsc/gprs_subscriber.h>
-#include <openbsc/gsup_client.h>
-
-#include <openbsc/sgsn.h>
-#include <openbsc/gprs_sgsn.h>
-#include <openbsc/gprs_gmm.h>
-#include <openbsc/gprs_utils.h>
-
-#include <openbsc/debug.h>
-
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <limits.h>
-
-#define SGSN_SUBSCR_MAX_RETRIES 3
-#define SGSN_SUBSCR_RETRY_INTERVAL 10
-
-#define LOGGSUPP(level, gsup, fmt, args...) \
- LOGP(DGPRS, level, "GSUP(%s) " fmt, \
- (gsup)->imsi, \
- ## args)
-
-extern void *tall_bsc_ctx;
-
-LLIST_HEAD(_gprs_subscribers);
-struct llist_head * const gprs_subscribers = &_gprs_subscribers;
-
-static int gsup_read_cb(struct gsup_client *gsupc, struct msgb *msg);
-
-/* TODO: Some functions are specific to the SGSN, but this file is more general
- * (it has gprs_* name). Either move these functions elsewhere, split them and
- * move a part, or replace the gprs_ prefix by sgsn_. The applies to
- * gprs_subscr_init, gsup_read_cb, and gprs_subscr_tx_gsup_message.
- */
-
-int gprs_subscr_init(struct sgsn_instance *sgi)
-{
- const char *addr_str;
-
- if (!sgi->cfg.gsup_server_addr.sin_addr.s_addr)
- return 0;
-
- addr_str = inet_ntoa(sgi->cfg.gsup_server_addr.sin_addr);
-
- sgi->gsup_client = gsup_client_create(
- "SGSN",
- addr_str, sgi->cfg.gsup_server_port,
- &gsup_read_cb,
- &sgi->cfg.oap);
-
- if (!sgi->gsup_client)
- return -1;
-
- return 1;
-}
-
-static int gsup_read_cb(struct gsup_client *gsupc, struct msgb *msg)
-{
- int rc;
-
- rc = gprs_subscr_rx_gsup_message(msg);
- msgb_free(msg);
- if (rc < 0)
- return -1;
-
- return rc;
-}
-
-int gprs_subscr_purge(struct gprs_subscr *subscr);
-
-static struct sgsn_subscriber_data *sgsn_subscriber_data_alloc(void *ctx)
-{
- struct sgsn_subscriber_data *sdata;
- int idx;
-
- sdata = talloc_zero(ctx, struct sgsn_subscriber_data);
-
- sdata->error_cause = SGSN_ERROR_CAUSE_NONE;
-
- for (idx = 0; idx < ARRAY_SIZE(sdata->auth_triplets); idx++)
- sdata->auth_triplets[idx].key_seq = GSM_KEY_SEQ_INVAL;
-
- INIT_LLIST_HEAD(&sdata->pdp_list);
-
- return sdata;
-}
-
-struct sgsn_subscriber_pdp_data* sgsn_subscriber_pdp_data_alloc(
- struct sgsn_subscriber_data *sdata)
-{
- struct sgsn_subscriber_pdp_data* pdata;
-
- pdata = talloc_zero(sdata, struct sgsn_subscriber_pdp_data);
-
- llist_add_tail(&pdata->list, &sdata->pdp_list);
-
- return pdata;
-}
-
-struct gprs_subscr *gprs_subscr_get_by_imsi(const char *imsi)
-{
- struct gprs_subscr *gsub;
-
- if (!imsi || !*imsi)
- return NULL;
-
- llist_for_each_entry(gsub, gprs_subscribers, entry) {
- if (!strcmp(gsub->imsi, imsi))
- return gprs_subscr_get(gsub);
- }
- return NULL;
-}
-
-static struct gprs_subscr *gprs_subscr_alloc(void)
-{
- struct gprs_subscr *gsub;
- gsub = talloc_zero(tall_bsc_ctx, struct gprs_subscr);
- if (!gsub)
- return NULL;
- llist_add_tail(&gsub->entry, gprs_subscribers);
- gsub->use_count = 1;
- gsub->tmsi = GSM_RESERVED_TMSI;
- return gsub;
-}
-
-struct gprs_subscr *gprs_subscr_get_or_create(const char *imsi)
-{
- struct gprs_subscr *gsub;
-
- gsub = gprs_subscr_get_by_imsi(imsi);
- if (!gsub) {
- gsub = gprs_subscr_alloc();
- if (!gsub)
- return NULL;
- osmo_strlcpy(gsub->imsi, imsi, sizeof(gsub->imsi));
- }
-
- if (!gsub->sgsn_data)
- gsub->sgsn_data = sgsn_subscriber_data_alloc(gsub);
- return gsub;
-}
-
-void gprs_subscr_cleanup(struct gprs_subscr *subscr)
-{
- if (subscr->sgsn_data->mm) {
- gprs_subscr_put(subscr->sgsn_data->mm->subscr);
- subscr->sgsn_data->mm->subscr = NULL;
- subscr->sgsn_data->mm = NULL;
- }
-
- if (subscr->flags & GPRS_SUBSCRIBER_ENABLE_PURGE) {
- gprs_subscr_purge(subscr);
- subscr->flags &= ~GPRS_SUBSCRIBER_ENABLE_PURGE;
- }
-}
-
-void gprs_subscr_cancel(struct gprs_subscr *subscr)
-{
- subscr->authorized = 0;
- subscr->flags |= GPRS_SUBSCRIBER_CANCELLED;
- subscr->flags &= ~GPRS_SUBSCRIBER_ENABLE_PURGE;
-
- gprs_subscr_update(subscr);
- gprs_subscr_cleanup(subscr);
-}
-
-static int gprs_subscr_tx_gsup_message(struct gprs_subscr *subscr,
- struct osmo_gsup_message *gsup_msg)
-{
- struct msgb *msg = gsup_client_msgb_alloc();
-
- if (strlen(gsup_msg->imsi) == 0 && subscr)
- osmo_strlcpy(gsup_msg->imsi, subscr->imsi,
- sizeof(gsup_msg->imsi));
- gsup_msg->cn_domain = OSMO_GSUP_CN_DOMAIN_PS;
- osmo_gsup_encode(msg, gsup_msg);
-
- LOGGSUBSCRP(LOGL_INFO, subscr,
- "Sending GSUP, will send: %s\n", msgb_hexdump(msg));
-
- if (!sgsn->gsup_client) {
- msgb_free(msg);
- return -ENOTSUP;
- }
-
- return gsup_client_send(sgsn->gsup_client, msg);
-}
-
-static int gprs_subscr_tx_gsup_error_reply(struct gprs_subscr *subscr,
- struct osmo_gsup_message *gsup_orig,
- enum gsm48_gmm_cause cause)
-{
- struct osmo_gsup_message gsup_reply = {0};
-
- osmo_strlcpy(gsup_reply.imsi, gsup_orig->imsi,
- sizeof(gsup_reply.imsi));
- gsup_reply.cause = cause;
- gsup_reply.message_type =
- OSMO_GSUP_TO_MSGT_ERROR(gsup_orig->message_type);
-
- return gprs_subscr_tx_gsup_message(subscr, &gsup_reply);
-}
-
-static int gprs_subscr_handle_gsup_auth_res(struct gprs_subscr *subscr,
- struct osmo_gsup_message *gsup_msg)
-{
- unsigned idx;
- struct sgsn_subscriber_data *sdata = subscr->sgsn_data;
-
- LOGGSUBSCRP(LOGL_INFO, subscr,
- "Got SendAuthenticationInfoResult, num_auth_vectors = %zu\n",
- gsup_msg->num_auth_vectors);
-
- if (gsup_msg->num_auth_vectors > 0) {
- memset(sdata->auth_triplets, 0, sizeof(sdata->auth_triplets));
-
- for (idx = 0; idx < ARRAY_SIZE(sdata->auth_triplets); idx++)
- sdata->auth_triplets[idx].key_seq = GSM_KEY_SEQ_INVAL;
- }
-
- for (idx = 0; idx < gsup_msg->num_auth_vectors; idx++) {
- size_t key_seq = idx;
- LOGGSUBSCRP(LOGL_DEBUG, subscr,
- "Adding auth tuple, cksn = %zu\n", key_seq);
- if (key_seq >= ARRAY_SIZE(sdata->auth_triplets)) {
- LOGGSUBSCRP(LOGL_NOTICE, subscr,
- "Skipping auth triplet with invalid cksn %zu\n",
- key_seq);
- continue;
- }
- sdata->auth_triplets[key_seq].vec = gsup_msg->auth_vectors[idx];
- sdata->auth_triplets[key_seq].key_seq = key_seq;
- }
-
- sdata->auth_triplets_updated = 1;
- sdata->error_cause = SGSN_ERROR_CAUSE_NONE;
-
- gprs_subscr_update_auth_info(subscr);
-
- return 0;
-}
-
-static int gprs_subscr_pdp_data_clear(struct gprs_subscr *subscr)
-{
- struct sgsn_subscriber_pdp_data *pdp, *pdp2;
- int count = 0;
-
- llist_for_each_entry_safe(pdp, pdp2, &subscr->sgsn_data->pdp_list, list) {
- llist_del(&pdp->list);
- talloc_free(pdp);
- count += 1;
- }
-
- return count;
-}
-
-static struct sgsn_subscriber_pdp_data *gprs_subscr_pdp_data_get_by_id(
- struct gprs_subscr *subscr, unsigned context_id)
-{
- struct sgsn_subscriber_pdp_data *pdp;
-
- llist_for_each_entry(pdp, &subscr->sgsn_data->pdp_list, list) {
- if (pdp->context_id == context_id)
- return pdp;
- }
-
- return NULL;
-}
-
-
-static void gprs_subscr_gsup_insert_data(struct gprs_subscr *subscr,
- struct osmo_gsup_message *gsup_msg)
-{
- struct sgsn_subscriber_data *sdata = subscr->sgsn_data;
- unsigned idx;
- int rc;
-
- if (gsup_msg->msisdn_enc) {
- if (gsup_msg->msisdn_enc_len > sizeof(sdata->msisdn)) {
- LOGP(DGPRS, LOGL_ERROR, "MSISDN too long (%zu)\n",
- gsup_msg->msisdn_enc_len);
- sdata->msisdn_len = 0;
- } else {
- memcpy(sdata->msisdn, gsup_msg->msisdn_enc,
- gsup_msg->msisdn_enc_len);
- sdata->msisdn_len = gsup_msg->msisdn_enc_len;
- }
- }
-
- if (gsup_msg->hlr_enc) {
- if (gsup_msg->hlr_enc_len > sizeof(sdata->hlr)) {
- LOGP(DGPRS, LOGL_ERROR, "HLR-Number too long (%zu)\n",
- gsup_msg->hlr_enc_len);
- sdata->hlr_len = 0;
- } else {
- memcpy(sdata->hlr, gsup_msg->hlr_enc,
- gsup_msg->hlr_enc_len);
- sdata->hlr_len = gsup_msg->hlr_enc_len;
- }
- }
-
- if (gsup_msg->pdp_info_compl) {
- rc = gprs_subscr_pdp_data_clear(subscr);
- if (rc > 0)
- LOGP(DGPRS, LOGL_INFO, "Cleared existing PDP info\n");
- }
-
- for (idx = 0; idx < gsup_msg->num_pdp_infos; idx++) {
- struct osmo_gsup_pdp_info *pdp_info = &gsup_msg->pdp_infos[idx];
- size_t ctx_id = pdp_info->context_id;
- struct sgsn_subscriber_pdp_data *pdp_data;
-
- if (pdp_info->apn_enc_len >= sizeof(pdp_data->apn_str)-1) {
- LOGGSUBSCRP(LOGL_ERROR, subscr,
- "APN too long, context id = %zu, APN = %s\n",
- ctx_id, osmo_hexdump(pdp_info->apn_enc,
- pdp_info->apn_enc_len));
- continue;
- }
-
- if (pdp_info->qos_enc_len > sizeof(pdp_data->qos_subscribed)) {
- LOGGSUBSCRP(LOGL_ERROR, subscr,
- "QoS info too long (%zu)\n",
- pdp_info->qos_enc_len);
- continue;
- }
-
- LOGGSUBSCRP(LOGL_INFO, subscr,
- "Will set PDP info, context id = %zu, APN = %s\n",
- ctx_id, osmo_hexdump(pdp_info->apn_enc, pdp_info->apn_enc_len));
-
- /* Set PDP info [ctx_id] */
- pdp_data = gprs_subscr_pdp_data_get_by_id(subscr, ctx_id);
- if (!pdp_data) {
- pdp_data = sgsn_subscriber_pdp_data_alloc(subscr->sgsn_data);
- pdp_data->context_id = ctx_id;
- }
-
- OSMO_ASSERT(pdp_data != NULL);
- pdp_data->pdp_type = pdp_info->pdp_type;
- 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 gprs_subscr *subscr,
- struct osmo_gsup_message *gsup_msg)
-{
- /* contrary to MAP, we allow piggy-backing subscriber data onto
- * the UPDATE LOCATION RESULT, and don't mandate the use of a
- * separate nested INSERT SUBSCRIBER DATA transaction */
- gprs_subscr_gsup_insert_data(subscr, gsup_msg);
-
- subscr->authorized = 1;
- subscr->sgsn_data->error_cause = SGSN_ERROR_CAUSE_NONE;
-
- subscr->flags |= GPRS_SUBSCRIBER_ENABLE_PURGE;
-
- gprs_subscr_update(subscr);
- return 0;
-}
-
-static int gprs_subscr_handle_gsup_dsd_req(struct gprs_subscr *subscr,
- struct osmo_gsup_message *gsup_msg)
-{
- struct osmo_gsup_message gsup_reply = {0};
-
- if (gsup_msg->cn_domain != OSMO_GSUP_CN_DOMAIN_PS) {
- LOGGSUBSCRP(LOGL_ERROR, subscr,
- "Rx GSUP message %s not supported for CS\n",
- osmo_gsup_message_type_name(gsup_msg->message_type));
- gsup_reply.cause = GMM_CAUSE_MSGT_NOTEXIST_NOTIMPL;
- gsup_reply.message_type = OSMO_GSUP_MSGT_DELETE_DATA_ERROR;
- } else {
- gsm0408_gprs_access_cancelled(subscr->sgsn_data->mm,
- GMM_CAUSE_GPRS_NOTALLOWED);
- gsup_reply.message_type = OSMO_GSUP_MSGT_DELETE_DATA_RESULT;
- }
-
- return gprs_subscr_tx_gsup_message(subscr, &gsup_reply);
-}
-
-static int gprs_subscr_handle_gsup_isd_req(struct gprs_subscr *subscr,
- struct osmo_gsup_message *gsup_msg)
-{
- struct osmo_gsup_message gsup_reply = {0};
-
- gprs_subscr_gsup_insert_data(subscr, gsup_msg);
-
- subscr->authorized = 1;
- subscr->sgsn_data->error_cause = SGSN_ERROR_CAUSE_NONE;
- subscr->flags |= GPRS_SUBSCRIBER_ENABLE_PURGE;
- gprs_subscr_update(subscr);
-
- gsup_reply.message_type = OSMO_GSUP_MSGT_INSERT_DATA_RESULT;
- return gprs_subscr_tx_gsup_message(subscr, &gsup_reply);
-}
-
-static int check_cause(int cause)
-{
- switch (cause) {
- case GMM_CAUSE_IMSI_UNKNOWN ... GMM_CAUSE_ILLEGAL_ME:
- case GMM_CAUSE_GPRS_NOTALLOWED ... GMM_CAUSE_NO_GPRS_PLMN:
- return EACCES;
-
- case GMM_CAUSE_MSC_TEMP_NOTREACH ... GMM_CAUSE_CONGESTION:
- return EHOSTUNREACH;
-
- case GMM_CAUSE_SEM_INCORR_MSG ... GMM_CAUSE_PROTO_ERR_UNSPEC:
- default:
- return EINVAL;
- }
-}
-
-static int gprs_subscr_handle_gsup_auth_err(struct gprs_subscr *subscr,
- struct osmo_gsup_message *gsup_msg)
-{
- unsigned idx;
- struct sgsn_subscriber_data *sdata = subscr->sgsn_data;
- int cause_err;
-
- cause_err = check_cause(gsup_msg->cause);
-
- LOGGSUBSCRP(LOGL_DEBUG, subscr,
- "Send authentication info has failed with cause %d, "
- "handled as: %s\n",
- gsup_msg->cause, strerror(cause_err));
-
- switch (cause_err) {
- case EACCES:
- LOGGSUBSCRP(LOGL_NOTICE, subscr,
- "GPRS send auth info req failed, access denied, "
- "GMM cause = '%s' (%d)\n",
- get_value_string(gsm48_gmm_cause_names, gsup_msg->cause),
- gsup_msg->cause);
- /* Clear auth tuples */
- memset(sdata->auth_triplets, 0, sizeof(sdata->auth_triplets));
- for (idx = 0; idx < ARRAY_SIZE(sdata->auth_triplets); idx++)
- sdata->auth_triplets[idx].key_seq = GSM_KEY_SEQ_INVAL;
-
- subscr->authorized = 0;
- sdata->error_cause = gsup_msg->cause;
- gprs_subscr_update_auth_info(subscr);
- break;
-
- case EHOSTUNREACH:
- LOGGSUBSCRP(LOGL_NOTICE, subscr,
- "GPRS send auth info req failed, GMM cause = '%s' (%d)\n",
- get_value_string(gsm48_gmm_cause_names, gsup_msg->cause),
- gsup_msg->cause);
-
- sdata->error_cause = gsup_msg->cause;
- gprs_subscr_update_auth_info(subscr);
- break;
-
- default:
- case EINVAL:
- LOGGSUBSCRP(LOGL_ERROR, subscr,
- "GSUP protocol remote error, GMM cause = '%s' (%d)\n",
- get_value_string(gsm48_gmm_cause_names, gsup_msg->cause),
- gsup_msg->cause);
- break;
- }
-
- return -gsup_msg->cause;
-}
-
-static int gprs_subscr_handle_gsup_upd_loc_err(struct gprs_subscr *subscr,
- struct osmo_gsup_message *gsup_msg)
-{
- int cause_err;
-
- cause_err = check_cause(gsup_msg->cause);
-
- LOGGSUBSCRP(LOGL_DEBUG, subscr,
- "Update location has failed with cause %d, handled as: %s\n",
- gsup_msg->cause, strerror(cause_err));
-
- switch (cause_err) {
- case EACCES:
- LOGGSUBSCRP(LOGL_NOTICE, subscr,
- "GPRS update location failed, access denied, "
- "GMM cause = '%s' (%d)\n",
- get_value_string(gsm48_gmm_cause_names, gsup_msg->cause),
- gsup_msg->cause);
-
- subscr->authorized = 0;
- subscr->sgsn_data->error_cause = gsup_msg->cause;
- gprs_subscr_update_auth_info(subscr);
- break;
-
- case EHOSTUNREACH:
- LOGGSUBSCRP(LOGL_NOTICE, subscr,
- "GPRS update location failed, GMM cause = '%s' (%d)\n",
- get_value_string(gsm48_gmm_cause_names, gsup_msg->cause),
- gsup_msg->cause);
-
- subscr->sgsn_data->error_cause = gsup_msg->cause;
- gprs_subscr_update_auth_info(subscr);
- break;
-
- default:
- case EINVAL:
- LOGGSUBSCRP(LOGL_ERROR, subscr,
- "GSUP protocol remote error, GMM cause = '%s' (%d)\n",
- get_value_string(gsm48_gmm_cause_names, gsup_msg->cause),
- gsup_msg->cause);
- break;
- }
-
- return -gsup_msg->cause;
-}
-
-static int gprs_subscr_handle_gsup_purge_no_subscr(
- struct osmo_gsup_message *gsup_msg)
-{
- if (OSMO_GSUP_IS_MSGT_ERROR(gsup_msg->message_type)) {
- LOGGSUPP(LOGL_NOTICE, gsup_msg,
- "Purge MS has failed with cause '%s' (%d)\n",
- get_value_string(gsm48_gmm_cause_names, gsup_msg->cause),
- gsup_msg->cause);
- return -gsup_msg->cause;
- }
-
- LOGGSUPP(LOGL_INFO, gsup_msg, "Completing purge MS\n");
- return 0;
-}
-
-static int gprs_subscr_handle_gsup_purge_res(struct gprs_subscr *subscr,
- struct osmo_gsup_message *gsup_msg)
-{
- LOGGSUBSCRP(LOGL_INFO, subscr, "Completing purge MS\n");
-
- /* Force silent cancellation */
- subscr->sgsn_data->error_cause = SGSN_ERROR_CAUSE_NONE;
- gprs_subscr_cancel(subscr);
-
- return 0;
-}
-
-static int gprs_subscr_handle_gsup_purge_err(struct gprs_subscr *subscr,
- struct osmo_gsup_message *gsup_msg)
-{
- LOGGSUBSCRP(LOGL_NOTICE, subscr,
- "Purge MS has failed with cause '%s' (%d)\n",
- get_value_string(gsm48_gmm_cause_names, gsup_msg->cause),
- gsup_msg->cause);
-
- /* In GSM 09.02, 19.1.4.4, the text and the SDL diagram imply that
- * the subscriber data is not removed if the request has failed. On the
- * other hand, keeping the subscriber data in either error case
- * (subscriber unknown, syntactical message error, connection error)
- * doesn't seem to give any advantage, since the data will be restored
- * on the next Attach Request anyway.
- * This approach ensures, that the subscriber record will not stick if
- * an error happens.
- */
-
- /* TODO: Check whether this behaviour is acceptable and either just
- * remove this TODO-notice or change the implementation to not delete
- * the subscriber data (eventually resetting the ENABLE_PURGE flag and
- * restarting the expiry timer based on the cause).
- *
- * Subscriber Unknown: cancel subscr
- * Temporary network problems: do nothing (handled by timer based retry)
- * Message problems (syntax, nyi, ...): cancel subscr (retry won't help)
- */
-
- gprs_subscr_handle_gsup_purge_res(subscr, gsup_msg);
-
- return -gsup_msg->cause;
-}
-
-static int gprs_subscr_handle_loc_cancel_req(struct gprs_subscr *subscr,
- struct osmo_gsup_message *gsup_msg)
-{
- struct osmo_gsup_message gsup_reply = {0};
- int is_update_procedure = !gsup_msg->cancel_type ||
- gsup_msg->cancel_type == OSMO_GSUP_CANCEL_TYPE_UPDATE;
-
- LOGGSUBSCRP(LOGL_INFO, subscr, "Cancelling MS subscriber (%s)\n",
- is_update_procedure ?
- "update procedure" : "subscription withdraw");
-
- gsup_reply.message_type = OSMO_GSUP_MSGT_LOCATION_CANCEL_RESULT;
- gprs_subscr_tx_gsup_message(subscr, &gsup_reply);
-
- if (is_update_procedure)
- subscr->sgsn_data->error_cause = SGSN_ERROR_CAUSE_NONE;
- else
- /* Since a withdraw cause is not specified, just abort the
- * current attachment. The following re-attachment should then
- * be rejected with a proper cause value.
- */
- subscr->sgsn_data->error_cause = GMM_CAUSE_IMPL_DETACHED;
-
- gprs_subscr_cancel(subscr);
-
- return 0;
-}
-
-static int gprs_subscr_handle_unknown_imsi(struct osmo_gsup_message *gsup_msg)
-{
- if (OSMO_GSUP_IS_MSGT_REQUEST(gsup_msg->message_type)) {
- gprs_subscr_tx_gsup_error_reply(NULL, gsup_msg,
- GMM_CAUSE_IMSI_UNKNOWN);
- LOGP(DGPRS, LOGL_NOTICE,
- "Unknown IMSI %s, discarding GSUP request "
- "of type 0x%02x\n",
- gsup_msg->imsi, gsup_msg->message_type);
- } else if (OSMO_GSUP_IS_MSGT_ERROR(gsup_msg->message_type)) {
- LOGP(DGPRS, LOGL_NOTICE,
- "Unknown IMSI %s, discarding GSUP error "
- "of type 0x%02x, cause '%s' (%d)\n",
- gsup_msg->imsi, gsup_msg->message_type,
- get_value_string(gsm48_gmm_cause_names, gsup_msg->cause),
- gsup_msg->cause);
- } else {
- LOGP(DGPRS, LOGL_NOTICE,
- "Unknown IMSI %s, discarding GSUP response "
- "of type 0x%02x\n",
- gsup_msg->imsi, gsup_msg->message_type);
- }
-
- return -GMM_CAUSE_IMSI_UNKNOWN;
-}
-
-int gprs_subscr_rx_gsup_message(struct msgb *msg)
-{
- uint8_t *data = msgb_l2(msg);
- size_t data_len = msgb_l2len(msg);
- int rc = 0;
-
- struct osmo_gsup_message gsup_msg = {0};
- struct gprs_subscr *subscr;
-
- rc = osmo_gsup_decode(data, data_len, &gsup_msg);
- if (rc < 0) {
- LOGP(DGPRS, LOGL_ERROR,
- "decoding GSUP message fails with error '%s' (%d)\n",
- get_value_string(gsm48_gmm_cause_names, -rc), -rc);
- return rc;
- }
-
- if (!gsup_msg.imsi[0]) {
- LOGP(DGPRS, LOGL_ERROR, "Missing IMSI in GSUP message\n");
-
- if (OSMO_GSUP_IS_MSGT_REQUEST(gsup_msg.message_type))
- gprs_subscr_tx_gsup_error_reply(NULL, &gsup_msg,
- GMM_CAUSE_INV_MAND_INFO);
- return -GMM_CAUSE_INV_MAND_INFO;
- }
-
- if (!gsup_msg.cause && OSMO_GSUP_IS_MSGT_ERROR(gsup_msg.message_type))
- gsup_msg.cause = GMM_CAUSE_NET_FAIL;
-
- subscr = gprs_subscr_get_by_imsi(gsup_msg.imsi);
-
- if (!subscr) {
- switch (gsup_msg.message_type) {
- case OSMO_GSUP_MSGT_PURGE_MS_RESULT:
- case OSMO_GSUP_MSGT_PURGE_MS_ERROR:
- return gprs_subscr_handle_gsup_purge_no_subscr(&gsup_msg);
- default:
- return gprs_subscr_handle_unknown_imsi(&gsup_msg);
- }
- }
-
- LOGGSUBSCRP(LOGL_INFO, subscr,
- "Received GSUP message %s\n",
- osmo_gsup_message_type_name(gsup_msg.message_type));
-
- switch (gsup_msg.message_type) {
- case OSMO_GSUP_MSGT_LOCATION_CANCEL_REQUEST:
- rc = gprs_subscr_handle_loc_cancel_req(subscr, &gsup_msg);
- break;
-
- case OSMO_GSUP_MSGT_SEND_AUTH_INFO_RESULT:
- rc = gprs_subscr_handle_gsup_auth_res(subscr, &gsup_msg);
- break;
-
- case OSMO_GSUP_MSGT_SEND_AUTH_INFO_ERROR:
- rc = gprs_subscr_handle_gsup_auth_err(subscr, &gsup_msg);
- break;
-
- case OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT:
- rc = gprs_subscr_handle_gsup_upd_loc_res(subscr, &gsup_msg);
- break;
-
- case OSMO_GSUP_MSGT_UPDATE_LOCATION_ERROR:
- rc = gprs_subscr_handle_gsup_upd_loc_err(subscr, &gsup_msg);
- break;
-
- case OSMO_GSUP_MSGT_PURGE_MS_ERROR:
- rc = gprs_subscr_handle_gsup_purge_err(subscr, &gsup_msg);
- break;
-
- case OSMO_GSUP_MSGT_PURGE_MS_RESULT:
- rc = gprs_subscr_handle_gsup_purge_res(subscr, &gsup_msg);
- break;
-
- case OSMO_GSUP_MSGT_INSERT_DATA_REQUEST:
- rc = gprs_subscr_handle_gsup_isd_req(subscr, &gsup_msg);
- break;
-
- case OSMO_GSUP_MSGT_DELETE_DATA_REQUEST:
- rc = gprs_subscr_handle_gsup_dsd_req(subscr, &gsup_msg);
- break;
-
- default:
- LOGGSUBSCRP(LOGL_ERROR, subscr,
- "Rx GSUP message %s not valid at SGSN\n",
- osmo_gsup_message_type_name(gsup_msg.message_type));
- if (OSMO_GSUP_IS_MSGT_REQUEST(gsup_msg.message_type))
- gprs_subscr_tx_gsup_error_reply(
- subscr, &gsup_msg, GMM_CAUSE_MSGT_NOTEXIST_NOTIMPL);
- rc = -GMM_CAUSE_MSGT_NOTEXIST_NOTIMPL;
- break;
- };
-
- gprs_subscr_put(subscr);
-
- return rc;
-}
-
-int gprs_subscr_purge(struct gprs_subscr *subscr)
-{
- struct sgsn_subscriber_data *sdata = subscr->sgsn_data;
- struct osmo_gsup_message gsup_msg = {0};
-
- LOGGSUBSCRP(LOGL_INFO, subscr, "purging MS subscriber\n");
-
- gsup_msg.message_type = OSMO_GSUP_MSGT_PURGE_MS_REQUEST;
-
- /* Provide the HLR number in case it is known */
- gsup_msg.hlr_enc_len = sdata->hlr_len;
- gsup_msg.hlr_enc = sdata->hlr;
-
- return gprs_subscr_tx_gsup_message(subscr, &gsup_msg);
-}
-
-static int gprs_subscr_query_auth_info(struct gprs_subscr *subscr,
- const uint8_t *auts,
- const uint8_t *auts_rand)
-{
- struct osmo_gsup_message gsup_msg = {0};
-
- /* Make sure we have a complete resync or clearly no resync. */
- OSMO_ASSERT((auts != NULL) == (auts_rand != NULL));
-
- LOGGSUBSCRP(LOGL_INFO, subscr, "requesting auth info%s\n",
- auts ? " with AUTS (UMTS Resynch)" : "");
-
- gsup_msg.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST;
- gsup_msg.auts = auts;
- gsup_msg.rand = auts_rand;
- return gprs_subscr_tx_gsup_message(subscr, &gsup_msg);
-}
-
-int gprs_subscr_location_update(struct gprs_subscr *subscr)
-{
- struct osmo_gsup_message gsup_msg = {0};
-
- LOGGSUBSCRP(LOGL_INFO, subscr,
- "subscriber data is not available\n");
-
- gsup_msg.message_type = OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST;
- return gprs_subscr_tx_gsup_message(subscr, &gsup_msg);
-}
-
-void gprs_subscr_update(struct gprs_subscr *subscr)
-{
- LOGGSUBSCRP(LOGL_DEBUG, subscr, "Updating subscriber data\n");
-
- subscr->flags &= ~GPRS_SUBSCRIBER_UPDATE_LOCATION_PENDING;
- subscr->flags &= ~GPRS_SUBSCRIBER_FIRST_CONTACT;
-
- if (subscr->sgsn_data->mm)
- sgsn_update_subscriber_data(subscr->sgsn_data->mm);
-}
-
-void gprs_subscr_update_auth_info(struct gprs_subscr *subscr)
-{
- LOGGSUBSCRP(LOGL_DEBUG, subscr,
- "Updating subscriber authentication info\n");
-
- subscr->flags &= ~GPRS_SUBSCRIBER_UPDATE_AUTH_INFO_PENDING;
- subscr->flags &= ~GPRS_SUBSCRIBER_FIRST_CONTACT;
-
- if (subscr->sgsn_data->mm)
- sgsn_update_subscriber_data(subscr->sgsn_data->mm);
-}
-
-struct gprs_subscr *gprs_subscr_get_or_create_by_mmctx(struct sgsn_mm_ctx *mmctx)
-{
- struct gprs_subscr *subscr = NULL;
-
- if (mmctx->subscr)
- return gprs_subscr_get(mmctx->subscr);
-
- if (mmctx->imsi[0])
- subscr = gprs_subscr_get_by_imsi(mmctx->imsi);
-
- if (!subscr) {
- subscr = gprs_subscr_get_or_create(mmctx->imsi);
- subscr->flags |= GPRS_SUBSCRIBER_FIRST_CONTACT;
- subscr->flags &= ~GPRS_SUBSCRIBER_ENABLE_PURGE;
- }
-
- osmo_strlcpy(subscr->imei, mmctx->imei, sizeof(subscr->imei));
-
- if (subscr->lac != mmctx->ra.lac)
- subscr->lac = mmctx->ra.lac;
-
- subscr->sgsn_data->mm = mmctx;
- mmctx->subscr = gprs_subscr_get(subscr);
-
- return subscr;
-}
-
-int gprs_subscr_request_update_location(struct sgsn_mm_ctx *mmctx)
-{
- struct gprs_subscr *subscr = NULL;
- int rc;
-
- LOGMMCTXP(LOGL_DEBUG, mmctx, "Requesting subscriber data update\n");
-
- subscr = gprs_subscr_get_or_create_by_mmctx(mmctx);
-
- subscr->flags |= GPRS_SUBSCRIBER_UPDATE_LOCATION_PENDING;
-
- rc = gprs_subscr_location_update(subscr);
- gprs_subscr_put(subscr);
- return rc;
-}
-
-/*! \brief Send Update Auth Info request via GSUP, with or without resync.
- * \param[in] mmctx MM context to request authentication tuples for.
- * \param[in] auts 14 octet AUTS token for UMTS resync, or NULL.
- * \param[in] auts_rand 16 octet Random token for UMTS resync, or NULL.
- * In case of normal Authentication Info request, both \a auts and \a auts_rand
- * must be NULL. For resync, both must be non-NULL.
- */
-int gprs_subscr_request_auth_info(struct sgsn_mm_ctx *mmctx,
- const uint8_t *auts,
- const uint8_t *auts_rand)
-{
- struct gprs_subscr *subscr = NULL;
- int rc;
-
- LOGMMCTXP(LOGL_DEBUG, mmctx, "Requesting subscriber authentication info\n");
-
- subscr = gprs_subscr_get_or_create_by_mmctx(mmctx);
-
- subscr->flags |= GPRS_SUBSCRIBER_UPDATE_AUTH_INFO_PENDING;
-
- rc = gprs_subscr_query_auth_info(subscr, auts, auts_rand);
- gprs_subscr_put(subscr);
- return rc;
-}
-
-static void gprs_subscr_free(struct gprs_subscr *gsub)
-{
- llist_del(&gsub->entry);
- talloc_free(gsub);
-}
-
-struct gprs_subscr *_gprs_subscr_get(struct gprs_subscr *gsub,
- const char *file, int line)
-{
- OSMO_ASSERT(gsub->use_count < INT_MAX);
- gsub->use_count++;
- LOGPSRC(DREF, LOGL_DEBUG, file, line,
- "subscr %s usage increases to: %d\n",
- gsub->imsi, gsub->use_count);
- return gsub;
-}
-
-struct gprs_subscr *_gprs_subscr_put(struct gprs_subscr *gsub,
- const char *file, int line)
-{
- gsub->use_count--;
- LOGPSRC(DREF, gsub->use_count >= 0? LOGL_DEBUG : LOGL_ERROR,
- file, line,
- "subscr %s usage decreases to: %d%s\n",
- gsub->imsi, gsub->use_count,
- gsub->keep_in_ram? ", keep-in-ram flag is set" : "");
- if (gsub->use_count > 0)
- return gsub;
- if (gsub->keep_in_ram)
- return gsub;
- gprs_subscr_free(gsub);
- return NULL;
-}
diff --git a/src/gprs/gprs_utils.c b/src/gprs/gprs_utils.c
deleted file mode 100644
index 64ed9788d..000000000
--- a/src/gprs/gprs_utils.c
+++ /dev/null
@@ -1,274 +0,0 @@
-/* GPRS utility functions */
-
-/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
- * (C) 2010-2014 by On-Waves
- * (C) 2013 by Holger Hans Peter Freyther
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-#include <openbsc/gprs_utils.h>
-
-#include <osmocom/core/msgb.h>
-#include <osmocom/gprs/gprs_ns.h>
-
-#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
-#include <osmocom/gsm/protocol/gsm_04_08.h>
-#include <osmocom/gsm/gsm48.h>
-
-#include <string.h>
-
-/* FIXME: this needs to go to libosmocore/msgb.c */
-struct msgb *gprs_msgb_copy(const struct msgb *msg, const char *name)
-{
- struct libgb_msgb_cb *old_cb, *new_cb;
- struct msgb *new_msg;
-
- new_msg = msgb_alloc(msg->data_len, name);
- if (!new_msg)
- return NULL;
-
- /* copy data */
- memcpy(new_msg->_data, msg->_data, new_msg->data_len);
-
- /* copy header */
- new_msg->len = msg->len;
- new_msg->data += msg->data - msg->_data;
- new_msg->head += msg->head - msg->_data;
- new_msg->tail += msg->tail - msg->_data;
-
- if (msg->l1h)
- new_msg->l1h = new_msg->_data + (msg->l1h - msg->_data);
- if (msg->l2h)
- new_msg->l2h = new_msg->_data + (msg->l2h - msg->_data);
- if (msg->l3h)
- new_msg->l3h = new_msg->_data + (msg->l3h - msg->_data);
- if (msg->l4h)
- new_msg->l4h = new_msg->_data + (msg->l4h - msg->_data);
-
- /* copy GB specific data */
- old_cb = LIBGB_MSGB_CB(msg);
- new_cb = LIBGB_MSGB_CB(new_msg);
-
- if (old_cb->bssgph)
- new_cb->bssgph = new_msg->_data + (old_cb->bssgph - msg->_data);
- if (old_cb->llch)
- new_cb->llch = new_msg->_data + (old_cb->llch - msg->_data);
-
- /* bssgp_cell_id is a pointer into the old msgb, so we need to make
- * it a pointer into the new msgb */
- if (old_cb->bssgp_cell_id)
- new_cb->bssgp_cell_id = new_msg->_data +
- (old_cb->bssgp_cell_id - msg->_data);
- new_cb->nsei = old_cb->nsei;
- new_cb->bvci = old_cb->bvci;
- new_cb->tlli = old_cb->tlli;
-
- return new_msg;
-}
-
-/* TODO: Move this to libosmocore/msgb.c */
-int gprs_msgb_resize_area(struct msgb *msg, uint8_t *area,
- size_t old_size, size_t new_size)
-{
- int rc;
- uint8_t *rest = area + old_size;
- int rest_len = msg->len - old_size - (area - msg->data);
- int delta_size = (int)new_size - (int)old_size;
-
- if (delta_size == 0)
- return 0;
-
- if (delta_size > 0) {
- rc = msgb_trim(msg, msg->len + delta_size);
- if (rc < 0)
- return rc;
- }
-
- memmove(area + new_size, area + old_size, rest_len);
-
- if (msg->l1h >= rest)
- msg->l1h += delta_size;
- if (msg->l2h >= rest)
- msg->l2h += delta_size;
- if (msg->l3h >= rest)
- msg->l3h += delta_size;
- if (msg->l4h >= rest)
- msg->l4h += delta_size;
-
- if (delta_size < 0)
- msgb_trim(msg, msg->len + delta_size);
-
- return 0;
-}
-
-/* TODO: Move these conversion functions to a utils file. */
-/* TODO: consolidate with gprs_apn2str(). */
-/** memmove apn_enc to out_str, replacing the length octets in apn_enc with '.'
- * (omitting the first one) and terminating with a '\0'.
- * 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);
-}
-
-int gprs_ra_id_equals(const struct gprs_ra_id *id1,
- const struct gprs_ra_id *id2)
-{
- return (id1->mcc == id2->mcc && id1->mnc == id2->mnc &&
- id1->lac == id2->lac && id1->rac == id2->rac);
-}
diff --git a/src/gprs/gtphub.c b/src/gprs/gtphub.c
deleted file mode 100644
index 211018b53..000000000
--- a/src/gprs/gtphub.c
+++ /dev/null
@@ -1,2931 +0,0 @@
-/* GTP Hub Implementation */
-
-/* (C) 2015 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
- * All Rights Reserved
- *
- * Author: Neels Hofmeyr
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <string.h>
-#include <errno.h>
-#include <inttypes.h>
-#include <time.h>
-#include <limits.h>
-#include <unistd.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-
-#include <gtp.h>
-#include <gtpie.h>
-
-#include <openbsc/gtphub.h>
-#include <openbsc/debug.h>
-#include <openbsc/gprs_utils.h>
-
-#include <osmocom/core/utils.h>
-#include <osmocom/core/logging.h>
-#include <osmocom/core/socket.h>
-#include <osmocom/core/rate_ctr.h>
-#include <osmocom/core/stats.h>
-
-
-static const int GTPH_GC_TICK_SECONDS = 1;
-
-void *osmo_gtphub_ctx;
-
-/* Convenience makro, note: only within this C file. */
-#define LOG(level, fmt, args...) \
- LOGP(DGTPHUB, level, fmt, ##args)
-
-#define ZERO_STRUCT(struct_pointer) memset(struct_pointer, '\0', \
- sizeof(*(struct_pointer)))
-
-/* TODO move this to osmocom/core/select.h ? */
-typedef int (*osmo_fd_cb_t)(struct osmo_fd *fd, unsigned int what);
-
-/* TODO move this to osmocom/core/linuxlist.h ? */
-#define __llist_first(head) (((head)->next == (head)) ? NULL : (head)->next)
-#define llist_first(head, type, entry) \
- llist_entry(__llist_first(head), type, entry)
-
-#define __llist_last(head) (((head)->next == (head)) ? NULL : (head)->prev)
-#define llist_last(head, type, entry) \
- llist_entry(__llist_last(head), type, entry)
-
-/* TODO move GTP header stuff to openggsn/gtp/ ? See gtp_decaps*() */
-
-enum gtp_rc {
- GTP_RC_UNKNOWN = 0,
- GTP_RC_TINY = 1, /* no IEs (like ping/pong) */
- GTP_RC_PDU_C = 2, /* a real packet with IEs */
- GTP_RC_PDU_U = 3, /* a real packet with User data */
-
- GTP_RC_TOOSHORT = -1,
- GTP_RC_UNSUPPORTED_VERSION = -2,
- GTP_RC_INVALID_IE = -3,
-};
-
-struct gtp_packet_desc {
- union gtp_packet *data;
- int data_len;
- int header_len;
- int version;
- uint8_t type;
- uint16_t seq;
- uint32_t header_tei_rx;
- uint32_t header_tei;
- int rc; /* enum gtp_rc */
- unsigned int plane_idx;
- unsigned int side_idx;
- struct gtphub_tunnel *tun;
- time_t timestamp;
- union gtpie_member *ie[GTPIE_SIZE];
-};
-
-struct pending_delete {
- struct llist_head entry;
- struct expiring_item expiry_entry;
-
- struct gtphub_tunnel *tun;
- uint8_t teardown_ind;
- uint8_t nsapi;
-};
-
-
-/* counters */
-
-enum gtphub_counters_io {
- GTPH_CTR_PKTS_IN = 0,
- GTPH_CTR_PKTS_OUT,
- GTPH_CTR_BYTES_IN,
- GTPH_CTR_BYTES_OUT
-};
-
-static const struct rate_ctr_desc gtphub_counters_io_desc[] = {
- { "packets.in", "Packets ( In)" },
- { "packets.out", "Packets (Out)" },
- { "bytes.in", "Bytes ( In)" },
- { "bytes.out", "Bytes (Out)" },
-};
-
-static const struct rate_ctr_group_desc gtphub_ctrg_io_desc = {
- .group_name_prefix = "gtphub.bind",
- .group_description = "I/O Statistics",
- .num_ctr = ARRAY_SIZE(gtphub_counters_io_desc),
- .ctr_desc = gtphub_counters_io_desc,
- .class_id = OSMO_STATS_CLASS_GLOBAL,
-};
-
-
-/* support */
-
-static const char *gtp_type_str(uint8_t type)
-{
- switch (type) {
- case 1:
- return " (Echo Request)";
- case 2:
- return " (Echo Response)";
- case 16:
- return " (Create PDP Ctx Request)";
- case 17:
- return " (Create PDP Ctx Response)";
- case 18:
- return " (Update PDP Ctx Request)";
- case 19:
- return " (Update PDP Ctx Response)";
- case 20:
- return " (Delete PDP Ctx Request)";
- case 21:
- return " (Delete PDP Ctx Response)";
- case 255:
- return " (User Data)";
- default:
- return "";
- }
-}
-
-void gsn_addr_copy(struct gsn_addr *gsna, const struct gsn_addr *src)
-{
- *gsna = *src;
-}
-
-int gsn_addr_from_sockaddr(struct gsn_addr *gsna, uint16_t *port,
- const struct osmo_sockaddr *sa)
-{
- char addr_str[256];
- char port_str[6];
-
- if (osmo_sockaddr_to_strs(addr_str, sizeof(addr_str),
- port_str, sizeof(port_str),
- sa, (NI_NUMERICHOST | NI_NUMERICSERV))
- != 0) {
- return -1;
- }
-
- if (port)
- *port = atoi(port_str);
-
- return gsn_addr_from_str(gsna, addr_str);
-}
-
-int gsn_addr_from_str(struct gsn_addr *gsna, const char *numeric_addr_str)
-{
- if ((!gsna) || (!numeric_addr_str))
- return -1;
-
- int af = AF_INET;
- gsna->len = 4;
- const char *pos = numeric_addr_str;
- for (; *pos; pos++) {
- if (*pos == ':') {
- af = AF_INET6;
- gsna->len = 16;
- break;
- }
- }
-
- int rc = inet_pton(af, numeric_addr_str, gsna->buf);
- if (rc != 1) {
- LOG(LOGL_ERROR, "Cannot resolve numeric address: '%s'\n",
- numeric_addr_str);
- return -1;
- }
- return 0;
-}
-
-const char *gsn_addr_to_str(const struct gsn_addr *gsna)
-{
- static char buf[INET6_ADDRSTRLEN + 1];
- return gsn_addr_to_strb(gsna, buf, sizeof(buf));
-}
-
-const char *gsn_addr_to_strb(const struct gsn_addr *gsna,
- char *strbuf,
- int strbuf_len)
-{
- int af;
- switch (gsna->len) {
- case 4:
- af = AF_INET;
- break;
- case 16:
- af = AF_INET6;
- break;
- default:
- return NULL;
- }
-
- const char *r = inet_ntop(af, gsna->buf, strbuf, strbuf_len);
- if (!r) {
- LOG(LOGL_ERROR, "Cannot convert gsn_addr to string:"
- " %s: len=%d, buf=%s\n",
- strerror(errno),
- (int)gsna->len,
- osmo_hexdump(gsna->buf, sizeof(gsna->buf)));
- }
- return r;
-}
-
-int gsn_addr_same(const struct gsn_addr *a, const struct gsn_addr *b)
-{
- if (a == b)
- return 1;
- if ((!a) || (!b))
- return 0;
- if (a->len != b->len)
- return 0;
- return (memcmp(a->buf, b->buf, a->len) == 0)? 1 : 0;
-}
-
-static int gsn_addr_get(struct gsn_addr *gsna, const struct gtp_packet_desc *p,
- int idx)
-{
- if (p->rc != GTP_RC_PDU_C)
- return -1;
-
- unsigned int len;
- /* gtpie.h fails to declare gtpie_gettlv()'s first arg as const. */
- if (gtpie_gettlv((union gtpie_member**)p->ie, GTPIE_GSN_ADDR, idx,
- &len, gsna->buf, sizeof(gsna->buf))
- != 0)
- return -1;
- gsna->len = len;
- return 0;
-}
-
-static int gsn_addr_put(const struct gsn_addr *gsna, struct gtp_packet_desc *p,
- int idx)
-{
- if (p->rc != GTP_RC_PDU_C)
- return -1;
-
- int ie_idx;
- ie_idx = gtpie_getie(p->ie, GTPIE_GSN_ADDR, idx);
-
- if (ie_idx < 0)
- return -1;
-
- struct gtpie_tlv *ie = &p->ie[ie_idx]->tlv;
- int ie_l = ntoh16(ie->l);
- if (ie_l != gsna->len) {
- LOG(LOGL_ERROR, "Not implemented:"
- " replace an IE address of different size:"
- " replace %d with %d\n", (int)ie_l, (int)gsna->len);
- return -1;
- }
-
- memcpy(ie->v, gsna->buf, (int)ie_l);
- return 0;
-}
-
-/* Validate GTP version 0 data; analogous to validate_gtp1_header(), see there.
- */
-void validate_gtp0_header(struct gtp_packet_desc *p)
-{
- const struct gtp0_header *pheader = &(p->data->gtp0.h);
- p->rc = GTP_RC_UNKNOWN;
- p->header_len = 0;
-
- OSMO_ASSERT(p->data_len >= 1);
- OSMO_ASSERT(p->version == 0);
-
- if (p->data_len < GTP0_HEADER_SIZE) {
- LOG(LOGL_ERROR, "GTP0 packet too short: %d\n", p->data_len);
- p->rc = GTP_RC_TOOSHORT;
- return;
- }
-
- p->type = ntoh8(pheader->type);
- p->seq = ntoh16(pheader->seq);
- p->header_tei_rx = 0; /* TODO */
- p->header_tei = p->header_tei_rx;
-
- if (p->data_len == GTP0_HEADER_SIZE) {
- p->rc = GTP_RC_TINY;
- p->header_len = GTP0_HEADER_SIZE;
- return;
- }
-
- /* Check packet length field versus length of packet */
- if (p->data_len != (ntoh16(pheader->length) + GTP0_HEADER_SIZE)) {
- LOG(LOGL_ERROR, "GTP packet length field (%d + %d) does not"
- " match actual length (%d)\n",
- GTP0_HEADER_SIZE, (int)ntoh16(pheader->length),
- p->data_len);
- p->rc = GTP_RC_TOOSHORT;
- return;
- }
-
- LOG(LOGL_DEBUG, "GTP v0 TID = %" PRIu64 "\n", pheader->tid);
- p->header_len = GTP0_HEADER_SIZE;
- p->rc = GTP_RC_PDU_C;
-}
-
-/* Validate GTP version 1 data, and update p->rc with the result, as well as
- * p->header_len in case of a valid header. */
-void validate_gtp1_header(struct gtp_packet_desc *p)
-{
- const struct gtp1_header_long *pheader = &(p->data->gtp1l.h);
- p->rc = GTP_RC_UNKNOWN;
- p->header_len = 0;
-
- OSMO_ASSERT(p->data_len >= 1);
- OSMO_ASSERT(p->version == 1);
-
- if ((p->data_len < GTP1_HEADER_SIZE_LONG)
- && (p->data_len != GTP1_HEADER_SIZE_SHORT)){
- LOG(LOGL_ERROR, "GTP packet too short: %d\n", p->data_len);
- p->rc = GTP_RC_TOOSHORT;
- return;
- }
-
- p->type = ntoh8(pheader->type);
- p->header_tei_rx = ntoh32(pheader->tei);
- p->header_tei = p->header_tei_rx;
- p->seq = ntoh16(pheader->seq);
-
- LOG(LOGL_DEBUG, "| GTPv1\n");
- LOG(LOGL_DEBUG, "| type = %" PRIu8 " 0x%02" PRIx8 "\n", p->type, p->type);
- LOG(LOGL_DEBUG, "| length = %" PRIu16 " 0x%04" PRIx16 "\n", ntoh16(pheader->length), ntoh16(pheader->length));
- LOG(LOGL_DEBUG, "| TEI = %" PRIu32 " 0x%08" PRIx32 "\n", p->header_tei_rx, p->header_tei_rx);
- LOG(LOGL_DEBUG, "| seq = %" PRIu16 " 0x%04" PRIx16 "\n", p->seq, p->seq);
- LOG(LOGL_DEBUG, "| npdu = %" PRIu8 " 0x%02" PRIx8 "\n", pheader->npdu, pheader->npdu);
- LOG(LOGL_DEBUG, "| next = %" PRIu8 " 0x%02" PRIx8 "\n", pheader->next, pheader->next);
-
- if (p->data_len <= GTP1_HEADER_SIZE_LONG) {
- p->rc = GTP_RC_TINY;
- p->header_len = GTP1_HEADER_SIZE_SHORT;
- return;
- }
-
- /* Check packet length field versus length of packet */
- int announced_len = ntoh16(pheader->length) + GTP1_HEADER_SIZE_SHORT;
- if (p->data_len != announced_len) {
- LOG(LOGL_ERROR, "GTP packet length field (%d + %d) does not"
- " match actual length (%d)\n",
- GTP1_HEADER_SIZE_SHORT, (int)ntoh16(pheader->length),
- p->data_len);
- p->rc = GTP_RC_TOOSHORT;
- return;
- }
-
- p->rc = GTP_RC_PDU_C;
- p->header_len = GTP1_HEADER_SIZE_LONG;
-}
-
-/* Examine whether p->data of size p->data_len has a valid GTP header. Set
- * p->version, p->rc and p->header_len. On error, p->rc <= 0 (see enum
- * gtp_rc). p->data must point at a buffer with p->data_len set. */
-void validate_gtp_header(struct gtp_packet_desc *p)
-{
- p->rc = GTP_RC_UNKNOWN;
-
- /* Need at least 1 byte in order to check version */
- if (p->data_len < 1) {
- LOG(LOGL_ERROR, "Discarding packet - too small: %d\n",
- p->data_len);
- p->rc = GTP_RC_TOOSHORT;
- return;
- }
-
- p->version = p->data->flags >> 5;
-
- switch (p->version) {
- case 0:
- validate_gtp0_header(p);
- break;
- case 1:
- validate_gtp1_header(p);
- break;
- default:
- LOG(LOGL_ERROR, "Unsupported GTP version: %d\n", p->version);
- p->rc = GTP_RC_UNSUPPORTED_VERSION;
- break;
- }
-}
-
-
-/* Return the value of the i'th IMSI IEI by copying to *imsi.
- * The first IEI is reached by passing i = 0.
- * imsi must point at allocated space of (at least) 8 bytes.
- * Return 1 on success, or 0 if not found. */
-static int get_ie_imsi(union gtpie_member *ie[], int i, uint8_t *imsi)
-{
- return gtpie_gettv0(ie, GTPIE_IMSI, i, imsi, 8) == 0;
-}
-
-/* Analogous to get_ie_imsi(). nsapi must point at a single uint8_t. */
-static int get_ie_nsapi(union gtpie_member *ie[], int i, uint8_t *nsapi)
-{
- return gtpie_gettv1(ie, GTPIE_NSAPI, i, nsapi) == 0;
-}
-
-static char imsi_digit_to_char(uint8_t nibble)
-{
- nibble &= 0x0f;
- if (nibble > 9)
- return (nibble == 0x0f) ? '\0' : '?';
- return '0' + nibble;
-}
-
-/* Return a human readable IMSI string, in a static buffer.
- * imsi must point at 8 octets of IMSI IE encoded IMSI data. */
-static int imsi_to_str(uint8_t *imsi, const char **imsi_str)
-{
- static char str[17];
- int i;
-
- for (i = 0; i < 8; i++) {
- char c;
- c = imsi_digit_to_char(imsi[i]);
- if (c == '?')
- return -1;
- str[2*i] = c;
-
- c = imsi_digit_to_char(imsi[i] >> 4);
- if (c == '?')
- return -1;
- str[2*i + 1] = c;
- }
- str[16] = '\0';
- *imsi_str = str;
- return 1;
-}
-
-/* Return 0 if not present, 1 if present and decoded successfully, -1 if
- * present but cannot be decoded. */
-static int get_ie_imsi_str(union gtpie_member *ie[], int i,
- const char **imsi_str)
-{
- uint8_t imsi_buf[8];
- if (!get_ie_imsi(ie, i, imsi_buf))
- return 0;
- return imsi_to_str(imsi_buf, imsi_str);
-}
-
-/* Return 0 if not present, 1 if present and decoded successfully, -1 if
- * present but cannot be decoded. */
-static int get_ie_apn_str(union gtpie_member *ie[], const char **apn_str)
-{
- static char apn_buf[GSM_APN_LENGTH];
- unsigned int len;
- if (gtpie_gettlv(ie, GTPIE_APN, 0,
- &len, apn_buf, sizeof(apn_buf)) != 0)
- return 0;
-
- if (len < 2) {
- LOG(LOGL_ERROR, "APN IE: invalid length: %d\n",
- (int)len);
- return -1;
- }
-
- if (len > (sizeof(apn_buf) - 1))
- len = sizeof(apn_buf) - 1;
- apn_buf[len] = '\0';
-
- *apn_str = gprs_apn_to_str(apn_buf, (uint8_t*)apn_buf, len);
- if (!(*apn_str)) {
- LOG(LOGL_ERROR, "APN IE: present but cannot be decoded: %s\n",
- osmo_hexdump((uint8_t*)apn_buf, len));
- return -1;
- }
- return 1;
-}
-
-
-/* Validate header, and index information elements. Write decoded packet
- * information to *res. res->data will point at the given data buffer. On
- * error, p->rc is set <= 0 (see enum gtp_rc). */
-static void gtp_decode(const uint8_t *data, int data_len,
- unsigned int from_side_idx,
- unsigned int from_plane_idx,
- struct gtp_packet_desc *res,
- time_t now)
-{
- ZERO_STRUCT(res);
- res->data = (union gtp_packet*)data;
- res->data_len = data_len;
- res->side_idx = from_side_idx;
- res->plane_idx = from_plane_idx;
- res->timestamp = now;
-
- validate_gtp_header(res);
-
- if (res->rc <= 0)
- return;
-
- LOG(LOGL_DEBUG, "Valid GTP header (v%d)\n", res->version);
-
- if (from_plane_idx == GTPH_PLANE_USER) {
- res->rc = GTP_RC_PDU_U;
- return;
- }
-
- if (res->rc != GTP_RC_PDU_C) {
- LOG(LOGL_DEBUG, "no IEs in this GTP packet\n");
- return;
- }
-
- if (gtpie_decaps(res->ie, res->version,
- (void*)(data + res->header_len),
- res->data_len - res->header_len) != 0) {
- res->rc = GTP_RC_INVALID_IE;
- LOG(LOGL_ERROR, "INVALID: cannot decode IEs."
- " Dropping GTP packet%s.\n",
- gtp_type_str(res->type)
- );
- return;
- }
-
-#if 1
- /* TODO if (<loglevel is debug>)
- (waiting for a commit from jerlbeck) */
- int i;
-
- for (i = 0; i < 10; i++) {
- const char *imsi;
- if (get_ie_imsi_str(res->ie, i, &imsi) < 1)
- break;
- LOG(LOGL_DEBUG, "| IMSI %s\n", imsi);
- }
-
- for (i = 0; i < 10; i++) {
- uint8_t nsapi;
- if (!get_ie_nsapi(res->ie, i, &nsapi))
- break;
- LOG(LOGL_DEBUG, "| NSAPI %d\n", (int)nsapi);
- }
-
- for (i = 0; i < 2; i++) {
- struct gsn_addr addr;
- if (gsn_addr_get(&addr, res, i) == 0)
- LOG(LOGL_DEBUG, "| addr %s\n", gsn_addr_to_str(&addr));
- }
-
- for (i = 0; i < 10; i++) {
- uint32_t tei;
- if (gtpie_gettv4(res->ie, GTPIE_TEI_DI, i, &tei) != 0)
- break;
- LOG(LOGL_DEBUG, "| TEI DI (USER) %" PRIu32 " 0x%08" PRIx32 "\n",
- tei, tei);
- }
-
- for (i = 0; i < 10; i++) {
- uint32_t tei;
- if (gtpie_gettv4(res->ie, GTPIE_TEI_C, i, &tei) != 0)
- break;
- LOG(LOGL_DEBUG, "| TEI (CTRL) %" PRIu32 " 0x%08" PRIx32 "\n",
- tei, tei);
- }
-#endif
-}
-
-
-/* expiry */
-
-void expiry_init(struct expiry *exq, int expiry_in_seconds)
-{
- ZERO_STRUCT(exq);
- exq->expiry_in_seconds = expiry_in_seconds;
- INIT_LLIST_HEAD(&exq->items);
-}
-
-void expiry_add(struct expiry *exq, struct expiring_item *item, time_t now)
-{
- item->expiry = now + exq->expiry_in_seconds;
-
- OSMO_ASSERT(llist_empty(&exq->items)
- || (item->expiry
- >= llist_last(&exq->items, struct expiring_item, entry)->expiry));
-
- /* Add/move to the tail to always sort by expiry, ascending. */
- llist_del(&item->entry);
- llist_add_tail(&item->entry, &exq->items);
-}
-
-int expiry_tick(struct expiry *exq, time_t now)
-{
- int expired = 0;
- struct expiring_item *m, *n;
- llist_for_each_entry_safe(m, n, &exq->items, entry) {
- if (m->expiry <= now) {
- expiring_item_del(m);
- expired ++;
- } else {
- /* The items are added sorted by expiry. So when we hit
- * an unexpired entry, only more unexpired ones will
- * follow. */
- break;
- }
- }
- return expired;
-}
-
-void expiry_clear(struct expiry *exq)
-{
- struct expiring_item *m, *n;
- llist_for_each_entry_safe(m, n, &exq->items, entry) {
- expiring_item_del(m);
- }
-}
-
-void expiring_item_init(struct expiring_item *item)
-{
- ZERO_STRUCT(item);
- INIT_LLIST_HEAD(&item->entry);
-}
-
-void expiring_item_del(struct expiring_item *item)
-{
- OSMO_ASSERT(item);
- llist_del(&item->entry);
- INIT_LLIST_HEAD(&item->entry);
- if (item->del_cb) {
- /* avoid loops */
- del_cb_t del_cb = item->del_cb;
- item->del_cb = 0;
- (del_cb)(item);
- }
-}
-
-
-/* nr_map, nr_pool */
-
-void nr_pool_init(struct nr_pool *pool, nr_t nr_min, nr_t nr_max)
-{
- *pool = (struct nr_pool){
- .nr_min = nr_min,
- .nr_max = nr_max,
- .last_nr = nr_max
- };
-}
-
-nr_t nr_pool_next(struct nr_pool *pool)
-{
- if (pool->last_nr >= pool->nr_max)
- pool->last_nr = pool->nr_min;
- else
- pool->last_nr ++;
-
- return pool->last_nr;
-}
-
-void nr_map_init(struct nr_map *map, struct nr_pool *pool,
- struct expiry *exq)
-{
- ZERO_STRUCT(map);
- map->pool = pool;
- map->add_items_to_expiry = exq;
- INIT_LLIST_HEAD(&map->mappings);
-}
-
-void nr_mapping_init(struct nr_mapping *m)
-{
- ZERO_STRUCT(m);
- INIT_LLIST_HEAD(&m->entry);
- expiring_item_init(&m->expiry_entry);
-}
-
-void nr_map_add(struct nr_map *map, struct nr_mapping *mapping, time_t now)
-{
- /* Generate a mapped number */
- mapping->repl = nr_pool_next(map->pool);
-
- /* Add to the tail to always yield a list sorted by expiry, in
- * ascending order. */
- llist_add_tail(&mapping->entry, &map->mappings);
- nr_map_refresh(map, mapping, now);
-}
-
-void nr_map_refresh(struct nr_map *map, struct nr_mapping *mapping, time_t now)
-{
- if (!map->add_items_to_expiry)
- return;
- expiry_add(map->add_items_to_expiry,
- &mapping->expiry_entry,
- now);
-}
-
-void nr_map_clear(struct nr_map *map)
-{
- struct nr_mapping *m;
- struct nr_mapping *n;
- llist_for_each_entry_safe(m, n, &map->mappings, entry) {
- nr_mapping_del(m);
- }
-}
-
-int nr_map_empty(const struct nr_map *map)
-{
- return llist_empty(&map->mappings);
-}
-
-struct nr_mapping *nr_map_get(const struct nr_map *map,
- void *origin, nr_t nr_orig)
-{
- struct nr_mapping *mapping;
- llist_for_each_entry(mapping, &map->mappings, entry) {
- if ((mapping->origin == origin)
- && (mapping->orig == nr_orig))
- return mapping;
- }
- /* Not found. */
- return NULL;
-}
-
-struct nr_mapping *nr_map_get_inv(const struct nr_map *map, nr_t nr_repl)
-{
- struct nr_mapping *mapping;
- llist_for_each_entry(mapping, &map->mappings, entry) {
- if (mapping->repl == nr_repl) {
- return mapping;
- }
- }
- /* Not found. */
- return NULL;
-}
-
-void nr_mapping_del(struct nr_mapping *mapping)
-{
- OSMO_ASSERT(mapping);
- llist_del(&mapping->entry);
- INIT_LLIST_HEAD(&mapping->entry);
- expiring_item_del(&mapping->expiry_entry);
-}
-
-
-/* gtphub */
-
-const char* const gtphub_plane_idx_names[GTPH_PLANE_N] = {
- "CTRL",
- "USER",
-};
-
-const uint16_t gtphub_plane_idx_default_port[GTPH_PLANE_N] = {
- 2123,
- 2152,
-};
-
-const char* const gtphub_side_idx_names[GTPH_SIDE_N] = {
- "SGSN",
- "GGSN",
-};
-
-time_t gtphub_now(void)
-{
- struct timespec now_tp;
- OSMO_ASSERT(clock_gettime(CLOCK_MONOTONIC, &now_tp) >= 0);
- return now_tp.tv_sec;
-}
-
-/* Remove a gtphub_peer from its list and free it. */
-static void gtphub_peer_del(struct gtphub_peer *peer)
-{
- OSMO_ASSERT(llist_empty(&peer->addresses));
- nr_map_clear(&peer->seq_map);
- llist_del(&peer->entry);
- talloc_free(peer);
-}
-
-static void gtphub_peer_addr_del(struct gtphub_peer_addr *pa)
-{
- OSMO_ASSERT(llist_empty(&pa->ports));
- llist_del(&pa->entry);
- talloc_free(pa);
-}
-
-static void gtphub_peer_port_del(struct gtphub_peer_port *pp)
-{
- OSMO_ASSERT(pp->ref_count == 0);
- llist_del(&pp->entry);
- rate_ctr_group_free(pp->counters_io);
- talloc_free(pp);
-}
-
-/* From the information in the gtp_packet_desc, return the address of a GGSN.
- * Return -1 on error. */
-static int gtphub_resolve_ggsn(struct gtphub *hub,
- struct gtp_packet_desc *p,
- struct gtphub_peer_port **pp);
-
-/* See gtphub_ext.c (wrapped by unit test) */
-struct gtphub_peer_port *gtphub_resolve_ggsn_addr(struct gtphub *hub,
- const char *imsi_str,
- const char *apn_ni_str);
-int gtphub_ares_init(struct gtphub *hub);
-
-static void gtphub_zero(struct gtphub *hub)
-{
- ZERO_STRUCT(hub);
- INIT_LLIST_HEAD(&hub->ggsn_lookups);
- INIT_LLIST_HEAD(&hub->resolved_ggsns);
-}
-
-static int gtphub_sock_init(struct osmo_fd *ofd,
- const struct gtphub_cfg_addr *addr,
- osmo_fd_cb_t cb,
- void *data,
- int ofd_id)
-{
- if (!addr->addr_str) {
- LOG(LOGL_FATAL, "Cannot bind: empty address.\n");
- return -1;
- }
- if (!addr->port) {
- LOG(LOGL_FATAL, "Cannot bind: zero port not permitted.\n");
- return -1;
- }
-
- ofd->when = BSC_FD_READ;
- ofd->cb = cb;
- ofd->data = data;
- ofd->priv_nr = ofd_id;
-
- int rc;
- rc = osmo_sock_init_ofd(ofd,
- AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
- addr->addr_str, addr->port,
- OSMO_SOCK_F_BIND);
- if (rc < 1) {
- LOG(LOGL_FATAL, "Cannot bind to %s port %d (rc %d)\n",
- addr->addr_str, (int)addr->port, rc);
- return -1;
- }
-
- return 0;
-}
-
-static void gtphub_sock_close(struct osmo_fd *ofd)
-{
- close(ofd->fd);
- osmo_fd_unregister(ofd);
- ofd->cb = NULL;
-}
-
-static void gtphub_bind_init(struct gtphub_bind *b)
-{
- ZERO_STRUCT(b);
-
- INIT_LLIST_HEAD(&b->peers);
-
- b->counters_io = rate_ctr_group_alloc(osmo_gtphub_ctx,
- &gtphub_ctrg_io_desc, 0);
- OSMO_ASSERT(b->counters_io);
-}
-
-static int gtphub_bind_start(struct gtphub_bind *b,
- const struct gtphub_cfg_bind *cfg,
- osmo_fd_cb_t cb, void *cb_data,
- unsigned int ofd_id)
-{
- LOG(LOGL_DEBUG, "Starting bind %s\n", b->label);
- if (gsn_addr_from_str(&b->local_addr, cfg->bind.addr_str) != 0) {
- LOG(LOGL_FATAL, "Invalid bind address for %s: %s\n",
- b->label, cfg->bind.addr_str);
- return -1;
- }
- if (gtphub_sock_init(&b->ofd, &cfg->bind, cb, cb_data, ofd_id) != 0) {
- LOG(LOGL_FATAL, "Cannot bind for %s: %s\n",
- b->label, cfg->bind.addr_str);
- return -1;
- }
- b->local_port = cfg->bind.port;
- return 0;
-}
-
-static void gtphub_bind_free(struct gtphub_bind *b)
-{
- OSMO_ASSERT(llist_empty(&b->peers));
- rate_ctr_group_free(b->counters_io);
-}
-
-static void gtphub_bind_stop(struct gtphub_bind *b) {
- gtphub_sock_close(&b->ofd);
- gtphub_bind_free(b);
-}
-
-/* Recv datagram from from->fd, write sender's address to *from_addr.
- * Return the number of bytes read, zero on error. */
-static int gtphub_read(const struct osmo_fd *from,
- struct osmo_sockaddr *from_addr,
- uint8_t *buf, size_t buf_len)
-{
- OSMO_ASSERT(from_addr);
-
- /* recvfrom requires the available length set in *from_addr_len. */
- from_addr->l = sizeof(from_addr->a);
- errno = 0;
- ssize_t received = recvfrom(from->fd, buf, buf_len, 0,
- (struct sockaddr*)&from_addr->a,
- &from_addr->l);
- /* TODO use recvmsg and get a MSG_TRUNC flag to make sure the message
- * is not truncated. Then maybe reduce buf's size. */
-
- if (received <= 0) {
- LOG((errno == EAGAIN? LOGL_DEBUG : LOGL_ERROR),
- "error: %s\n", strerror(errno));
- return 0;
- }
-
- LOG(LOGL_DEBUG, "Received %d bytes from %s: %s%s\n",
- (int)received, osmo_sockaddr_to_str(from_addr),
- osmo_hexdump(buf, received > 1000? 1000 : received),
- received > 1000 ? "..." : "");
-
- return received;
-}
-
-static inline void gtphub_port_ref_count_inc(struct gtphub_peer_port *pp)
-{
- OSMO_ASSERT(pp);
- OSMO_ASSERT(pp->ref_count < UINT_MAX);
- pp->ref_count++;
-}
-
-static inline void gtphub_port_ref_count_dec(struct gtphub_peer_port *pp)
-{
- OSMO_ASSERT(pp);
- OSMO_ASSERT(pp->ref_count > 0);
- pp->ref_count--;
-}
-
-static inline void set_seq(struct gtp_packet_desc *p, uint16_t seq)
-{
- OSMO_ASSERT(p->version == 1);
- p->data->gtp1l.h.seq = hton16(seq);
- p->seq = seq;
-}
-
-static inline void set_tei(struct gtp_packet_desc *p, uint32_t tei)
-{
- OSMO_ASSERT(p->version == 1);
- p->data->gtp1l.h.tei = hton32(tei);
- p->header_tei = tei;
-}
-
-static void gtphub_mapping_del_cb(struct expiring_item *expi);
-
-static struct nr_mapping *gtphub_mapping_new()
-{
- struct nr_mapping *nrm;
- nrm = talloc_zero(osmo_gtphub_ctx, struct nr_mapping);
- OSMO_ASSERT(nrm);
-
- nr_mapping_init(nrm);
- nrm->expiry_entry.del_cb = gtphub_mapping_del_cb;
- return nrm;
-}
-
-
-#define APPEND(args...) \
- l = snprintf(pos, left, args); \
- pos += l; \
- left -= l
-
-static const char *gtphub_tunnel_side_str(struct gtphub_tunnel *tun,
- int side_idx)
-{
- static char buf[256];
- char *pos = buf;
- int left = sizeof(buf);
- int l;
-
- struct gtphub_tunnel_endpoint *c, *u;
- c = &tun->endpoint[side_idx][GTPH_PLANE_CTRL];
- u = &tun->endpoint[side_idx][GTPH_PLANE_USER];
-
- /* print both only if they differ. */
- if (!c->peer) {
- APPEND("(uninitialized)");
- } else {
- APPEND("%s", gsn_addr_to_str(&c->peer->peer_addr->addr));
- }
-
- if (!u->peer) {
- if (c->peer) {
- APPEND("/(uninitialized)");
- }
- } else if ((!c->peer)
- || (!gsn_addr_same(&u->peer->peer_addr->addr,
- &c->peer->peer_addr->addr))) {
- APPEND("/%s", gsn_addr_to_str(&u->peer->peer_addr->addr));
- }
-
- APPEND(" (TEI C=%x U=%x)",
- c->tei_orig,
- u->tei_orig);
- return buf;
-}
-
-const char *gtphub_tunnel_str(struct gtphub_tunnel *tun)
-{
- static char buf[512];
- char *pos = buf;
- int left = sizeof(buf);
- int l;
-
- if (!tun)
- return "null-tunnel";
-
- APPEND("TEI=%x: ", tun->tei_repl);
- APPEND("%s", gtphub_tunnel_side_str(tun, GTPH_SIDE_SGSN));
- APPEND(" <-> %s", gtphub_tunnel_side_str(tun, GTPH_SIDE_GGSN));
-
- return buf;
-}
-
-#undef APPEND
-
-void gtphub_tunnel_endpoint_set_peer(struct gtphub_tunnel_endpoint *te,
- struct gtphub_peer_port *pp)
-{
- if (te->peer)
- gtphub_port_ref_count_dec(te->peer);
- te->peer = pp;
- if (te->peer)
- gtphub_port_ref_count_inc(te->peer);
-}
-
-int gtphub_tunnel_complete(struct gtphub_tunnel *tun)
-{
- if (!tun)
- return 0;
- if (!tun->tei_repl)
- return 0;
- int side_idx;
- int plane_idx;
- for_each_side_and_plane(side_idx, plane_idx) {
- struct gtphub_tunnel_endpoint *te =
- &tun->endpoint[side_idx][plane_idx];
- if (!(te->peer && te->tei_orig))
- return 0;
- }
- return 1;
-}
-
-static void gtphub_tunnel_del_cb(struct expiring_item *expi)
-{
- struct gtphub_tunnel *tun = container_of(expi,
- struct gtphub_tunnel,
- expiry_entry);
- LOG(LOGL_DEBUG, "expired: %s\n", gtphub_tunnel_str(tun));
-
- llist_del(&tun->entry);
- INIT_LLIST_HEAD(&tun->entry); /* mark unused */
-
- expi->del_cb = 0; /* avoid recursion loops */
- expiring_item_del(&tun->expiry_entry); /* usually already done, but make sure. */
-
- int side_idx;
- int plane_idx;
- for_each_side_and_plane(side_idx, plane_idx) {
- struct gtphub_tunnel_endpoint *te = &tun->endpoint[side_idx][plane_idx];
-
- /* clear ref count */
- gtphub_tunnel_endpoint_set_peer(te, NULL);
-
- rate_ctr_group_free(te->counters_io);
- }
-
- talloc_free(tun);
-}
-
-static struct gtphub_tunnel *gtphub_tunnel_new()
-{
- struct gtphub_tunnel *tun;
- tun = talloc_zero(osmo_gtphub_ctx, struct gtphub_tunnel);
- OSMO_ASSERT(tun);
-
- INIT_LLIST_HEAD(&tun->entry);
- expiring_item_init(&tun->expiry_entry);
-
- int side_idx, plane_idx;
- for_each_side_and_plane(side_idx, plane_idx) {
- struct gtphub_tunnel_endpoint *te = &tun->endpoint[side_idx][plane_idx];
- te->counters_io = rate_ctr_group_alloc(osmo_gtphub_ctx,
- &gtphub_ctrg_io_desc,
- 0);
- OSMO_ASSERT(te->counters_io);
- }
-
- tun->expiry_entry.del_cb = gtphub_tunnel_del_cb;
- return tun;
-}
-
-static const char *gtphub_peer_strb(struct gtphub_peer *peer, char *buf,
- int buflen)
-{
- if (llist_empty(&peer->addresses))
- return "(addressless)";
-
- struct gtphub_peer_addr *a = llist_first(&peer->addresses,
- struct gtphub_peer_addr,
- entry);
- return gsn_addr_to_strb(&a->addr, buf, buflen);
-}
-
-static const char *gtphub_port_strb(struct gtphub_peer_port *port, char *buf,
- int buflen)
-{
- if (!port)
- return "(null port)";
-
- snprintf(buf, buflen, "%s port %d",
- gsn_addr_to_str(&port->peer_addr->addr),
- (int)port->port);
- return buf;
-}
-
-const char *gtphub_peer_str(struct gtphub_peer *peer)
-{
- static char buf[256];
- return gtphub_peer_strb(peer, buf, sizeof(buf));
-}
-
-const char *gtphub_port_str(struct gtphub_peer_port *port)
-{
- static char buf[256];
- return gtphub_port_strb(port, buf, sizeof(buf));
-}
-
-static const char *gtphub_port_str2(struct gtphub_peer_port *port)
-{
- static char buf[256];
- return gtphub_port_strb(port, buf, sizeof(buf));
-}
-
-static void gtphub_mapping_del_cb(struct expiring_item *expi)
-{
- expi->del_cb = 0; /* avoid recursion loops */
- expiring_item_del(expi); /* usually already done, but make sure. */
-
- struct nr_mapping *nrm = container_of(expi,
- struct nr_mapping,
- expiry_entry);
- llist_del(&nrm->entry);
- INIT_LLIST_HEAD(&nrm->entry); /* mark unused */
-
- /* Just for log */
- struct gtphub_peer_port *from = nrm->origin;
- OSMO_ASSERT(from);
- LOG(LOGL_DEBUG, "expired: %d: nr mapping from %s: %u->%u\n",
- (int)nrm->expiry_entry.expiry,
- gtphub_port_str(from),
- (unsigned int)nrm->orig, (unsigned int)nrm->repl);
-
- gtphub_port_ref_count_dec(from);
-
- talloc_free(nrm);
-}
-
-static struct nr_mapping *gtphub_mapping_have(struct nr_map *map,
- struct gtphub_peer_port *from,
- nr_t orig_nr,
- time_t now)
-{
- struct nr_mapping *nrm;
-
- nrm = nr_map_get(map, from, orig_nr);
-
- if (!nrm) {
- nrm = gtphub_mapping_new();
- nrm->orig = orig_nr;
- nrm->origin = from;
- nr_map_add(map, nrm, now);
- gtphub_port_ref_count_inc(from);
- LOG(LOGL_DEBUG, "peer %s: sequence map %d --> %d\n",
- gtphub_port_str(from),
- (int)(nrm->orig), (int)(nrm->repl));
- } else {
- nr_map_refresh(map, nrm, now);
- }
-
- OSMO_ASSERT(nrm);
- return nrm;
-}
-
-static void gtphub_map_seq(struct gtp_packet_desc *p,
- struct gtphub_peer_port *from_port,
- struct gtphub_peer_port *to_port)
-{
- /* Store a mapping in to_peer's map, so when we later receive a GTP
- * packet back from to_peer, the seq nr can be unmapped back to its
- * origin (from_peer here). */
- struct nr_mapping *nrm;
- nrm = gtphub_mapping_have(&to_port->peer_addr->peer->seq_map,
- from_port, p->seq, p->timestamp);
-
- /* Change the GTP packet to yield the new, mapped seq nr */
- set_seq(p, nrm->repl);
-}
-
-static struct gtphub_peer_port *gtphub_unmap_seq(struct gtp_packet_desc *p,
- struct gtphub_peer_port *responding_port)
-{
- OSMO_ASSERT(p->version == 1);
- struct nr_mapping *nrm =
- nr_map_get_inv(&responding_port->peer_addr->peer->seq_map,
- p->seq);
- if (!nrm)
- return NULL;
- LOG(LOGL_DEBUG, "peer %p: sequence unmap %d <-- %d\n",
- nrm->origin, (int)(nrm->orig), (int)(nrm->repl));
- set_seq(p, nrm->orig);
- return nrm->origin;
-}
-
-static int gtphub_check_mapped_tei(struct gtphub_tunnel *new_tun,
- struct gtphub_tunnel *iterated_tun,
- uint32_t *tei_min,
- uint32_t *tei_max)
-{
- if (!new_tun->tei_repl || !iterated_tun->tei_repl)
- return 1;
-
- *tei_min = (*tei_min < iterated_tun->tei_repl)? *tei_min : iterated_tun->tei_repl;
- *tei_max = (*tei_max > iterated_tun->tei_repl)? *tei_max : iterated_tun->tei_repl;
-
- if (new_tun->tei_repl != iterated_tun->tei_repl)
- return 1;
-
- /* new_tun->tei_repl is already taken. Try to find one out of the known
- * range. */
- LOG(LOGL_DEBUG, "TEI replacement %d already taken.\n", new_tun->tei_repl);
-
- if ((*tei_max) < 0xffffffff) {
- (*tei_max)++;
- new_tun->tei_repl = *tei_max;
- LOG(LOGL_DEBUG, "Using TEI %d instead.\n", new_tun->tei_repl);
- return 1;
- } else if ((*tei_min) > 1) {
- (*tei_min)--;
- new_tun->tei_repl = *tei_min;
- LOG(LOGL_DEBUG, "Using TEI %d instead.\n", new_tun->tei_repl);
- return 1;
- }
-
- /* None seems to be available. */
- return 0;
-}
-
-static int gtphub_check_reused_teis(struct gtphub *hub,
- struct gtphub_tunnel *new_tun)
-{
- uint32_t tei_min = 0xffffffff;
- uint32_t tei_max = 0;
- int side_idx;
- int plane_idx;
- struct gtphub_tunnel_endpoint *te;
- struct gtphub_tunnel_endpoint *te2;
-
- struct gtphub_tunnel *tun, *ntun;
-
- llist_for_each_entry_safe(tun, ntun, &hub->tunnels, entry) {
- if (tun == new_tun)
- continue;
-
- /* Check whether the GSN sent a TEI that it is reusing from a
- * previous tunnel. */
- int tun_continue = 0;
- for_each_side(side_idx) {
- for_each_plane(plane_idx) {
- te = &tun->endpoint[side_idx][plane_idx];
- te2 = &new_tun->endpoint[side_idx][plane_idx];
- if ((te->tei_orig == 0)
- || (te->tei_orig != te2->tei_orig)
- || (!te->peer)
- || (!te2->peer)
- || !gsn_addr_same(&te->peer->peer_addr->addr,
- &te2->peer->peer_addr->addr))
- continue;
-
- /* The peer is reusing a TEI that I believe to
- * be part of another tunnel. The other tunnel
- * must be stale, then. */
- LOG(LOGL_NOTICE,
- "Expiring tunnel due to reused TEI:"
- " %s peer %s sent %s TEI %x,"
- " previously used by tunnel %s...\n",
- gtphub_side_idx_names[side_idx],
- gtphub_port_str(te->peer),
- gtphub_plane_idx_names[plane_idx],
- te->tei_orig,
- gtphub_tunnel_str(tun));
- LOG(LOGL_NOTICE, "...while establishing tunnel %s\n",
- gtphub_tunnel_str(new_tun));
-
- expiring_item_del(&tun->expiry_entry);
- /* continue to find more matches. There shouldn't be
- * any, but let's make sure. However, tun is deleted,
- * so we need to skip to the next tunnel. */
- tun_continue = 1;
- break;
- }
- if (tun_continue)
- break;
- }
- if (tun_continue)
- continue;
-
- /* Check whether the mapped TEI is already used by another
- * tunnel. */
- if (!gtphub_check_mapped_tei(new_tun, tun, &tei_min, &tei_max)) {
- LOG(LOGL_ERROR,
- "No mapped TEI is readily available."
- " Searching for holes between occupied"
- " TEIs not implemented.");
- return 0;
- }
-
- }
-
- return 1;
-}
-
-static void gtphub_tunnel_refresh(struct gtphub *hub,
- struct gtphub_tunnel *tun,
- time_t now)
-{
- expiry_add(&hub->expire_slowly,
- &tun->expiry_entry,
- now);
-}
-
-static struct gtphub_tunnel_endpoint *gtphub_unmap_tei(struct gtphub *hub,
- struct gtp_packet_desc *p,
- struct gtphub_peer_port *from,
- struct gtphub_tunnel **unmapped_from_tun)
-{
- OSMO_ASSERT(from);
- int other_side = other_side_idx(p->side_idx);
-
- struct gtphub_tunnel *tun;
- llist_for_each_entry(tun, &hub->tunnels, entry) {
- struct gtphub_tunnel_endpoint *te_from =
- &tun->endpoint[p->side_idx][p->plane_idx];
- struct gtphub_tunnel_endpoint *te_to =
- &tun->endpoint[other_side][p->plane_idx];
- if ((tun->tei_repl == p->header_tei_rx)
- && te_from->peer
- && gsn_addr_same(&te_from->peer->peer_addr->addr,
- &from->peer_addr->addr)) {
- gtphub_tunnel_refresh(hub, tun, p->timestamp);
- if (unmapped_from_tun)
- *unmapped_from_tun = tun;
- return te_to;
- }
- }
-
- if (unmapped_from_tun)
- *unmapped_from_tun = NULL;
- return NULL;
-}
-
-static void gtphub_map_restart_counter(struct gtphub *hub,
- struct gtp_packet_desc *p)
-{
- if (p->rc != GTP_RC_PDU_C)
- return;
-
- int ie_idx;
- ie_idx = gtpie_getie(p->ie, GTPIE_RECOVERY, 0);
- if (ie_idx < 0)
- return;
-
- /* Always send gtphub's own restart counter */
- p->ie[ie_idx]->tv1.v = hton8(hub->restart_counter);
-}
-
-static int gtphub_unmap_header_tei(struct gtphub_peer_port **to_port_p,
- struct gtphub_tunnel **unmapped_from_tun,
- struct gtphub *hub,
- struct gtp_packet_desc *p,
- struct gtphub_peer_port *from_port)
-{
- OSMO_ASSERT(p->version == 1);
- *to_port_p = NULL;
- if (unmapped_from_tun)
- *unmapped_from_tun = NULL;
-
- /* If the header's TEI is zero, no PDP context has been established
- * yet. If nonzero, a mapping should actually already exist for this
- * TEI, since it must have been announced in a PDP context creation. */
- if (!p->header_tei_rx)
- return 0;
-
- /* to_peer has previously announced a TEI, which was stored and
- * mapped in a tunnel struct. */
- struct gtphub_tunnel_endpoint *to;
- to = gtphub_unmap_tei(hub, p, from_port, unmapped_from_tun);
- if (!to) {
- LOG(LOGL_ERROR, "Received unknown TEI %" PRIx32 " from %s\n",
- p->header_tei_rx, gtphub_port_str(from_port));
- return -1;
- }
-
- if (unmapped_from_tun) {
- OSMO_ASSERT(*unmapped_from_tun);
- LOG(LOGL_DEBUG, "Unmapped TEI coming from: %s\n",
- gtphub_tunnel_str(*unmapped_from_tun));
- }
-
- uint32_t unmapped_tei = to->tei_orig;
- set_tei(p, unmapped_tei);
-
- /* May be NULL for an invalidated tunnel. */
- *to_port_p = to->peer;
-
- return 0;
-}
-
-static int gtphub_handle_create_pdp_ctx(struct gtphub *hub,
- struct gtp_packet_desc *p,
- struct gtphub_peer_port *from_ctrl,
- struct gtphub_peer_port *to_ctrl)
-{
- int plane_idx;
-
- osmo_static_assert((GTPH_PLANE_CTRL == 0) && (GTPH_PLANE_USER == 1),
- plane_nrs_match_GSN_addr_IE_indices);
-
- struct gtphub_tunnel *tun = p->tun;
-
- if (p->type == GTP_CREATE_PDP_REQ) {
- if (p->side_idx != GTPH_SIDE_SGSN) {
- LOG(LOGL_ERROR, "Wrong side: Create PDP Context"
- " Request from the GGSN side: %s",
- gtphub_port_str(from_ctrl));
- return -1;
- }
-
- if (tun) {
- LOG(LOGL_ERROR, "Not implemented: Received"
- " Create PDP Context Request for an already"
- " established tunnel:"
- " from %s, tunnel %s\n",
- gtphub_port_str(from_ctrl),
- gtphub_tunnel_str(p->tun));
- return -1;
- }
-
- /* A new tunnel. */
- p->tun = tun = gtphub_tunnel_new();
-
- /* Create TEI mapping */
- tun->tei_repl = nr_pool_next(&hub->tei_pool);
-
- llist_add(&tun->entry, &hub->tunnels);
- gtphub_tunnel_refresh(hub, tun, p->timestamp);
- /* The endpoint peers on this side (SGSN) will be set from IEs
- * below. Also set the GGSN Ctrl endpoint, for logging. */
- gtphub_tunnel_endpoint_set_peer(&tun->endpoint[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL],
- to_ctrl);
- } else if (p->type == GTP_CREATE_PDP_RSP) {
- if (p->side_idx != GTPH_SIDE_GGSN) {
- LOG(LOGL_ERROR, "Wrong side: Create PDP Context"
- " Response from the SGSN side: %s",
- gtphub_port_str(from_ctrl));
- return -1;
- }
-
- /* The tunnel should already have been resolved from the header
- * TEI and be available in tun (== p->tun). Just fill in the
- * GSN Addresses below.*/
- OSMO_ASSERT(tun);
- OSMO_ASSERT(tun->tei_repl == p->header_tei_rx);
- OSMO_ASSERT(to_ctrl);
- }
-
- uint8_t ie_type[] = { GTPIE_TEI_C, GTPIE_TEI_DI };
- int ie_mandatory = (p->type == GTP_CREATE_PDP_REQ);
- unsigned int side_idx = p->side_idx;
-
- for (plane_idx = 0; plane_idx < 2; plane_idx++) {
- int rc;
- struct gsn_addr use_addr;
- uint16_t use_port;
- uint32_t tei_from_ie;
- int ie_idx;
-
- /* Fetch GSN Address and TEI from IEs. As ensured by above
- * static asserts, plane_idx corresponds to the GSN Address IE
- * index (the first one = 0 = ctrl, second one = 1 = user). */
- rc = gsn_addr_get(&use_addr, p, plane_idx);
- if (rc) {
- LOG(LOGL_ERROR, "Cannot read %s GSN Address IE\n",
- gtphub_plane_idx_names[plane_idx]);
- return -1;
- }
- LOG(LOGL_DEBUG, "Read %s GSN addr %s (%d)\n",
- gtphub_plane_idx_names[plane_idx],
- gsn_addr_to_str(&use_addr),
- use_addr.len);
-
- ie_idx = gtpie_getie(p->ie, ie_type[plane_idx], 0);
- if (ie_idx < 0) {
- if (ie_mandatory) {
- LOG(LOGL_ERROR,
- "Create PDP Context message invalid:"
- " missing IE %d\n",
- (int)ie_type[plane_idx]);
- return -1;
- }
- tei_from_ie = 0;
- }
- else
- tei_from_ie = ntoh32(p->ie[ie_idx]->tv4.v);
-
- /* Make sure an entry for this peer address with default port
- * exists.
- *
- * Exception: if sgsn_use_sender is set, instead use the
- * sender's address and port for Ctrl -- the User port is not
- * known until the first User packet arrives.
- *
- * Note: doing this here is just an optimization, because
- * gtphub_handle_buf() has code to replace the tunnel
- * endpoints' addresses with the sender (needed for User
- * plane). We could just ignore sgsn_use_sender here. But if we
- * set up a default port here and replace it in
- * gtphub_handle_buf(), we'd be creating a peer port just to
- * expire it right away. */
- if (hub->sgsn_use_sender && (side_idx == GTPH_SIDE_SGSN)) {
- gsn_addr_from_sockaddr(&use_addr, &use_port, &from_ctrl->sa);
- } else {
- use_port = gtphub_plane_idx_default_port[plane_idx];
-
- }
-
- struct gtphub_peer_port *peer_from_ie;
- peer_from_ie = gtphub_port_have(hub,
- &hub->to_gsns[side_idx][plane_idx],
- &use_addr, use_port);
-
- gtphub_tunnel_endpoint_set_peer(&tun->endpoint[side_idx][plane_idx],
- peer_from_ie);
-
- if (!tei_from_ie &&
- !tun->endpoint[side_idx][plane_idx].tei_orig) {
- LOG(LOGL_ERROR,
- "Create PDP Context message omits %s TEI, but"
- " no TEI has been announced for this tunnel: %s\n",
- gtphub_plane_idx_names[plane_idx],
- gtphub_tunnel_str(tun));
- return -1;
- }
-
- if (tei_from_ie) {
- /* Replace TEI in GTP packet IE */
- tun->endpoint[side_idx][plane_idx].tei_orig = tei_from_ie;
- p->ie[ie_idx]->tv4.v = hton32(tun->tei_repl);
-
- if (!gtphub_check_reused_teis(hub, tun)) {
- /* It's highly unlikely that all TEIs are
- * taken. But the code looking for an unused
- * TEI is, at the time of writing this comment,
- * not able to find gaps in the TEI space. To
- * explicitly alert the user of this problem,
- * rather abort than carry on. */
- LOG(LOGL_FATAL, "TEI range exhausted. Cannot create TEI mapping, aborting.\n");
- abort();
- }
- }
-
- /* Replace the GSN address to reflect gtphub. */
- rc = gsn_addr_put(&hub->to_gsns[other_side_idx(side_idx)][plane_idx].local_addr,
- p, plane_idx);
- if (rc) {
- LOG(LOGL_ERROR, "Cannot write %s GSN Address IE\n",
- gtphub_plane_idx_names[plane_idx]);
- return -1;
- }
- }
-
- if (p->type == GTP_CREATE_PDP_REQ) {
- LOG(LOGL_DEBUG, "New tunnel, first half: %s\n",
- gtphub_tunnel_str(tun));
- } else if (p->type == GTP_CREATE_PDP_RSP) {
- LOG(LOGL_DEBUG, "New tunnel: %s\n",
- gtphub_tunnel_str(tun));
- }
-
- return 0;
-}
-
-static void pending_delete_del_cb(struct expiring_item *expi)
-{
- struct pending_delete *pd;
- pd = container_of(expi, struct pending_delete, expiry_entry);
-
- llist_del(&pd->entry);
- INIT_LLIST_HEAD(&pd->entry);
-
- pd->expiry_entry.del_cb = 0;
- expiring_item_del(&pd->expiry_entry);
-
- talloc_free(pd);
-}
-
-static struct pending_delete *pending_delete_new(void)
-{
- struct pending_delete *pd = talloc_zero(osmo_gtphub_ctx, struct pending_delete);
- INIT_LLIST_HEAD(&pd->entry);
- expiring_item_init(&pd->expiry_entry);
- pd->expiry_entry.del_cb = pending_delete_del_cb;
- return pd;
-}
-
-static int gtphub_handle_delete_pdp_ctx(struct gtphub *hub,
- struct gtp_packet_desc *p,
- struct gtphub_peer_port *from_ctrl,
- struct gtphub_peer_port *to_ctrl)
-{
- struct gtphub_tunnel *known_tun = p->tun;
-
- if (p->type == GTP_DELETE_PDP_REQ) {
- if (!known_tun) {
- LOG(LOGL_ERROR, "Cannot find tunnel for Delete PDP Context Request.\n");
- return -1;
- }
-
- /* Store the Delete Request until a successful Response is seen. */
- uint8_t teardown_ind;
- uint8_t nsapi;
-
- if (gtpie_gettv1(p->ie, GTPIE_TEARDOWN, 0, &teardown_ind) != 0) {
- LOG(LOGL_ERROR, "Missing Teardown Ind IE in Delete PDP Context Request.\n");
- return -1;
- }
-
- if (gtpie_gettv1(p->ie, GTPIE_NSAPI, 0, &nsapi) != 0) {
- LOG(LOGL_ERROR, "Missing NSAPI IE in Delete PDP Context Request.\n");
- return -1;
- }
-
- struct pending_delete *pd = NULL;
-
- struct pending_delete *pdi = NULL;
- llist_for_each_entry(pdi, &hub->pending_deletes, entry) {
- if ((pdi->tun == known_tun)
- && (pdi->teardown_ind == teardown_ind)
- && (pdi->nsapi == nsapi)) {
- pd = pdi;
- break;
- }
- }
-
- if (!pd) {
- pd = pending_delete_new();
- pd->tun = known_tun;
- pd->teardown_ind = teardown_ind;
- pd->nsapi = nsapi;
-
- LOG(LOGL_DEBUG, "Tunnel delete pending: %s\n",
- gtphub_tunnel_str(known_tun));
- llist_add(&pd->entry, &hub->pending_deletes);
- }
-
- /* Add or refresh timeout. */
- expiry_add(&hub->expire_quickly, &pd->expiry_entry, p->timestamp);
-
- /* If a pending_delete should expire before the response to
- * indicate success comes in, the responding peer will have the
- * tunnel deactivated, while the requesting peer gets no reply
- * and keeps the tunnel. The hope is that the requesting peer
- * will try again and get a useful response. */
- } else if (p->type == GTP_DELETE_PDP_RSP) {
- /* Find the Delete Request for this Response. */
- struct pending_delete *pd = NULL;
-
- struct pending_delete *pdi;
- llist_for_each_entry(pdi, &hub->pending_deletes, entry) {
- if (known_tun == pdi->tun) {
- pd = pdi;
- break;
- }
- }
-
- if (!pd) {
- LOG(LOGL_ERROR, "Delete PDP Context Response:"
- " Cannot find matching request.");
- /* If we delete the tunnel now, anyone can send a
- * Delete response to kill tunnels at will. */
- return -1;
- }
-
- /* TODO handle teardown_ind and nsapi */
-
- expiring_item_del(&pd->expiry_entry);
-
- uint8_t cause;
- if (gtpie_gettv1(p->ie, GTPIE_CAUSE, 0, &cause) != 0) {
- LOG(LOGL_ERROR, "Delete PDP Context Response:"
- " Missing Cause IE.");
- /* If we delete the tunnel now, at least one of the
- * peers may still think it is active. */
- return -1;
- }
-
- if (cause != GTPCAUSE_ACC_REQ) {
- LOG(LOGL_NOTICE,
- "Delete PDP Context Response indicates failure;"
- "for %s\n",
- gtphub_tunnel_str(known_tun));
- return -1;
- }
-
- LOG(LOGL_DEBUG, "Delete PDP Context: removing tunnel %s\n",
- gtphub_tunnel_str(known_tun));
- p->tun = NULL;
- expiring_item_del(&known_tun->expiry_entry);
- }
-
- return 0;
-}
-
-static int gtphub_handle_update_pdp_ctx(struct gtphub *hub,
- struct gtp_packet_desc *p,
- struct gtphub_peer_port *from_ctrl,
- struct gtphub_peer_port *to_ctrl)
-{
- /* TODO */
- return 0;
-}
-
-/* Read GSN address IEs from p, and make sure these peer addresses exist in
- * bind[plane_idx] with default ports, in their respective planes (both Ctrl
- * and User). Map TEIs announced in IEs, and write mapped TEIs in-place into
- * the packet p. */
-static int gtphub_handle_pdp_ctx(struct gtphub *hub,
- struct gtp_packet_desc *p,
- struct gtphub_peer_port *from_ctrl,
- struct gtphub_peer_port *to_ctrl)
-{
- OSMO_ASSERT(p->plane_idx == GTPH_PLANE_CTRL);
-
- switch (p->type) {
- case GTP_CREATE_PDP_REQ:
- case GTP_CREATE_PDP_RSP:
- return gtphub_handle_create_pdp_ctx(hub, p,
- from_ctrl, to_ctrl);
-
- case GTP_DELETE_PDP_REQ:
- case GTP_DELETE_PDP_RSP:
- return gtphub_handle_delete_pdp_ctx(hub, p,
- from_ctrl, to_ctrl);
-
- case GTP_UPDATE_PDP_REQ:
- case GTP_UPDATE_PDP_RSP:
- return gtphub_handle_update_pdp_ctx(hub, p,
- from_ctrl, to_ctrl);
-
- default:
- /* Nothing to do for this message type. */
- return 0;
- }
-
-}
-
-static int gtphub_send_del_pdp_ctx(struct gtphub *hub,
- struct gtphub_tunnel *tun,
- int to_side)
-{
- static uint8_t del_ctx_msg[16] = {
- 0x32, /* GTP v1 flags */
- GTP_DELETE_PDP_REQ,
- 0x00, 0x08, /* Length in network byte order */
- 0x00, 0x00, 0x00, 0x00, /* TEI to be replaced */
- 0, 0, /* Seq, to be replaced */
- 0, 0, /* no extensions */
- 0x13, 0xff, /* 19: Teardown ind = 1 */
- 0x14, 0 /* 20: NSAPI = 0 */
- };
-
- uint32_t *tei = (uint32_t*)&del_ctx_msg[4];
- uint16_t *seq = (uint16_t*)&del_ctx_msg[8];
-
- struct gtphub_tunnel_endpoint *te =
- &tun->endpoint[to_side][GTPH_PLANE_CTRL];
-
- if (! te->peer)
- return 0;
-
- *tei = hton32(te->tei_orig);
- *seq = hton16(nr_pool_next(&te->peer->peer_addr->peer->seq_pool));
-
- struct gtphub_bind *to_bind = &hub->to_gsns[to_side][GTPH_PLANE_CTRL];
- int rc = gtphub_write(&to_bind->ofd, &te->peer->sa,
- del_ctx_msg, sizeof(del_ctx_msg));
- if (rc != 0) {
- LOG(LOGL_ERROR,
- "Failed to send out-of-band Delete PDP Context Request to %s\n",
- gtphub_port_str(te->peer));
- }
- return rc;
-}
-
-/* Tell all peers on the other end of tunnels that PDP contexts are void. */
-static void gtphub_restarted(struct gtphub *hub,
- struct gtp_packet_desc *p,
- struct gtphub_peer_port *pp)
-{
- LOG(LOGL_NOTICE, "Peer has restarted: %s\n",
- gtphub_port_str(pp));
-
- int deleted_count = 0;
- struct gtphub_tunnel *tun;
- llist_for_each_entry(tun, &hub->tunnels, entry) {
- int side_idx;
- for_each_side(side_idx) {
- struct gtphub_tunnel_endpoint *te = &tun->endpoint[side_idx][GTPH_PLANE_CTRL];
- struct gtphub_tunnel_endpoint *te2 = &tun->endpoint[other_side_idx(side_idx)][GTPH_PLANE_CTRL];
- if ((!te->peer)
- || (!te2->tei_orig)
- || (pp->peer_addr->peer != te->peer->peer_addr->peer))
- continue;
-
- LOG(LOGL_DEBUG, "Deleting tunnel due to peer restart: %s\n",
- gtphub_tunnel_str(tun));
- deleted_count ++;
-
- /* Send a Delete PDP Context Request to the
- * peer on the other side, remember the pending
- * delete and wait for the response to delete
- * the tunnel. Clear this side of the tunnel to
- * make sure it isn't used.
- *
- * Should the delete message send fail, or if no
- * response is received, this tunnel will expire. If
- * its TEIs come up in a new PDP Context Request, it
- * will be removed. If messages for this tunnel should
- * come in (from the not restarted side), they will be
- * dropped because the tunnel is rendered unusable. */
- gtphub_send_del_pdp_ctx(hub, tun, other_side_idx(side_idx));
-
- gtphub_tunnel_endpoint_set_peer(&tun->endpoint[side_idx][GTPH_PLANE_CTRL],
- NULL);
- gtphub_tunnel_endpoint_set_peer(&tun->endpoint[side_idx][GTPH_PLANE_USER],
- NULL);
- }
- }
-
- if (deleted_count)
- LOG(LOGL_NOTICE, "Deleting %d tunnels due to restart of: %s\n",
- deleted_count,
- gtphub_port_str(pp));
-}
-
-static int get_restart_count(struct gtp_packet_desc *p)
-{
- int ie_idx;
- ie_idx = gtpie_getie(p->ie, GTPIE_RECOVERY, 0);
- if (ie_idx < 0)
- return -1;
- return ntoh8(p->ie[ie_idx]->tv1.v);
-}
-
-static void gtphub_check_restart_counter(struct gtphub *hub,
- struct gtp_packet_desc *p,
- struct gtphub_peer_port *from)
-{
- /* If the peer is sending a Recovery IE (7.7.11) with a restart counter
- * that doesn't match the peer's previously sent restart counter, clear
- * that peer and cancel PDP contexts. */
-
- int restart = get_restart_count(p);
-
- if ((restart < 0) || (restart > 255))
- return;
-
- if ((from->last_restart_count >= 0) && (from->last_restart_count <= 255)) {
- if (from->last_restart_count != restart) {
- gtphub_restarted(hub, p, from);
- }
- }
-
- from->last_restart_count = restart;
-}
-
-static int from_sgsns_read_cb(struct osmo_fd *from_sgsns_ofd, unsigned int what)
-{
- unsigned int plane_idx = from_sgsns_ofd->priv_nr;
- OSMO_ASSERT(plane_idx < GTPH_PLANE_N);
- LOG(LOGL_DEBUG, "=== reading from SGSN (%s)\n",
- gtphub_plane_idx_names[plane_idx]);
-
- if (!(what & BSC_FD_READ))
- return 0;
-
- struct gtphub *hub = from_sgsns_ofd->data;
-
- static uint8_t buf[4096];
- struct osmo_sockaddr from_addr;
- struct osmo_sockaddr to_addr;
- struct osmo_fd *to_ofd;
- int len;
- uint8_t *reply_buf;
-
- len = gtphub_read(from_sgsns_ofd, &from_addr, buf, sizeof(buf));
- if (len < 1)
- return 0;
-
- len = gtphub_handle_buf(hub, GTPH_SIDE_SGSN, plane_idx, &from_addr,
- buf, len, gtphub_now(),
- &reply_buf, &to_ofd, &to_addr);
- if (len < 1)
- return 0;
-
- return gtphub_write(to_ofd, &to_addr, reply_buf, len);
-}
-
-static int from_ggsns_read_cb(struct osmo_fd *from_ggsns_ofd, unsigned int what)
-{
- unsigned int plane_idx = from_ggsns_ofd->priv_nr;
- OSMO_ASSERT(plane_idx < GTPH_PLANE_N);
- LOG(LOGL_DEBUG, "=== reading from GGSN (%s)\n",
- gtphub_plane_idx_names[plane_idx]);
- if (!(what & BSC_FD_READ))
- return 0;
-
- struct gtphub *hub = from_ggsns_ofd->data;
-
- static uint8_t buf[4096];
- struct osmo_sockaddr from_addr;
- struct osmo_sockaddr to_addr;
- struct osmo_fd *to_ofd;
- int len;
- uint8_t *reply_buf;
-
- len = gtphub_read(from_ggsns_ofd, &from_addr, buf, sizeof(buf));
- if (len < 1)
- return 0;
-
- len = gtphub_handle_buf(hub, GTPH_SIDE_GGSN, plane_idx, &from_addr,
- buf, len, gtphub_now(),
- &reply_buf, &to_ofd, &to_addr);
- if (len < 1)
- return 0;
-
- return gtphub_write(to_ofd, &to_addr, reply_buf, len);
-}
-
-static int gtphub_unmap(struct gtphub *hub,
- struct gtp_packet_desc *p,
- struct gtphub_peer_port *from,
- struct gtphub_peer_port *to_proxy,
- struct gtphub_peer_port **final_unmapped,
- struct gtphub_peer_port **unmapped_from_seq)
-{
- /* Always (try to) unmap sequence and TEI numbers, which need to be
- * replaced in the packet. Either way, give precedence to the proxy, if
- * configured. */
-
- if (unmapped_from_seq)
- *unmapped_from_seq = NULL;
- if (final_unmapped)
- *final_unmapped = NULL;
- p->tun = NULL;
-
- struct gtphub_peer_port *from_seq = NULL;
- struct gtphub_peer_port *from_tei = NULL;
- struct gtphub_peer_port *unmapped = NULL;
-
- from_seq = gtphub_unmap_seq(p, from);
-
- if (gtphub_unmap_header_tei(&from_tei, &p->tun, hub, p, from) < 0)
- return -1;
-
- struct gtphub_peer *from_peer = from->peer_addr->peer;
- if (from_seq && from_tei && (from_seq != from_tei)) {
- LOG(LOGL_DEBUG,
- "Seq unmap and TEI unmap yield two different peers."
- " Using seq unmap."
- " (from %s %s: seq %d yields %s, tei %u yields %s)\n",
- gtphub_plane_idx_names[p->plane_idx],
- gtphub_peer_str(from_peer),
- (int)p->seq,
- gtphub_port_str(from_seq),
- (unsigned int)p->header_tei_rx,
- gtphub_port_str2(from_tei)
- );
- }
- unmapped = (from_seq? from_seq : from_tei);
-
- if (unmapped && to_proxy && (unmapped != to_proxy)) {
- LOG(LOGL_NOTICE,
- "Unmap yields a different peer than the configured proxy."
- " Using proxy."
- " unmapped: %s proxy: %s\n",
- gtphub_port_str(unmapped),
- gtphub_port_str2(to_proxy)
- );
- }
- unmapped = (to_proxy? to_proxy : unmapped);
-
- if (!unmapped) {
- /* Return no error, but returned pointers are all NULL. */
- return 0;
- }
-
- if (unmapped_from_seq)
- *unmapped_from_seq = from_seq;
- if (final_unmapped)
- *final_unmapped = unmapped;
- return 0;
-}
-
-static int gsn_addr_to_sockaddr(struct gsn_addr *src,
- uint16_t port,
- struct osmo_sockaddr *dst)
-{
- return osmo_sockaddr_init_udp(dst, gsn_addr_to_str(src), port);
-}
-
-/* If p is an Echo request, replace p's data with the matching response and
- * return 1. If p is no Echo request, return 0, or -1 if an invalid packet is
- * detected. */
-static int gtphub_handle_echo_req(struct gtphub *hub, struct gtp_packet_desc *p,
- uint8_t **reply_buf)
-{
- if (p->type != GTP_ECHO_REQ)
- return 0;
-
- static uint8_t echo_response_data[14] = {
- 0x32, /* GTP v1 flags */
- GTP_ECHO_RSP,
- 0x00, 14 - 8, /* Length in network byte order */
- 0x00, 0x00, 0x00, 0x00, /* Zero TEI */
- 0, 0, /* Seq, to be replaced */
- 0, 0, /* no extensions */
- 0x0e, /* Recovery IE */
- 0 /* Restart counter, to be replaced */
- };
- uint16_t *seq = (uint16_t*)&echo_response_data[8];
- uint8_t *recovery = &echo_response_data[13];
-
- *seq = hton16(p->seq);
- *recovery = hub->restart_counter;
-
- *reply_buf = echo_response_data;
-
- return sizeof(echo_response_data);
-}
-
-struct gtphub_peer_port *gtphub_known_addr_have_port(const struct gtphub_bind *bind,
- const struct osmo_sockaddr *addr);
-
-/* Parse buffer as GTP packet, replace elements in-place and return the ofd and
- * address to forward to. Return a pointer to the osmo_fd, but copy the
- * sockaddr to *to_addr. The reason for this is that the sockaddr may expire at
- * any moment, while the osmo_fd is guaranteed to persist. Return the number of
- * bytes to forward, 0 or less on failure. */
-int gtphub_handle_buf(struct gtphub *hub,
- unsigned int side_idx,
- unsigned int plane_idx,
- const struct osmo_sockaddr *from_addr,
- uint8_t *buf,
- size_t received,
- time_t now,
- uint8_t **reply_buf,
- struct osmo_fd **to_ofd,
- struct osmo_sockaddr *to_addr)
-{
- struct gtphub_bind *from_bind = &hub->to_gsns[side_idx][plane_idx];
- struct gtphub_bind *to_bind = &hub->to_gsns[other_side_idx(side_idx)][plane_idx];
-
- rate_ctr_add(&from_bind->counters_io->ctr[GTPH_CTR_BYTES_IN],
- received);
-
- struct gtp_packet_desc p;
- gtp_decode(buf, received, side_idx, plane_idx, &p, now);
-
- LOG(LOGL_DEBUG, "%s rx %s from %s %s%s\n",
- (side_idx == GTPH_SIDE_GGSN)? "<-" : "->",
- gtphub_plane_idx_names[plane_idx],
- gtphub_side_idx_names[side_idx],
- osmo_sockaddr_to_str(from_addr),
- gtp_type_str(p.type));
-
- if (p.rc <= 0) {
- LOG(LOGL_ERROR, "INVALID: dropping GTP packet%s from %s %s %s\n",
- gtp_type_str(p.type),
- gtphub_side_idx_names[side_idx],
- gtphub_plane_idx_names[plane_idx],
- osmo_sockaddr_to_str(from_addr));
- return -1;
- }
-
- rate_ctr_inc(&from_bind->counters_io->ctr[GTPH_CTR_PKTS_IN]);
-
- int reply_len;
- reply_len = gtphub_handle_echo_req(hub, &p, reply_buf);
- if (reply_len > 0) {
- /* It was an echo. Nothing left to do. */
- osmo_sockaddr_copy(to_addr, from_addr);
- *to_ofd = &from_bind->ofd;
-
- rate_ctr_inc(&from_bind->counters_io->ctr[GTPH_CTR_PKTS_OUT]);
- rate_ctr_add(&from_bind->counters_io->ctr[GTPH_CTR_BYTES_OUT],
- reply_len);
- LOG(LOGL_DEBUG, "%s Echo response to %s: %d bytes to %s\n",
- (side_idx == GTPH_SIDE_GGSN)? "-->" : "<--",
- gtphub_side_idx_names[side_idx],
- (int)reply_len, osmo_sockaddr_to_str(to_addr));
- return reply_len;
- }
- if (reply_len < 0)
- return -1;
-
- *to_ofd = &to_bind->ofd;
-
- /* If a proxy is configured, check that it's indeed that proxy talking
- * to us. A proxy is a forced 1:1 connection, e.g. to another gtphub,
- * so no-one else is allowed to talk to us from that side. */
- struct gtphub_peer_port *from_peer = hub->proxy[side_idx][plane_idx];
- if (from_peer) {
- if (osmo_sockaddr_cmp(&from_peer->sa, from_addr) != 0) {
- LOG(LOGL_ERROR,
- "Rejecting: %s proxy configured, but GTP packet"
- " received on %s bind is from another sender:"
- " proxy: %s sender: %s\n",
- gtphub_side_idx_names[side_idx],
- gtphub_side_idx_names[side_idx],
- gtphub_port_str(from_peer),
- osmo_sockaddr_to_str(from_addr));
- return -1;
- }
- }
-
- if (!from_peer) {
- /* Find or create a peer with a matching address. The sender's
- * port may in fact differ. */
- from_peer = gtphub_known_addr_have_port(from_bind, from_addr);
- }
-
- /* If any PDP context has been created, we already have an entry for
- * this GSN. If we don't have an entry, a GGSN has nothing to tell us
- * about, while an SGSN may initiate a PDP context. */
- if (!from_peer) {
- if (side_idx == GTPH_SIDE_GGSN) {
- LOG(LOGL_ERROR, "Dropping packet%s: unknown GGSN peer: %s\n",
- gtp_type_str(p.type),
- osmo_sockaddr_to_str(from_addr));
- return -1;
- } else {
- /* SGSN */
- /* A new peer. If this is on the Ctrl plane, an SGSN
- * may make first contact without being known yet, so
- * create the peer struct for the current sender. */
- if (plane_idx != GTPH_PLANE_CTRL) {
- LOG(LOGL_ERROR,
- "Dropping packet%s: User plane peer was not"
- "announced by PDP Context: %s\n",
- gtp_type_str(p.type),
- osmo_sockaddr_to_str(from_addr));
- return -1;
- }
-
- struct gsn_addr from_gsna;
- uint16_t from_port;
- if (gsn_addr_from_sockaddr(&from_gsna, &from_port, from_addr) != 0)
- return -1;
-
- from_peer = gtphub_port_have(hub, from_bind, &from_gsna, from_port);
- }
- }
-
- if (!from_peer) {
- /* This could theoretically happen for invalid address data or
- * somesuch. */
- LOG(LOGL_ERROR, "Dropping packet%s: invalid %s peer: %s\n",
- gtp_type_str(p.type),
- gtphub_side_idx_names[side_idx],
- osmo_sockaddr_to_str(from_addr));
- return -1;
- }
-
- rate_ctr_add(&from_peer->counters_io->ctr[GTPH_CTR_BYTES_IN],
- received);
- rate_ctr_inc(&from_peer->counters_io->ctr[GTPH_CTR_PKTS_IN]);
-
- LOG(LOGL_DEBUG, "from %s peer: %s\n", gtphub_side_idx_names[side_idx],
- gtphub_port_str(from_peer));
-
- gtphub_check_restart_counter(hub, &p, from_peer);
- gtphub_map_restart_counter(hub, &p);
-
- struct gtphub_peer_port *to_peer_from_seq;
- struct gtphub_peer_port *to_peer;
- if (gtphub_unmap(hub, &p, from_peer,
- hub->proxy[other_side_idx(side_idx)][plane_idx],
- &to_peer, &to_peer_from_seq)
- != 0) {
- return -1;
- }
-
- if (p.tun) {
- struct gtphub_tunnel_endpoint *te = &p.tun->endpoint[p.side_idx][p.plane_idx];
- rate_ctr_add(&te->counters_io->ctr[GTPH_CTR_BYTES_IN],
- received);
- rate_ctr_inc(&te->counters_io->ctr[GTPH_CTR_PKTS_IN]);
- }
-
- if ((!to_peer) && (side_idx == GTPH_SIDE_SGSN)) {
- if (gtphub_resolve_ggsn(hub, &p, &to_peer) < 0)
- return -1;
- }
-
- if (!to_peer && p.tun && p.type == GTP_DELETE_PDP_RSP) {
- /* It's a delete confirmation for a tunnel that is partly
- * invalid, probably marked unsuable due to a restarted peer.
- * Remove the tunnel and be happy without forwarding. */
- expiring_item_del(&p.tun->expiry_entry);
- p.tun = NULL;
- return 0;
- }
-
- if (!to_peer) {
- LOG(LOGL_ERROR, "No %s to send to. Dropping packet%s"
- " (type=%" PRIu8 ", header-TEI=%" PRIx32 ", seq=%" PRIx16 ").\n",
- gtphub_side_idx_names[other_side_idx(side_idx)],
- gtp_type_str(p.type),
- p.type, p.header_tei_rx, p.seq
- );
- return -1;
- }
-
- if (plane_idx == GTPH_PLANE_CTRL) {
- /* This may be a Create PDP Context response. If it is, there
- * are other addresses in the GTP message to set up apart from
- * the sender. */
- if (gtphub_handle_pdp_ctx(hub, &p, from_peer, to_peer)
- != 0)
- return -1;
- }
-
- /* Either to_peer was resolved from an existing tunnel,
- * or a PDP Ctx and thus a tunnel has just been created,
- * or the tunnel has been deleted due to this message. */
- OSMO_ASSERT(p.tun || (p.type == GTP_DELETE_PDP_RSP));
-
- /* If the GGSN is replying to an SGSN request, the sequence nr has
- * already been unmapped above (to_peer_from_seq != NULL), and we need not
- * create a new mapping. */
- if (!to_peer_from_seq)
- gtphub_map_seq(&p, from_peer, to_peer);
-
- osmo_sockaddr_copy(to_addr, &to_peer->sa);
-
- *reply_buf = (uint8_t*)p.data;
-
- if (received) {
- rate_ctr_inc(&to_bind->counters_io->ctr[GTPH_CTR_PKTS_OUT]);
- rate_ctr_add(&to_bind->counters_io->ctr[GTPH_CTR_BYTES_OUT],
- received);
-
- rate_ctr_inc(&to_peer->counters_io->ctr[GTPH_CTR_PKTS_OUT]);
- rate_ctr_add(&to_peer->counters_io->ctr[GTPH_CTR_BYTES_OUT],
- received);
- }
-
- if (p.tun) {
- struct gtphub_tunnel_endpoint *te = &p.tun->endpoint[other_side_idx(p.side_idx)][p.plane_idx];
- rate_ctr_inc(&te->counters_io->ctr[GTPH_CTR_PKTS_OUT]);
- rate_ctr_add(&te->counters_io->ctr[GTPH_CTR_BYTES_OUT],
- received);
- }
-
- LOG(LOGL_DEBUG, "%s Forward to %s:"
- " header-TEI %" PRIx32", seq %" PRIx16", %d bytes to %s\n",
- (side_idx == GTPH_SIDE_SGSN)? "-->" : "<--",
- gtphub_side_idx_names[other_side_idx(side_idx)],
- p.header_tei, p.seq,
- (int)received, osmo_sockaddr_to_str(to_addr));
- return received;
-}
-
-static void resolved_gssn_del_cb(struct expiring_item *expi)
-{
- struct gtphub_resolved_ggsn *ggsn;
- ggsn = container_of(expi, struct gtphub_resolved_ggsn, expiry_entry);
-
- gtphub_port_ref_count_dec(ggsn->peer);
- llist_del(&ggsn->entry);
-
- ggsn->expiry_entry.del_cb = 0;
- expiring_item_del(&ggsn->expiry_entry);
-
- talloc_free(ggsn);
-}
-
-void gtphub_resolved_ggsn(struct gtphub *hub, const char *apn_oi_str,
- struct gsn_addr *resolved_addr,
- time_t now)
-{
- struct gtphub_peer_port *pp;
- struct gtphub_resolved_ggsn *ggsn;
-
- LOG(LOGL_DEBUG, "Resolved GGSN callback: %s %s\n",
- apn_oi_str, osmo_hexdump((unsigned char*)resolved_addr,
- sizeof(*resolved_addr)));
-
- pp = gtphub_port_have(hub, &hub->to_gsns[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL],
- resolved_addr, 2123);
- if (!pp) {
- LOG(LOGL_ERROR, "Internal: Cannot create/find peer '%s'\n",
- gsn_addr_to_str(resolved_addr));
- return;
- }
-
- ggsn = talloc_zero(osmo_gtphub_ctx, struct gtphub_resolved_ggsn);
- OSMO_ASSERT(ggsn);
- INIT_LLIST_HEAD(&ggsn->entry);
- expiring_item_init(&ggsn->expiry_entry);
-
- ggsn->peer = pp;
- gtphub_port_ref_count_inc(pp);
-
- osmo_strlcpy(ggsn->apn_oi_str, apn_oi_str, sizeof(ggsn->apn_oi_str));
-
- ggsn->expiry_entry.del_cb = resolved_gssn_del_cb;
- expiry_add(&hub->expire_slowly, &ggsn->expiry_entry, now);
-
- llist_add(&ggsn->entry, &hub->resolved_ggsns);
-}
-
-static int gtphub_gc_peer_port(struct gtphub_peer_port *pp)
-{
- return pp->ref_count == 0;
-}
-
-static int gtphub_gc_peer_addr(struct gtphub_peer_addr *pa)
-{
- struct gtphub_peer_port *pp, *npp;
- llist_for_each_entry_safe(pp, npp, &pa->ports, entry) {
- if (gtphub_gc_peer_port(pp)) {
- LOG(LOGL_DEBUG, "expired: peer %s\n",
- gtphub_port_str(pp));
- gtphub_peer_port_del(pp);
- }
- }
- return llist_empty(&pa->ports);
-}
-
-static int gtphub_gc_peer(struct gtphub_peer *p)
-{
- struct gtphub_peer_addr *pa, *npa;
- llist_for_each_entry_safe(pa, npa, &p->addresses, entry) {
- if (gtphub_gc_peer_addr(pa)) {
- gtphub_peer_addr_del(pa);
- }
- }
-
- /* Note that there's a ref_count in each gtphub_peer_port instance
- * listed within p->addresses, referenced by TEI mappings from
- * hub->tei_map. As long as those don't expire, this peer will stay. */
-
- return llist_empty(&p->addresses)
- && nr_map_empty(&p->seq_map);
-}
-
-static void gtphub_gc_bind(struct gtphub_bind *b)
-{
- struct gtphub_peer *p, *n;
- llist_for_each_entry_safe(p, n, &b->peers, entry) {
- if (gtphub_gc_peer(p)) {
- gtphub_peer_del(p);
- }
- }
-}
-
-void gtphub_gc(struct gtphub *hub, time_t now)
-{
- int expired;
- expired = expiry_tick(&hub->expire_quickly, now);
- expired += expiry_tick(&hub->expire_slowly, now);
-
- /* ... */
-
- if (expired) {
- int s, p;
- for_each_side_and_plane(s, p) {
- gtphub_gc_bind(&hub->to_gsns[s][p]);
- }
- }
-}
-
-static void gtphub_gc_cb(void *data)
-{
- struct gtphub *hub = data;
- gtphub_gc(hub, gtphub_now());
- osmo_timer_schedule(&hub->gc_timer, GTPH_GC_TICK_SECONDS, 0);
-}
-
-static void gtphub_gc_start(struct gtphub *hub)
-{
- osmo_timer_setup(&hub->gc_timer, gtphub_gc_cb, hub);
- osmo_timer_schedule(&hub->gc_timer, GTPH_GC_TICK_SECONDS, 0);
-}
-
-/* called by unit tests */
-void gtphub_init(struct gtphub *hub)
-{
- gtphub_zero(hub);
-
- INIT_LLIST_HEAD(&hub->tunnels);
- INIT_LLIST_HEAD(&hub->pending_deletes);
-
- expiry_init(&hub->expire_quickly, GTPH_EXPIRE_QUICKLY_SECS);
- expiry_init(&hub->expire_slowly, GTPH_EXPIRE_SLOWLY_MINUTES * 60);
-
- nr_pool_init(&hub->tei_pool, 1, 0xffffffff);
-
- int side_idx;
- int plane_idx;
- for_each_side_and_plane(side_idx, plane_idx) {
- gtphub_bind_init(&hub->to_gsns[side_idx][plane_idx]);
- }
-
- hub->to_gsns[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].label = "SGSN Ctrl";
- hub->to_gsns[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].label = "GGSN Ctrl";
- hub->to_gsns[GTPH_SIDE_SGSN][GTPH_PLANE_USER].label = "SGSN User";
- hub->to_gsns[GTPH_SIDE_GGSN][GTPH_PLANE_USER].label = "GGSN User";
-}
-
-/* For the test suite, this is kept separate from gtphub_stop(), which also
- * closes sockets. The test suite avoids using sockets and would cause
- * segfaults when trying to close uninitialized ofds. */
-void gtphub_free(struct gtphub *hub)
-{
- /* By expiring all mappings, a garbage collection should free
- * everything else. A gtphub_bind_free() will assert that everything is
- * indeed empty. */
- expiry_clear(&hub->expire_quickly);
- expiry_clear(&hub->expire_slowly);
-
- int side_idx;
- int plane_idx;
- for_each_side_and_plane(side_idx, plane_idx) {
- gtphub_gc_bind(&hub->to_gsns[side_idx][plane_idx]);
- gtphub_bind_free(&hub->to_gsns[side_idx][plane_idx]);
- }
-}
-
-void gtphub_stop(struct gtphub *hub)
-{
- int side_idx;
- int plane_idx;
- for_each_side_and_plane(side_idx, plane_idx) {
- gtphub_bind_stop(&hub->to_gsns[side_idx][plane_idx]);
- }
- gtphub_free(hub);
-}
-
-static int gtphub_make_proxy(struct gtphub *hub,
- struct gtphub_peer_port **pp,
- struct gtphub_bind *bind,
- const struct gtphub_cfg_addr *addr)
-{
- if (!addr->addr_str)
- return 0;
-
- struct gsn_addr gsna;
- if (gsn_addr_from_str(&gsna, addr->addr_str) != 0)
- return -1;
-
- *pp = gtphub_port_have(hub, bind, &gsna, addr->port);
-
- /* This is *the* proxy. Make sure it is never expired. */
- gtphub_port_ref_count_inc(*pp);
- return 0;
-}
-
-int gtphub_start(struct gtphub *hub, struct gtphub_cfg *cfg,
- uint8_t restart_counter)
-{
- gtphub_init(hub);
-
- hub->restart_counter = restart_counter;
- hub->sgsn_use_sender = cfg->sgsn_use_sender? 1 : 0;
-
- /* If a Ctrl plane proxy is configured, ares will never be used. */
- if (!cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].addr_str) {
- if (gtphub_ares_init(hub) != 0) {
- LOG(LOGL_FATAL, "Failed to initialize ares\n");
- return -1;
- }
- }
-
- int side_idx;
- int plane_idx;
- for_each_side_and_plane(side_idx, plane_idx) {
- int rc;
- rc = gtphub_bind_start(&hub->to_gsns[side_idx][plane_idx],
- &cfg->to_gsns[side_idx][plane_idx],
- (side_idx == GTPH_SIDE_SGSN)
- ? from_sgsns_read_cb
- : from_ggsns_read_cb,
- hub, plane_idx);
- if (rc) {
- LOG(LOGL_FATAL, "Failed to bind for %ss (%s)\n",
- gtphub_side_idx_names[side_idx],
- gtphub_plane_idx_names[plane_idx]);
- return rc;
- }
- }
-
- for_each_side_and_plane(side_idx, plane_idx) {
- if (gtphub_make_proxy(hub,
- &hub->proxy[side_idx][plane_idx],
- &hub->to_gsns[side_idx][plane_idx],
- &cfg->proxy[side_idx][plane_idx])
- != 0) {
- LOG(LOGL_FATAL, "Cannot configure %s proxy"
- " %s port %d.\n",
- gtphub_side_idx_names[side_idx],
- cfg->proxy[side_idx][plane_idx].addr_str,
- (int)cfg->proxy[side_idx][plane_idx].port);
- return -1;
- }
- }
-
- for_each_side_and_plane(side_idx, plane_idx) {
- if (hub->proxy[side_idx][plane_idx])
- LOG(LOGL_NOTICE, "Using %s %s proxy %s\n",
- gtphub_side_idx_names[side_idx],
- gtphub_plane_idx_names[plane_idx],
- gtphub_port_str(hub->proxy[side_idx][plane_idx]));
- }
-
- if (hub->sgsn_use_sender)
- LOG(LOGL_NOTICE, "Using sender address and port for SGSN instead of GSN Addr IE and default ports.\n");
-
- gtphub_gc_start(hub);
- return 0;
-}
-
-static struct gtphub_peer_addr *gtphub_peer_find_addr(const struct gtphub_peer *peer,
- const struct gsn_addr *addr)
-{
- struct gtphub_peer_addr *a;
- llist_for_each_entry(a, &peer->addresses, entry) {
- if (gsn_addr_same(&a->addr, addr))
- return a;
- }
- return NULL;
-}
-
-static struct gtphub_peer_port *gtphub_addr_find_port(const struct gtphub_peer_addr *a,
- uint16_t port)
-{
- OSMO_ASSERT(port);
- struct gtphub_peer_port *pp;
- llist_for_each_entry(pp, &a->ports, entry) {
- if (pp->port == port)
- return pp;
- }
- return NULL;
-}
-
-static struct gtphub_peer_addr *gtphub_addr_find(const struct gtphub_bind *bind,
- const struct gsn_addr *addr)
-{
- struct gtphub_peer *peer;
- llist_for_each_entry(peer, &bind->peers, entry) {
- struct gtphub_peer_addr *a = gtphub_peer_find_addr(peer, addr);
- if (a)
- return a;
- }
- return NULL;
-}
-
-static struct gtphub_peer_port *gtphub_port_find(const struct gtphub_bind *bind,
- const struct gsn_addr *addr,
- uint16_t port)
-{
- struct gtphub_peer_addr *a = gtphub_addr_find(bind, addr);
- if (!a)
- return NULL;
- return gtphub_addr_find_port(a, port);
-}
-
-struct gtphub_peer_port *gtphub_port_find_sa(const struct gtphub_bind *bind,
- const struct osmo_sockaddr *addr)
-{
- struct gsn_addr gsna;
- uint16_t port;
- gsn_addr_from_sockaddr(&gsna, &port, addr);
- return gtphub_port_find(bind, &gsna, port);
-}
-
-static struct gtphub_peer *gtphub_peer_new(struct gtphub *hub,
- struct gtphub_bind *bind)
-{
- struct gtphub_peer *peer = talloc_zero(osmo_gtphub_ctx,
- struct gtphub_peer);
- OSMO_ASSERT(peer);
-
- INIT_LLIST_HEAD(&peer->addresses);
-
- nr_pool_init(&peer->seq_pool, 0, 0xffff);
- nr_map_init(&peer->seq_map, &peer->seq_pool, &hub->expire_quickly);
-
- /* TODO use something random to pick the initial sequence nr.
- 0x6d31 produces the ASCII character sequence 'm1', currently used in
- gtphub_nc_test.sh. */
- peer->seq_pool.last_nr = 0x6d31 - 1;
-
- llist_add(&peer->entry, &bind->peers);
- return peer;
-}
-
-static struct gtphub_peer_addr *gtphub_peer_add_addr(struct gtphub_peer *peer,
- const struct gsn_addr *addr)
-{
- struct gtphub_peer_addr *a;
- a = talloc_zero(osmo_gtphub_ctx, struct gtphub_peer_addr);
- OSMO_ASSERT(a);
- a->peer = peer;
- gsn_addr_copy(&a->addr, addr);
- INIT_LLIST_HEAD(&a->ports);
- llist_add(&a->entry, &peer->addresses);
-
- return a;
-}
-
-static struct gtphub_peer_addr *gtphub_addr_have(struct gtphub *hub,
- struct gtphub_bind *bind,
- const struct gsn_addr *addr)
-{
- struct gtphub_peer_addr *a = gtphub_addr_find(bind, addr);
- if (a)
- return a;
-
- /* If we haven't found an address, that means we need to create an
- * entirely new peer for the new address. More addresses may be added
- * to this peer later, but not via this function. */
- struct gtphub_peer *peer = gtphub_peer_new(hub, bind);
-
- a = gtphub_peer_add_addr(peer, addr);
-
- LOG(LOGL_DEBUG, "New peer address: %s %s\n",
- bind->label,
- gsn_addr_to_str(&a->addr));
-
- return a;
-}
-
-static struct gtphub_peer_port *gtphub_addr_add_port(struct gtphub_peer_addr *a,
- uint16_t port)
-{
- struct gtphub_peer_port *pp;
-
- pp = talloc_zero(osmo_gtphub_ctx, struct gtphub_peer_port);
- OSMO_ASSERT(pp);
- pp->peer_addr = a;
- pp->port = port;
- pp->last_restart_count = -1;
-
- if (gsn_addr_to_sockaddr(&a->addr, port, &pp->sa) != 0) {
- talloc_free(pp);
- return NULL;
- }
-
- pp->counters_io = rate_ctr_group_alloc(osmo_gtphub_ctx,
- &gtphub_ctrg_io_desc, 0);
-
- llist_add(&pp->entry, &a->ports);
-
- LOG(LOGL_DEBUG, "New peer port: %s port %d\n",
- gsn_addr_to_str(&a->addr),
- (int)port);
-
- return pp;
-}
-
-struct gtphub_peer_port *gtphub_port_have(struct gtphub *hub,
- struct gtphub_bind *bind,
- const struct gsn_addr *addr,
- uint16_t port)
-{
- struct gtphub_peer_addr *a = gtphub_addr_have(hub, bind, addr);
-
- struct gtphub_peer_port *pp = gtphub_addr_find_port(a, port);
- if (pp)
- return pp;
-
- return gtphub_addr_add_port(a, port);
-}
-
-/* Find a GGSN peer with a matching address. If the address is known but the
- * port not, create a new port for that peer address. */
-struct gtphub_peer_port *gtphub_known_addr_have_port(const struct gtphub_bind *bind,
- const struct osmo_sockaddr *addr)
-{
- struct gtphub_peer_addr *pa;
- struct gtphub_peer_port *pp;
-
- struct gsn_addr gsna;
- uint16_t port;
- gsn_addr_from_sockaddr(&gsna, &port, addr);
-
- pa = gtphub_addr_find(bind, &gsna);
- if (!pa)
- return NULL;
-
- pp = gtphub_addr_find_port(pa, port);
-
- if (!pp)
- pp = gtphub_addr_add_port(pa, port);
-
- return pp;
-}
-
-
-/* Return 0 if the message in p is not applicable for GGSN resolution, -1 if
- * resolution should be possible but failed, and 1 if resolution was
- * successful. *pp will be set to NULL if <1 is returned. */
-static int gtphub_resolve_ggsn(struct gtphub *hub,
- struct gtp_packet_desc *p,
- struct gtphub_peer_port **pp)
-{
- *pp = NULL;
-
- /* TODO determine from message type whether IEs should be present? */
-
- int rc;
- const char *imsi_str;
- rc = get_ie_imsi_str(p->ie, 0, &imsi_str);
- if (rc < 1)
- return rc;
- OSMO_ASSERT(imsi_str);
-
- const char *apn_str;
- rc = get_ie_apn_str(p->ie, &apn_str);
- if (rc < 1)
- return rc;
- OSMO_ASSERT(apn_str);
-
- *pp = gtphub_resolve_ggsn_addr(hub, imsi_str, apn_str);
- return (*pp)? 1 : -1;
-}
-
-
-/* TODO move to osmocom/core/socket.c ? */
-/* use this in osmo_sock_init() to remove dup. */
-/* Internal: call getaddrinfo for osmo_sockaddr_init(). The caller is required
- to call freeaddrinfo(*result), iff zero is returned. */
-static int _osmo_getaddrinfo(struct addrinfo **result,
- uint16_t family, uint16_t type, uint8_t proto,
- const char *host, uint16_t port)
-{
- struct addrinfo hints;
- char portbuf[16];
-
- sprintf(portbuf, "%u", port);
- memset(&hints, '\0', sizeof(struct addrinfo));
- hints.ai_family = family;
- if (type == SOCK_RAW) {
- /* Workaround for glibc, that returns EAI_SERVICE (-8) if
- * SOCK_RAW and IPPROTO_GRE is used.
- */
- hints.ai_socktype = SOCK_DGRAM;
- hints.ai_protocol = IPPROTO_UDP;
- } else {
- hints.ai_socktype = type;
- hints.ai_protocol = proto;
- }
-
- return getaddrinfo(host, portbuf, &hints, result);
-}
-
-/* TODO move to osmocom/core/socket.c ? */
-int osmo_sockaddr_init(struct osmo_sockaddr *addr,
- uint16_t family, uint16_t type, uint8_t proto,
- const char *host, uint16_t port)
-{
- struct addrinfo *res;
- int rc;
- rc = _osmo_getaddrinfo(&res, family, type, proto, host, port);
-
- if (rc != 0) {
- LOG(LOGL_ERROR, "getaddrinfo returned error %d\n", (int)rc);
- return -EINVAL;
- }
-
- OSMO_ASSERT(res->ai_addrlen <= sizeof(addr->a));
- memcpy(&addr->a, res->ai_addr, res->ai_addrlen);
- addr->l = res->ai_addrlen;
- freeaddrinfo(res);
-
- return 0;
-}
-
-int osmo_sockaddr_to_strs(char *addr_str, size_t addr_str_len,
- char *port_str, size_t port_str_len,
- const struct osmo_sockaddr *addr,
- int flags)
-{
- int rc;
-
- if ((addr->l < 1) || (addr->l > sizeof(addr->a))) {
- LOGP(DGTPHUB, LOGL_ERROR, "Invalid address size: %d\n", addr->l);
- return -1;
- }
-
- if (addr->l > sizeof(addr->a)) {
- LOGP(DGTPHUB, LOGL_ERROR, "Invalid address: too long: %d\n",
- addr->l);
- return -1;
- }
-
- rc = getnameinfo((struct sockaddr*)&addr->a, addr->l,
- addr_str, addr_str_len,
- port_str, port_str_len,
- flags);
-
- if (rc)
- LOGP(DGTPHUB, LOGL_ERROR, "Invalid address: %s: %s\n",
- gai_strerror(rc), osmo_hexdump((uint8_t*)&addr->a,
- addr->l));
-
- return rc;
-}
-
-const char *osmo_sockaddr_to_strb(const struct osmo_sockaddr *addr,
- char *buf, size_t buf_len)
-{
- const int portbuf_len = 6;
- OSMO_ASSERT(buf_len > portbuf_len);
- char *portbuf = buf + buf_len - portbuf_len;
- buf_len -= portbuf_len;
- if (osmo_sockaddr_to_strs(buf, buf_len,
- portbuf, portbuf_len,
- addr,
- NI_NUMERICHOST | NI_NUMERICSERV))
- return NULL;
-
- char *pos = buf + strnlen(buf, buf_len-1);
- size_t len = buf_len - (pos - buf);
-
- snprintf(pos, len, " port %s", portbuf);
- buf[buf_len-1] = '\0';
-
- return buf;
-}
-
-const char *osmo_sockaddr_to_str(const struct osmo_sockaddr *addr)
-{
- static char buf[256];
- const char *result = osmo_sockaddr_to_strb(addr, buf, sizeof(buf));
- if (! result)
- return "(invalid)";
- return result;
-}
-
-int osmo_sockaddr_cmp(const struct osmo_sockaddr *a,
- const struct osmo_sockaddr *b)
-{
- if (a == b)
- return 0;
- if (!a)
- return -1;
- if (!b)
- return 1;
- if (a->l != b->l) {
- /* Lengths are not the same, but determine the order. Will
- * anyone ever sort a list by osmo_sockaddr though...? */
- int cmp = memcmp(&a->a, &b->a, (a->l < b->l)? a->l : b->l);
- if (cmp == 0) {
- if (a->l < b->l)
- return -1;
- else
- return 1;
- }
- return cmp;
- }
- return memcmp(&a->a, &b->a, a->l);
-}
-
-void osmo_sockaddr_copy(struct osmo_sockaddr *dst,
- const struct osmo_sockaddr *src)
-{
- OSMO_ASSERT(src->l <= sizeof(dst->a));
- memcpy(&dst->a, &src->a, src->l);
- dst->l = src->l;
-}
diff --git a/src/gprs/gtphub_ares.c b/src/gprs/gtphub_ares.c
deleted file mode 100644
index afeeda657..000000000
--- a/src/gprs/gtphub_ares.c
+++ /dev/null
@@ -1,220 +0,0 @@
-/* GTP Hub Implementation */
-
-/* (C) 2015 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
- * All Rights Reserved
- *
- * gtphub_ares.c.
- *
- * This file is kept separate so that these functions can be wrapped for
- * gtphub_test.c. When a function and its callers are in the same compilational
- * unit, the wrappability may be optimized away.
- *
- * Author: Neels Hofmeyr
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <string.h>
-#include <unistd.h>
-
-#include <openbsc/gtphub.h>
-#include <openbsc/debug.h>
-
-#include <osmocom/core/utils.h>
-#include <osmocom/gsm/apn.h>
-
-/* TODO split GRX ares from sgsn into a separate struct and allow use without
- * globals. */
-#include <openbsc/sgsn.h>
-extern struct sgsn_instance *sgsn;
-
-struct sgsn_instance sgsn_inst = { 0 };
-struct sgsn_instance *sgsn = &sgsn_inst;
-
-extern void *osmo_gtphub_ctx;
-
-int gtphub_ares_init(struct gtphub *hub)
-{
- return sgsn_ares_init(sgsn);
-}
-
-struct ggsn_lookup {
- struct llist_head entry;
- struct expiring_item expiry_entry;
-
- struct gtphub *hub;
-
- char imsi_str[GSM23003_IMSI_MAX_DIGITS+1];
- char apn_ni_str[GSM_APN_LENGTH];
- char apn_oi_str[GSM_APN_LENGTH];
- int have_3dig_mnc;
-};
-
-static int start_ares_query(struct ggsn_lookup *lookup);
-
-static void ggsn_lookup_cb(void *arg, int status, int timeouts,
- struct hostent *hostent)
-{
- struct ggsn_lookup *lookup = arg;
- LOGP(DGTPHUB, LOGL_NOTICE, "ggsn_lookup_cb(%p / %p)", lookup,
- &lookup->expiry_entry);
-
- if (status != ARES_SUCCESS) {
- LOGP(DGTPHUB, LOGL_ERROR, "DNS query failed.\n");
-
- /* Need to try with three digits now */
- if (!lookup->have_3dig_mnc) {
- lookup->have_3dig_mnc = 1;
- if (start_ares_query(lookup) == 0)
- return;
- }
-
- LOGP(DGTPHUB, LOGL_ERROR, "Failed to resolve GGSN. (%p)\n",
- lookup);
- goto remove_from_queue;
- }
-
- struct gsn_addr resolved_addr;
- if (hostent->h_length > sizeof(resolved_addr.buf)) {
- LOGP(DGTPHUB, LOGL_ERROR, "Addr size too large: %d > %d\n",
- (int)hostent->h_length, (int)sizeof(resolved_addr.buf));
- goto remove_from_queue;
- }
-
- /* Get the first addr from the list */
- char *addr0 = hostent->h_addr_list[0];
- if (!addr0) {
- LOGP(DGTPHUB, LOGL_ERROR, "No host address.\n");
- goto remove_from_queue;
- }
-
- memcpy(resolved_addr.buf, addr0, hostent->h_length);
- resolved_addr.len = hostent->h_length;
-
- LOGP(DGTPHUB, LOGL_NOTICE, "resolved addr %s\n",
- osmo_hexdump((unsigned char*)&resolved_addr,
- sizeof(resolved_addr)));
-
- gtphub_resolved_ggsn(lookup->hub, lookup->apn_oi_str, &resolved_addr,
- gtphub_now());
-
-remove_from_queue:
- LOGP(DGTPHUB, LOGL_ERROR, "Removing GGSN lookup. (%p / %p)\n", lookup,
- &lookup->expiry_entry);
- expiring_item_del(&lookup->expiry_entry);
-}
-
-static void make_addr_str(struct ggsn_lookup *lookup)
-{
- char *apn_oi_str;
- apn_oi_str = osmo_apn_qualify_from_imsi(lookup->imsi_str,
- lookup->apn_ni_str,
- lookup->have_3dig_mnc);
- osmo_strlcpy(lookup->apn_oi_str, apn_oi_str,
- sizeof(lookup->apn_oi_str));
-}
-
-static int start_ares_query(struct ggsn_lookup *lookup)
-{
- LOGP(DGTPHUB, LOGL_DEBUG, "Going to query %s (%p / %p)\n",
- lookup->apn_oi_str, lookup, &lookup->expiry_entry);
-
- int rc = sgsn_ares_query(sgsn, lookup->apn_oi_str, ggsn_lookup_cb,
- lookup);
- if (rc != 0)
- LOGP(DGTPHUB, LOGL_ERROR, "Failed to start ares query.\n");
- return rc;
-}
-
-static void ggsn_lookup_del_cb(struct expiring_item *expi)
-{
- struct ggsn_lookup *lookup;
- lookup = container_of(expi, struct ggsn_lookup, expiry_entry);
-
- LOGP(DGTPHUB, LOGL_NOTICE, "ggsn_lookup_del_cb(%p / %p)\n", lookup,
- expi);
-
- lookup->expiry_entry.del_cb = 0;
- expiring_item_del(expi);
-
- llist_del(&lookup->entry);
- talloc_free(lookup);
-}
-
-struct gtphub_peer_port *gtphub_resolve_ggsn_addr(struct gtphub *hub,
- const char *imsi_str,
- const char *apn_ni_str)
-{
- OSMO_ASSERT(imsi_str);
- OSMO_ASSERT(apn_ni_str);
-
- struct ggsn_lookup *lookup = talloc_zero(osmo_gtphub_ctx,
- struct ggsn_lookup);
- OSMO_ASSERT(lookup);
-
- LOGP(DGTPHUB, LOGL_DEBUG, "Request to resolve IMSI"
- " '%s' with APN-NI '%s' (%p / %p)\n",
- imsi_str, apn_ni_str, lookup, &lookup->expiry_entry);
-
- expiring_item_init(&lookup->expiry_entry);
- lookup->hub = hub;
-
- osmo_strlcpy(lookup->imsi_str, imsi_str, sizeof(lookup->imsi_str));
- osmo_strlcpy(lookup->apn_ni_str, apn_ni_str,
- sizeof(lookup->apn_ni_str));
-
- make_addr_str(lookup);
-
- struct ggsn_lookup *active;
- llist_for_each_entry(active, &hub->ggsn_lookups, entry) {
- if (strncmp(active->apn_oi_str, lookup->apn_oi_str,
- sizeof(lookup->apn_oi_str)) == 0) {
- LOGP(DGTPHUB, LOGL_DEBUG,
- "Query already pending for %s\n",
- lookup->apn_oi_str);
- /* A query already pending. Just tip our hat. */
- return NULL;
- }
- }
-
- struct gtphub_resolved_ggsn *resolved;
- llist_for_each_entry(resolved, &hub->resolved_ggsns, entry) {
- if (strncmp(resolved->apn_oi_str, lookup->apn_oi_str,
- sizeof(lookup->apn_oi_str)) == 0) {
- LOGP(DGTPHUB, LOGL_DEBUG,
- "GGSN resolved from cache: %s -> %s\n",
- lookup->apn_oi_str,
- gtphub_port_str(resolved->peer));
- return resolved->peer;
- }
- }
-
- /* Kick off a resolution, but so far return nothing. The hope is that
- * the peer will resend the request (a couple of times), and by then
- * the GGSN will be resolved. */
- LOGP(DGTPHUB, LOGL_DEBUG,
- "Sending out DNS query for %s..."
- " (Returning failure, hoping for a retry once resolution"
- " has concluded)\n",
- lookup->apn_oi_str);
-
- llist_add(&lookup->entry, &hub->ggsn_lookups);
-
- lookup->expiry_entry.del_cb = ggsn_lookup_del_cb;
- expiry_add(&hub->expire_quickly, &lookup->expiry_entry, gtphub_now());
-
- start_ares_query(lookup);
-
- return NULL;
-}
diff --git a/src/gprs/gtphub_main.c b/src/gprs/gtphub_main.c
deleted file mode 100644
index 73a122c31..000000000
--- a/src/gprs/gtphub_main.c
+++ /dev/null
@@ -1,357 +0,0 @@
-/* GTP Hub main program */
-
-/* (C) 2015 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
- * All Rights Reserved
- *
- * Author: Neels Hofmeyr
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <unistd.h>
-#include <signal.h>
-#include <string.h>
-#include <errno.h>
-#include <inttypes.h>
-#include <sys/stat.h>
-
-#define _GNU_SOURCE
-#include <getopt.h>
-
-#include <osmocom/core/signal.h>
-#include <osmocom/core/application.h>
-#include <osmocom/core/logging.h>
-#include <osmocom/core/utils.h>
-#include <osmocom/core/rate_ctr.h>
-
-#include <osmocom/vty/logging.h>
-#include <osmocom/vty/telnet_interface.h>
-#include <osmocom/vty/ports.h>
-
-#include <openbsc/debug.h>
-#include <openbsc/gtphub.h>
-#include <openbsc/vty.h>
-
-#include "../../bscconfig.h"
-
-extern void *osmo_gtphub_ctx;
-
-
-const char *gtphub_copyright =
- "Copyright (C) 2015 sysmocom s.f.m.c GmbH <info@sysmocom.de>\r\n"
- "License AGPLv3+: GNU AGPL version 2 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
- "This is free software: you are free to change and redistribute it.\r\n"
- "There is NO WARRANTY, to the extent permitted by law.\r\n";
-
-static struct log_info_cat gtphub_categories[] = {
- [DGTPHUB] = {
- .name = "DGTPHUB",
- .description = "GTP Hub",
- .color = "\033[1;33m",
- .enabled = 1,
- .loglevel = LOGL_INFO,
- },
-};
-
-int gtphub_log_filter_fn(const struct log_context *ctx,
- struct log_target *tar)
-{
- return 0;
-}
-
-static const struct log_info gtphub_log_info = {
- .filter_fn = gtphub_log_filter_fn,
- .cat = gtphub_categories,
- .num_cat = ARRAY_SIZE(gtphub_categories),
-};
-
-void log_cfg(struct gtphub_cfg *cfg)
-{
- int side_idx, plane_idx;
- for_each_side_and_plane(side_idx, plane_idx) {
- struct gtphub_cfg_addr *a;
- a = &cfg->to_gsns[side_idx][plane_idx].bind;
- LOGP(DGTPHUB, LOGL_NOTICE,
- "to-%ss bind, %s: %s port %d\n",
- gtphub_side_idx_names[side_idx],
- gtphub_plane_idx_names[plane_idx],
- a->addr_str, a->port);
- }
-}
-
-static void signal_handler(int signal)
-{
- fprintf(stdout, "signal %d received\n", signal);
-
- switch (signal) {
- case SIGINT:
- osmo_signal_dispatch(SS_L_GLOBAL, S_L_GLOBAL_SHUTDOWN, NULL);
- sleep(1);
- exit(0);
- break;
- case SIGABRT:
- /* in case of abort, we want to obtain a talloc report
- * and then return to the caller, who will abort the process */
- case SIGUSR1:
- case SIGUSR2:
- talloc_report_full(osmo_gtphub_ctx, stderr);
- break;
- default:
- break;
- }
-}
-
-extern int bsc_vty_go_parent(struct vty *vty);
-
-static struct vty_app_info vty_info = {
- .name = "OsmoGTPhub",
- .version = PACKAGE_VERSION,
- .go_parent_cb = bsc_vty_go_parent,
- .is_config_node = bsc_vty_is_config_node,
-};
-
-struct cmdline_cfg {
- const char *config_file;
- const char *restart_counter_file;
- int daemonize;
-};
-
-static uint8_t next_restart_count(const char *path)
-{
- int umask_was = umask(022);
-
- uint8_t counter = 0;
-
- FILE *f = fopen(path, "r");
- if (f) {
- int rc = fscanf(f, "%hhu", &counter);
-
- if (rc != 1)
- goto failed_to_read;
-
- char c;
- while (fread(&c, 1, 1, f) > 0) {
- switch (c) {
- case ' ':
- case '\t':
- case '\n':
- case '\r':
- break;
- default:
- goto failed_to_read;
- }
- }
- fclose(f);
- }
-
- counter ++;
-
- f = fopen(path, "w");
- if (!f)
- goto failed_to_write;
- if (fprintf(f, "%" PRIu8 "\n", counter) < 2)
- goto failed_to_write;
- if (fclose(f)) {
- f = NULL;
- goto failed_to_write;
- }
-
- umask(umask_was);
-
- LOGP(DGTPHUB, LOGL_NOTICE, "Restarted with counter %hhu\n", counter);
- return counter;
-
-failed_to_read:
- fclose(f);
- umask(umask_was);
- LOGP(DGTPHUB, LOGL_FATAL, "Restart counter file cannot be parsed:"
- " %s\n", path);
- exit(1);
-
-failed_to_write:
- if (f)
- fclose(f);
- umask(umask_was);
- LOGP(DGTPHUB, LOGL_FATAL, "Restart counter file cannot be written:"
- " %s\n", path);
- exit(1);
-}
-
-static void print_help(struct cmdline_cfg *ccfg)
-{
- printf("gtphub commandline options\n");
- printf(" -h,--help This text.\n");
- printf(" -D,--daemonize Fork the process into a background daemon.\n");
- printf(" -d,--debug <cat> Enable Debugging for this category.\n");
- printf(" Pass '-d list' to get a category listing.\n");
- printf(" -s,--disable-color\n");
- printf(" -c,--config-file <path> The config file to use [%s].\n",
- ccfg->config_file);
- printf(" -e,--log-level <nr> Set a global log level.\n");
- printf(" -r,--restart-file <path> File for counting restarts [%s].\n",
- ccfg->restart_counter_file);
-}
-
-static void list_categories(void)
-{
- printf("Avaliable debug categories:\n");
- int i;
- for (i = 0; i < gtphub_log_info.num_cat; ++i) {
- if (!gtphub_log_info.cat[i].name)
- continue;
-
- printf("%s\n", gtphub_log_info.cat[i].name);
- }
-}
-
-static void handle_options(struct cmdline_cfg *ccfg, int argc, char **argv)
-{
- while (1) {
- int option_index = 0, c;
- static struct option long_options[] = {
- {"help", 0, 0, 'h'},
- {"debug", 1, 0, 'd'},
- {"daemonize", 0, 0, 'D'},
- {"config-file", 1, 0, 'c'},
- {"disable-color", 0, 0, 's'},
- {"timestamp", 0, 0, 'T'},
- {"log-level", 1, 0, 'e'},
- {"restart-file", 1, 0, 'r'},
- {NULL, 0, 0, 0}
- };
-
- c = getopt_long(argc, argv, "hd:Dc:sTe:r:",
- long_options, &option_index);
- if (c == -1) {
- if (optind < argc) {
- LOGP(DGTPHUB, LOGL_FATAL,
- "Excess commandline arguments ('%s').\n",
- argv[optind]);
- exit(2);
- }
- break;
- }
-
- switch (c) {
- case 'h':
- //print_usage();
- print_help(ccfg);
- exit(0);
- case 's':
- log_set_use_color(osmo_stderr_target, 0);
- break;
- case 'd':
- if (strcmp("list", optarg) == 0) {
- list_categories();
- exit(0);
- } else
- log_parse_category_mask(osmo_stderr_target, optarg);
- break;
- case 'D':
- ccfg->daemonize = 1;
- break;
- case 'c':
- ccfg->config_file = optarg;
- break;
- case 'T':
- log_set_print_timestamp(osmo_stderr_target, 1);
- break;
- case 'e':
- log_set_log_level(osmo_stderr_target, atoi(optarg));
- break;
- case 'r':
- ccfg->restart_counter_file = optarg;
- break;
- default:
- LOGP(DGTPHUB, LOGL_FATAL, "Invalid command line argument, abort.\n");
- exit(1);
- break;
- }
- }
-}
-
-int main(int argc, char **argv)
-{
- int rc;
-
- struct cmdline_cfg _ccfg;
- struct cmdline_cfg *ccfg = &_ccfg;
- memset(ccfg, '\0', sizeof(*ccfg));
- ccfg->config_file = "./gtphub.conf";
- ccfg->restart_counter_file = "./gtphub_restart_count";
-
- struct gtphub_cfg _cfg;
- struct gtphub_cfg *cfg = &_cfg;
- memset(cfg, '\0', sizeof(*cfg));
-
- struct gtphub _hub;
- struct gtphub *hub = &_hub;
-
- osmo_gtphub_ctx = talloc_named_const(NULL, 0, "osmo_gtphub");
- msgb_talloc_ctx_init(osmo_gtphub_ctx, 0);
-
- signal(SIGINT, &signal_handler);
- signal(SIGABRT, &signal_handler);
- signal(SIGUSR1, &signal_handler);
- signal(SIGUSR2, &signal_handler);
- osmo_init_ignore_signals();
-
- osmo_init_logging(&gtphub_log_info);
-
- vty_info.copyright = gtphub_copyright;
- vty_init(&vty_info);
- logging_vty_add_cmds(NULL);
- gtphub_vty_init(hub, cfg);
-
- rate_ctr_init(osmo_gtphub_ctx);
-
- handle_options(ccfg, argc, argv);
-
- rc = gtphub_cfg_read(cfg, ccfg->config_file);
- if (rc < 0) {
- LOGP(DGTPHUB, LOGL_FATAL, "Cannot parse config file '%s'\n",
- ccfg->config_file);
- exit(2);
- }
-
- /* start telnet after reading config for vty_get_bind_addr() */
- rc = telnet_init_dynif(osmo_gtphub_ctx, 0, vty_get_bind_addr(),
- OSMO_VTY_PORT_GTPHUB);
- if (rc < 0)
- exit(1);
-
- if (gtphub_start(hub, cfg,
- next_restart_count(ccfg->restart_counter_file))
- != 0)
- return -1;
-
- log_cfg(cfg);
-
- if (ccfg->daemonize) {
- rc = osmo_daemonize();
- if (rc < 0) {
- LOGP(DGTPHUB, LOGL_FATAL, "Error during daemonize");
- exit(1);
- }
- }
-
- while (1) {
- rc = osmo_select_main(0);
- if (rc < 0)
- exit(3);
- }
-
- /* not reached */
- exit(0);
-}
diff --git a/src/gprs/gtphub_sock.c b/src/gprs/gtphub_sock.c
deleted file mode 100644
index 60bebaaeb..000000000
--- a/src/gprs/gtphub_sock.c
+++ /dev/null
@@ -1,60 +0,0 @@
-/* GTP Hub Implementation */
-
-/* (C) 2015 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
- * All Rights Reserved
- *
- * gtphub_sock.c.
- *
- * This file is kept separate so that these functions can be wrapped for
- * gtphub_test.c. When a function and its callers are in the same compilational
- * unit, the wrappability may be optimized away.
- *
- * Author: Neels Hofmeyr
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <openbsc/gtphub.h>
-#include <openbsc/debug.h>
-
-/* Convenience makro, note: only within this C file. */
-#define LOG(level, fmt, args...) \
- LOGP(DGTPHUB, level, fmt, ##args)
-
-int gtphub_write(const struct osmo_fd *to,
- const struct osmo_sockaddr *to_addr,
- const uint8_t *buf, size_t buf_len)
-{
- errno = 0;
- ssize_t sent = sendto(to->fd, buf, buf_len, 0,
- (struct sockaddr*)&to_addr->a, to_addr->l);
- LOG(LOGL_DEBUG, "to %s\n", osmo_sockaddr_to_str(to_addr));
-
- if (sent == -1) {
- LOG(LOGL_ERROR, "error: %s\n", strerror(errno));
- return -EINVAL;
- }
-
- if (sent != buf_len)
- LOG(LOGL_ERROR, "sent(%d) != data_len(%d)\n",
- (int)sent, (int)buf_len);
- else
- LOG(LOGL_DEBUG, "Sent %d: %s%s\n",
- (int)sent,
- osmo_hexdump(buf, sent > 1000? 1000 : sent),
- sent > 1000 ? "..." : "");
-
- return 0;
-}
-
diff --git a/src/gprs/gtphub_vty.c b/src/gprs/gtphub_vty.c
deleted file mode 100644
index a30ad2a54..000000000
--- a/src/gprs/gtphub_vty.c
+++ /dev/null
@@ -1,613 +0,0 @@
-/* (C) 2015 by sysmocom s.f.m.c. GmbH
- * All Rights Reserved
- *
- * Author: Neels Hofmeyr
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <string.h>
-#include <inttypes.h>
-
-#include <ares.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-
-#include <osmocom/core/talloc.h>
-#include <osmocom/vty/command.h>
-#include <osmocom/vty/misc.h>
-
-#include <openbsc/vty.h>
-#include <openbsc/gtphub.h>
-
-/* TODO split GRX ares from sgsn into a separate struct and allow use without
- * globals. */
-#include <openbsc/sgsn.h>
-extern struct sgsn_instance *sgsn;
-
-static struct gtphub *g_hub = 0;
-static struct gtphub_cfg *g_cfg = 0;
-
-static struct cmd_node gtphub_node = {
- GTPHUB_NODE,
- "%s(config-gtphub)# ",
- 1,
-};
-
-#define GTPH_DEFAULT_CONTROL_PORT 2123
-#define GTPH_DEFAULT_USER_PORT 2152
-
-static void write_addrs(struct vty *vty, const char *name,
- struct gtphub_cfg_addr *c, struct gtphub_cfg_addr *u)
-{
- if ((c->port == GTPH_DEFAULT_CONTROL_PORT)
- && (u->port == GTPH_DEFAULT_USER_PORT)
- && (strcmp(c->addr_str, u->addr_str) == 0)) {
- /* Default port numbers and same IP address: write "short"
- * variant. */
- vty_out(vty, " %s %s%s",
- name,
- c->addr_str,
- VTY_NEWLINE);
- return;
- }
-
- vty_out(vty, " %s ctrl %s %d user %s %d%s",
- name,
- c->addr_str, (int)c->port,
- u->addr_str, (int)u->port,
- VTY_NEWLINE);
-
- struct ares_addr_node *server;
- for (server = sgsn->ares_servers; server; server = server->next)
- vty_out(vty, " grx-dns-add %s%s", inet_ntoa(server->addr.addr4), VTY_NEWLINE);
-}
-
-static int config_write_gtphub(struct vty *vty)
-{
- vty_out(vty, "gtphub%s", VTY_NEWLINE);
-
- write_addrs(vty, "bind-to-sgsns",
- &g_cfg->to_gsns[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].bind,
- &g_cfg->to_gsns[GTPH_SIDE_SGSN][GTPH_PLANE_USER].bind);
-
- write_addrs(vty, "bind-to-ggsns",
- &g_cfg->to_gsns[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].bind,
- &g_cfg->to_gsns[GTPH_SIDE_GGSN][GTPH_PLANE_USER].bind);
-
- if (g_cfg->sgsn_use_sender) {
- vty_out(vty, "sgsn-use-sender%s", VTY_NEWLINE);
- }
-
- if (g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].addr_str) {
- write_addrs(vty, "sgsn-proxy",
- &g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL],
- &g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_USER]);
- }
-
- if (g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].addr_str) {
- write_addrs(vty, "ggsn-proxy",
- &g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL],
- &g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_USER]);
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_gtphub, cfg_gtphub_cmd,
- "gtphub",
- "Configure the GTP hub\n")
-{
- vty->node = GTPHUB_NODE;
- return CMD_SUCCESS;
-}
-
-#define BIND_ARGS "ctrl ADDR <0-65535> user ADDR <0-65535>"
-#define BIND_DOCS \
- "Set GTP-C bind\n" \
- "GTP-C local IP address (v4 or v6)\n" \
- "GTP-C local port\n" \
- "Set GTP-U bind\n" \
- "GTP-U local IP address (v4 or v6)\n" \
- "GTP-U local port\n"
-
-
-DEFUN(cfg_gtphub_bind_to_sgsns_short, cfg_gtphub_bind_to_sgsns_short_cmd,
- "bind-to-sgsns ADDR",
- "GTP Hub Parameters\n"
- "Set the local bind address to listen for SGSNs, for both GTP-C and GTP-U\n"
- "Local IP address (v4 or v6)\n"
- )
-{
- int i;
- for_each_plane(i)
- g_cfg->to_gsns[GTPH_SIDE_SGSN][i].bind.addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
- g_cfg->to_gsns[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].bind.port = GTPH_DEFAULT_CONTROL_PORT;
- g_cfg->to_gsns[GTPH_SIDE_SGSN][GTPH_PLANE_USER].bind.port = GTPH_DEFAULT_USER_PORT;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_gtphub_bind_to_ggsns_short, cfg_gtphub_bind_to_ggsns_short_cmd,
- "bind-to-ggsns ADDR",
- "GTP Hub Parameters\n"
- "Set the local bind address to listen for GGSNs, for both GTP-C and GTP-U\n"
- "Local IP address (v4 or v6)\n"
- )
-{
- int i;
- for_each_plane(i)
- g_cfg->to_gsns[GTPH_SIDE_GGSN][i].bind.addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
- g_cfg->to_gsns[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].bind.port = GTPH_DEFAULT_CONTROL_PORT;
- g_cfg->to_gsns[GTPH_SIDE_GGSN][GTPH_PLANE_USER].bind.port = GTPH_DEFAULT_USER_PORT;
- return CMD_SUCCESS;
-}
-
-
-static int handle_binds(struct gtphub_cfg_bind *b, const char **argv)
-{
- b[GTPH_PLANE_CTRL].bind.addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
- b[GTPH_PLANE_CTRL].bind.port = atoi(argv[1]);
- b[GTPH_PLANE_USER].bind.addr_str = talloc_strdup(tall_vty_ctx, argv[2]);
- b[GTPH_PLANE_USER].bind.port = atoi(argv[3]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_gtphub_bind_to_sgsns, cfg_gtphub_bind_to_sgsns_cmd,
- "bind-to-sgsns " BIND_ARGS,
- "GTP Hub Parameters\n"
- "Set the local bind addresses and ports to listen for SGSNs\n"
- BIND_DOCS
- )
-{
- return handle_binds(g_cfg->to_gsns[GTPH_SIDE_SGSN], argv);
-}
-
-DEFUN(cfg_gtphub_bind_to_ggsns, cfg_gtphub_bind_to_ggsns_cmd,
- "bind-to-ggsns " BIND_ARGS,
- "GTP Hub Parameters\n"
- "Set the local bind addresses and ports to listen for GGSNs\n"
- BIND_DOCS
- )
-{
- return handle_binds(g_cfg->to_gsns[GTPH_SIDE_GGSN], argv);
-}
-
-DEFUN(cfg_gtphub_ggsn_proxy_short, cfg_gtphub_ggsn_proxy_short_cmd,
- "ggsn-proxy ADDR",
- "GTP Hub Parameters\n"
- "Redirect all GGSN bound traffic to default ports on this address (another gtphub)\n"
- "Remote IP address (v4 or v6)\n"
- )
-{
- g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
- g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].port = GTPH_DEFAULT_CONTROL_PORT;
- g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_USER].addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
- g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_USER].port = GTPH_DEFAULT_USER_PORT;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_gtphub_ggsn_proxy, cfg_gtphub_ggsn_proxy_cmd,
- "ggsn-proxy " BIND_ARGS,
- "GTP Hub Parameters\n"
- "Redirect all GGSN bound traffic to these addresses and ports (another gtphub)\n"
- BIND_DOCS
- )
-{
- g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
- g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].port = atoi(argv[1]);
- g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_USER].addr_str = talloc_strdup(tall_vty_ctx, argv[2]);
- g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_USER].port = atoi(argv[3]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_gtphub_sgsn_proxy_short, cfg_gtphub_sgsn_proxy_short_cmd,
- "sgsn-proxy ADDR",
- "GTP Hub Parameters\n"
- "Redirect all SGSN bound traffic to default ports on this address (another gtphub)\n"
- "Remote IP address (v4 or v6)\n"
- )
-{
- g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
- g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].port = GTPH_DEFAULT_CONTROL_PORT;
- g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_USER].addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
- g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_USER].port = GTPH_DEFAULT_USER_PORT;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_gtphub_sgsn_proxy, cfg_gtphub_sgsn_proxy_cmd,
- "sgsn-proxy " BIND_ARGS,
- "GTP Hub Parameters\n"
- "Redirect all SGSN bound traffic to these addresses and ports (another gtphub)\n"
- BIND_DOCS
- )
-{
- g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
- g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].port = atoi(argv[1]);
- g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_USER].addr_str = talloc_strdup(tall_vty_ctx, argv[2]);
- g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_USER].port = atoi(argv[3]);
- return CMD_SUCCESS;
-}
-
-
-#define SGSN_USE_SENDER_STR \
- "Ignore SGSN's Address IEs, use sender address and port (useful over NAT)\n"
-
-DEFUN(cfg_gtphub_sgsn_use_sender,
- cfg_gtphub_sgsn_use_sender_cmd,
- "sgsn-use-sender",
- SGSN_USE_SENDER_STR)
-{
- g_cfg->sgsn_use_sender = 1;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_gtphub_no_sgsn_use_sender,
- cfg_gtphub_no_sgsn_use_sender_cmd,
- "no sgsn-use-sender",
- NO_STR SGSN_USE_SENDER_STR)
-{
- g_cfg->sgsn_use_sender = 0;
- return CMD_SUCCESS;
-}
-
-
-/* Copied from sgsn_vty.h */
-DEFUN(cfg_grx_ggsn, cfg_grx_ggsn_cmd,
- "grx-dns-add A.B.C.D",
- "Add DNS server\nIPv4 address\n")
-{
- struct ares_addr_node *node = talloc_zero(tall_bsc_ctx, struct ares_addr_node);
- node->family = AF_INET;
- inet_aton(argv[0], &node->addr.addr4);
-
- node->next = sgsn->ares_servers;
- sgsn->ares_servers = node;
- return CMD_SUCCESS;
-}
-
-
-static void show_bind_stats_all(struct vty *vty)
-{
- int plane_idx;
- for_each_plane(plane_idx) {
- vty_out(vty, "- %s Plane:%s",
- gtphub_plane_idx_names[plane_idx], VTY_NEWLINE);
-
- int side_idx;
- for_each_side(side_idx) {
- struct gtphub_bind *b = &g_hub->to_gsns[side_idx][plane_idx];
- vty_out(vty, " - local addr to/from %ss: %s port %d%s",
- gtphub_side_idx_names[side_idx],
- gsn_addr_to_str(&b->local_addr), (int)b->local_port,
- VTY_NEWLINE);
- vty_out_rate_ctr_group(vty, " ", b->counters_io);
- }
- }
-}
-
-static void show_tunnel_stats(struct vty *vty, struct gtphub_tunnel *tun)
-{
- int plane_idx;
- for_each_plane(plane_idx) {
- vty_out(vty, "- %s Plane:%s",
- gtphub_plane_idx_names[plane_idx], VTY_NEWLINE);
-
- int side_idx;
- for_each_side(side_idx) {
- struct gtphub_tunnel_endpoint *te = &tun->endpoint[side_idx][plane_idx];
- vty_out(vty, " - to/from %s:%s",
- gtphub_side_idx_names[side_idx],
- VTY_NEWLINE);
- vty_out_rate_ctr_group(vty, " ", te->counters_io);
- }
- }
-}
-
-static void show_peer_summary(struct vty *vty, const char *prefix,
- int side_idx, int plane_idx,
- struct gtphub_peer *p, int with_io_stats)
-{
- struct gtphub_peer_addr *pa;
- int p2l = strlen(prefix) + 4 + 1;
- char prefix2[p2l];
- memset(prefix2, ' ', p2l - 1);
- prefix2[p2l - 1] = '\0';
-
- if (with_io_stats) {
- llist_for_each_entry(pa, &p->addresses, entry) {
- vty_out(vty, "%s- %s %s %s%s", prefix,
- gtphub_side_idx_names[side_idx],
- gtphub_plane_idx_names[plane_idx],
- gsn_addr_to_str(&pa->addr),
- VTY_NEWLINE);
-
-
- struct gtphub_peer_port *pp;
- llist_for_each_entry(pp, &pa->ports, entry) {
- vty_out(vty, "%s Port %" PRIu16 "%s", prefix, pp->port, VTY_NEWLINE);
- vty_out_rate_ctr_group(vty, prefix2, pp->counters_io);
- }
- }
- } else {
- llist_for_each_entry(pa, &p->addresses, entry) {
- vty_out(vty, "%s- %s %s %s", prefix,
- gtphub_side_idx_names[side_idx],
- gtphub_plane_idx_names[plane_idx],
- gsn_addr_to_str(&pa->addr));
- struct gtphub_peer_port *pp;
- llist_for_each_entry(pp, &pa->ports, entry) {
- vty_out(vty, ":%" PRIu16, pp->port);
- }
- vty_out(vty, VTY_NEWLINE);
- }
- }
-}
-
-static void show_peers_summary(struct vty *vty)
-{
- int side_idx;
- int plane_idx;
-
- int count[GTPH_SIDE_N][GTPH_PLANE_N] = {{0}};
-
- for_each_side(side_idx) {
- for_each_plane(plane_idx) {
- struct gtphub_peer *p;
- llist_for_each_entry(p, &g_hub->to_gsns[side_idx][plane_idx].peers, entry) {
- count[side_idx][plane_idx] ++;
- }
- }
- }
-
- vty_out(vty, "Peers Count:%s", VTY_NEWLINE);
- for_each_side_and_plane(side_idx, plane_idx) {
- vty_out(vty, " %s %s peers: %d%s",
- gtphub_side_idx_names[side_idx],
- gtphub_plane_idx_names[plane_idx],
- count[side_idx][plane_idx],
- VTY_NEWLINE);
- }
-}
-
-static void show_peers_all(struct vty *vty, int with_io_stats)
-{
- int side_idx;
- int plane_idx;
-
- int count[GTPH_SIDE_N][GTPH_PLANE_N] = {{0}};
-
- vty_out(vty, "All Peers%s%s",
- with_io_stats? " with I/O stats" : "",
- VTY_NEWLINE);
- for_each_side(side_idx) {
- vty_out(vty, "- %s%s", gtphub_side_idx_names[side_idx], VTY_NEWLINE);
- for_each_plane(plane_idx) {
- struct gtphub_peer *p;
- llist_for_each_entry(p, &g_hub->to_gsns[side_idx][plane_idx].peers, entry) {
- count[side_idx][plane_idx] ++;
- show_peer_summary(vty, " ", side_idx, plane_idx, p, with_io_stats);
- }
- }
- }
- for_each_side_and_plane(side_idx, plane_idx) {
- vty_out(vty, "%s %s peers: %d%s",
- gtphub_side_idx_names[side_idx],
- gtphub_plane_idx_names[plane_idx],
- count[side_idx][plane_idx],
- VTY_NEWLINE);
- }
-}
-
-
-static void show_tunnels_summary(struct vty *vty)
-{
- time_t now = gtphub_now();
-
- const int w = 36;
- int max_expiry = g_hub->expire_slowly.expiry_in_seconds;
- float seconds_per_step = ((float)max_expiry) / w;
-
- /* Print TEI mapping expiry in an ASCII histogram, like:
- TEI map summary
- Legend: '_'=0 '.'<=1% ':'<=2% '|'<=10% '#'>10% (10.0 m/step)
- CTRL: 30 mappings, valid for 360m[# :. | . : . ]1m
- USER: 30 mappings, valid for 360m[# :. | . : . ]1m
- 4 TEI mappings in total, last expiry in 359.4 min
- */
- vty_out(vty,
- "Tunnels summary%s"
- " Legend: ' '=0 '.'<=1%% ':'<=2%% '|'<=10%% '#'>10%% (%.1f m/step)%s",
- VTY_NEWLINE,
- seconds_per_step / 60.,
- VTY_NEWLINE);
-
- int last_expiry = 0;
-
- unsigned int count = 0;
-
- int histogram[w];
- memset(histogram, 0, sizeof(histogram));
-
- struct gtphub_tunnel *t;
- llist_for_each_entry(t, &g_hub->tunnels, entry) {
- count ++;
- int expiry = t->expiry_entry.expiry - now;
- last_expiry = (last_expiry > expiry) ? last_expiry : expiry;
-
- int hi = ((float)expiry) / seconds_per_step;
- if (hi < 0)
- hi = 0;
- if (hi > (w - 1))
- hi = w - 1;
- histogram[hi] ++;
- }
-
- vty_out(vty,
- " %u tunnels, valid for %dm[",
- count, max_expiry / 60);
-
- int i;
- for (i = w - 1; i >= 0; i--) {
- char c;
- int val = histogram[i];
- int percent = 100. * val / count;
- if (!val)
- c = ' ';
- else if (percent <= 1)
- c = '.';
- else if (percent <= 2)
- c = ':';
- else if (percent <= 10)
- c = '|';
- else c = '#';
- vty_out(vty, "%c", c);
- }
- vty_out(vty, "]1m%s", VTY_NEWLINE);
-
- vty_out(vty, " last expiry in %.1f min%s",
- ((float)last_expiry) / 60.,
- VTY_NEWLINE);
-}
-
-static void show_tunnels_all(struct vty *vty, int with_io_stats)
-{
- time_t now = gtphub_now();
-
- vty_out(vty, "All tunnels%s:%s"
- "Legend: TEI=<hex>: SGSN <-> GGSN (expiry in minutes), with each:%s"
- " <IP-Ctrl>[/<IP-User>] (TEI C=<TEI-Ctrl-hex> U=<TEI-User-hex>)%s",
- with_io_stats? "with I/O stats" : "",
- VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
-
- unsigned int count = 0;
- unsigned int incomplete = 0;
- struct gtphub_tunnel *tun;
- llist_for_each_entry(tun, &g_hub->tunnels, entry) {
- vty_out(vty,
- "%s (expiry in %dm)%s",
- gtphub_tunnel_str(tun),
- (int)((tun->expiry_entry.expiry - now) / 60),
- VTY_NEWLINE);
- count ++;
- if (!gtphub_tunnel_complete(tun))
- incomplete ++;
- if (with_io_stats)
- show_tunnel_stats(vty, tun);
- }
- vty_out(vty, "Total: %u tunnels (of which %u incomplete)%s",
- count, incomplete, VTY_NEWLINE);
-}
-
-#define SHOW_GTPHUB_STRS SHOW_STR "Show info on running GTP hub\n"
-#define SHOW_GTPHUB_PEERS_STRS SHOW_GTPHUB_STRS "Active peers\n"
-#define SHOW_GTPHUB_TUNS_STRS SHOW_GTPHUB_STRS "Active tunnels\n"
-
-DEFUN(show_gtphub_peers_summary, show_gtphub_peers_summary_cmd, "show gtphub peers summary",
- SHOW_GTPHUB_PEERS_STRS "Summary of all peers\n")
-{
- show_peers_summary(vty);
- return CMD_SUCCESS;
-}
-
-DEFUN(show_gtphub_peers_list, show_gtphub_peers_list_cmd, "show gtphub peers list",
- SHOW_GTPHUB_PEERS_STRS "List all peers\n")
-{
- show_peers_all(vty, 0);
- return CMD_SUCCESS;
-}
-
-DEFUN(show_gtphub_peers_stats, show_gtphub_peers_stats_cmd, "show gtphub peers stats",
- SHOW_GTPHUB_PEERS_STRS "List all peers with I/O stats\n")
-{
- show_peers_all(vty, 1);
- return CMD_SUCCESS;
-}
-
-DEFUN(show_gtphub_tunnels_summary, show_gtphub_tunnels_summary_cmd, "show gtphub tunnels summary",
- SHOW_GTPHUB_TUNS_STRS "Summary of all tunnels\n")
-{
- show_tunnels_summary(vty);
- return CMD_SUCCESS;
-}
-
-DEFUN(show_gtphub_tunnels_list, show_gtphub_tunnels_list_cmd, "show gtphub tunnels list",
- SHOW_GTPHUB_TUNS_STRS "List all tunnels\n")
-{
- show_tunnels_all(vty, 0);
- return CMD_SUCCESS;
-}
-
-DEFUN(show_gtphub_tunnels_stats, show_gtphub_tunnels_stats_cmd, "show gtphub tunnels stats",
- SHOW_GTPHUB_TUNS_STRS "List all tunnels with I/O stats\n")
-{
- show_tunnels_all(vty, 1);
- return CMD_SUCCESS;
-}
-
-DEFUN(show_gtphub, show_gtphub_cmd, "show gtphub all",
- SHOW_GTPHUB_STRS "Summarize everything about the GTP hub\n")
-{
- show_bind_stats_all(vty);
- show_peers_summary(vty);
- show_tunnels_summary(vty);
- return CMD_SUCCESS;
-}
-
-
-int gtphub_vty_init(struct gtphub *global_hub, struct gtphub_cfg *global_cfg)
-{
- g_hub = global_hub;
- g_cfg = global_cfg;
-
- install_element_ve(&show_gtphub_cmd);
- install_element_ve(&show_gtphub_peers_summary_cmd);
- install_element_ve(&show_gtphub_peers_list_cmd);
- install_element_ve(&show_gtphub_peers_stats_cmd);
- install_element_ve(&show_gtphub_tunnels_summary_cmd);
- install_element_ve(&show_gtphub_tunnels_list_cmd);
- install_element_ve(&show_gtphub_tunnels_stats_cmd);
-
- install_element(CONFIG_NODE, &cfg_gtphub_cmd);
- install_node(&gtphub_node, config_write_gtphub);
- vty_install_default(GTPHUB_NODE);
-
- install_element(GTPHUB_NODE, &cfg_gtphub_bind_to_sgsns_short_cmd);
- install_element(GTPHUB_NODE, &cfg_gtphub_bind_to_sgsns_cmd);
- install_element(GTPHUB_NODE, &cfg_gtphub_bind_to_ggsns_short_cmd);
- install_element(GTPHUB_NODE, &cfg_gtphub_bind_to_ggsns_cmd);
- install_element(GTPHUB_NODE, &cfg_gtphub_ggsn_proxy_short_cmd);
- install_element(GTPHUB_NODE, &cfg_gtphub_ggsn_proxy_cmd);
- install_element(GTPHUB_NODE, &cfg_gtphub_sgsn_proxy_short_cmd);
- install_element(GTPHUB_NODE, &cfg_gtphub_sgsn_proxy_cmd);
- install_element(GTPHUB_NODE, &cfg_gtphub_sgsn_use_sender_cmd);
- install_element(GTPHUB_NODE, &cfg_gtphub_no_sgsn_use_sender_cmd);
- install_element(GTPHUB_NODE, &cfg_grx_ggsn_cmd);
-
- return 0;
-}
-
-int gtphub_cfg_read(struct gtphub_cfg *cfg, const char *config_file)
-{
- int rc;
-
- rc = vty_read_config_file(config_file, NULL);
- if (rc < 0) {
- fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
- return rc;
- }
-
- return 0;
-}
diff --git a/src/gprs/osmo_sgsn.cfg b/src/gprs/osmo_sgsn.cfg
deleted file mode 100644
index c4c9ec1cf..000000000
--- a/src/gprs/osmo_sgsn.cfg
+++ /dev/null
@@ -1,23 +0,0 @@
-!
-! Osmocom SGSN (0.9.0.474-0ede2) configuration saved from vty
-!!
-!
-line vty
- no login
-!
-sgsn
- gtp local-ip 192.168.100.11
- ggsn 0 remote-ip 192.168.100.239
- ggsn 0 gtp-version 1
-ns
- timer tns-block 3
- timer tns-block-retries 3
- timer tns-reset 3
- timer tns-reset-retries 3
- timer tns-test 30
- timer tns-alive 3
- timer tns-alive-retries 10
- encapsulation udp local-ip 192.168.100.11
- encapsulation udp local-port 23000
- encapsulation framerelay-gre enabled 0
-bssgp
diff --git a/src/gprs/sgsn_ares.c b/src/gprs/sgsn_ares.c
deleted file mode 100644
index d94d184a3..000000000
--- a/src/gprs/sgsn_ares.c
+++ /dev/null
@@ -1,173 +0,0 @@
-/* C-ARES DNS resolver integration */
-
-/*
- * (C) 2015 by Holger Hans Peter Freyther
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <openbsc/sgsn.h>
-#include <openbsc/debug.h>
-
-#include <netdb.h>
-
-struct cares_event_fd {
- struct llist_head head;
- struct osmo_fd fd;
-};
-
-struct cares_cb_data {
- ares_host_callback cb;
- void *data;
-};
-
-static void osmo_ares_reschedule(struct sgsn_instance *sgsn);
-static void ares_cb(void *_arg, int status, int timeouts, struct hostent *hostent)
-{
- struct cares_cb_data *arg = _arg;
-
- arg->cb(arg->data, status, timeouts, hostent);
- osmo_ares_reschedule(sgsn);
- talloc_free(arg);
-}
-
-static int ares_osmo_fd_cb(struct osmo_fd *fd, unsigned int what)
-{
- LOGP(DGPRS, LOGL_DEBUG, "C-ares fd(%d) ready(%d)\n", fd->fd, what);
-
- ares_process_fd(sgsn->ares_channel,
- (what & BSC_FD_READ) ? fd->fd : ARES_SOCKET_BAD,
- (what & BSC_FD_WRITE) ? fd->fd : ARES_SOCKET_BAD);
- osmo_ares_reschedule(sgsn);
- return 0;
-}
-
-static void ares_timeout_cb(void *data)
-{
- struct sgsn_instance *sgsn = data;
-
- LOGP(DGPRS, LOGL_DEBUG, "C-ares triggering timeout\n");
- ares_process_fd(sgsn->ares_channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD);
- osmo_ares_reschedule(sgsn);
-}
-
-static void osmo_ares_reschedule(struct sgsn_instance *sgsn)
-{
- struct timeval *timeout, tv;
-
- osmo_timer_del(&sgsn->ares_timer);
- timeout = ares_timeout(sgsn->ares_channel, NULL, &tv);
- if (timeout) {
- LOGP(DGPRS, LOGL_DEBUG, "C-ares scheduling timeout %llu.%llu\n",
- (unsigned long long) tv.tv_sec,
- (unsigned long long) tv.tv_usec);
- osmo_timer_setup(&sgsn->ares_timer, ares_timeout_cb, sgsn);
- osmo_timer_schedule(&sgsn->ares_timer, tv.tv_sec, tv.tv_usec);
- }
-}
-
-static void setup_ares_osmo_fd(void *data, int fd, int read, int write)
-{
- struct cares_event_fd *ufd, *tmp;
-
- /* delete the entry */
- if (read == 0 && write == 0) {
- llist_for_each_entry_safe(ufd, tmp, &sgsn->ares_fds, head) {
- if (ufd->fd.fd != fd)
- continue;
-
- LOGP(DGPRS, LOGL_DEBUG,
- "Removing C-ares watched fd (%d)\n", fd);
- osmo_fd_unregister(&ufd->fd);
- llist_del(&ufd->head);
- talloc_free(ufd);
- return;
- }
- }
-
- /* Search for the fd or create a new one */
- llist_for_each_entry(ufd, &sgsn->ares_fds, head) {
- if (ufd->fd.fd != fd)
- continue;
-
- LOGP(DGPRS, LOGL_DEBUG, "Updating C-ares fd (%d)\n", fd);
- goto update_fd;
- }
-
- LOGP(DGPRS, LOGL_DEBUG, "Registering C-ares fd (%d)\n", fd);
- ufd = talloc_zero(tall_bsc_ctx, struct cares_event_fd);
- ufd->fd.fd = fd;
- ufd->fd.cb = ares_osmo_fd_cb;
- ufd->fd.data = data;
- if (osmo_fd_register(&ufd->fd) != 0)
- LOGP(DGPRS, LOGL_ERROR, "Failed to register C-ares fd (%d)\n", fd);
- llist_add(&ufd->head, &sgsn->ares_fds);
-
-update_fd:
- if (read)
- ufd->fd.when |= BSC_FD_READ;
- else
- ufd->fd.when &= ~BSC_FD_READ;
-
- if (write)
- ufd->fd.when |= BSC_FD_WRITE;
- else
- ufd->fd.when &= ~BSC_FD_WRITE;
-
- osmo_ares_reschedule(sgsn);
-}
-
-int sgsn_ares_query(struct sgsn_instance *sgsn, const char *name,
- ares_host_callback cb, void *data)
-{
- struct cares_cb_data *cb_data;
-
- cb_data = talloc_zero(tall_bsc_ctx, struct cares_cb_data);
- cb_data->cb = cb;
- cb_data->data = data;
- ares_gethostbyname(sgsn->ares_channel, name, AF_INET, ares_cb, cb_data);
- osmo_ares_reschedule(sgsn);
- return 0;
-}
-
-int sgsn_ares_init(struct sgsn_instance *sgsn)
-{
- struct ares_options options;
- int optmask;
- int rc;
-
- INIT_LLIST_HEAD(&sgsn->ares_fds);
- memset(&options, 0, sizeof(options));
- options.sock_state_cb = setup_ares_osmo_fd;
- options.sock_state_cb_data = sgsn;
-
- optmask = ARES_OPT_FLAGS | ARES_OPT_SOCK_STATE_CB | ARES_OPT_DOMAINS;
-
- if (sgsn->ares_servers)
- optmask |= ARES_OPT_SERVERS;
-
- ares_library_init(ARES_LIB_INIT_ALL);
- rc = ares_init_options(&sgsn->ares_channel, &options, optmask);
- if (rc != ARES_SUCCESS)
- return rc;
-
- if (sgsn->ares_servers)
- rc = ares_set_servers(sgsn->ares_channel, sgsn->ares_servers);
-
- return rc;
-}
-
-osmo_static_assert(ARES_SUCCESS == 0, ares_success_zero);
diff --git a/src/gprs/sgsn_auth.c b/src/gprs/sgsn_auth.c
deleted file mode 100644
index a64339c3e..000000000
--- a/src/gprs/sgsn_auth.c
+++ /dev/null
@@ -1,312 +0,0 @@
-/* MS authorization and subscriber data handling */
-
-/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
-#include <osmocom/core/utils.h>
-#include <openbsc/sgsn.h>
-#include <openbsc/gprs_sgsn.h>
-#include <openbsc/gprs_gmm.h>
-#include <openbsc/gprs_subscriber.h>
-#include <openbsc/debug.h>
-
-const struct value_string auth_state_names[] = {
- { SGSN_AUTH_ACCEPTED, "accepted"},
- { SGSN_AUTH_REJECTED, "rejected"},
- { SGSN_AUTH_UNKNOWN, "unknown"},
- { SGSN_AUTH_AUTHENTICATE, "authenticate" },
- { SGSN_AUTH_UMTS_RESYNC, "UMTS-resync" },
- { 0, NULL }
-};
-
-const struct value_string *sgsn_auth_state_names = auth_state_names;
-
-void sgsn_auth_init(void)
-{
- INIT_LLIST_HEAD(&sgsn->cfg.imsi_acl);
-}
-
-/* temporary IMSI ACL hack */
-struct imsi_acl_entry *sgsn_acl_lookup(const char *imsi, struct sgsn_config *cfg)
-{
- struct imsi_acl_entry *acl;
- llist_for_each_entry(acl, &cfg->imsi_acl, list) {
- if (!strcmp(imsi, acl->imsi))
- return acl;
- }
- return NULL;
-}
-
-int sgsn_acl_add(const char *imsi, struct sgsn_config *cfg)
-{
- struct imsi_acl_entry *acl;
-
- if (sgsn_acl_lookup(imsi, cfg))
- return -EEXIST;
-
- acl = talloc_zero(NULL, struct imsi_acl_entry);
- if (!acl)
- return -ENOMEM;
- osmo_strlcpy(acl->imsi, imsi, sizeof(acl->imsi));
-
- llist_add(&acl->list, &cfg->imsi_acl);
-
- return 0;
-}
-
-int sgsn_acl_del(const char *imsi, struct sgsn_config *cfg)
-{
- struct imsi_acl_entry *acl;
-
- acl = sgsn_acl_lookup(imsi, cfg);
- if (!acl)
- return -ENODEV;
-
- llist_del(&acl->list);
- talloc_free(acl);
-
- return 0;
-}
-
-enum sgsn_auth_state sgsn_auth_state(struct sgsn_mm_ctx *mmctx)
-{
- char mccmnc[16];
- int check_net = 0;
- int check_acl = 0;
-
- OSMO_ASSERT(mmctx);
-
- switch (sgsn->cfg.auth_policy) {
- case SGSN_AUTH_POLICY_OPEN:
- return SGSN_AUTH_ACCEPTED;
-
- case SGSN_AUTH_POLICY_CLOSED:
- check_net = 1;
- check_acl = 1;
- break;
-
- case SGSN_AUTH_POLICY_ACL_ONLY:
- check_acl = 1;
- break;
-
- case SGSN_AUTH_POLICY_REMOTE:
- if (!mmctx->subscr)
- return mmctx->auth_state;
-
- if (mmctx->subscr->flags & GPRS_SUBSCRIBER_UPDATE_PENDING_MASK)
- return mmctx->auth_state;
-
- if (sgsn->cfg.require_authentication &&
- (!mmctx->is_authenticated ||
- mmctx->subscr->sgsn_data->auth_triplets_updated))
- return SGSN_AUTH_AUTHENTICATE;
-
- if (mmctx->subscr->authorized)
- return SGSN_AUTH_ACCEPTED;
-
- return SGSN_AUTH_REJECTED;
- }
-
- if (!strlen(mmctx->imsi)) {
- LOGMMCTXP(LOGL_NOTICE, mmctx,
- "Missing IMSI, authorization state not known\n");
- return SGSN_AUTH_UNKNOWN;
- }
-
- if (check_net) {
- /* We simply assume that the IMSI exists, as long as it is part
- * of 'our' network */
- snprintf(mccmnc, sizeof(mccmnc), "%03d%02d",
- mmctx->ra.mcc, mmctx->ra.mnc);
- if (strncmp(mccmnc, mmctx->imsi, 5) == 0)
- return SGSN_AUTH_ACCEPTED;
- }
-
- if (check_acl && sgsn_acl_lookup(mmctx->imsi, &sgsn->cfg))
- return SGSN_AUTH_ACCEPTED;
-
- return SGSN_AUTH_REJECTED;
-}
-
-/*
- * This function is directly called by e.g. the GMM layer. It returns either
- * after calling sgsn_auth_update directly or after triggering an asynchronous
- * procedure which will call sgsn_auth_update later on.
- */
-int sgsn_auth_request(struct sgsn_mm_ctx *mmctx)
-{
- struct gprs_subscr *subscr;
- struct gsm_auth_tuple *at;
- int need_update_location;
- int rc;
-
- LOGMMCTXP(LOGL_DEBUG, mmctx, "Requesting authorization\n");
-
- if (sgsn->cfg.auth_policy != SGSN_AUTH_POLICY_REMOTE) {
- sgsn_auth_update(mmctx);
- return 0;
- }
-
- need_update_location = sgsn->cfg.require_update_location &&
- (mmctx->subscr == NULL ||
- mmctx->pending_req == GSM48_MT_GMM_ATTACH_REQ);
-
- /* This has the side effect of registering the subscr with the mmctx */
- subscr = gprs_subscr_get_or_create_by_mmctx(mmctx);
- gprs_subscr_put(subscr);
-
- OSMO_ASSERT(mmctx->subscr != NULL);
-
- if (sgsn->cfg.require_authentication && !mmctx->is_authenticated) {
- /* Find next tuple */
- at = sgsn_auth_get_tuple(mmctx, mmctx->auth_triplet.key_seq);
-
- if (!at) {
- /* No valid tuple found, request fresh ones */
- mmctx->auth_triplet.key_seq = GSM_KEY_SEQ_INVAL;
- LOGMMCTXP(LOGL_INFO, mmctx,
- "Requesting authentication tuples\n");
- rc = gprs_subscr_request_auth_info(mmctx, NULL, NULL);
- if (rc >= 0)
- return 0;
-
- return rc;
- }
-
- mmctx->auth_triplet = *at;
- } else if (need_update_location) {
- LOGMMCTXP(LOGL_INFO, mmctx,
- "Missing information, requesting subscriber data\n");
- rc = gprs_subscr_request_update_location(mmctx);
- if (rc >= 0)
- return 0;
-
- return rc;
- }
-
- sgsn_auth_update(mmctx);
- return 0;
-}
-
-void sgsn_auth_update(struct sgsn_mm_ctx *mmctx)
-{
- enum sgsn_auth_state auth_state;
- struct gprs_subscr *subscr = mmctx->subscr;
- struct gsm_auth_tuple *at;
- int gmm_cause;
-
- auth_state = sgsn_auth_state(mmctx);
-
- LOGMMCTXP(LOGL_DEBUG, mmctx, "Updating authorization (%s -> %s)\n",
- get_value_string(sgsn_auth_state_names, mmctx->auth_state),
- get_value_string(sgsn_auth_state_names, auth_state));
-
- if (auth_state == SGSN_AUTH_UNKNOWN && subscr &&
- !(subscr->flags & GPRS_SUBSCRIBER_UPDATE_PENDING_MASK)) {
- /* Reject requests if gprs_subscr_request_update_location fails */
- LOGMMCTXP(LOGL_ERROR, mmctx,
- "Missing information, authorization not possible\n");
- auth_state = SGSN_AUTH_REJECTED;
- }
-
- if (auth_state == SGSN_AUTH_AUTHENTICATE &&
- mmctx->auth_triplet.key_seq == GSM_KEY_SEQ_INVAL) {
- /* The current tuple is not valid, but we are possibly called
- * because new auth tuples have been received */
- at = sgsn_auth_get_tuple(mmctx, mmctx->auth_triplet.key_seq);
- if (!at) {
- LOGMMCTXP(LOGL_ERROR, mmctx,
- "Missing auth tuples, authorization not possible\n");
- auth_state = SGSN_AUTH_REJECTED;
- } else {
- mmctx->auth_triplet = *at;
- }
- }
-
- if (mmctx->auth_state == auth_state)
- return;
-
- LOGMMCTXP(LOGL_INFO, mmctx, "Got authorization update: state %s -> %s\n",
- get_value_string(sgsn_auth_state_names, mmctx->auth_state),
- get_value_string(sgsn_auth_state_names, auth_state));
-
- mmctx->auth_state = auth_state;
-
- switch (auth_state) {
- case SGSN_AUTH_AUTHENTICATE:
- if (subscr)
- subscr->sgsn_data->auth_triplets_updated = 0;
-
- gsm0408_gprs_authenticate(mmctx);
- break;
- case SGSN_AUTH_ACCEPTED:
- gsm0408_gprs_access_granted(mmctx);
- break;
- case SGSN_AUTH_REJECTED:
- gmm_cause =
- subscr ? subscr->sgsn_data->error_cause :
- SGSN_ERROR_CAUSE_NONE;
-
- if (subscr && (subscr->flags & GPRS_SUBSCRIBER_CANCELLED) != 0)
- gsm0408_gprs_access_cancelled(mmctx, gmm_cause);
- else
- gsm0408_gprs_access_denied(mmctx, gmm_cause);
- break;
- default:
- break;
- }
-}
-
-struct gsm_auth_tuple *sgsn_auth_get_tuple(struct sgsn_mm_ctx *mmctx,
- unsigned key_seq)
-{
- unsigned count;
- unsigned idx;
- struct gsm_auth_tuple *at = NULL;
-
- struct sgsn_subscriber_data *sdata;
-
- if (!mmctx->subscr)
- return NULL;
-
- if (key_seq == GSM_KEY_SEQ_INVAL)
- /* Start with 0 after increment module array size */
- idx = ARRAY_SIZE(sdata->auth_triplets) - 1;
- else
- idx = key_seq;
-
- sdata = mmctx->subscr->sgsn_data;
-
- /* Find next tuple */
- for (count = ARRAY_SIZE(sdata->auth_triplets); count > 0; count--) {
- idx = (idx + 1) % ARRAY_SIZE(sdata->auth_triplets);
-
- if (sdata->auth_triplets[idx].key_seq == GSM_KEY_SEQ_INVAL)
- continue;
-
- if (sdata->auth_triplets[idx].use_count == 0) {
- at = &sdata->auth_triplets[idx];
- at->use_count = 1;
- return at;
- }
- }
-
- return NULL;
-}
diff --git a/src/gprs/sgsn_cdr.c b/src/gprs/sgsn_cdr.c
deleted file mode 100644
index 091089610..000000000
--- a/src/gprs/sgsn_cdr.c
+++ /dev/null
@@ -1,258 +0,0 @@
-/* GPRS SGSN CDR dumper */
-
-/* (C) 2015 by Holger Hans Peter Freyther
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <openbsc/sgsn.h>
-#include <openbsc/signal.h>
-#include <openbsc/gprs_utils.h>
-#include <openbsc/debug.h>
-
-#include <openbsc/vty.h>
-
-#include <gtp.h>
-#include <pdp.h>
-
-#include <arpa/inet.h>
-
-#include <time.h>
-
-#include <stdio.h>
-#include <inttypes.h>
-
-/* TODO...avoid going through a global */
-extern struct sgsn_instance *sgsn;
-
-/**
- * The CDR module will generate an entry like:
- *
- * IMSI, # Subscriber IMSI
- * IMEI, # Subscriber IMEI
- * MSISDN, # Subscriber MISDN
- * Charging_Timestamp, # Event start Time
- * Charging_UTC, # Time zone of event start time
- * Duration, # Session DURATION
- * Cell_Id, # CELL_ID
- * Location_Area, # LAC
- * GGSN_ADDR, # GGSN_ADDR
- * SGSN_ADDR, # SGSN_ADDR
- * APNI, # APNI
- * PDP_ADDR, # PDP_ADDR
- * VOL_IN, # VOL_IN in Bytes
- * VOL_OUT, # VOL_OUT in Bytes
- * CAUSE_FOR_TERM, # CAUSE_FOR_TERM
- */
-
-
-static void maybe_print_header(FILE *cdr_file)
-{
- if (ftell(cdr_file) != 0)
- return;
-
- fprintf(cdr_file, "timestamp,imsi,imei,msisdn,cell_id,lac,hlr,event,pdp_duration,ggsn_addr,sgsn_addr,apni,eua_addr,vol_in,vol_out,charging_id\n");
-}
-
-static void cdr_log_mm(struct sgsn_instance *inst, const char *ev,
- struct sgsn_mm_ctx *mmctx)
-{
- FILE *cdr_file;
- struct tm tm;
- struct timeval tv;
-
- if (!inst->cfg.cdr.filename)
- return;
-
- cdr_file = fopen(inst->cfg.cdr.filename, "a");
- if (!cdr_file) {
- LOGP(DGPRS, LOGL_ERROR, "Failed to open %s\n",
- inst->cfg.cdr.filename);
- return;
- }
-
- maybe_print_header(cdr_file);
- gettimeofday(&tv, NULL);
- gmtime_r(&tv.tv_sec, &tm);
- fprintf(cdr_file, "%04d%02d%02d%02d%02d%02d%03d,%s,%s,%s,%d,%d,%s,%s\n",
- tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
- tm.tm_hour, tm.tm_min, tm.tm_sec,
- (int)(tv.tv_usec / 1000),
- mmctx->imsi,
- mmctx->imei,
- mmctx->msisdn,
- mmctx->gb.cell_id,
- mmctx->ra.lac,
- mmctx->hlr,
- ev);
-
- fclose(cdr_file);
-}
-
-static void extract_eua(struct ul66_t *eua, char *eua_addr)
-{
- if (eua->l < 2)
- return;
-
- /* there is no addr for ETSI/PPP */
- if ((eua->v[0] & 0x0F) != 1) {
- strcpy(eua_addr, "ETSI");
- return;
- }
-
- if (eua->v[1] == 0x21 && eua->l == 6)
- inet_ntop(AF_INET, &eua->v[2], eua_addr, INET_ADDRSTRLEN);
- else if (eua->v[1] == 0x57 && eua->l == 18)
- inet_ntop(AF_INET6, &eua->v[2], eua_addr, INET6_ADDRSTRLEN);
- else {
- /* e.g. both IPv4 and IPv6 */
- strcpy(eua_addr, "Unknown address");
- }
-}
-
-static void cdr_log_pdp(struct sgsn_instance *inst, const char *ev,
- struct sgsn_pdp_ctx *pdp)
-{
- FILE *cdr_file;
- char apni[(pdp->lib ? pdp->lib->apn_use.l : 0) + 1];
- char ggsn_addr[INET_ADDRSTRLEN + 1];
- char sgsn_addr[INET_ADDRSTRLEN + 1];
- char eua_addr[INET6_ADDRSTRLEN + 1];
- struct tm tm;
- struct timeval tv;
- time_t duration;
- struct timespec tp;
-
- if (!inst->cfg.cdr.filename)
- return;
-
- memset(apni, 0, sizeof(apni));
- memset(ggsn_addr, 0, sizeof(ggsn_addr));
- memset(eua_addr, 0, sizeof(eua_addr));
-
-
- if (pdp->lib) {
- 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->gb.cell_id : -1,
- pdp->mm ? pdp->mm->ra.lac : -1,
- pdp->mm ? pdp->mm->hlr : "N/A",
- ev,
- (unsigned long ) duration,
- ggsn_addr,
- sgsn_addr,
- apni,
- eua_addr,
- pdp->cdr_bytes_in,
- pdp->cdr_bytes_out,
- pdp->cdr_charging_id);
- fclose(cdr_file);
-}
-
-static void cdr_pdp_timeout(void *_data)
-{
- struct sgsn_pdp_ctx *pdp = _data;
- cdr_log_pdp(sgsn, "pdp-periodic", pdp);
- osmo_timer_schedule(&pdp->cdr_timer, sgsn->cfg.cdr.interval, 0);
-}
-
-static int handle_sgsn_sig(unsigned int subsys, unsigned int signal,
- void *handler_data, void *_signal_data)
-{
- struct sgsn_signal_data *signal_data = _signal_data;
- struct sgsn_instance *inst = handler_data;
-
- if (subsys != SS_SGSN)
- return 0;
-
- switch (signal) {
- case S_SGSN_ATTACH:
- cdr_log_mm(inst, "attach", signal_data->mm);
- break;
- case S_SGSN_UPDATE:
- cdr_log_mm(inst, "update", signal_data->mm);
- break;
- case S_SGSN_DETACH:
- cdr_log_mm(inst, "detach", signal_data->mm);
- break;
- case S_SGSN_MM_FREE:
- cdr_log_mm(inst, "free", signal_data->mm);
- break;
- case S_SGSN_PDP_ACT:
- clock_gettime(CLOCK_MONOTONIC, &signal_data->pdp->cdr_start);
- signal_data->pdp->cdr_charging_id = signal_data->pdp->lib->cid;
- cdr_log_pdp(inst, "pdp-act", signal_data->pdp);
- osmo_timer_setup(&signal_data->pdp->cdr_timer, cdr_pdp_timeout,
- signal_data->pdp);
- osmo_timer_schedule(&signal_data->pdp->cdr_timer, inst->cfg.cdr.interval, 0);
- break;
- case S_SGSN_PDP_DEACT:
- cdr_log_pdp(inst, "pdp-deact", signal_data->pdp);
- osmo_timer_del(&signal_data->pdp->cdr_timer);
- break;
- case S_SGSN_PDP_TERMINATE:
- cdr_log_pdp(inst, "pdp-terminate", signal_data->pdp);
- osmo_timer_del(&signal_data->pdp->cdr_timer);
- break;
- case S_SGSN_PDP_FREE:
- cdr_log_pdp(inst, "pdp-free", signal_data->pdp);
- osmo_timer_del(&signal_data->pdp->cdr_timer);
- break;
- }
-
- return 0;
-}
-
-int sgsn_cdr_init(struct sgsn_instance *sgsn)
-{
- /* register for CDR related events */
- sgsn->cfg.cdr.interval = 10 * 60;
- osmo_signal_register_handler(SS_SGSN, handle_sgsn_sig, sgsn);
-
- return 0;
-}
diff --git a/src/gprs/sgsn_ctrl.c b/src/gprs/sgsn_ctrl.c
deleted file mode 100644
index 31ac74f1f..000000000
--- a/src/gprs/sgsn_ctrl.c
+++ /dev/null
@@ -1,69 +0,0 @@
-/* Control Interface Implementation for the SGSN */
-/*
- * (C) 2014 by Holger Hans Peter Freyther
- * (C) 2014 by sysmocom s.f.m.c. GmbH
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <osmocom/ctrl/control_if.h>
-#include <osmocom/ctrl/control_cmd.h>
-#include <openbsc/gsm_data.h>
-#include <openbsc/gprs_sgsn.h>
-#include <openbsc/sgsn.h>
-#include <openbsc/debug.h>
-
-#include <pdp.h>
-
-extern vector ctrl_node_vec;
-
-static int get_subscriber_list(struct ctrl_cmd *cmd, void *d)
-{
- struct sgsn_mm_ctx *mm;
-
- cmd->reply = talloc_strdup(cmd, "");
- llist_for_each_entry(mm, &sgsn_mm_ctxts, list) {
- char *addr = NULL;
- struct sgsn_pdp_ctx *pdp;
-
- if (strlen(mm->imsi) == 0)
- continue;
-
- llist_for_each_entry(pdp, &mm->pdp_list, list)
- addr = gprs_pdpaddr2str(pdp->lib->eua.v,
- pdp->lib->eua.l);
-
- cmd->reply = talloc_asprintf_append(
- cmd->reply,
- "%s,%s\n", mm->imsi, addr ? addr : "");
- }
-
- return CTRL_CMD_REPLY;
-}
-CTRL_CMD_DEFINE_RO(subscriber_list, "subscriber-list-active-v1");
-
-int sgsn_ctrl_cmds_install(void)
-{
- int rc = 0;
- rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_subscriber_list);
- return rc;
-}
-
-struct ctrl_handle *sgsn_controlif_setup(struct gsm_network *net,
- const char *bind_addr, uint16_t port)
-{
- return ctrl_interface_setup_dynip(net, bind_addr, port, NULL);
-}
diff --git a/src/gprs/sgsn_libgtp.c b/src/gprs/sgsn_libgtp.c
deleted file mode 100644
index 1da31d292..000000000
--- a/src/gprs/sgsn_libgtp.c
+++ /dev/null
@@ -1,870 +0,0 @@
-/* GPRS SGSN integration with libgtp of OpenGGSN */
-/* libgtp implements the GPRS Tunelling Protocol GTP per TS 09.60 / 29.060 */
-
-/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
- * (C) 2010 by On-Waves
- * (C) 2015 by Holger Hans Peter Freyther
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <unistd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <getopt.h>
-#include <errno.h>
-#include <signal.h>
-#include <sys/fcntl.h>
-#include <sys/stat.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-
-#include "bscconfig.h"
-
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/select.h>
-#include <osmocom/core/rate_ctr.h>
-#include <osmocom/gprs/gprs_bssgp.h>
-#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
-
-#include <openbsc/signal.h>
-#include <openbsc/debug.h>
-#include <openbsc/sgsn.h>
-#include <openbsc/gprs_llc.h>
-#include <openbsc/gprs_sgsn.h>
-#include <openbsc/gprs_gmm.h>
-#include <openbsc/gprs_subscriber.h>
-#include <openbsc/gprs_sndcp.h>
-
-#ifdef BUILD_IU
-#include <osmocom/ranap/iu_client.h>
-#include <osmocom/ranap/ranap_ies_defs.h>
-#endif
-
-#include <gtp.h>
-#include <pdp.h>
-
-/* TS 23.003: The MSISDN shall take the dummy MSISDN value composed of
- * 15 digits set to 0 (encoded as an E.164 international number) when
- * the MSISDN is not available in messages in which the presence of the
- * MSISDN parameter */
-static const uint8_t dummy_msisdn[] =
- { 0x91, /* No extension, international, E.164 */
- 0, 0, 0, 0, 0, 0, 0, /* 14 digits of zeroes */
- 0xF0 /* 15th digit of zero + padding */ };
-
-const struct value_string gtp_cause_strs[] = {
- { GTPCAUSE_REQ_IMSI, "Request IMSI" },
- { GTPCAUSE_REQ_IMEI, "Request IMEI" },
- { GTPCAUSE_REQ_IMSI_IMEI, "Request IMSI and IMEI" },
- { GTPCAUSE_NO_ID_NEEDED, "No identity needed" },
- { GTPCAUSE_MS_REFUSES_X, "MS refuses" },
- { GTPCAUSE_MS_NOT_RESP_X, "MS is not GPRS responding" },
- { GTPCAUSE_ACC_REQ, "Request accepted" },
- { GTPCAUSE_NON_EXIST, "Non-existent" },
- { GTPCAUSE_INVALID_MESSAGE, "Invalid message format" },
- { GTPCAUSE_IMSI_NOT_KNOWN, "IMSI not known" },
- { GTPCAUSE_MS_DETACHED, "MS is GPRS detached" },
- { GTPCAUSE_MS_NOT_RESP, "MS is not GPRS responding" },
- { GTPCAUSE_MS_REFUSES, "MS refuses" },
- { GTPCAUSE_NO_RESOURCES, "No resources available" },
- { GTPCAUSE_NOT_SUPPORTED, "Service not supported" },
- { GTPCAUSE_MAN_IE_INCORRECT, "Mandatory IE incorrect" },
- { GTPCAUSE_MAN_IE_MISSING, "Mandatory IE missing" },
- { GTPCAUSE_OPT_IE_INCORRECT, "Optional IE incorrect" },
- { GTPCAUSE_SYS_FAIL, "System failure" },
- { GTPCAUSE_ROAMING_REST, "Roaming restrictions" },
- { GTPCAUSE_PTIMSI_MISMATCH, "P-TMSI Signature mismatch" },
- { GTPCAUSE_CONN_SUSP, "GPRS connection suspended" },
- { GTPCAUSE_AUTH_FAIL, "Authentication failure" },
- { GTPCAUSE_USER_AUTH_FAIL, "User authentication failed" },
- { GTPCAUSE_CONTEXT_NOT_FOUND, "Context not found" },
- { GTPCAUSE_ADDR_OCCUPIED, "All dynamic PDP addresses occupied" },
- { GTPCAUSE_NO_MEMORY, "No memory is available" },
- { GTPCAUSE_RELOC_FAIL, "Relocation failure" },
- { GTPCAUSE_UNKNOWN_MAN_EXTHEADER, "Unknown mandatory ext. header" },
- { GTPCAUSE_SEM_ERR_TFT, "Semantic error in TFT operation" },
- { GTPCAUSE_SYN_ERR_TFT, "Syntactic error in TFT operation" },
- { GTPCAUSE_SEM_ERR_FILTER, "Semantic errors in packet filter" },
- { GTPCAUSE_SYN_ERR_FILTER, "Syntactic errors in packet filter" },
- { GTPCAUSE_MISSING_APN, "Missing or unknown APN" },
- { GTPCAUSE_UNKNOWN_PDP, "Unknown PDP address or PDP type" },
- { 0, NULL }
-};
-
-/* Generate the GTP IMSI IE according to 09.60 Section 7.9.2 */
-static uint64_t imsi_str2gtp(char *str)
-{
- uint64_t imsi64 = 0;
- unsigned int n;
- unsigned int imsi_len = strlen(str);
-
- if (imsi_len > 16) {
- LOGP(DGPRS, LOGL_NOTICE, "IMSI length > 16 not supported!\n");
- return 0;
- }
-
- for (n = 0; n < 16; n++) {
- uint64_t val;
- if (n < imsi_len)
- val = (str[n]-'0') & 0xf;
- else
- val = 0xf;
- imsi64 |= (val << (n*4));
- }
- return imsi64;
-}
-
-/* generate a PDP context based on the IE's from the 04.08 message,
- * and send the GTP create pdp context request to the GGSN */
-struct sgsn_pdp_ctx *sgsn_create_pdp_ctx(struct sgsn_ggsn_ctx *ggsn,
- struct sgsn_mm_ctx *mmctx,
- uint16_t nsapi,
- struct tlv_parsed *tp)
-{
- struct gprs_ra_id raid;
- struct sgsn_pdp_ctx *pctx;
- struct pdp_t *pdp;
- uint64_t imsi_ui64;
- size_t qos_len;
- const uint8_t *qos;
- int rc;
-
- LOGP(DGPRS, LOGL_ERROR, "Create PDP Context\n");
- pctx = sgsn_pdp_ctx_alloc(mmctx, nsapi);
- if (!pctx) {
- LOGP(DGPRS, LOGL_ERROR, "Couldn't allocate PDP Ctx\n");
- return NULL;
- }
-
- imsi_ui64 = imsi_str2gtp(mmctx->imsi);
-
- rc = pdp_newpdp(&pdp, imsi_ui64, nsapi, NULL);
- if (rc) {
- LOGP(DGPRS, LOGL_ERROR, "Out of libgtp PDP Contexts\n");
- return NULL;
- }
- pdp->priv = pctx;
- pctx->lib = pdp;
- pctx->ggsn = ggsn;
-
- //pdp->peer = /* sockaddr_in of GGSN (receive) */
- //pdp->ipif = /* not used by library */
- pdp->version = ggsn->gtp_version;
- pdp->hisaddr0 = ggsn->remote_addr;
- pdp->hisaddr1 = ggsn->remote_addr;
- //pdp->cch_pdp = 512; /* Charging Flat Rate */
-
- /* MS provided APN, subscription was verified by the caller */
- pdp->selmode = 0xFC | 0x00;
-
- /* IMSI, TEID/TEIC, FLLU/FLLC, TID, NSAPI set in pdp_newpdp */
-
- /* Put the MSISDN in case we have it */
- if (mmctx->subscr && mmctx->subscr->sgsn_data->msisdn_len) {
- pdp->msisdn.l = mmctx->subscr->sgsn_data->msisdn_len;
- if (pdp->msisdn.l > sizeof(pdp->msisdn.v))
- pdp->msisdn.l = sizeof(pdp->msisdn.v);
- memcpy(pdp->msisdn.v, mmctx->subscr->sgsn_data->msisdn,
- pdp->msisdn.l);
- } else {
- /* use the dummy 15-digits-zero MSISDN value */
- pdp->msisdn.l = sizeof(dummy_msisdn);
- memcpy(pdp->msisdn.v, dummy_msisdn, pdp->msisdn.l);
- }
-
- /* End User Address from GMM requested PDP address */
- pdp->eua.l = TLVP_LEN(tp, OSMO_IE_GSM_REQ_PDP_ADDR);
- if (pdp->eua.l > sizeof(pdp->eua.v))
- pdp->eua.l = sizeof(pdp->eua.v);
- memcpy(pdp->eua.v, TLVP_VAL(tp, OSMO_IE_GSM_REQ_PDP_ADDR),
- pdp->eua.l);
- /* Highest 4 bits of first byte need to be set to 1, otherwise
- * the IE is identical with the 04.08 PDP Address IE */
- pdp->eua.v[0] |= 0xf0;
-
- /* APN name from GMM */
- pdp->apn_use.l = TLVP_LEN(tp, GSM48_IE_GSM_APN);
- if (pdp->apn_use.l > sizeof(pdp->apn_use.v))
- pdp->apn_use.l = sizeof(pdp->apn_use.v);
- memcpy(pdp->apn_use.v, TLVP_VAL(tp, GSM48_IE_GSM_APN),
- pdp->apn_use.l);
-
- /* Protocol Configuration Options from GMM */
- pdp->pco_req.l = TLVP_LEN(tp, GSM48_IE_GSM_PROTO_CONF_OPT);
- if (pdp->pco_req.l > sizeof(pdp->pco_req.v))
- pdp->pco_req.l = sizeof(pdp->pco_req.v);
- memcpy(pdp->pco_req.v, TLVP_VAL(tp, GSM48_IE_GSM_PROTO_CONF_OPT),
- pdp->pco_req.l);
-
- /* QoS options from GMM or remote */
- if (TLVP_LEN(tp, OSMO_IE_GSM_SUB_QOS) > 0) {
- qos_len = TLVP_LEN(tp, OSMO_IE_GSM_SUB_QOS);
- qos = TLVP_VAL(tp, OSMO_IE_GSM_SUB_QOS);
- } else {
- qos_len = TLVP_LEN(tp, OSMO_IE_GSM_REQ_QOS);
- qos = TLVP_VAL(tp, OSMO_IE_GSM_REQ_QOS);
- }
-
- if (qos_len <= 3) {
- pdp->qos_req.l = qos_len + 1;
- if (pdp->qos_req.l > sizeof(pdp->qos_req.v))
- pdp->qos_req.l = sizeof(pdp->qos_req.v);
- pdp->qos_req.v[0] = 0; /* Allocation/Retention policy */
- memcpy(&pdp->qos_req.v[1], qos, pdp->qos_req.l - 1);
- } else {
- pdp->qos_req.l = qos_len;
- if (pdp->qos_req.l > sizeof(pdp->qos_req.v))
- pdp->qos_req.l = sizeof(pdp->qos_req.v);
- memcpy(pdp->qos_req.v, qos, pdp->qos_req.l);
- }
-
- /* SGSN address for control plane */
- pdp->gsnlc.l = sizeof(sgsn->cfg.gtp_listenaddr.sin_addr);
- memcpy(pdp->gsnlc.v, &sgsn->cfg.gtp_listenaddr.sin_addr,
- sizeof(sgsn->cfg.gtp_listenaddr.sin_addr));
-
- /* SGSN address for user plane
- * Default to the control plane addr for now. If we are connected to a
- * hnbgw via IuPS we'll need to send a PDP context update with the
- * correct IP address after the RAB Assignment is complete */
- pdp->gsnlu.l = sizeof(sgsn->cfg.gtp_listenaddr.sin_addr);
- memcpy(pdp->gsnlu.v, &sgsn->cfg.gtp_listenaddr.sin_addr,
- sizeof(sgsn->cfg.gtp_listenaddr.sin_addr));
-
- /* Routing Area Identifier with LAC and RAC fixed values, as
- * requested in 29.006 7.3.1 */
- 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->rattype.l = 1;
- pdp->rattype_given = 1;
-
- switch (mmctx->ran_type) {
- case MM_CTX_T_GERAN_Gb:
- case MM_CTX_T_GERAN_Iu:
- pdp->rattype.v[0] = 2;
- /* User Location Information */
- pdp->userloc_given = 1;
- pdp->userloc.l = 8;
- pdp->userloc.v[0] = 0; /* CGI for GERAN */
- bssgp_create_cell_id(&pdp->userloc.v[1], &mmctx->ra, mmctx->gb.cell_id);
- break;
- case MM_CTX_T_UTRAN_Iu:
- pdp->rattype.v[0] = 1;
- /* FIXME: Optional User Location Information with SAI */
- break;
- }
-
- /* include the IMEI(SV) */
- pdp->imeisv_given = 1;
- gsm48_encode_bcd_number(&pdp->imeisv.v[0], 8, 0, mmctx->imei);
- pdp->imeisv.l = pdp->imeisv.v[0];
- memmove(&pdp->imeisv.v[0], &pdp->imeisv.v[1], 8);
-
- /* change pdp state to 'requested' */
- pctx->state = PDP_STATE_CR_REQ;
-
- rc = gtp_create_context_req(ggsn->gsn, pdp, pctx);
- /* FIXME */
-
- return pctx;
-}
-
-/* SGSN wants to delete a PDP context */
-int sgsn_delete_pdp_ctx(struct sgsn_pdp_ctx *pctx)
-{
- LOGPDPCTXP(LOGL_ERROR, pctx, "Delete PDP Context\n");
-
- /* FIXME: decide if we need teardown or not ! */
- return gtp_delete_context_req(pctx->ggsn->gsn, pctx->lib, pctx, 1);
-}
-
-struct cause_map {
- uint8_t cause_in;
- uint8_t cause_out;
-};
-
-static uint8_t cause_map(const struct cause_map *map, uint8_t in, uint8_t deflt)
-{
- const struct cause_map *m;
-
- for (m = map; m->cause_in && m->cause_out; m++) {
- if (m->cause_in == in)
- return m->cause_out;
- }
- return deflt;
-}
-
-/* how do we map from gtp cause to SM cause */
-static const struct cause_map gtp2sm_cause_map[] = {
- { GTPCAUSE_NO_RESOURCES, GSM_CAUSE_INSUFF_RSRC },
- { GTPCAUSE_NOT_SUPPORTED, GSM_CAUSE_SERV_OPT_NOTSUPP },
- { GTPCAUSE_MAN_IE_INCORRECT, GSM_CAUSE_INV_MAND_INFO },
- { GTPCAUSE_MAN_IE_MISSING, GSM_CAUSE_INV_MAND_INFO },
- { GTPCAUSE_OPT_IE_INCORRECT, GSM_CAUSE_PROTO_ERR_UNSPEC },
- { GTPCAUSE_SYS_FAIL, GSM_CAUSE_NET_FAIL },
- { GTPCAUSE_ROAMING_REST, GSM_CAUSE_REQ_SERV_OPT_NOTSUB },
- { GTPCAUSE_PTIMSI_MISMATCH, GSM_CAUSE_PROTO_ERR_UNSPEC },
- { GTPCAUSE_CONN_SUSP, GSM_CAUSE_PROTO_ERR_UNSPEC },
- { GTPCAUSE_AUTH_FAIL, GSM_CAUSE_AUTH_FAILED },
- { GTPCAUSE_USER_AUTH_FAIL, GSM_CAUSE_ACT_REJ_GGSN },
- { GTPCAUSE_CONTEXT_NOT_FOUND, GSM_CAUSE_PROTO_ERR_UNSPEC },
- { GTPCAUSE_ADDR_OCCUPIED, GSM_CAUSE_INSUFF_RSRC },
- { GTPCAUSE_NO_MEMORY, GSM_CAUSE_INSUFF_RSRC },
- { GTPCAUSE_RELOC_FAIL, GSM_CAUSE_PROTO_ERR_UNSPEC },
- { GTPCAUSE_UNKNOWN_MAN_EXTHEADER, GSM_CAUSE_PROTO_ERR_UNSPEC },
- { GTPCAUSE_MISSING_APN, GSM_CAUSE_MISSING_APN },
- { GTPCAUSE_UNKNOWN_PDP, GSM_CAUSE_UNKNOWN_PDP },
- { 0, 0 }
-};
-
-static int send_act_pdp_cont_acc(struct sgsn_pdp_ctx *pctx)
-{
- struct sgsn_signal_data sig_data;
- int rc;
- struct gprs_llc_lle *lle;
-
- /* Inform others about it */
- memset(&sig_data, 0, sizeof(sig_data));
- sig_data.pdp = pctx;
- osmo_signal_dispatch(SS_SGSN, S_SGSN_PDP_ACT, &sig_data);
-
- /* Send PDP CTX ACT to MS */
- rc = gsm48_tx_gsm_act_pdp_acc(pctx);
- if (rc < 0)
- return rc;
-
- if (pctx->mm->ran_type == MM_CTX_T_GERAN_Gb) {
- /* Send SNDCP XID to MS */
- lle = &pctx->mm->gb.llme->lle[pctx->sapi];
- rc = sndcp_sn_xid_req(lle,pctx->nsapi);
- if (rc < 0)
- return rc;
- }
-
- return 0;
-}
-
-/* The GGSN has confirmed the creation of a PDP Context */
-static int create_pdp_conf(struct pdp_t *pdp, void *cbp, int cause)
-{
- struct sgsn_pdp_ctx *pctx = cbp;
- uint8_t reject_cause;
-
- LOGPDPCTXP(LOGL_INFO, pctx, "Received CREATE PDP CTX CONF, cause=%d(%s)\n",
- cause, get_value_string(gtp_cause_strs, cause));
-
- if (!pctx->mm) {
- LOGP(DGPRS, LOGL_INFO,
- "No MM context, aborting CREATE PDP CTX CONF\n");
- return -EIO;
- }
-
- /* Check for cause value if it was really successful */
- if (cause < 0) {
- LOGP(DGPRS, LOGL_NOTICE, "Create PDP ctx req timed out\n");
- if (pdp && pdp->version == 1) {
- pdp->version = 0;
- gtp_create_context_req(sgsn->gsn, pdp, cbp);
- return 0;
- } else {
- reject_cause = GSM_CAUSE_NET_FAIL;
- goto reject;
- }
- }
-
- /* Check for cause value if it was really successful */
- if (cause != GTPCAUSE_ACC_REQ) {
- reject_cause = cause_map(gtp2sm_cause_map, cause,
- GSM_CAUSE_ACT_REJ_GGSN);
- goto reject;
- }
-
- if (pctx->mm->ran_type == MM_CTX_T_GERAN_Gb) {
- /* Activate the SNDCP layer */
- sndcp_sm_activate_ind(&pctx->mm->gb.llme->lle[pctx->sapi], pctx->nsapi);
- return send_act_pdp_cont_acc(pctx);
- } else if (pctx->mm->ran_type == MM_CTX_T_UTRAN_Iu) {
-#ifdef BUILD_IU
- /* Activate a radio bearer */
- iu_rab_act_ps(pdp->nsapi, pctx);
- return 0;
-#else
- return -ENOTSUP;
-#endif
- }
-
- LOGP(DGPRS, LOGL_ERROR, "Unknown ran_type %d\n",
- pctx->mm->ran_type);
- reject_cause = GSM_CAUSE_PROTO_ERR_UNSPEC;
-
-reject:
- /*
- * In case of a timeout pdp will be NULL but we have a valid pointer
- * in pctx->lib. For other rejects pctx->lib and pdp might be the
- * same.
- */
- pctx->state = PDP_STATE_NONE;
- if (pctx->lib && pctx->lib != pdp)
- pdp_freepdp(pctx->lib);
- pctx->lib = NULL;
-
- if (pdp)
- pdp_freepdp(pdp);
- /* Send PDP CTX ACT REJ to MS */
- gsm48_tx_gsm_act_pdp_rej(pctx->mm, pctx->ti, reject_cause,
- 0, NULL);
- sgsn_pdp_ctx_free(pctx);
-
- return EOF;
-}
-
-void sgsn_pdp_upd_gtp_u(struct sgsn_pdp_ctx *pdp, void *addr, size_t alen)
-{
- pdp->lib->gsnlu.l = alen;
- memcpy(pdp->lib->gsnlu.v, addr, alen);
- gtp_update_context(pdp->ggsn->gsn, pdp->lib, pdp, &pdp->lib->hisaddr0);
-}
-
-#ifdef BUILD_IU
-/* Callback for RAB assignment response */
-int sgsn_ranap_rab_ass_resp(struct sgsn_mm_ctx *ctx, RANAP_RAB_SetupOrModifiedItemIEs_t *setup_ies)
-{
- uint8_t rab_id;
- bool require_pdp_update = false;
- struct sgsn_pdp_ctx *pdp = NULL;
- RANAP_RAB_SetupOrModifiedItem_t *item = &setup_ies->raB_SetupOrModifiedItem;
-
- rab_id = item->rAB_ID.buf[0];
-
- pdp = sgsn_pdp_ctx_by_nsapi(ctx, rab_id);
- if (!pdp) {
- LOGP(DRANAP, LOGL_ERROR, "RAB Assignment Response for unknown RAB/NSAPI=%u\n", rab_id);
- return -1;
- }
-
- if (item->transportLayerAddress) {
- LOGPC(DRANAP, LOGL_INFO, " Setup: (%u/%s)", rab_id, osmo_hexdump(item->transportLayerAddress->buf,
- item->transportLayerAddress->size));
- switch (item->transportLayerAddress->size) {
- case 7:
- /* It must be IPv4 inside a X213 NSAP */
- memcpy(pdp->lib->gsnlu.v, &item->transportLayerAddress->buf[3], 4);
- break;
- case 4:
- /* It must be a raw IPv4 address */
- memcpy(pdp->lib->gsnlu.v, item->transportLayerAddress->buf, 4);
- break;
- case 16:
- /* TODO: It must be a raw IPv6 address */
- case 19:
- /* TODO: It must be IPv6 inside a X213 NSAP */
- default:
- LOGP(DRANAP, LOGL_ERROR, "RAB Assignment Resp: Unknown "
- "transport layer address size %u\n",
- item->transportLayerAddress->size);
- return -1;
- }
- require_pdp_update = true;
- }
-
- /* The TEI on the RNC side might have changed, too */
- if (item->iuTransportAssociation &&
- item->iuTransportAssociation->present == RANAP_IuTransportAssociation_PR_gTP_TEI &&
- item->iuTransportAssociation->choice.gTP_TEI.buf &&
- item->iuTransportAssociation->choice.gTP_TEI.size >= 4) {
- uint32_t tei = osmo_load32be(item->iuTransportAssociation->choice.gTP_TEI.buf);
- LOGP(DRANAP, LOGL_DEBUG, "Updating TEID on RNC side from 0x%08x to 0x%08x\n",
- pdp->lib->teid_own, tei);
- pdp->lib->teid_own = tei;
- require_pdp_update = true;
- }
-
- if (require_pdp_update)
- gtp_update_context(pdp->ggsn->gsn, pdp->lib, pdp, &pdp->lib->hisaddr0);
-
- if (pdp->state != PDP_STATE_CR_CONF) {
- send_act_pdp_cont_acc(pdp);
- pdp->state = PDP_STATE_CR_CONF;
- }
- return 0;
-
-}
-#endif
-
-/* Confirmation of a PDP Context Delete */
-static int delete_pdp_conf(struct pdp_t *pdp, void *cbp, int cause)
-{
- struct sgsn_signal_data sig_data;
- struct sgsn_pdp_ctx *pctx = cbp;
- int rc = 0;
-
- LOGPDPCTXP(LOGL_INFO, pctx, "Received DELETE PDP CTX CONF, cause=%d(%s)\n",
- cause, get_value_string(gtp_cause_strs, cause));
-
- memset(&sig_data, 0, sizeof(sig_data));
- sig_data.pdp = pctx;
- osmo_signal_dispatch(SS_SGSN, S_SGSN_PDP_DEACT, &sig_data);
-
- if (pctx->mm) {
- if (pctx->mm->ran_type == MM_CTX_T_GERAN_Gb) {
- /* Deactivate the SNDCP layer */
- sndcp_sm_deactivate_ind(&pctx->mm->gb.llme->lle[pctx->sapi], pctx->nsapi);
- } else {
-#ifdef BUILD_IU
- /* Deactivate radio bearer */
- ranap_iu_rab_deact(pctx->mm->iu.ue_ctx, 1);
-#else
- return -ENOTSUP;
-#endif
- }
-
- /* Confirm deactivation of PDP context to MS */
- rc = gsm48_tx_gsm_deact_pdp_acc(pctx);
- } else {
- LOGPDPCTXP(LOGL_NOTICE, pctx,
- "Not deactivating SNDCP layer since the MM context "
- "is not available\n");
- }
-
- /* unlink the now non-existing library handle from the pdp
- * context */
- pctx->lib = NULL;
-
- sgsn_pdp_ctx_free(pctx);
-
- return rc;
-}
-
-/* Confirmation of an GTP ECHO request */
-static int echo_conf(struct pdp_t *pdp, void *cbp, int recovery)
-{
- if (recovery < 0) {
- LOGP(DGPRS, LOGL_NOTICE, "GTP Echo Request timed out\n");
- /* FIXME: if version == 1, retry with version 0 */
- } else {
- DEBUGP(DGPRS, "GTP Rx Echo Response\n");
- }
- return 0;
-}
-
-/* Any message received by GGSN contains a recovery IE */
-static int cb_recovery(struct sockaddr_in *peer, uint8_t recovery)
-{
- struct sgsn_ggsn_ctx *ggsn;
-
- ggsn = sgsn_ggsn_ctx_by_addr(&peer->sin_addr);
- if (!ggsn) {
- LOGP(DGPRS, LOGL_NOTICE, "Received Recovery IE for unknown GGSN\n");
- return -EINVAL;
- }
-
- if (ggsn->remote_restart_ctr == -1) {
- /* First received ECHO RESPONSE, note the restart ctr */
- ggsn->remote_restart_ctr = recovery;
- } else if (ggsn->remote_restart_ctr != recovery) {
- /* counter has changed (GGSN restart): release all PDP */
- LOGP(DGPRS, LOGL_NOTICE, "GGSN recovery (%u->%u), "
- "releasing all PDP contexts\n",
- ggsn->remote_restart_ctr, recovery);
- ggsn->remote_restart_ctr = recovery;
- drop_all_pdp_for_ggsn(ggsn);
- }
- return 0;
-}
-
-/* libgtp callback for confirmations */
-static int cb_conf(int type, int cause, struct pdp_t *pdp, void *cbp)
-{
- DEBUGP(DGPRS, "libgtp cb_conf(type=%d, cause=%d, pdp=%p, cbp=%p)\n",
- type, cause, pdp, cbp);
-
- if (cause == EOF)
- LOGP(DGPRS, LOGL_ERROR, "libgtp EOF (type=%u, pdp=%p, cbp=%p)\n",
- type, pdp, cbp);
-
- switch (type) {
- case GTP_ECHO_REQ:
- /* libgtp hands us the RECOVERY number instead of a cause */
- return echo_conf(pdp, cbp, cause);
- case GTP_CREATE_PDP_REQ:
- return create_pdp_conf(pdp, cbp, cause);
- case GTP_DELETE_PDP_REQ:
- return delete_pdp_conf(pdp, cbp, cause);
- default:
- break;
- }
- return 0;
-}
-
-/* Called whenever a PDP context is deleted for any reason */
-static int cb_delete_context(struct pdp_t *pdp)
-{
- LOGP(DGPRS, LOGL_INFO, "PDP Context was deleted\n");
- return 0;
-}
-
-/* Called when we receive a Version Not Supported message */
-static int cb_unsup_ind(struct sockaddr_in *peer)
-{
- LOGP(DGPRS, LOGL_INFO, "GTP Version not supported Indication "
- "from %s:%u\n", inet_ntoa(peer->sin_addr),
- ntohs(peer->sin_port));
- return 0;
-}
-
-/* Called when we receive a Supported Ext Headers Notification */
-static int cb_extheader_ind(struct sockaddr_in *peer)
-{
- LOGP(DGPRS, LOGL_INFO, "GTP Supported Ext Headers Noficiation "
- "from %s:%u\n", inet_ntoa(peer->sin_addr),
- ntohs(peer->sin_port));
- return 0;
-}
-
-/* Called whenever we recive a DATA packet */
-static int cb_data_ind(struct pdp_t *lib, void *packet, unsigned int len)
-{
- struct bssgp_paging_info pinfo;
- struct sgsn_pdp_ctx *pdp;
- struct sgsn_mm_ctx *mm;
- struct msgb *msg;
- uint8_t *ud;
-
- pdp = lib->priv;
- if (!pdp) {
- LOGP(DGPRS, LOGL_NOTICE,
- "GTP DATA IND from GGSN for unknown PDP\n");
- return -EIO;
- }
- mm = pdp->mm;
- if (!mm) {
- LOGP(DGPRS, LOGL_ERROR,
- "PDP context (address=%u) without MM context!\n",
- pdp->address);
- return -EIO;
- }
-
- DEBUGP(DGPRS, "GTP DATA IND from GGSN for %s, length=%u\n", mm->imsi,
- len);
-
- if (mm->ran_type == MM_CTX_T_UTRAN_Iu) {
-#ifdef BUILD_IU
- /* Ignore the packet for now and page the UE to get the RAB
- * reestablished */
- ranap_iu_page_ps(mm->imsi, &mm->p_tmsi, mm->ra.lac, mm->ra.rac);
-
- return 0;
-#else
- return -ENOTSUP;
-#endif
- }
-
- msg = msgb_alloc_headroom(len+256, 128, "GTP->SNDCP");
- ud = msgb_put(msg, len);
- memcpy(ud, packet, len);
-
- msgb_tlli(msg) = mm->gb.tlli;
- msgb_bvci(msg) = mm->gb.bvci;
- msgb_nsei(msg) = mm->gb.nsei;
-
- switch (mm->gmm_state) {
- case GMM_REGISTERED_SUSPENDED:
- /* initiate PS PAGING procedure */
- memset(&pinfo, 0, sizeof(pinfo));
- pinfo.mode = BSSGP_PAGING_PS;
- pinfo.scope = BSSGP_PAGING_BVCI;
- pinfo.bvci = mm->gb.bvci;
- pinfo.imsi = mm->imsi;
- pinfo.ptmsi = &mm->p_tmsi;
- pinfo.drx_params = mm->drx_parms;
- pinfo.qos[0] = 0; // FIXME
- bssgp_tx_paging(mm->gb.nsei, 0, &pinfo);
- rate_ctr_inc(&mm->ctrg->ctr[GMM_CTR_PAGING_PS]);
- /* FIXME: queue the packet we received from GTP */
- break;
- case GMM_REGISTERED_NORMAL:
- break;
- default:
- LOGP(DGPRS, LOGL_ERROR, "GTP DATA IND for TLLI %08X in state "
- "%u\n", mm->gb.tlli, mm->gmm_state);
- msgb_free(msg);
- return -1;
- }
-
- rate_ctr_inc(&pdp->ctrg->ctr[PDP_CTR_PKTS_UDATA_OUT]);
- rate_ctr_add(&pdp->ctrg->ctr[PDP_CTR_BYTES_UDATA_OUT], len);
- rate_ctr_inc(&mm->ctrg->ctr[GMM_CTR_PKTS_UDATA_OUT]);
- rate_ctr_add(&mm->ctrg->ctr[GMM_CTR_BYTES_UDATA_OUT], len);
-
- /* It is easier to have a global count */
- pdp->cdr_bytes_out += len;
-
- return sndcp_unitdata_req(msg, &mm->gb.llme->lle[pdp->sapi],
- pdp->nsapi, mm);
-}
-
-/* Called by SNDCP when it has received/re-assembled a N-PDU */
-int sgsn_rx_sndcp_ud_ind(struct gprs_ra_id *ra_id, int32_t tlli, uint8_t nsapi,
- struct msgb *msg, uint32_t npdu_len, uint8_t *npdu)
-{
- struct sgsn_mm_ctx *mmctx;
- struct sgsn_pdp_ctx *pdp;
-
- /* look-up the MM context for this message */
- mmctx = sgsn_mm_ctx_by_tlli(tlli, ra_id);
- if (!mmctx) {
- LOGP(DGPRS, LOGL_ERROR,
- "Cannot find MM CTX for TLLI %08x\n", tlli);
- return -EIO;
- }
- /* look-up the PDP context for this message */
- pdp = sgsn_pdp_ctx_by_nsapi(mmctx, nsapi);
- if (!pdp) {
- LOGP(DGPRS, LOGL_ERROR, "Cannot find PDP CTX for "
- "TLLI=%08x, NSAPI=%u\n", tlli, nsapi);
- return -EIO;
- }
- if (!pdp->lib) {
- LOGP(DGPRS, LOGL_ERROR, "PDP CTX without libgtp\n");
- return -EIO;
- }
-
- rate_ctr_inc(&pdp->ctrg->ctr[PDP_CTR_PKTS_UDATA_IN]);
- rate_ctr_add(&pdp->ctrg->ctr[PDP_CTR_BYTES_UDATA_IN], npdu_len);
- rate_ctr_inc(&mmctx->ctrg->ctr[GMM_CTR_PKTS_UDATA_IN]);
- rate_ctr_add(&mmctx->ctrg->ctr[GMM_CTR_BYTES_UDATA_IN], npdu_len);
-
- /* It is easier to have a global count */
- pdp->cdr_bytes_in += npdu_len;
-
- return gtp_data_req(pdp->ggsn->gsn, pdp->lib, npdu, npdu_len);
-}
-
-/* libgtp select loop integration */
-static int sgsn_gtp_fd_cb(struct osmo_fd *fd, unsigned int what)
-{
- struct sgsn_instance *sgi = fd->data;
- int rc;
-
- if (!(what & BSC_FD_READ))
- return 0;
-
- switch (fd->priv_nr) {
- case 0:
- rc = gtp_decaps0(sgi->gsn);
- break;
- case 1:
- rc = gtp_decaps1c(sgi->gsn);
- break;
- case 2:
- rc = gtp_decaps1u(sgi->gsn);
- break;
- default:
- rc = -EINVAL;
- break;
- }
- return rc;
-}
-
-static void sgsn_gtp_tmr_start(struct sgsn_instance *sgi)
-{
- struct timeval next;
-
- /* Retrieve next retransmission as struct timeval */
- gtp_retranstimeout(sgi->gsn, &next);
-
- /* re-schedule the timer */
- osmo_timer_schedule(&sgi->gtp_timer, next.tv_sec, next.tv_usec/1000);
-}
-
-/* timer callback for libgtp retransmissions and ping */
-static void sgsn_gtp_tmr_cb(void *data)
-{
- struct sgsn_instance *sgi = data;
-
- /* Do all the retransmissions as needed */
- gtp_retrans(sgi->gsn);
-
- sgsn_gtp_tmr_start(sgi);
-}
-
-int sgsn_gtp_init(struct sgsn_instance *sgi)
-{
- int rc;
- struct gsn_t *gsn;
-
- rc = gtp_new(&sgi->gsn, sgi->cfg.gtp_statedir,
- &sgi->cfg.gtp_listenaddr.sin_addr, GTP_MODE_SGSN);
- if (rc) {
- LOGP(DGPRS, LOGL_ERROR, "Failed to create GTP: %d\n", rc);
- return rc;
- }
- gsn = sgi->gsn;
-
- sgi->gtp_fd0.fd = gsn->fd0;
- sgi->gtp_fd0.priv_nr = 0;
- sgi->gtp_fd0.data = sgi;
- sgi->gtp_fd0.when = BSC_FD_READ;
- sgi->gtp_fd0.cb = sgsn_gtp_fd_cb;
- rc = osmo_fd_register(&sgi->gtp_fd0);
- if (rc < 0)
- return rc;
-
- sgi->gtp_fd1c.fd = gsn->fd1c;
- sgi->gtp_fd1c.priv_nr = 1;
- sgi->gtp_fd1c.data = sgi;
- sgi->gtp_fd1c.when = BSC_FD_READ;
- sgi->gtp_fd1c.cb = sgsn_gtp_fd_cb;
- rc = osmo_fd_register(&sgi->gtp_fd1c);
- if (rc < 0) {
- osmo_fd_unregister(&sgi->gtp_fd0);
- return rc;
- }
-
- sgi->gtp_fd1u.fd = gsn->fd1u;
- sgi->gtp_fd1u.priv_nr = 2;
- sgi->gtp_fd1u.data = sgi;
- sgi->gtp_fd1u.when = BSC_FD_READ;
- sgi->gtp_fd1u.cb = sgsn_gtp_fd_cb;
- rc = osmo_fd_register(&sgi->gtp_fd1u);
- if (rc < 0) {
- osmo_fd_unregister(&sgi->gtp_fd0);
- osmo_fd_unregister(&sgi->gtp_fd1c);
- return rc;
- }
-
- /* Start GTP re-transmission timer */
- osmo_timer_setup(&sgi->gtp_timer, sgsn_gtp_tmr_cb, sgi);
- sgsn_gtp_tmr_start(sgi);
-
- /* Register callbackcs with libgtp */
- gtp_set_cb_delete_context(gsn, cb_delete_context);
- gtp_set_cb_conf(gsn, cb_conf);
- gtp_set_cb_recovery(gsn, cb_recovery);
- gtp_set_cb_data_ind(gsn, cb_data_ind);
- gtp_set_cb_unsup_ind(gsn, cb_unsup_ind);
- gtp_set_cb_extheader_ind(gsn, cb_extheader_ind);
-
- return 0;
-}
diff --git a/src/gprs/sgsn_main.c b/src/gprs/sgsn_main.c
deleted file mode 100644
index ca21151dc..000000000
--- a/src/gprs/sgsn_main.c
+++ /dev/null
@@ -1,472 +0,0 @@
-/* GPRS SGSN Implementation */
-
-/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
- * (C) 2010 by On-Waves
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <unistd.h>
-#include <stdio.h>
-#include <time.h>
-#include <stdlib.h>
-#include <string.h>
-#include <getopt.h>
-#include <errno.h>
-#include <signal.h>
-#include <sys/fcntl.h>
-#include <sys/stat.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-
-#include <osmocom/core/application.h>
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/select.h>
-#include <osmocom/core/rate_ctr.h>
-#include <osmocom/core/logging.h>
-#include <osmocom/core/stats.h>
-
-#include <osmocom/gsm/gsup.h>
-
-#include <osmocom/gprs/gprs_ns.h>
-#include <osmocom/gprs/gprs_bssgp.h>
-
-#include <osmocom/vty/telnet_interface.h>
-#include <osmocom/vty/logging.h>
-#include <osmocom/vty/stats.h>
-#include <osmocom/vty/ports.h>
-
-#include <osmocom/ctrl/control_vty.h>
-
-#include <osmocom/ranap/iu_client.h>
-
-#include <openbsc/signal.h>
-#include <openbsc/debug.h>
-#include <openbsc/vty.h>
-#include <openbsc/sgsn.h>
-#include <openbsc/gprs_llc.h>
-#include <openbsc/gprs_gmm.h>
-
-#include <osmocom/ctrl/control_if.h>
-#include <osmocom/ctrl/ports.h>
-
-#include <osmocom/sigtran/protocol/m3ua.h>
-
-#include <gtp.h>
-
-#include "../../bscconfig.h"
-
-#define _GNU_SOURCE
-#include <getopt.h>
-
-void *tall_bsc_ctx;
-
-struct gprs_ns_inst *sgsn_nsi;
-static int daemonize = 0;
-const char *openbsc_copyright =
- "Copyright (C) 2010 Harald Welte and On-Waves\r\n"
- "License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
- "This is free software: you are free to change and redistribute it.\r\n"
- "There is NO WARRANTY, to the extent permitted by law.\r\n";
-
-static struct sgsn_instance sgsn_inst = {
- .config_file = "osmo_sgsn.cfg",
- .cfg = {
- .gtp_statedir = "./",
- .auth_policy = SGSN_AUTH_POLICY_CLOSED,
- .gsup_server_port = OSMO_GSUP_PORT,
- },
-};
-struct sgsn_instance *sgsn = &sgsn_inst;
-
-/* call-back function for the NS protocol */
-static int sgsn_ns_cb(enum gprs_ns_evt event, struct gprs_nsvc *nsvc,
- struct msgb *msg, uint16_t bvci)
-{
- int rc = 0;
-
- switch (event) {
- case GPRS_NS_EVT_UNIT_DATA:
- /* hand the message into the BSSGP implementation */
- rc = bssgp_rcvmsg(msg);
- break;
- default:
- LOGP(DGPRS, LOGL_ERROR, "SGSN: Unknown event %u from NS\n", event);
- if (msg)
- msgb_free(msg);
- rc = -EIO;
- break;
- }
- return rc;
-}
-
-/* call-back function for the BSSGP protocol */
-int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
-{
- struct osmo_bssgp_prim *bp;
- bp = container_of(oph, struct osmo_bssgp_prim, oph);
-
- switch (oph->sap) {
- case SAP_BSSGP_LL:
- switch (oph->primitive) {
- case PRIM_BSSGP_UL_UD:
- return gprs_llc_rcvmsg(oph->msg, bp->tp);
- }
- break;
- case SAP_BSSGP_GMM:
- switch (oph->primitive) {
- case PRIM_BSSGP_GMM_SUSPEND:
- return gprs_gmm_rx_suspend(bp->ra_id, bp->tlli);
- case PRIM_BSSGP_GMM_RESUME:
- return gprs_gmm_rx_resume(bp->ra_id, bp->tlli,
- bp->u.resume.suspend_ref);
- }
- break;
- case SAP_BSSGP_NM:
- break;
- }
- return 0;
-}
-
-static void signal_handler(int signal)
-{
- fprintf(stdout, "signal %u received\n", signal);
-
- switch (signal) {
- case SIGINT:
- osmo_signal_dispatch(SS_L_GLOBAL, S_L_GLOBAL_SHUTDOWN, NULL);
- sleep(1);
- exit(0);
- break;
- case SIGABRT:
- /* in case of abort, we want to obtain a talloc report
- * and then return to the caller, who will abort the process */
- case SIGUSR1:
- talloc_report(tall_vty_ctx, stderr);
- talloc_report_full(tall_bsc_ctx, stderr);
- break;
- case SIGUSR2:
- talloc_report_full(tall_vty_ctx, stderr);
- break;
- default:
- break;
- }
-}
-
-/* NSI that BSSGP uses when transmitting on NS */
-extern struct gprs_ns_inst *bssgp_nsi;
-
-extern int bsc_vty_go_parent(struct vty *vty);
-
-static struct vty_app_info vty_info = {
- .name = "OsmoSGSN",
- .version = PACKAGE_VERSION,
- .go_parent_cb = bsc_vty_go_parent,
- .is_config_node = bsc_vty_is_config_node,
-};
-
-static void print_help(void)
-{
- printf("Some useful help...\n");
- printf(" -h --help\tthis text\n");
- printf(" -D --daemonize\tFork the process into a background daemon\n");
- printf(" -d option --debug\tenable Debugging\n");
- printf(" -s --disable-color\n");
- printf(" -c --config-file\tThe config file to use [%s]\n", sgsn->config_file);
- printf(" -e --log-level number\tSet a global log level\n");
-}
-
-static void handle_options(int argc, char **argv)
-{
- while (1) {
- int option_index = 0, c;
- static struct option long_options[] = {
- {"help", 0, 0, 'h'},
- {"debug", 1, 0, 'd'},
- {"daemonize", 0, 0, 'D'},
- {"config-file", 1, 0, 'c'},
- {"disable-color", 0, 0, 's'},
- {"timestamp", 0, 0, 'T'},
- { "version", 0, 0, 'V' },
- {"log-level", 1, 0, 'e'},
- {NULL, 0, 0, 0}
- };
-
- c = getopt_long(argc, argv, "hd:Dc:sTVe:",
- long_options, &option_index);
- if (c == -1)
- break;
-
- switch (c) {
- case 'h':
- //print_usage();
- print_help();
- exit(0);
- case 's':
- log_set_use_color(osmo_stderr_target, 0);
- break;
- case 'd':
- log_parse_category_mask(osmo_stderr_target, optarg);
- break;
- case 'D':
- daemonize = 1;
- break;
- case 'c':
- sgsn_inst.config_file = strdup(optarg);
- break;
- case 'T':
- log_set_print_timestamp(osmo_stderr_target, 1);
- break;
- case 'V':
- print_version(1);
- exit(0);
- break;
- case 'e':
- log_set_log_level(osmo_stderr_target, atoi(optarg));
- break;
- default:
- /* ignore */
- break;
- }
- }
-}
-
-/* default categories */
-static struct log_info_cat gprs_categories[] = {
- [DMM] = {
- .name = "DMM",
- .description = "Layer3 Mobility Management (MM)",
- .color = "\033[1;33m",
- .enabled = 1, .loglevel = LOGL_NOTICE,
- },
- [DPAG] = {
- .name = "DPAG",
- .description = "Paging Subsystem",
- .color = "\033[1;38m",
- .enabled = 1, .loglevel = LOGL_NOTICE,
- },
- [DMEAS] = {
- .name = "DMEAS",
- .description = "Radio Measurement Processing",
- .enabled = 0, .loglevel = LOGL_NOTICE,
- },
- [DREF] = {
- .name = "DREF",
- .description = "Reference Counting",
- .enabled = 0, .loglevel = LOGL_NOTICE,
- },
- [DGPRS] = {
- .name = "DGPRS",
- .description = "GPRS Packet Service",
- .enabled = 1, .loglevel = LOGL_DEBUG,
- },
- [DNS] = {
- .name = "DNS",
- .description = "GPRS Network Service (NS)",
- .enabled = 1, .loglevel = LOGL_INFO,
- },
- [DBSSGP] = {
- .name = "DBSSGP",
- .description = "GPRS BSS Gateway Protocol (BSSGP)",
- .enabled = 1, .loglevel = LOGL_DEBUG,
- },
- [DLLC] = {
- .name = "DLLC",
- .description = "GPRS Logical Link Control Protocol (LLC)",
- .enabled = 1, .loglevel = LOGL_DEBUG,
- },
- [DSNDCP] = {
- .name = "DSNDCP",
- .description = "GPRS Sub-Network Dependent Control Protocol (SNDCP)",
- .enabled = 1, .loglevel = LOGL_DEBUG,
- },
- [DRANAP] = {
- .name = "DRANAP",
- .description = "RAN Application Part (RANAP)",
- .enabled = 1, .loglevel = LOGL_DEBUG,
- },
- [DSUA] = {
- .name = "DSUA",
- .description = "SCCP User Adaptation (SUA)",
- .enabled = 1, .loglevel = LOGL_DEBUG,
- },
- [DSLHC] = {
- .name = "DSLHC",
- .description = "RFC1144 TCP/IP Header compression (SLHC)",
- .enabled = 1, .loglevel = LOGL_DEBUG,
- },
- [DV42BIS] = {
- .name = "DV42BIS",
- .description = "V.42bis data compression (SNDCP)",
- .enabled = 1, .loglevel = LOGL_DEBUG,
- }
-};
-
-static const struct log_info gprs_log_info = {
- .filter_fn = gprs_log_filter_fn,
- .cat = gprs_categories,
- .num_cat = ARRAY_SIZE(gprs_categories),
-};
-
-int sgsn_ranap_iu_event(struct ue_conn_ctx *ctx, enum ranap_iu_event_type type, void *data);
-
-int main(int argc, char **argv)
-{
- struct ctrl_handle *ctrl;
- struct gsm_network dummy_network;
- struct osmo_sccp_instance *sccp;
- int rc;
-
- srand(time(NULL));
- tall_bsc_ctx = talloc_named_const(NULL, 0, "osmo_sgsn");
- msgb_talloc_ctx_init(tall_bsc_ctx, 0);
-
- signal(SIGINT, &signal_handler);
- signal(SIGABRT, &signal_handler);
- signal(SIGUSR1, &signal_handler);
- signal(SIGUSR2, &signal_handler);
-
- osmo_init_ignore_signals();
- osmo_init_logging(&gprs_log_info);
- osmo_stats_init(tall_bsc_ctx);
-
- vty_info.copyright = openbsc_copyright;
- vty_init(&vty_info);
- logging_vty_add_cmds(NULL);
- osmo_stats_vty_add_cmds(&gprs_log_info);
- sgsn_vty_init(&sgsn_inst.cfg);
- ctrl_vty_init(tall_bsc_ctx);
- osmo_ss7_init();
-
- handle_options(argc, argv);
-
- rate_ctr_init(tall_bsc_ctx);
-
- gprs_ns_set_log_ss(DNS);
- bssgp_set_log_ss(DBSSGP);
-
- sgsn_nsi = gprs_ns_instantiate(&sgsn_ns_cb, tall_bsc_ctx);
- if (!sgsn_nsi) {
- LOGP(DGPRS, LOGL_ERROR, "Unable to instantiate NS\n");
- exit(1);
- }
- bssgp_nsi = sgsn_inst.cfg.nsi = sgsn_nsi;
-
- gprs_llc_init("/usr/local/lib/osmocom/crypt/");
- sgsn_rate_ctr_init();
- sgsn_inst_init();
-
- gprs_ns_vty_init(bssgp_nsi);
- bssgp_vty_init();
- gprs_llc_vty_init();
- gprs_sndcp_vty_init();
- sgsn_auth_init();
- sgsn_cdr_init(&sgsn_inst);
- /* FIXME: register signal handler for SS_L_NS */
-
- rc = sgsn_parse_config(sgsn_inst.config_file);
- if (rc < 0) {
- LOGP(DGPRS, LOGL_FATAL, "Error in config file\n");
- exit(2);
- }
-
- /* start telnet after reading config for vty_get_bind_addr() */
- rc = telnet_init_dynif(tall_bsc_ctx, &dummy_network,
- vty_get_bind_addr(), OSMO_VTY_PORT_SGSN);
- if (rc < 0)
- exit(1);
-
- /* start control interface after reading config for
- * ctrl_vty_get_bind_addr() */
- ctrl = sgsn_controlif_setup(NULL, ctrl_vty_get_bind_addr(),
- OSMO_CTRL_PORT_SGSN);
- if (!ctrl) {
- LOGP(DGPRS, LOGL_ERROR, "Failed to create CTRL interface.\n");
- exit(1);
- }
-
- if (sgsn_ctrl_cmds_install() != 0) {
- LOGP(DGPRS, LOGL_ERROR, "Failed to install CTRL commands.\n");
- exit(1);
- }
-
-
- rc = sgsn_gtp_init(&sgsn_inst);
- if (rc) {
- LOGP(DGPRS, LOGL_FATAL, "Cannot bind/listen on GTP socket\n");
- exit(2);
- }
-
- rc = gprs_subscr_init(&sgsn_inst);
- if (rc < 0) {
- LOGP(DGPRS, LOGL_FATAL, "Cannot set up subscriber management\n");
- exit(2);
- }
-
- rc = gprs_ns_nsip_listen(sgsn_nsi);
- if (rc < 0) {
- LOGP(DGPRS, LOGL_FATAL, "Cannot bind/listen on NSIP socket\n");
- exit(2);
- }
-
- rc = gprs_ns_frgre_listen(sgsn_nsi);
- if (rc < 0) {
- LOGP(DGPRS, LOGL_FATAL, "Cannot bind/listen GRE "
- "socket. Do you have CAP_NET_RAW?\n");
- exit(2);
- }
-
- if (sgsn->cfg.dynamic_lookup) {
- if (sgsn_ares_init(sgsn) != 0) {
- LOGP(DGPRS, LOGL_FATAL,
- "Failed to initialize c-ares(%d)\n", rc);
- exit(4);
- }
- }
-
-#ifdef BUILD_IU
- sccp = osmo_sccp_simple_client(tall_bsc_ctx, "OsmoSGSN",
- 2 /* FIXME: configurable */,
- OSMO_SS7_ASP_PROT_M3UA, 0,
- "127.0.0.4" /* FIXME: configurable */,
- M3UA_PORT,
- "127.0.0.1" /* FIXME: configurable */);
- if (!sccp) {
- printf("Setting up SCCP client failed.\n");
- return 8;
- }
-
- ranap_iu_init(tall_bsc_ctx, DRANAP, "OsmoSGSN-IuPS", sccp, gsm0408_gprs_rcvmsg_iu, sgsn_ranap_iu_event);
-#endif
-
- if (daemonize) {
- rc = osmo_daemonize();
- if (rc < 0) {
- perror("Error during daemonize");
- exit(1);
- }
- }
-
- while (1) {
- rc = osmo_select_main(0);
- if (rc < 0)
- exit(3);
- }
-
- /* not reached */
- exit(0);
-}
diff --git a/src/gprs/sgsn_vty.c b/src/gprs/sgsn_vty.c
deleted file mode 100644
index 8469b010a..000000000
--- a/src/gprs/sgsn_vty.c
+++ /dev/null
@@ -1,1340 +0,0 @@
-/*
- * (C) 2010-2016 by Harald Welte <laforge@gnumonks.org>
- * (C) 2010 by On-Waves
- * (C) 2015 by Holger Hans Peter Freyther
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <time.h>
-
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/utils.h>
-#include <osmocom/core/rate_ctr.h>
-#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
-
-#include <openbsc/debug.h>
-#include <openbsc/sgsn.h>
-#include <osmocom/gprs/gprs_ns.h>
-#include <openbsc/gprs_sgsn.h>
-#include <openbsc/vty.h>
-#include <openbsc/gsup_client.h>
-
-#include <osmocom/vty/command.h>
-#include <osmocom/vty/vty.h>
-#include <osmocom/vty/misc.h>
-#include <osmocom/crypt/gprs_cipher.h>
-#include <osmocom/abis/ipa.h>
-
-#include <pdp.h>
-
-#include "../../bscconfig.h"
-
-#ifdef BUILD_IU
-#include <osmocom/ranap/iu_client.h>
-#endif
-
-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 network 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 "\nTimer 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 request -> 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, "AA-Ready timer (s)")
-
-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 */
-/* TODO: consolidate with gprs_apn_to_str(). */
-/** Copy apn to a static buffer, replacing the length octets in apn_enc with '.'
- * and terminating with a '\0'. Return the static buffer.
- * len: the length of the encoded APN (which has no terminating zero).
- */
-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);
-
- if (g_cfg->cipher != GPRS_ALGO_GEA0)
- vty_out(vty, " encryption %s%s",
- get_value_string(gprs_cipher_names, g_cfg->cipher),
- 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);
- vty_out(vty, " auth-policy %s%s",
- get_value_string(sgsn_auth_pol_strs, g_cfg->auth_policy),
- VTY_NEWLINE);
-
- vty_out(vty, " gsup oap-id %d%s",
- (int)g_cfg->oap.client_id, VTY_NEWLINE);
- if (g_cfg->oap.secret_k_present != 0)
- vty_out(vty, " gsup oap-k %s%s",
- osmo_hexdump_nospc(g_cfg->oap.secret_k, sizeof(g_cfg->oap.secret_k)),
- VTY_NEWLINE);
- if (g_cfg->oap.secret_opc_present != 0)
- vty_out(vty, " gsup oap-opc %s%s",
- osmo_hexdump_nospc(g_cfg->oap.secret_opc, sizeof(g_cfg->oap.secret_opc)),
- 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);
-
- if (g_cfg->pcomp_rfc1144.active) {
- vty_out(vty, " compression rfc1144 active slots %d%s",
- g_cfg->pcomp_rfc1144.s01 + 1, VTY_NEWLINE);
- } else if (g_cfg->pcomp_rfc1144.passive) {
- vty_out(vty, " compression rfc1144 passive%s", VTY_NEWLINE);
- } else
- vty_out(vty, " no compression rfc1144%s", VTY_NEWLINE);
-
- if (g_cfg->dcomp_v42bis.active && g_cfg->dcomp_v42bis.p0 == 1) {
- vty_out(vty,
- " compression v42bis active direction sgsn codewords %d strlen %d%s",
- g_cfg->dcomp_v42bis.p1, g_cfg->dcomp_v42bis.p2,
- VTY_NEWLINE);
- } else if (g_cfg->dcomp_v42bis.active && g_cfg->dcomp_v42bis.p0 == 2) {
- vty_out(vty,
- " compression v42bis active direction ms codewords %d strlen %d%s",
- g_cfg->dcomp_v42bis.p1, g_cfg->dcomp_v42bis.p2,
- VTY_NEWLINE);
- } else if (g_cfg->dcomp_v42bis.active && g_cfg->dcomp_v42bis.p0 == 3) {
- vty_out(vty,
- " compression v42bis active direction both codewords %d strlen %d%s",
- g_cfg->dcomp_v42bis.p1, g_cfg->dcomp_v42bis.p2,
- VTY_NEWLINE);
- } else if (g_cfg->dcomp_v42bis.passive) {
- vty_out(vty, " compression v42bis passive%s", VTY_NEWLINE);
- } else
- vty_out(vty, " no compression v42bis%s", VTY_NEWLINE);
-
-#ifdef BUILD_IU
- ranap_iu_vty_config_write(vty, " ");
-#endif
-
- 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 char *gtp_ntoa(struct ul16_t *ul)
-{
- if (ul->l == 4) {
- struct in_addr *ia = (struct in_addr *) ul;
- return inet_ntoa(*ia);
- } else {
- return "UNKNOWN";
- }
-}
-
-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, TI: %u%s",
- pfx, imsi, pdp->sapi, pdp->nsapi, pdp->ti, 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(vty, "%s GTP Local Control(%s / TEIC: 0x%08x) ", pfx,
- gtp_ntoa(&pdp->lib->gsnlc), pdp->lib->teic_own);
- vty_out(vty, "Data(%s / TEID: 0x%08x)%s",
- gtp_ntoa(&pdp->lib->gsnlu), pdp->lib->teid_own, VTY_NEWLINE);
- vty_out(vty, "%s GTP Remote Control(%s / TEIC: 0x%08x) ", pfx,
- gtp_ntoa(&pdp->lib->gsnrc), pdp->lib->teic_gn);
- vty_out(vty, "Data(%s / TEID: 0x%08x)%s",
- gtp_ntoa(&pdp->lib->gsnru), pdp->lib->teid_gn, 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->gb.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->gmm_state),
- mm->ra.mcc, mm->ra.mnc, mm->ra.lac, mm->ra.rac,
- mm->gb.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")
-{
- char imsi_sanitized[GSM23003_IMSI_MAX_DIGITS+1];
- const char *op = argv[0];
- const char *imsi = imsi_sanitized;
- int rc;
-
- /* Sanitize IMSI */
- if (strlen(argv[1]) > GSM23003_IMSI_MAX_DIGITS) {
- vty_out(vty, "%% IMSI (%s) too long -- ignored!%s",
- argv[1], VTY_NEWLINE);
- return CMD_WARNING;
- }
- memset(imsi_sanitized, '0', sizeof(imsi_sanitized));
- strcpy(imsi_sanitized+GSM23003_IMSI_MAX_DIGITS-strlen(argv[1]),argv[1]);
-
- 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_encrypt, cfg_encrypt_cmd,
- "encryption (GEA0|GEA1|GEA2|GEA3|GEA4)",
- "Set encryption algorithm for SGSN\n"
- "Use GEA0 (no encryption)\n"
- "Use GEA1\nUse GEA2\nUse GEA3\nUse GEA4\n")
-{
- enum gprs_ciph_algo c = get_string_value(gprs_cipher_names, argv[0]);
- if (c != GPRS_ALGO_GEA0) {
- if (!gprs_cipher_supported(c)) {
- vty_out(vty, "%% cipher %s is unsupported in current version%s", argv[0], VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- if (!g_cfg->require_authentication) {
- vty_out(vty, "%% unable to use encryption %s without authentication: please adjust auth-policy%s",
- argv[0], VTY_NEWLINE);
- return CMD_WARNING;
- }
- }
-
- g_cfg->cipher = c;
-
- 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 <openbsc/gprs_subscriber.h>
-
-static void subscr_dump_full_vty(struct vty *vty, struct gprs_subscr *gsub, int pending)
-{
-#if 0
- char expire_time[200];
-#endif
- struct gsm_auth_tuple *at;
- int at_idx;
- struct sgsn_subscriber_pdp_data *pdp;
-
- vty_out(vty, " Authorized: %d%s",
- gsub->authorized, VTY_NEWLINE);
- vty_out(vty, " LAC: %d/0x%x%s",
- gsub->lac, gsub->lac, VTY_NEWLINE);
- vty_out(vty, " IMSI: %s%s", gsub->imsi, VTY_NEWLINE);
- if (gsub->tmsi != GSM_RESERVED_TMSI)
- vty_out(vty, " TMSI: %08X%s", gsub->tmsi,
- VTY_NEWLINE);
- if (gsub->sgsn_data->msisdn_len > 0)
- vty_out(vty, " MSISDN (BCD): %s%s",
- osmo_hexdump(gsub->sgsn_data->msisdn,
- gsub->sgsn_data->msisdn_len),
- VTY_NEWLINE);
-
- if (strlen(gsub->imei) > 0)
- vty_out(vty, " IMEI: %s%s", gsub->imei, VTY_NEWLINE);
-
- for (at_idx = 0; at_idx < ARRAY_SIZE(gsub->sgsn_data->auth_triplets);
- at_idx++) {
- at = &gsub->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, " CKSN: %d, ",
- at->key_seq);
- if (at->vec.auth_types & OSMO_AUTH_TYPE_GSM) {
- vty_out(vty, "RAND: %s, ",
- osmo_hexdump(at->vec.rand,
- sizeof(at->vec.rand)));
- vty_out(vty, "SRES: %s, ",
- osmo_hexdump(at->vec.sres,
- sizeof(at->vec.sres)));
- vty_out(vty, "Kc: %s%s",
- osmo_hexdump(at->vec.kc,
- sizeof(at->vec.kc)), VTY_NEWLINE);
- }
- if (at->vec.auth_types & OSMO_AUTH_TYPE_UMTS) {
- vty_out(vty, " AUTN: %s, ",
- osmo_hexdump(at->vec.autn,
- sizeof(at->vec.autn)));
- vty_out(vty, "RES: %s, ",
- osmo_hexdump(at->vec.res, at->vec.res_len));
- vty_out(vty, "IK: %s, ",
- osmo_hexdump(at->vec.ik, sizeof(at->vec.ik)));
- vty_out(vty, "CK: %s, ",
- osmo_hexdump(at->vec.ck, sizeof(at->vec.ck)));
- }
- }
-
- llist_for_each_entry(pdp, &gsub->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);
- }
-
-#if 0
- /* print the expiration time of a subscriber */
- if (gsub->expire_lu) {
- strftime(expire_time, sizeof(expire_time),
- "%a, %d %b %Y %T %z", localtime(&gsub->expire_lu));
- expire_time[sizeof(expire_time) - 1] = '\0';
- vty_out(vty, " Expiration Time: %s%s", expire_time, VTY_NEWLINE);
- }
-#endif
-
- if (gsub->flags)
- vty_out(vty, " Flags: %s%s%s%s%s%s",
- gsub->flags & GPRS_SUBSCRIBER_FIRST_CONTACT ?
- "FIRST_CONTACT " : "",
- gsub->flags & GPRS_SUBSCRIBER_CANCELLED ?
- "CANCELLED " : "",
- gsub->flags & GPRS_SUBSCRIBER_UPDATE_LOCATION_PENDING ?
- "UPDATE_LOCATION_PENDING " : "",
- gsub->flags & GPRS_SUBSCRIBER_UPDATE_AUTH_INFO_PENDING ?
- "AUTH_INFO_PENDING " : "",
- gsub->flags & GPRS_SUBSCRIBER_ENABLE_PURGE ?
- "ENABLE_PURGE " : "",
- VTY_NEWLINE);
-
- vty_out(vty, " Use count: %u%s", gsub->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 gprs_subscr *subscr;
-
- llist_for_each_entry(subscr, gprs_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 gprs_subscr *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.vec.sres[0], sizeof(at.vec.sres)) < 0) {
- vty_out(vty, "%% invalid SRES value '%s'%s",
- sres_str, VTY_NEWLINE);
- goto failed;
- }
- if (osmo_hexparse(rand_str, &at.vec.rand[0], sizeof(at.vec.rand)) < 0) {
- vty_out(vty, "%% invalid RAND value '%s'%s",
- rand_str, VTY_NEWLINE);
- goto failed;
- }
- if (osmo_hexparse(kc_str, &at.vec.kc[0], sizeof(at.vec.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;
-
- gprs_subscr_put(subscr);
-
- return CMD_SUCCESS;
-
-failed:
- gprs_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 gprs_subscr *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);
- gprs_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 gprs_subscr *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;
- gprs_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 gprs_subscr *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);
- gprs_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 gprs_subscr *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);
-
- gprs_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 gprs_subscr *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);
-
- gprs_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_gsup_oap_id, cfg_gsup_oap_id_cmd,
- "gsup oap-id <0-65535>",
- "GSUP Parameters\n"
- "Set the SGSN's OAP client ID\nOAP client ID (0 == disabled)\n")
-{
- /* VTY ensures range */
- g_cfg->oap.client_id = (uint16_t)atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_gsup_oap_k, cfg_gsup_oap_k_cmd,
- "gsup oap-k K",
- "GSUP Parameters\n"
- "Set the OAP shared secret K\nK value (16 byte) hex\n")
-{
- const char *k = argv[0];
-
- g_cfg->oap.secret_k_present = 0;
-
- if ((!k) || (strlen(k) == 0))
- goto disable;
-
- int k_len = osmo_hexparse(k,
- g_cfg->oap.secret_k,
- sizeof(g_cfg->oap.secret_k));
- if (k_len != 16) {
- vty_out(vty, "%% need exactly 16 octets for oap-k, got %d.%s",
- k_len, VTY_NEWLINE);
- goto disable;
- }
-
- g_cfg->oap.secret_k_present = 1;
- return CMD_SUCCESS;
-
-disable:
- if (g_cfg->oap.client_id > 0) {
- vty_out(vty, "%% OAP client ID set, but invalid oap-k value disables OAP.%s",
- VTY_NEWLINE);
- return CMD_WARNING;
- }
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_gsup_oap_opc, cfg_gsup_oap_opc_cmd,
- "gsup oap-opc OPC",
- "GSUP Parameters\n"
- "Set the OAP shared secret OPC\nOPC value (16 byte) hex\n")
-{
- const char *opc = argv[0];
-
- g_cfg->oap.secret_opc_present = 0;
-
- if ((!opc) || (strlen(opc) == 0))
- goto disable;
-
- int opc_len = osmo_hexparse(opc,
- g_cfg->oap.secret_opc,
- sizeof(g_cfg->oap.secret_opc));
- if (opc_len != 16) {
- vty_out(vty, "%% need exactly 16 octets for oap-opc, got %d.%s",
- opc_len, VTY_NEWLINE);
- goto disable;
- }
-
- g_cfg->oap.secret_opc_present = 1;
- return CMD_SUCCESS;
-
-disable:
- if (g_cfg->oap.client_id > 0) {
- vty_out(vty, "%% OAP client ID set, but invalid oap-opc value disables OAP.%s",
- VTY_NEWLINE);
- return CMD_WARNING;
- }
- 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;
-}
-
-#define COMPRESSION_STR "Configure compression\n"
-DEFUN(cfg_no_comp_rfc1144, cfg_no_comp_rfc1144_cmd,
- "no compression rfc1144",
- NO_STR COMPRESSION_STR "disable rfc1144 TCP/IP header compression\n")
-{
- g_cfg->pcomp_rfc1144.active = 0;
- g_cfg->pcomp_rfc1144.passive = 0;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_comp_rfc1144, cfg_comp_rfc1144_cmd,
- "compression rfc1144 active slots <1-256>",
- COMPRESSION_STR
- "RFC1144 Header compresion scheme\n"
- "Compression is actively proposed\n"
- "Number of compression state slots\n"
- "Number of compression state slots\n")
-{
- g_cfg->pcomp_rfc1144.active = 1;
- g_cfg->pcomp_rfc1144.passive = 1;
- g_cfg->pcomp_rfc1144.s01 = atoi(argv[0]) - 1;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_comp_rfc1144p, cfg_comp_rfc1144p_cmd,
- "compression rfc1144 passive",
- COMPRESSION_STR
- "RFC1144 Header compresion scheme\n"
- "Compression is available on request\n")
-{
- g_cfg->pcomp_rfc1144.active = 0;
- g_cfg->pcomp_rfc1144.passive = 1;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_no_comp_v42bis, cfg_no_comp_v42bis_cmd,
- "no compression v42bis",
- NO_STR COMPRESSION_STR "disable V.42bis data compression\n")
-{
- g_cfg->dcomp_v42bis.active = 0;
- g_cfg->dcomp_v42bis.passive = 0;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_comp_v42bis, cfg_comp_v42bis_cmd,
- "compression v42bis active direction (ms|sgsn|both) codewords <512-65535> strlen <6-250>",
- COMPRESSION_STR
- "V.42bis data compresion scheme\n"
- "Compression is actively proposed\n"
- "Direction in which the compression shall be active (p0)\n"
- "Compress ms->sgsn direction only\n"
- "Compress sgsn->ms direction only\n"
- "Both directions\n"
- "Number of codewords (p1)\n"
- "Number of codewords\n"
- "Maximum string length (p2)\n" "Maximum string length\n")
-{
- g_cfg->dcomp_v42bis.active = 1;
- g_cfg->dcomp_v42bis.passive = 1;
-
- switch (argv[0][0]) {
- case 'm':
- g_cfg->dcomp_v42bis.p0 = 1;
- break;
- case 's':
- g_cfg->dcomp_v42bis.p0 = 2;
- break;
- case 'b':
- g_cfg->dcomp_v42bis.p0 = 3;
- break;
- }
-
- g_cfg->dcomp_v42bis.p1 = atoi(argv[1]);
- g_cfg->dcomp_v42bis.p2 = atoi(argv[2]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_comp_v42bisp, cfg_comp_v42bisp_cmd,
- "compression v42bis passive",
- COMPRESSION_STR
- "V.42bis data compresion scheme\n"
- "Compression is available on request\n")
-{
- g_cfg->dcomp_v42bis.active = 0;
- g_cfg->dcomp_v42bis.passive = 1;
- return CMD_SUCCESS;
-}
-
-int sgsn_vty_init(struct sgsn_config *cfg)
-{
- g_cfg = cfg;
-
- 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_encrypt_cmd);
- install_element(SGSN_NODE, &cfg_gsup_remote_ip_cmd);
- install_element(SGSN_NODE, &cfg_gsup_remote_port_cmd);
- install_element(SGSN_NODE, &cfg_gsup_oap_id_cmd);
- install_element(SGSN_NODE, &cfg_gsup_oap_k_cmd);
- install_element(SGSN_NODE, &cfg_gsup_oap_opc_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);
-
- install_element(SGSN_NODE, &cfg_no_comp_rfc1144_cmd);
- install_element(SGSN_NODE, &cfg_comp_rfc1144_cmd);
- install_element(SGSN_NODE, &cfg_comp_rfc1144p_cmd);
- install_element(SGSN_NODE, &cfg_no_comp_v42bis_cmd);
- install_element(SGSN_NODE, &cfg_comp_v42bis_cmd);
- install_element(SGSN_NODE, &cfg_comp_v42bisp_cmd);
-
-#ifdef BUILD_IU
- ranap_iu_vty_init(SGSN_NODE, &g_cfg->iu.rab_assign_addr_enc);
-#endif
- return 0;
-}
-
-int sgsn_parse_config(const char *config_file)
-{
- int rc;
-
- /* make sure sgsn_vty_init() was called before this */
- OSMO_ASSERT(g_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;
- }
-
- if (g_cfg->auth_policy == SGSN_AUTH_POLICY_REMOTE
- && !(g_cfg->gsup_server_addr.sin_addr.s_addr
- && g_cfg->gsup_server_port)) {
- fprintf(stderr, "Configuration error:"
- " 'auth-policy remote' requires both"
- " 'gsup remote-ip' and 'gsup remote-port'\n");
- return -EINVAL;
- }
-
- return 0;
-}
diff --git a/src/gprs/slhc.c b/src/gprs/slhc.c
deleted file mode 100644
index cbdf8dbd8..000000000
--- a/src/gprs/slhc.c
+++ /dev/null
@@ -1,813 +0,0 @@
-/*
- * Routines to compress and uncompress tcp packets (for transmission
- * over low speed serial lines).
- *
- * Copyright (c) 1989 Regents of the University of California.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by the University of California, Berkeley. The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
- *
- * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
- * - Initial distribution.
- *
- *
- * modified for KA9Q Internet Software Package by
- * Katie Stevens (dkstevens@ucdavis.edu)
- * University of California, Davis
- * Computing Services
- * - 01-31-90 initial adaptation (from 1.19)
- * PPP.05 02-15-90 [ks]
- * PPP.08 05-02-90 [ks] use PPP protocol field to signal compression
- * PPP.15 09-90 [ks] improve mbuf handling
- * PPP.16 11-02 [karn] substantially rewritten to use NOS facilities
- *
- * - Feb 1991 Bill_Simpson@um.cc.umich.edu
- * variable number of conversation slots
- * allow zero or one slots
- * separate routines
- * status display
- * - Jul 1994 Dmitry Gorodchanin
- * Fixes for memory leaks.
- * - Oct 1994 Dmitry Gorodchanin
- * Modularization.
- * - Jan 1995 Bjorn Ekwall
- * Use ip_fast_csum from ip.h
- * - July 1995 Christos A. Polyzols
- * Spotted bug in tcp option checking
- *
- *
- * This module is a difficult issue. It's clearly inet code but it's also clearly
- * driver code belonging close to PPP and SLIP
- */
-
-#include <string.h>
-#include <stdio.h>
-#include <errno.h>
-#include <stdint.h>
-#include <arpa/inet.h>
-#include <osmocom/core/utils.h>
-#include <osmocom/core/talloc.h>
-#include <openbsc/slhc.h>
-#include <openbsc/debug.h>
-
-#define ERR_PTR(x) x
-
-
-static unsigned char *encode(unsigned char *cp, unsigned short n);
-static long decode(unsigned char **cpp);
-static unsigned char * put16(unsigned char *cp, unsigned short x);
-static unsigned short pull16(unsigned char **cpp);
-
-/* Replacement for kernel space function ip_fast_csum() */
-static uint16_t ip_fast_csum(uint8_t *iph, int ihl)
-{
- int i;
- uint16_t temp;
- uint32_t accumulator = 0xFFFF;
-
- for(i=0;i<ihl*2;i++)
- {
- temp = ((*iph) << 8)&0xFF00;
- iph++;
- temp |= (*iph)&0xFF;
- iph++;
-
- accumulator+=temp;
- if(accumulator>0xFFFF)
- {
- accumulator++;
- accumulator&=0xFFFF;
- }
- }
-
- return (uint16_t)(htons(~accumulator)&0xFFFF);
-}
-
-/* Replacement for kernel space function put_unaligned() */
-static void put_unaligned(uint16_t val, void *ptr)
-{
- memcpy(ptr,&val,sizeof(val));
-}
-
-
-/* Allocate compression data structure
- * slots must be in range 0 to 255 (zero meaning no compression)
- * Returns pointer to structure or ERR_PTR() on error.
- */
-struct slcompress *
-slhc_init(const void *ctx, int rslots, int tslots)
-{
- register short i;
- register struct cstate *ts;
- struct slcompress *comp;
-
- if (rslots < 0 || rslots > 255 || tslots < 0 || tslots > 255)
- return NULL;
-
- comp = (struct slcompress *)talloc_zero_size(ctx,sizeof(struct slcompress));
- if (! comp)
- goto out_fail;
-
- if (rslots > 0) {
- size_t rsize = rslots * sizeof(struct cstate);
- comp->rstate = (struct cstate *) talloc_zero_size(ctx, rsize);
- if (! comp->rstate)
- goto out_free;
- comp->rslot_limit = rslots - 1;
- }
-
- if (tslots > 0) {
- size_t tsize = tslots * sizeof(struct cstate);
- comp->tstate = (struct cstate *) talloc_zero_size(ctx, tsize);
- if (! comp->tstate)
- goto out_free2;
- comp->tslot_limit = tslots - 1;
- }
-
- comp->xmit_oldest = 0;
- comp->xmit_current = 255;
- comp->recv_current = 255;
- /*
- * don't accept any packets with implicit index until we get
- * one with an explicit index. Otherwise the uncompress code
- * will try to use connection 255, which is almost certainly
- * out of range
- */
- comp->flags |= SLF_TOSS;
-
- if ( tslots > 0 ) {
- ts = comp->tstate;
- for(i = comp->tslot_limit; i > 0; --i){
- ts[i].cs_this = i;
- ts[i].next = &(ts[i - 1]);
- }
- ts[0].next = &(ts[comp->tslot_limit]);
- ts[0].cs_this = 0;
- }
- return comp;
-
-out_free2:
- talloc_free(comp->rstate);
-out_free:
- talloc_free(comp);
-out_fail:
- return NULL;
-}
-
-
-/* Free a compression data structure */
-void
-slhc_free(struct slcompress *comp)
-{
- DEBUGP(DSLHC, "slhc_free(): Freeing compression states...\n");
-
- if ( comp == NULLSLCOMPR )
- return;
-
- if ( comp->tstate != NULLSLSTATE )
- talloc_free(comp->tstate );
-
- if ( comp->rstate != NULLSLSTATE )
- talloc_free( comp->rstate );
-
- talloc_free( comp );
-}
-
-
-/* Put a short in host order into a char array in network order */
-static inline unsigned char *
-put16(unsigned char *cp, unsigned short x)
-{
- *cp++ = x >> 8;
- *cp++ = x;
-
- return cp;
-}
-
-
-/* Encode a number */
-static unsigned char *
-encode(unsigned char *cp, unsigned short n)
-{
- if(n >= 256 || n == 0){
- *cp++ = 0;
- cp = put16(cp,n);
- } else {
- *cp++ = n;
- }
-
- DEBUGP(DSLHC, "encode(): n=%04x\n",n);
- return cp;
-}
-
-/* Pull a 16-bit integer in host order from buffer in network byte order */
-static unsigned short
-pull16(unsigned char **cpp)
-{
- short rval;
-
- rval = *(*cpp)++;
- rval <<= 8;
- rval |= *(*cpp)++;
- return rval;
-}
-
-/* Decode a number */
-static long
-decode(unsigned char **cpp)
-{
- register int x;
-
- x = *(*cpp)++;
- if(x == 0){
- return pull16(cpp) & 0xffff; /* pull16 returns -1 on error */
- } else {
- return x & 0xff; /* -1 if PULLCHAR returned error */
- }
-}
-
-/*
- * icp and isize are the original packet.
- * ocp is a place to put a copy if necessary.
- * cpp is initially a pointer to icp. If the copy is used,
- * change it to ocp.
- */
-
-int
-slhc_compress(struct slcompress *comp, unsigned char *icp, int isize,
- unsigned char *ocp, unsigned char **cpp, int compress_cid)
-{
- register struct cstate *ocs = &(comp->tstate[comp->xmit_oldest]);
- register struct cstate *lcs = ocs;
- register struct cstate *cs = lcs->next;
- register unsigned long deltaS, deltaA;
- register short changes = 0;
- int hlen;
- unsigned char new_seq[16];
- register unsigned char *cp = new_seq;
- struct iphdr *ip;
- struct tcphdr *th, *oth;
- __sum16 csum;
-
-
- /*
- * Don't play with runt packets.
- */
-
- if(isize<sizeof(struct iphdr))
- return isize;
-
- ip = (struct iphdr *) icp;
-
- /* Bail if this packet isn't TCP, or is an IP fragment */
- if (ip->protocol != IPPROTO_TCP || (ntohs(ip->frag_off) & 0x3fff)) {
- /* Send as regular IP */
- if(ip->protocol != IPPROTO_TCP)
- comp->sls_o_nontcp++;
- else
- comp->sls_o_tcp++;
- DEBUGP(DSLHC, "slhc_compress(): Not a TCP packat, will not touch...\n");
- return isize;
- }
- /* Extract TCP header */
-
- th = (struct tcphdr *)(((unsigned char *)ip) + ip->ihl*4);
- hlen = ip->ihl*4 + th->doff*4;
-
- /* Bail if the TCP packet isn't `compressible' (i.e., ACK isn't set or
- * some other control bit is set). Also uncompressible if
- * it's a runt.
- */
- if(hlen > isize || th->syn || th->fin || th->rst ||
- ! (th->ack)){
- /* TCP connection stuff; send as regular IP */
- comp->sls_o_tcp++;
- DEBUGP(DSLHC, "slhc_compress(): Packet is part of a TCP connection, will not touch...\n");
- return isize;
- }
- /*
- * Packet is compressible -- we're going to send either a
- * COMPRESSED_TCP or UNCOMPRESSED_TCP packet. Either way,
- * we need to locate (or create) the connection state.
- *
- * States are kept in a circularly linked list with
- * xmit_oldest pointing to the end of the list. The
- * list is kept in lru order by moving a state to the
- * head of the list whenever it is referenced. Since
- * the list is short and, empirically, the connection
- * we want is almost always near the front, we locate
- * states via linear search. If we don't find a state
- * for the datagram, the oldest state is (re-)used.
- */
-
- DEBUGP(DSLHC, "slhc_compress(): Compressible packet detected!\n");
-
- for ( ; ; ) {
- if( ip->saddr == cs->cs_ip.saddr
- && ip->daddr == cs->cs_ip.daddr
- && th->source == cs->cs_tcp.source
- && th->dest == cs->cs_tcp.dest)
- goto found;
-
- /* if current equal oldest, at end of list */
- if ( cs == ocs )
- break;
- lcs = cs;
- cs = cs->next;
- comp->sls_o_searches++;
- }
- /*
- * Didn't find it -- re-use oldest cstate. Send an
- * uncompressed packet that tells the other side what
- * connection number we're using for this conversation.
- *
- * Note that since the state list is circular, the oldest
- * state points to the newest and we only need to set
- * xmit_oldest to update the lru linkage.
- */
-
- DEBUGP(DSLHC, "slhc_compress(): Header not yet seen, will memorize header for the next turn...\n");
- comp->sls_o_misses++;
- comp->xmit_oldest = lcs->cs_this;
- goto uncompressed;
-
-found:
- DEBUGP(DSLHC, "slhc_compress(): Header already seen, trying to compress...\n");
- /*
- * Found it -- move to the front on the connection list.
- */
- if(lcs == ocs) {
- /* found at most recently used */
- } else if (cs == ocs) {
- /* found at least recently used */
- comp->xmit_oldest = lcs->cs_this;
- } else {
- /* more than 2 elements */
- lcs->next = cs->next;
- cs->next = ocs->next;
- ocs->next = cs;
- }
-
- /*
- * Make sure that only what we expect to change changed.
- * Check the following:
- * IP protocol version, header length & type of service.
- * The "Don't fragment" bit.
- * The time-to-live field.
- * The TCP header length.
- * IP options, if any.
- * TCP options, if any.
- * If any of these things are different between the previous &
- * current datagram, we send the current datagram `uncompressed'.
- */
- oth = &cs->cs_tcp;
-
- /* Display a little more debug information about which of the
- * header fields changed unexpectedly */
- if(ip->version != cs->cs_ip.version)
- DEBUGP(DSLHC, "slhc_compress(): Unexpected change: ip->version != cs->cs_ip.version\n");
- if(ip->ihl != cs->cs_ip.ihl)
- DEBUGP(DSLHC, "slhc_compress(): Unexpected change: ip->ihl != cs->cs_ip.ihl\n");
- if(ip->tos != cs->cs_ip.tos)
- DEBUGP(DSLHC, "slhc_compress(): Unexpected change: ip->tos != cs->cs_ip.tos\n");
- if((ip->frag_off & htons(0x4000)) != (cs->cs_ip.frag_off & htons(0x4000)))
- DEBUGP(DSLHC, "slhc_compress(): Unexpected change: (ip->frag_off & htons(0x4000)) != (cs->cs_ip.frag_off & htons(0x4000))\n");
- if(ip->ttl != cs->cs_ip.ttl)
- DEBUGP(DSLHC, "slhc_compress(): Unexpected change: ip->ttl != cs->cs_ip.ttl\n");
- if(th->doff != cs->cs_tcp.doff)
- DEBUGP(DSLHC, "slhc_compress(): Unexpected change: th->doff != cs->cs_tcp.doff\n");
- if(ip->ihl > 5 && memcmp(ip+1,cs->cs_ipopt,((ip->ihl)-5)*4) != 0) {
- DEBUGP(DSLHC, "slhc_compress(): Unexpected change: (ip->ihl > 5 && memcmp(ip+1,cs->cs_ipopt,((ip->ihl)-5)*4) != 0)\n");
- DEBUGP(DSLHC, "slhc_compress(): ip->ihl = %i\n", ip->ihl);
- DEBUGP(DSLHC, "slhc_compress(): ip+1 = %s\n",
- osmo_hexdump_nospc((uint8_t*)(ip+1),((ip->ihl)-5)*4));
- DEBUGP(DSLHC, "slhc_compress(): Unexpected change: cs->cs_ipopt = %s\n",
- osmo_hexdump_nospc((uint8_t*)(cs->cs_ipopt),((ip->ihl)-5)*4));
- }
- if(th->doff > 5 && memcmp(th+1,cs->cs_tcpopt,((th->doff)-5)*4) != 0) {
- DEBUGP(DSLHC, "slhc_compress(): Unexpected change: (th->doff > 5 && memcmp(th+1,cs->cs_tcpopt,((th->doff)-5)*4) != 0)\n");
- DEBUGP(DSLHC, "slhc_compress(): th->doff = %i\n", th->doff);
- DEBUGP(DSLHC, "slhc_compress(): th+1 = %s\n",
- osmo_hexdump_nospc((uint8_t*)(th+1),((th->doff)-5)*4));
- DEBUGP(DSLHC, "slhc_compress(): cs->cs_tcpopt = %s\n",
- osmo_hexdump_nospc((uint8_t*)cs->cs_tcpopt,
- ((th->doff)-5)*4));
- }
-
-
- if(ip->version != cs->cs_ip.version || ip->ihl != cs->cs_ip.ihl
- || ip->tos != cs->cs_ip.tos
- || (ip->frag_off & htons(0x4000)) != (cs->cs_ip.frag_off & htons(0x4000))
- || ip->ttl != cs->cs_ip.ttl
- || th->doff != cs->cs_tcp.doff
- || (ip->ihl > 5 && memcmp(ip+1,cs->cs_ipopt,((ip->ihl)-5)*4) != 0)
- || (th->doff > 5 && memcmp(th+1,cs->cs_tcpopt,((th->doff)-5)*4) != 0)){
- DEBUGP(DSLHC, "slhc_compress(): The header contains unexpected changes, can't compress...\n");
- goto uncompressed;
- }
-
- /*
- * Figure out which of the changing fields changed. The
- * receiver expects changes in the order: urgent, window,
- * ack, seq (the order minimizes the number of temporaries
- * needed in this section of code).
- */
- if(th->urg){
- deltaS = ntohs(th->urg_ptr);
- DEBUGP(DSLHC, "slhc_compress(): flag: Urgent Pointer (U) = 1\n");
- cp = encode(cp,deltaS);
- changes |= NEW_U;
- } else if(th->urg_ptr != oth->urg_ptr){
- /* argh! URG not set but urp changed -- a sensible
- * implementation should never do this but RFC793
- * doesn't prohibit the change so we have to deal
- * with it. */
- DEBUGP(DSLHC, "slhc_compress(): URG not set but urp changed, can't compress...\n");
- goto uncompressed;
- }
- if((deltaS = ntohs(th->window) - ntohs(oth->window)) != 0){
- DEBUGP(DSLHC, "slhc_compress(): flag: Delta Window (W) = 1\n");
- cp = encode(cp,deltaS);
- changes |= NEW_W;
- }
- if((deltaA = ntohl(th->ack_seq) - ntohl(oth->ack_seq)) != 0L){
- if(deltaA > 0x0000ffff) {
- DEBUGP(DSLHC, "slhc_compress(): (deltaA = ntohl(th->ack_seq) - ntohl(oth->ack_seq)) != 0L, can't compress...\n");
- goto uncompressed;
- }
- DEBUGP(DSLHC, "slhc_compress(): flag: Delta Ack (A) = 1\n");
- cp = encode(cp,deltaA);
- changes |= NEW_A;
- }
- if((deltaS = ntohl(th->seq) - ntohl(oth->seq)) != 0L){
- if(deltaS > 0x0000ffff) {
- DEBUGP(DSLHC, "slhc_compress(): (deltaS = ntohl(th->seq) - ntohl(oth->seq)) != 0L, can't compress...\n");
- goto uncompressed;
- }
- DEBUGP(DSLHC, "slhc_compress(): flag: Delta Sequence (S) = 1\n");
- cp = encode(cp,deltaS);
- changes |= NEW_S;
- }
-
- switch(changes){
- case 0: /* Nothing changed. If this packet contains data and the
- * last one didn't, this is probably a data packet following
- * an ack (normal on an interactive connection) and we send
- * it compressed. Otherwise it's probably a retransmit,
- * retransmitted ack or window probe. Send it uncompressed
- * in case the other side missed the compressed version.
- */
- if(ip->tot_len != cs->cs_ip.tot_len &&
- ntohs(cs->cs_ip.tot_len) == hlen)
- break;
- DEBUGP(DSLHC, "slhc_compress(): Retransmitted packet detected, can't compress...\n");
- goto uncompressed;
- case SPECIAL_I:
- case SPECIAL_D:
- /* actual changes match one of our special case encodings --
- * send packet uncompressed.
- */
- DEBUGP(DSLHC, "slhc_compress(): Special case detected, can't compress...\n");
- goto uncompressed;
- case NEW_S|NEW_A:
- if(deltaS == deltaA &&
- deltaS == ntohs(cs->cs_ip.tot_len) - hlen){
- /* special case for echoed terminal traffic */
- DEBUGP(DSLHC, "slhc_compress(): Special case for echoed terminal traffic detected...\n");
- DEBUGP(DSLHC, "slhc_compress(): flag: Delta Sequence (S) = 1, Delta Window (W) = 1, Urgent Pointer (U) = 1\n");
- changes = SPECIAL_I;
- cp = new_seq;
- }
- break;
- case NEW_S:
- if(deltaS == ntohs(cs->cs_ip.tot_len) - hlen){
- /* special case for data xfer */
- DEBUGP(DSLHC, "slhc_compress(): Special case for data xfer detected...\n");
- DEBUGP(DSLHC, "slhc_compress(): flag: Delta Sequence (S) = 1, Delta Ack (A) = 1, Delta Window (W) = 1, Urgent Pointer (U) = 1\n");
- changes = SPECIAL_D;
- cp = new_seq;
- }
- break;
- }
- deltaS = ntohs(ip->id) - ntohs(cs->cs_ip.id);
- if(deltaS != 1){
- DEBUGP(DSLHC, "slhc_compress(): flag: Delta IP ID (I) = 1\n");
- cp = encode(cp,deltaS);
- changes |= NEW_I;
- }
- if(th->psh) {
- DEBUGP(DSLHC, "slhc_compress(): flag: Push (P) = 1\n");
- changes |= TCP_PUSH_BIT;
- }
- /* Grab the cksum before we overwrite it below. Then update our
- * state with this packet's header.
- */
- csum = th->check;
- memcpy(&cs->cs_ip,ip,20);
- memcpy(&cs->cs_tcp,th,20);
- /* We want to use the original packet as our compressed packet.
- * (cp - new_seq) is the number of bytes we need for compressed
- * sequence numbers. In addition we need one byte for the change
- * mask, one for the connection id and two for the tcp checksum.
- * So, (cp - new_seq) + 4 bytes of header are needed.
- */
- deltaS = cp - new_seq;
- if(compress_cid == 0 || comp->xmit_current != cs->cs_this){
- cp = ocp;
- *cpp = ocp;
- DEBUGP(DSLHC, "slhc_compress(): flag: Connection number (C) = 1\n");
- *cp++ = changes | NEW_C;
- *cp++ = cs->cs_this;
- comp->xmit_current = cs->cs_this;
- } else {
- cp = ocp;
- *cpp = ocp;
- *cp++ = changes;
- }
- *(__sum16 *)cp = csum;
- cp += 2;
-/* deltaS is now the size of the change section of the compressed header */
-
- DEBUGP(DSLHC, "slhc_compress(): Delta-list length (deltaS) = %li\n",deltaS);
- DEBUGP(DSLHC, "slhc_compress(): Original header len (hlen) = %i\n",hlen);
-
- memcpy(cp,new_seq,deltaS); /* Write list of deltas */
- memcpy(cp+deltaS,icp+hlen,isize-hlen);
- comp->sls_o_compressed++;
- ocp[0] |= SL_TYPE_COMPRESSED_TCP;
- return isize - hlen + deltaS + (cp - ocp);
-
- /* Update connection state cs & send uncompressed packet (i.e.,
- * a regular ip/tcp packet but with the 'conversation id' we hope
- * to use on future compressed packets in the protocol field).
- */
-uncompressed:
- DEBUGP(DSLHC, "slhc_compress(): Packet will be sent uncompressed...\n");
- memcpy(&cs->cs_ip,ip,20);
- memcpy(&cs->cs_tcp,th,20);
- if (ip->ihl > 5)
- memcpy(cs->cs_ipopt, ip+1, ((ip->ihl) - 5) * 4);
- if (th->doff > 5)
- memcpy(cs->cs_tcpopt, th+1, ((th->doff) - 5) * 4);
- comp->xmit_current = cs->cs_this;
- comp->sls_o_uncompressed++;
- memcpy(ocp, icp, isize);
- *cpp = ocp;
- ocp[9] = cs->cs_this;
- ocp[0] |= SL_TYPE_UNCOMPRESSED_TCP;
- return isize;
-}
-
-
-int
-slhc_uncompress(struct slcompress *comp, unsigned char *icp, int isize)
-{
- register int changes;
- long x;
- register struct tcphdr *thp;
- register struct iphdr *ip;
- register struct cstate *cs;
- int len, hdrlen;
- unsigned char *cp = icp;
-
- /* We've got a compressed packet; read the change byte */
- comp->sls_i_compressed++;
- if(isize < 3){
- comp->sls_i_error++;
- return 0;
- }
- changes = *cp++;
- if(changes & NEW_C){
- /* Make sure the state index is in range, then grab the state.
- * If we have a good state index, clear the 'discard' flag.
- */
- x = *cp++; /* Read conn index */
- if(x < 0 || x > comp->rslot_limit)
- goto bad;
-
- comp->flags &=~ SLF_TOSS;
- comp->recv_current = x;
- } else {
- /* this packet has an implicit state index. If we've
- * had a line error since the last time we got an
- * explicit state index, we have to toss the packet. */
- if(comp->flags & SLF_TOSS){
- comp->sls_i_tossed++;
- return 0;
- }
- }
- cs = &comp->rstate[comp->recv_current];
- thp = &cs->cs_tcp;
- ip = &cs->cs_ip;
-
- thp->check = *(__sum16 *)cp;
- cp += 2;
-
- thp->psh = (changes & TCP_PUSH_BIT) ? 1 : 0;
-/*
- * we can use the same number for the length of the saved header and
- * the current one, because the packet wouldn't have been sent
- * as compressed unless the options were the same as the previous one
- */
-
- hdrlen = ip->ihl * 4 + thp->doff * 4;
-
- switch(changes & SPECIALS_MASK){
- case SPECIAL_I: /* Echoed terminal traffic */
- DEBUGP(DSLHC, "slhc_uncompress(): Echoed terminal traffic detected\n");
-
- {
- register short i;
- i = ntohs(ip->tot_len) - hdrlen;
- thp->ack_seq = htonl( ntohl(thp->ack_seq) + i);
- thp->seq = htonl( ntohl(thp->seq) + i);
- }
- break;
-
- case SPECIAL_D: /* Unidirectional data */
- DEBUGP(DSLHC, "slhc_uncompress(): Unidirectional data detected\n");
- thp->seq = htonl( ntohl(thp->seq) +
- ntohs(ip->tot_len) - hdrlen);
- break;
-
- default:
- DEBUGP(DSLHC, "slhc_uncompress(): default packet type detected\n");
- if(changes & NEW_U){
- thp->urg = 1;
- if((x = decode(&cp)) == -1) {
- goto bad;
- }
- thp->urg_ptr = htons(x);
- } else
- thp->urg = 0;
- if(changes & NEW_W){
- if((x = decode(&cp)) == -1) {
- goto bad;
- }
- thp->window = htons( ntohs(thp->window) + x);
- }
- if(changes & NEW_A){
- if((x = decode(&cp)) == -1) {
- goto bad;
- }
- thp->ack_seq = htonl( ntohl(thp->ack_seq) + x);
- }
- if(changes & NEW_S){
- if((x = decode(&cp)) == -1) {
- goto bad;
- }
- thp->seq = htonl( ntohl(thp->seq) + x);
- }
- break;
- }
- if(changes & NEW_I){
- if((x = decode(&cp)) == -1) {
- goto bad;
- }
- ip->id = htons (ntohs (ip->id) + x);
- } else
- ip->id = htons (ntohs (ip->id) + 1);
-
- /*
- * At this point, cp points to the first byte of data in the
- * packet. Put the reconstructed TCP and IP headers back on the
- * packet. Recalculate IP checksum (but not TCP checksum).
- */
-
- len = isize - (cp - icp);
- if (len < 0)
- goto bad;
- len += hdrlen;
- ip->tot_len = htons(len);
- ip->check = 0;
-
- DEBUGP(DSLHC, "slhc_uncompress(): making space for the reconstructed header...\n");
- memmove(icp + hdrlen, cp, len - hdrlen);
-
- cp = icp;
- memcpy(cp, ip, 20);
- cp += 20;
-
- if (ip->ihl > 5) {
- memcpy(cp, cs->cs_ipopt, (ip->ihl - 5) * 4);
- cp += (ip->ihl - 5) * 4;
- }
-
- put_unaligned(ip_fast_csum(icp, ip->ihl),
- &((struct iphdr *)icp)->check);
-
- memcpy(cp, thp, 20);
- cp += 20;
-
- if (thp->doff > 5) {
- memcpy(cp, cs->cs_tcpopt, ((thp->doff) - 5) * 4);
- cp += ((thp->doff) - 5) * 4;
- }
-
- return len;
-bad:
- DEBUGP(DSLHC, "slhc_uncompress(): bad packet detected!\n");
- comp->sls_i_error++;
- return slhc_toss( comp );
-}
-
-
-int
-slhc_remember(struct slcompress *comp, unsigned char *icp, int isize)
-{
- register struct cstate *cs;
- unsigned ihl;
-
- unsigned char index;
-
- if(isize < 20) {
- /* The packet is shorter than a legal IP header */
- comp->sls_i_runt++;
- DEBUGP(DSLHC, "slhc_remember(): The packet is shorter than a legal IP header ==> slhc_toss()\n");
- return slhc_toss( comp );
- }
- /* Peek at the IP header's IHL field to find its length */
- ihl = icp[0] & 0xf;
- if(ihl < 20 / 4){
- /* The IP header length field is too small */
- comp->sls_i_runt++;
- DEBUGP(DSLHC, "slhc_remember(): The IP header length field is too small ==> slhc_toss()\n");
- return slhc_toss( comp );
- }
- index = icp[9];
- icp[9] = IPPROTO_TCP;
-
- if (ip_fast_csum(icp, ihl)) {
- /* Bad IP header checksum; discard */
- comp->sls_i_badcheck++;
- DEBUGP(DSLHC, "slhc_remember(): Bad IP header checksum; discard ==> slhc_toss()\n");
- return slhc_toss( comp );
- }
- if(index > comp->rslot_limit) {
- comp->sls_i_error++;
- DEBUGP(DSLHC, "slhc_remember(): index > comp->rslot_limit ==> slhc_toss()\n");
- return slhc_toss(comp);
- }
-
- /* Update local state */
- cs = &comp->rstate[comp->recv_current = index];
- comp->flags &=~ SLF_TOSS;
- memcpy(&cs->cs_ip,icp,20);
- memcpy(&cs->cs_tcp,icp + ihl*4,20);
- if (ihl > 5)
- memcpy(cs->cs_ipopt, icp + sizeof(struct iphdr), (ihl - 5) * 4);
- if (cs->cs_tcp.doff > 5)
- memcpy(cs->cs_tcpopt, icp + ihl*4 + sizeof(struct tcphdr), (cs->cs_tcp.doff - 5) * 4);
- cs->cs_hsize = ihl*2 + cs->cs_tcp.doff*2;
- /* Put headers back on packet
- * Neither header checksum is recalculated
- */
- comp->sls_i_uncompressed++;
- return isize;
-}
-
-int
-slhc_toss(struct slcompress *comp)
-{
- DEBUGP(DSLHC, "slhc_toss(): Reset compression state...\n");
- if ( comp == NULLSLCOMPR )
- return 0;
-
- comp->flags |= SLF_TOSS;
- return 0;
-}
-
-void slhc_i_status(struct slcompress *comp)
-{
- if (comp != NULLSLCOMPR) {
- DEBUGP(DSLHC, "slhc_i_status(): %d Cmp, %d Uncmp, %d Bad, %d Tossed\n",
- comp->sls_i_compressed,
- comp->sls_i_uncompressed,
- comp->sls_i_error,
- comp->sls_i_tossed);
- }
-}
-
-void slhc_o_status(struct slcompress *comp)
-{
- if (comp != NULLSLCOMPR) {
- DEBUGP(DSLHC, "slhc_o_status(): %d Cmp, %d Uncmp, %d AsIs, %d NotTCP %d Searches, %d Misses\n",
- comp->sls_o_compressed,
- comp->sls_o_uncompressed,
- comp->sls_o_tcp,
- comp->sls_o_nontcp,
- comp->sls_o_searches,
- comp->sls_o_misses);
- }
-}
-
diff --git a/src/gprs/v42bis.c b/src/gprs/v42bis.c
deleted file mode 100644
index a04b0af5c..000000000
--- a/src/gprs/v42bis.c
+++ /dev/null
@@ -1,767 +0,0 @@
-/*
- * SpanDSP - a series of DSP components for telephony
- *
- * v42bis.c
- *
- * Written by Steve Underwood <steveu@coppice.org>
- *
- * Copyright (C) 2005, 2011 Steve Underwood
- *
- * All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License version 2.1,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-/* THIS IS A WORK IN PROGRESS. IT IS NOT FINISHED.
- Currently it performs the core compression and decompression functions OK.
- However, a number of the bells and whistles in V.42bis are incomplete. */
-
-/*! \file */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <inttypes.h>
-#include <string.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <ctype.h>
-#include <assert.h>
-
-#include <openbsc/v42bis.h>
-#include <openbsc/v42bis_private.h>
-#include <openbsc/debug.h>
-#include <osmocom/core/talloc.h>
-
-
-#define span_log(x,y,msg, ...) DEBUGP(DV42BIS,msg, ##__VA_ARGS__)
-#define span_log_init(x,y,z)
-#define span_log_set_protocol(x,y)
-
-
-#define FALSE 0
-#define TRUE 1
-
-/* Fixed parameters from the spec. */
-/* Character size (bits) */
-#define V42BIS_N3 8
-/* Number of characters in the alphabet */
-#define V42BIS_N4 256
-/* Index number of first dictionary entry used to store a string */
-#define V42BIS_N5 (V42BIS_N4 + V42BIS_N6)
-/* Number of control codewords */
-#define V42BIS_N6 3
-/* V.42bis/9.2 */
-#define V42BIS_ESC_STEP 51
-
-/* Compreeibility monitoring parameters for assessing automated switches between
- transparent and compressed mode */
-#define COMPRESSIBILITY_MONITOR (256*V42BIS_N3)
-#define COMPRESSIBILITY_MONITOR_HYSTERESIS 11
-
-/* Control code words in compressed mode */
-enum
-{
- V42BIS_ETM = 0, /* Enter transparent mode */
- V42BIS_FLUSH = 1, /* Flush data */
- V42BIS_STEPUP = 2 /* Step up codeword size */
-};
-
-/* Command codes in transparent mode */
-enum
-{
- V42BIS_ECM = 0, /* Enter compression mode */
- V42BIS_EID = 1, /* Escape character in data */
- V42BIS_RESET = 2 /* Force reinitialisation */
-};
-
-static __inline__ void push_octet(v42bis_comp_state_t *s, int octet)
-{
- s->output_buf[s->output_octet_count++] = (uint8_t) octet;
- if (s->output_octet_count >= s->max_output_len)
- {
- s->handler(s->user_data, s->output_buf, s->output_octet_count);
- s->output_octet_count = 0;
- }
-}
-/*- End of function --------------------------------------------------------*/
-
-static __inline__ void push_octets(v42bis_comp_state_t *s, const uint8_t buf[], int len)
-{
- int i;
- int chunk;
-
- i = 0;
- while ((s->output_octet_count + len - i) >= s->max_output_len)
- {
- chunk = s->max_output_len - s->output_octet_count;
- memcpy(&s->output_buf[s->output_octet_count], &buf[i], chunk);
- s->handler(s->user_data, s->output_buf, s->max_output_len);
- s->output_octet_count = 0;
- i += chunk;
- }
- chunk = len - i;
- if (chunk > 0)
- {
- memcpy(&s->output_buf[s->output_octet_count], &buf[i], chunk);
- s->output_octet_count += chunk;
- }
-}
-/*- End of function --------------------------------------------------------*/
-
-static __inline__ void push_compressed_code(v42bis_comp_state_t *s, int code)
-{
- s->bit_buffer |= code << s->bit_count;
- s->bit_count += s->v42bis_parm_c2;
- while (s->bit_count >= 8)
- {
- push_octet(s, s->bit_buffer & 0xFF);
- s->bit_buffer >>= 8;
- s->bit_count -= 8;
- }
-}
-/*- End of function --------------------------------------------------------*/
-
-static __inline__ void push_octet_alignment(v42bis_comp_state_t *s)
-{
- if ((s->bit_count & 7))
- {
- s->bit_count += (8 - (s->bit_count & 7));
- while (s->bit_count >= 8)
- {
- push_octet(s, s->bit_buffer & 0xFF);
- s->bit_buffer >>= 8;
- s->bit_count -= 8;
- }
- }
-}
-/*- End of function --------------------------------------------------------*/
-
-static __inline__ void flush_octets(v42bis_comp_state_t *s)
-{
- if (s->output_octet_count > 0)
- {
- s->handler(s->user_data, s->output_buf, s->output_octet_count);
- s->output_octet_count = 0;
- }
-}
-/*- End of function --------------------------------------------------------*/
-
-static void dictionary_init(v42bis_comp_state_t *s)
-{
- int i;
-
- memset(s->dict, 0, sizeof(s->dict));
- for (i = 0; i < V42BIS_N4; i++)
- s->dict[i + V42BIS_N6].node_octet = i;
- s->v42bis_parm_c1 = V42BIS_N5;
- s->v42bis_parm_c2 = V42BIS_N3 + 1;
- s->v42bis_parm_c3 = V42BIS_N4 << 1;
- s->last_matched = 0;
- s->update_at = 0;
- s->last_added = 0;
- s->bit_buffer = 0;
- s->bit_count = 0;
- s->flushed_length = 0;
- s->string_length = 0;
- s->escape_code = 0;
- s->transparent = TRUE;
- s->escaped = FALSE;
- s->compression_performance = COMPRESSIBILITY_MONITOR;
-}
-/*- End of function --------------------------------------------------------*/
-
-static uint16_t match_octet(v42bis_comp_state_t *s, uint16_t at, uint8_t octet)
-{
- uint16_t e;
-
- if (at == 0)
- return octet + V42BIS_N6;
- e = s->dict[at].child;
- while (e)
- {
- if (s->dict[e].node_octet == octet)
- return e;
- e = s->dict[e].next;
- }
- return 0;
-}
-/*- End of function --------------------------------------------------------*/
-
-static uint16_t add_octet_to_dictionary(v42bis_comp_state_t *s, uint16_t at, uint8_t octet)
-{
- uint16_t newx;
- uint16_t next;
- uint16_t e;
-
- newx = s->v42bis_parm_c1;
- s->dict[newx].node_octet = octet;
- s->dict[newx].parent = at;
- s->dict[newx].child = 0;
- s->dict[newx].next = s->dict[at].child;
- s->dict[at].child = newx;
- next = newx;
- /* 6.5 Recovering a dictionary entry to use next */
- do
- {
- /* 6.5(a) and (b) */
- if (++next == s->v42bis_parm_n2)
- next = V42BIS_N5;
- }
- while (s->dict[next].child);
- /* 6.5(c) We need to reuse a leaf node */
- if (s->dict[next].parent)
- {
- /* 6.5(d) Detach the leaf node from its parent, and re-use it */
- e = s->dict[next].parent;
- if (s->dict[e].child == next)
- {
- s->dict[e].child = s->dict[next].next;
- }
- else
- {
- e = s->dict[e].child;
- while (s->dict[e].next != next)
- e = s->dict[e].next;
- s->dict[e].next = s->dict[next].next;
- }
- }
- s->v42bis_parm_c1 = next;
- return newx;
-}
-/*- End of function --------------------------------------------------------*/
-
-static void send_string(v42bis_comp_state_t *s)
-{
- push_octets(s, s->string, s->string_length);
- s->string_length = 0;
- s->flushed_length = 0;
-}
-/*- End of function --------------------------------------------------------*/
-
-static void expand_codeword_to_string(v42bis_comp_state_t *s, uint16_t code)
-{
- int i;
- uint16_t p;
-
- /* Work out the length */
- for (i = 0, p = code; p; i++)
- p = s->dict[p].parent;
- s->string_length += i;
- /* Now expand the known length of string */
- i = s->string_length - 1;
- for (p = code; p; )
- {
- s->string[i--] = s->dict[p].node_octet;
- p = s->dict[p].parent;
- }
-}
-/*- End of function --------------------------------------------------------*/
-
-static void send_encoded_data(v42bis_comp_state_t *s, uint16_t code)
-{
- int i;
-
- /* Update compressibility metric */
- /* Integrate at the compressed bit rate, and leak at the pre-compression bit rate */
- s->compression_performance += (s->v42bis_parm_c2 - s->compression_performance*s->string_length*V42BIS_N3/COMPRESSIBILITY_MONITOR);
- if (s->transparent)
- {
- for (i = 0; i < s->string_length; i++)
- {
- push_octet(s, s->string[i]);
- if (s->string[i] == s->escape_code)
- {
- push_octet(s, V42BIS_EID);
- s->escape_code += V42BIS_ESC_STEP;
- }
- }
- }
- else
- {
- /* Allow for any escape octets in the string */
- for (i = 0; i < s->string_length; i++)
- {
- if (s->string[i] == s->escape_code)
- s->escape_code += V42BIS_ESC_STEP;
- }
- /* 7.4 Encoding - we now have the longest matchable string, and will need to output the code for it. */
- while (code >= s->v42bis_parm_c3)
- {
- /* We need to increase the codeword size */
- /* 7.4(a) */
- push_compressed_code(s, V42BIS_STEPUP);
- /* 7.4(b) */
- s->v42bis_parm_c2++;
- /* 7.4(c) */
- s->v42bis_parm_c3 <<= 1;
- /* 7.4(d) this might need to be repeated, so we loop */
- }
- /* 7.5 Transfer - output the last state of the string */
- push_compressed_code(s, code);
- }
- s->string_length = 0;
- s->flushed_length = 0;
-}
-/*- End of function --------------------------------------------------------*/
-
-static void go_compressed(v42bis_state_t *ss)
-{
- v42bis_comp_state_t *s;
-
- s = &ss->compress;
- if (!s->transparent)
- return;
- span_log(&ss->logging, SPAN_LOG_FLOW, "Changing to compressed mode\n");
- /* Switch out of transparent now, between codes. We need to send the octet which did not
- match, just before switching. */
- if (s->last_matched)
- {
- s->update_at = s->last_matched;
- send_encoded_data(s, s->last_matched);
- s->last_matched = 0;
- }
- push_octet(s, s->escape_code);
- push_octet(s, V42BIS_ECM);
- s->bit_buffer = 0;
- s->transparent = FALSE;
-}
-/*- End of function --------------------------------------------------------*/
-
-static void go_transparent(v42bis_state_t *ss)
-{
- v42bis_comp_state_t *s;
-
- s = &ss->compress;
- if (s->transparent)
- return;
- span_log(&ss->logging, SPAN_LOG_FLOW, "Changing to transparent mode\n");
- /* Switch into transparent now, between codes, and the unmatched octet should
- go out in transparent mode, just below */
- if (s->last_matched)
- {
- s->update_at = s->last_matched;
- send_encoded_data(s, s->last_matched);
- s->last_matched = 0;
- }
- s->last_added = 0;
- push_compressed_code(s, V42BIS_ETM);
- push_octet_alignment(s);
- s->transparent = TRUE;
-}
-/*- End of function --------------------------------------------------------*/
-
-static void monitor_for_mode_change(v42bis_state_t *ss)
-{
- v42bis_comp_state_t *s;
-
- s = &ss->compress;
- switch (s->compression_mode)
- {
- case V42BIS_COMPRESSION_MODE_DYNAMIC:
- /* 7.8 Data compressibility test */
- if (s->transparent)
- {
- if (s->compression_performance < COMPRESSIBILITY_MONITOR - COMPRESSIBILITY_MONITOR_HYSTERESIS)
- {
- /* 7.8.1 Transition to compressed mode */
- go_compressed(ss);
- }
- }
- else
- {
- if (s->compression_performance > COMPRESSIBILITY_MONITOR)
- {
- /* 7.8.2 Transition to transparent mode */
- go_transparent(ss);
- }
- }
- /* 7.8.3 Reset function - TODO */
- break;
- case V42BIS_COMPRESSION_MODE_ALWAYS:
- if (s->transparent)
- go_compressed(ss);
- break;
- case V42BIS_COMPRESSION_MODE_NEVER:
- if (!s->transparent)
- go_transparent(ss);
- break;
- }
-}
-/*- End of function --------------------------------------------------------*/
-
-static int v42bis_comp_init(v42bis_comp_state_t *s,
- int p1,
- int p2,
- put_msg_func_t handler,
- void *user_data,
- int max_output_len)
-{
- memset(s, 0, sizeof(*s));
- s->v42bis_parm_n2 = p1;
- s->v42bis_parm_n7 = p2;
- s->handler = handler;
- s->user_data = user_data;
- s->max_output_len = (max_output_len < V42BIS_MAX_OUTPUT_LENGTH) ? max_output_len : V42BIS_MAX_OUTPUT_LENGTH;
- s->output_octet_count = 0;
- dictionary_init(s);
- return 0;
-}
-/*- End of function --------------------------------------------------------*/
-
-static int comp_exit(v42bis_comp_state_t *s)
-{
- s->v42bis_parm_n2 = 0;
- return 0;
-}
-/*- End of function --------------------------------------------------------*/
-
-SPAN_DECLARE(int) v42bis_compress(v42bis_state_t *ss, const uint8_t buf[], int len)
-{
- v42bis_comp_state_t *s;
- int i;
- uint16_t code;
-
- s = &ss->compress;
- if (!s->v42bis_parm_p0)
- {
- /* Compression is off - just push the incoming data out */
- push_octets(s, buf, len);
- return 0;
- }
- for (i = 0; i < len; )
- {
- /* 6.4 Add the string to the dictionary */
- if (s->update_at)
- {
- if (match_octet(s, s->update_at, buf[i]) == 0)
- s->last_added = add_octet_to_dictionary(s, s->update_at, buf[i]);
- s->update_at = 0;
- }
- /* Match string */
- while (i < len)
- {
- code = match_octet(s, s->last_matched, buf[i]);
- if (code == 0)
- {
- s->update_at = s->last_matched;
- send_encoded_data(s, s->last_matched);
- s->last_matched = 0;
- break;
- }
- if (code == s->last_added)
- {
- s->last_added = 0;
- send_encoded_data(s, s->last_matched);
- s->last_matched = 0;
- break;
- }
- s->last_matched = code;
- /* 6.3(b) If the string matches a dictionary entry, and the entry is not that entry
- created by the last invocation of the string matching procedure, then the
- next character shall be read and appended to the string and this step
- repeated. */
- s->string[s->string_length++] = buf[i++];
- /* 6.4(a) The string must not exceed N7 in length */
- if (s->string_length + s->flushed_length == s->v42bis_parm_n7)
- {
- send_encoded_data(s, s->last_matched);
- s->last_matched = 0;
- break;
- }
- }
- monitor_for_mode_change(ss);
- }
- return 0;
-}
-/*- End of function --------------------------------------------------------*/
-
-SPAN_DECLARE(int) v42bis_compress_flush(v42bis_state_t *ss)
-{
- v42bis_comp_state_t *s;
- int len;
-
- s = &ss->compress;
- if (s->update_at)
- return 0;
- if (s->last_matched)
- {
- len = s->string_length;
- send_encoded_data(s, s->last_matched);
- s->flushed_length += len;
- }
- if (!s->transparent)
- {
- s->update_at = s->last_matched;
- s->last_matched = 0;
- s->flushed_length = 0;
- push_compressed_code(s, V42BIS_FLUSH);
- push_octet_alignment(s);
- }
- flush_octets(s);
- return 0;
-}
-/*- End of function --------------------------------------------------------*/
-
-SPAN_DECLARE(int) v42bis_decompress(v42bis_state_t *ss, const uint8_t buf[], int len)
-{
- v42bis_comp_state_t *s;
- int i;
- int j;
- int yyy;
- uint16_t code;
- uint16_t p;
- uint8_t ch;
- uint8_t in;
-
- s = &ss->decompress;
- if (!s->v42bis_parm_p0)
- {
- /* Compression is off - just push the incoming data out */
- push_octets(s, buf, len);
- return 0;
- }
- for (i = 0; i < len; )
- {
- if (s->transparent)
- {
- in = buf[i];
- if (s->escaped)
- {
- /* Command */
- s->escaped = FALSE;
- switch (in)
- {
- case V42BIS_ECM:
- /* Enter compressed mode */
- span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_ECM\n");
- send_string(s);
- s->transparent = FALSE;
- s->update_at = s->last_matched;
- s->last_matched = 0;
- i++;
- continue;
- case V42BIS_EID:
- /* Escape symbol */
- span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_EID\n");
- in = s->escape_code;
- s->escape_code += V42BIS_ESC_STEP;
- break;
- case V42BIS_RESET:
- /* Reset dictionary */
- span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_RESET\n");
- /* TODO: */
- send_string(s);
- dictionary_init(s);
- i++;
- continue;
- default:
- span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_???? - %" PRIu32 "\n", in);
- return -1;
- }
- }
- else if (in == s->escape_code)
- {
- s->escaped = TRUE;
- i++;
- continue;
- }
-
- yyy = TRUE;
- for (j = 0; j < 2 && yyy; j++)
- {
- if (s->update_at)
- {
- if (match_octet(s, s->update_at, in) == 0)
- s->last_added = add_octet_to_dictionary(s, s->update_at, in);
- s->update_at = 0;
- }
-
- code = match_octet(s, s->last_matched, in);
- if (code == 0)
- {
- s->update_at = s->last_matched;
- send_string(s);
- s->last_matched = 0;
- }
- else if (code == s->last_added)
- {
- s->last_added = 0;
- send_string(s);
- s->last_matched = 0;
- }
- else
- {
- s->last_matched = code;
- s->string[s->string_length++] = in;
- if (s->string_length + s->flushed_length == s->v42bis_parm_n7)
- {
- send_string(s);
- s->last_matched = 0;
- }
- i++;
- yyy = FALSE;
- }
- }
- }
- else
- {
- /* Get code from input */
- while (s->bit_count < s->v42bis_parm_c2 && i < len)
- {
- s->bit_buffer |= buf[i++] << s->bit_count;
- s->bit_count += 8;
- }
- if (s->bit_count < s->v42bis_parm_c2)
- continue;
- code = s->bit_buffer & ((1 << s->v42bis_parm_c2) - 1);
- s->bit_buffer >>= s->v42bis_parm_c2;
- s->bit_count -= s->v42bis_parm_c2;
-
- if (code < V42BIS_N6)
- {
- /* We have a control code. */
- switch (code)
- {
- case V42BIS_ETM:
- /* Enter transparent mode */
- span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_ETM\n");
- s->bit_count = 0;
- s->transparent = TRUE;
- s->last_matched = 0;
- s->last_added = 0;
- break;
- case V42BIS_FLUSH:
- /* Flush signal */
- span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_FLUSH\n");
- s->bit_count = 0;
- break;
- case V42BIS_STEPUP:
- /* Increase code word size */
- span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_STEPUP\n");
- s->v42bis_parm_c2++;
- s->v42bis_parm_c3 <<= 1;
- if (s->v42bis_parm_c2 > (s->v42bis_parm_n2 >> 3))
- return -1;
- break;
- }
- continue;
- }
- /* Regular codeword */
- if (code == s->v42bis_parm_c1)
- return -1;
- expand_codeword_to_string(s, code);
- if (s->update_at)
- {
- ch = s->string[0];
- if ((p = match_octet(s, s->update_at, ch)) == 0)
- {
- s->last_added = add_octet_to_dictionary(s, s->update_at, ch);
- if (code == s->v42bis_parm_c1)
- return -1;
- }
- else if (p == s->last_added)
- {
- s->last_added = 0;
- }
- }
- s->update_at = ((s->string_length + s->flushed_length) == s->v42bis_parm_n7) ? 0 : code;
- /* Allow for any escapes which may be in this string */
- for (j = 0; j < s->string_length; j++)
- {
- if (s->string[j] == s->escape_code)
- s->escape_code += V42BIS_ESC_STEP;
- }
- send_string(s);
- }
- }
- return 0;
-}
-/*- End of function --------------------------------------------------------*/
-
-SPAN_DECLARE(int) v42bis_decompress_flush(v42bis_state_t *ss)
-{
- v42bis_comp_state_t *s;
- int len;
-
- s = &ss->decompress;
- len = s->string_length;
- send_string(s);
- s->flushed_length += len;
- flush_octets(s);
- return 0;
-}
-/*- End of function --------------------------------------------------------*/
-
-SPAN_DECLARE(void) v42bis_compression_control(v42bis_state_t *s, int mode)
-{
- s->compress.compression_mode = mode;
-}
-/*- End of function --------------------------------------------------------*/
-
-SPAN_DECLARE(v42bis_state_t *) v42bis_init(const void *ctx,
- v42bis_state_t *s,
- int negotiated_p0,
- int negotiated_p1,
- int negotiated_p2,
- put_msg_func_t encode_handler,
- void *encode_user_data,
- int max_encode_len,
- put_msg_func_t decode_handler,
- void *decode_user_data,
- int max_decode_len)
-{
- int ret;
-
- if (negotiated_p1 < V42BIS_MIN_DICTIONARY_SIZE || negotiated_p1 > 65535)
- return NULL;
- if (negotiated_p2 < V42BIS_MIN_STRING_SIZE || negotiated_p2 > V42BIS_MAX_STRING_SIZE)
- return NULL;
- if (s == NULL)
- {
- if ((s = (v42bis_state_t *) talloc_zero_size(ctx,sizeof(*s))) == NULL)
- return NULL;
- }
- memset(s, 0, sizeof(*s));
- span_log_init(&s->logging, SPAN_LOG_NONE, NULL);
- span_log_set_protocol(&s->logging, "V.42bis");
-
- if ((ret = v42bis_comp_init(&s->compress, negotiated_p1, negotiated_p2, encode_handler, encode_user_data, max_encode_len)))
- return NULL;
- if ((ret = v42bis_comp_init(&s->decompress, negotiated_p1, negotiated_p2, decode_handler, decode_user_data, max_decode_len)))
- {
- comp_exit(&s->compress);
- return NULL;
- }
- s->compress.v42bis_parm_p0 = negotiated_p0 & 2;
- s->decompress.v42bis_parm_p0 = negotiated_p0 & 1;
-
- return s;
-}
-/*- End of function --------------------------------------------------------*/
-
-SPAN_DECLARE(int) v42bis_release(v42bis_state_t *s)
-{
- return 0;
-}
-/*- End of function --------------------------------------------------------*/
-
-SPAN_DECLARE(int) v42bis_free(v42bis_state_t *s)
-{
- comp_exit(&s->compress);
- comp_exit(&s->decompress);
- talloc_free(s);
- return 0;
-}
-/*- End of function --------------------------------------------------------*/
-/*- End of file ------------------------------------------------------------*/
diff --git a/src/libbsc/abis_rsl.c b/src/libbsc/abis_rsl.c
index 1b5758848..dddc9bccd 100644
--- a/src/libbsc/abis_rsl.c
+++ b/src/libbsc/abis_rsl.c
@@ -28,7 +28,7 @@
#include <arpa/inet.h>
#include <openbsc/gsm_data.h>
-#include <openbsc/gsm_04_08.h>
+#include <openbsc/gsm_04_08_utils.h>
#include <osmocom/gsm/gsm_utils.h>
#include <openbsc/abis_rsl.h>
#include <openbsc/chan_alloc.h>
@@ -46,6 +46,7 @@
#include <osmocom/gsm/rsl.h>
#include <osmocom/core/talloc.h>
#include <openbsc/pcu_if.h>
+#include <openbsc/bsc_api.h>
#define RSL_ALLOC_SIZE 1024
#define RSL_ALLOC_HEADROOM 128
diff --git a/src/libbsc/bsc_api.c b/src/libbsc/bsc_api.c
index c2828e3e8..f37faf8c8 100644
--- a/src/libbsc/bsc_api.c
+++ b/src/libbsc/bsc_api.c
@@ -30,9 +30,8 @@
#include <openbsc/chan_alloc.h>
#include <openbsc/handover.h>
#include <openbsc/debug.h>
-#include <openbsc/gsm_04_08.h>
#include <openbsc/trau_mux.h>
-#include <openbsc/vlr.h>
+#include <openbsc/gsm_04_08_utils.h>
#include <osmocom/gsm/protocol/gsm_08_08.h>
#include <osmocom/gsm/gsm48.h>
diff --git a/src/libbsc/bsc_init.c b/src/libbsc/bsc_init.c
index 64dcd157b..4ab89bcb1 100644
--- a/src/libbsc/bsc_init.c
+++ b/src/libbsc/bsc_init.c
@@ -21,7 +21,6 @@
#include <openbsc/gsm_data.h>
#include <osmocom/gsm/gsm_utils.h>
-#include <openbsc/gsm_04_08.h>
#include <openbsc/abis_rsl.h>
#include <openbsc/abis_nm.h>
#include <openbsc/debug.h>
diff --git a/src/libbsc/bsc_vty.c b/src/libbsc/bsc_vty.c
index 722753ac1..ac192a418 100644
--- a/src/libbsc/bsc_vty.c
+++ b/src/libbsc/bsc_vty.c
@@ -30,6 +30,7 @@
#include <osmocom/vty/misc.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <osmocom/gsm/gsm0502.h>
+#include <osmocom/gsm/gsm48.h>
#include <arpa/inet.h>
@@ -43,7 +44,6 @@
#include <osmocom/gsm/abis_nm.h>
#include <openbsc/chan_alloc.h>
#include <openbsc/meas_rep.h>
-#include <openbsc/db.h>
#include <openbsc/vty.h>
#include <osmocom/gprs/gprs_ns.h>
#include <openbsc/system_information.h>
@@ -55,7 +55,7 @@
#include <openbsc/osmo_bsc_rf.h>
#include <openbsc/pcu_if.h>
#include <openbsc/common_cs.h>
-#include <openbsc/vlr.h>
+#include <openbsc/gsm_04_08_utils.h>
#include <inttypes.h>
@@ -1014,26 +1014,6 @@ DEFUN(show_ts,
return CMD_SUCCESS;
}
-static void subscr_dump_vty(struct vty *vty, struct vlr_subscr *vsub)
-{
- OSMO_ASSERT(vsub);
- if (strlen(vsub->name))
- vty_out(vty, " Name: '%s'%s", vsub->name, VTY_NEWLINE);
- if (strlen(vsub->msisdn))
- vty_out(vty, " Extension: %s%s", vsub->msisdn,
- VTY_NEWLINE);
- if (strlen(vsub->imsi))
- vty_out(vty, " IMSI: %s%s", vsub->imsi, VTY_NEWLINE);
- if (vsub->tmsi != GSM_RESERVED_TMSI)
- vty_out(vty, " TMSI: %08X%s", vsub->tmsi,
- VTY_NEWLINE);
- if (vsub->tmsi_new != GSM_RESERVED_TMSI)
- vty_out(vty, " new TMSI: %08X%s", vsub->tmsi_new,
- VTY_NEWLINE);
-
- vty_out(vty, " Use count: %u%s", vsub->use_count, VTY_NEWLINE);
-}
-
static void bsc_subscr_dump_vty(struct vty *vty, struct bsc_subscr *bsub)
{
if (strlen(bsub->imsi))
@@ -1159,9 +1139,9 @@ static void lchan_dump_full_vty(struct vty *vty, struct gsm_lchan *lchan)
vty_out(vty, " Channel Mode / Codec: %s%s",
get_value_string(gsm48_cmode_names, lchan->tch_mode),
VTY_NEWLINE);
- if (lchan->conn && lchan->conn->vsub) {
+ if (lchan->conn && lchan->conn->bsub) {
vty_out(vty, " Subscriber:%s", VTY_NEWLINE);
- subscr_dump_vty(vty, lchan->conn->vsub);
+ bsc_subscr_dump_vty(vty, lchan->conn->bsub);
} else
vty_out(vty, " No Subscriber%s", VTY_NEWLINE);
if (is_ipaccess_bts(lchan->ts->trx->bts)) {
diff --git a/src/libbsc/chan_alloc.c b/src/libbsc/chan_alloc.c
index 33b79a0b2..4192d65f7 100644
--- a/src/libbsc/chan_alloc.c
+++ b/src/libbsc/chan_alloc.c
@@ -32,6 +32,7 @@
#include <openbsc/debug.h>
#include <openbsc/rtp_proxy.h>
#include <openbsc/signal.h>
+#include <openbsc/gsm_04_08_utils.h>
#include <osmocom/core/talloc.h>
diff --git a/src/libbsc/gsm_04_08_utils.c b/src/libbsc/gsm_04_08_utils.c
index 7c5e0e97a..109beda49 100644
--- a/src/libbsc/gsm_04_08_utils.c
+++ b/src/libbsc/gsm_04_08_utils.c
@@ -32,11 +32,11 @@
#include <openbsc/abis_rsl.h>
#include <openbsc/debug.h>
-#include <openbsc/gsm_04_08.h>
#include <openbsc/transaction.h>
#include <openbsc/paging.h>
#include <openbsc/signal.h>
#include <openbsc/bsc_api.h>
+#include <openbsc/gsm_04_08_utils.h>
/* should ip.access BTS use direct RTP streams between each other (1),
* or should OpenBSC always act as RTP relay/proxy in between (0) ? */
diff --git a/src/libbsc/handover_logic.c b/src/libbsc/handover_logic.c
index c03563f6b..03ae966e3 100644
--- a/src/libbsc/handover_logic.c
+++ b/src/libbsc/handover_logic.c
@@ -33,14 +33,14 @@
#include <openbsc/gsm_data.h>
#include <osmocom/gsm/gsm_utils.h>
#include <openbsc/gsm_subscriber.h>
-#include <openbsc/gsm_04_08.h>
#include <openbsc/abis_rsl.h>
#include <openbsc/chan_alloc.h>
#include <openbsc/signal.h>
#include <osmocom/core/talloc.h>
#include <openbsc/transaction.h>
#include <openbsc/trau_mux.h>
-#include <openbsc/vlr.h>
+#include <openbsc/bsc_subscriber.h>
+#include <openbsc/gsm_04_08_utils.h>
struct bsc_handover {
struct llist_head list;
@@ -261,7 +261,7 @@ static int ho_gsm48_ho_compl(struct gsm_lchan *new_lchan)
net = new_lchan->ts->trx->bts->network;
LOGP(DHO, LOGL_INFO, "Subscriber %s HO from BTS %u->%u on ARFCN "
- "%u->%u\n", vlr_subscr_name(ho->old_lchan->conn->vsub),
+ "%u->%u\n", bsc_subscr_name(ho->old_lchan->conn->bsub),
ho->old_lchan->ts->trx->bts->nr, new_lchan->ts->trx->bts->nr,
ho->old_lchan->ts->trx->arfcn, new_lchan->ts->trx->arfcn);
diff --git a/src/libbsc/net_init.c b/src/libbsc/net_init.c
index bc5ed3510..faa07944b 100644
--- a/src/libbsc/net_init.c
+++ b/src/libbsc/net_init.c
@@ -20,6 +20,7 @@
#include <openbsc/common_cs.h>
#include <openbsc/osmo_bsc.h>
#include <openbsc/bsc_msc_data.h>
+#include <openbsc/gsm_04_08_utils.h>
struct gsm_network *bsc_network_init(void *ctx,
uint16_t country_code,
diff --git a/src/libbsc/paging.c b/src/libbsc/paging.c
index e19c2d1c4..02212a352 100644
--- a/src/libbsc/paging.c
+++ b/src/libbsc/paging.c
@@ -50,7 +50,6 @@
#include <openbsc/gsm_data.h>
#include <openbsc/chan_alloc.h>
#include <openbsc/bsc_api.h>
-#include <openbsc/vlr.h>
void *tall_paging_ctx;
diff --git a/src/libbsc/system_information.c b/src/libbsc/system_information.c
index dcabbbdd1..fe0b23d02 100644
--- a/src/libbsc/system_information.c
+++ b/src/libbsc/system_information.c
@@ -30,13 +30,15 @@
#include <osmocom/core/bitvec.h>
#include <osmocom/core/utils.h>
#include <osmocom/gsm/sysinfo.h>
+#include <osmocom/gsm/gsm48_ie.h>
+#include <osmocom/gsm/gsm48.h>
#include <openbsc/debug.h>
-#include <openbsc/gsm_04_08.h>
#include <openbsc/gsm_data.h>
#include <openbsc/abis_rsl.h>
#include <openbsc/rest_octets.h>
#include <openbsc/arfcn_range_encode.h>
+#include <openbsc/gsm_04_08_utils.h>
/*
* DCS1800 and PCS1900 have overlapping ARFCNs. We would need to set the
diff --git a/src/libcommon-cs/common_cs.c b/src/libcommon-cs/common_cs.c
index 532425417..57e4134a3 100644
--- a/src/libcommon-cs/common_cs.c
+++ b/src/libcommon-cs/common_cs.c
@@ -23,12 +23,13 @@
#include <stdbool.h>
#include <osmocom/gsm/gsm0480.h>
+#include <osmocom/gsm/gsm48.h>
#include <openbsc/common_cs.h>
#include <openbsc/gsm_data.h>
#include <openbsc/gsm_subscriber.h>
#include <openbsc/gsm_data.h>
-#include <openbsc/gsm_04_11.h>
+#include <openbsc/gsm_04_08_utils.h>
/* Warning: if bsc_network_init() is not called, some of the members of
* gsm_network are not initialized properly and must not be used! (In
diff --git a/src/libcommon/Makefile.am b/src/libcommon/Makefile.am
index 0b258c08a..6cfebc2da 100644
--- a/src/libcommon/Makefile.am
+++ b/src/libcommon/Makefile.am
@@ -23,25 +23,7 @@ libcommon_a_SOURCES = \
debug.c \
gsm_data.c \
gsm_data_shared.c \
- gsup_client.c \
- oap_client.c \
socket.c \
talloc_ctx.c \
gsm_subscriber_base.c \
$(NULL)
-
-noinst_PROGRAMS = \
- gsup_test_client \
- $(NULL)
-
-gsup_test_client_SOURCES = \
- gsup_test_client.c \
- $(NULL)
-gsup_test_client_LDADD = \
- libcommon.a \
- $(LIBOSMOCORE_LIBS) \
- $(LIBOSMOGSM_LIBS) \
- $(LIBOSMOVTY_LIBS) \
- $(LIBOSMOABIS_LIBS) \
- -lrt \
- $(NULL)
diff --git a/src/libcommon/gsm_data.c b/src/libcommon/gsm_data.c
index b5bf0599e..fb7ac88ba 100644
--- a/src/libcommon/gsm_data.c
+++ b/src/libcommon/gsm_data.c
@@ -32,6 +32,7 @@
#include <osmocom/gsm/abis_nm.h>
#include <osmocom/core/statistics.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/gsm/gsm48.h>
#include <openbsc/gsm_data.h>
#include <openbsc/bsc_msc_data.h>
diff --git a/src/libcommon/gsm_subscriber_base.c b/src/libcommon/gsm_subscriber_base.c
index 018ed210c..a2f6b2d1f 100644
--- a/src/libcommon/gsm_subscriber_base.c
+++ b/src/libcommon/gsm_subscriber_base.c
@@ -31,34 +31,7 @@
#include <osmocom/core/utils.h>
#include <openbsc/gsm_subscriber.h>
#include <openbsc/debug.h>
-#include <openbsc/vlr.h>
LLIST_HEAD(active_subscribers);
void *tall_subscr_ctx;
-/* return static buffer with printable name of VLR subscriber */
-const char *vlr_subscr_name(struct vlr_subscr *vsub)
-{
- static char buf[32];
- if (!vsub)
- return "unknown";
- if (vsub->msisdn[0])
- snprintf(buf, sizeof(buf), "MSISDN:%s", vsub->msisdn);
- else if (vsub->imsi[0])
- snprintf(buf, sizeof(buf), "IMSI:%s", vsub->imsi);
- else if (vsub->tmsi != GSM_RESERVED_TMSI)
- snprintf(buf, sizeof(buf), "TMSI:0x%08x", vsub->tmsi);
- else if (vsub->tmsi_new != GSM_RESERVED_TMSI)
- snprintf(buf, sizeof(buf), "TMSI(new):0x%08x", vsub->tmsi_new);
- else
- return "unknown";
- buf[sizeof(buf)-1] = '\0';
- return buf;
-}
-
-const char *vlr_subscr_msisdn_or_name(struct vlr_subscr *vsub)
-{
- if (!vsub || !vsub->msisdn[0])
- return vlr_subscr_name(vsub);
- return vsub->msisdn;
-}
diff --git a/src/libcommon/gsup_client.c b/src/libcommon/gsup_client.c
deleted file mode 100644
index fd65e7b0b..000000000
--- a/src/libcommon/gsup_client.c
+++ /dev/null
@@ -1,347 +0,0 @@
-/* Generic Subscriber Update Protocol client */
-
-/* (C) 2014-2016 by Sysmocom s.f.m.c. GmbH
- * All Rights Reserved
- *
- * Author: Jacob Erlbeck
- * Author: Neels Hofmeyr
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <openbsc/gsup_client.h>
-
-#include <osmocom/abis/ipa.h>
-#include <osmocom/gsm/protocol/ipaccess.h>
-#include <osmocom/core/msgb.h>
-#include <osmocom/core/logging.h>
-
-#include <openbsc/debug.h>
-
-#include <errno.h>
-#include <string.h>
-
-extern void *tall_bsc_ctx;
-
-static void start_test_procedure(struct gsup_client *gsupc);
-
-static void gsup_client_send_ping(struct gsup_client *gsupc)
-{
- struct msgb *msg = gsup_client_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 gsup_client *gsupc)
-{
- int rc;
-
- if (gsupc->is_connected)
- return 0;
-
- if (osmo_timer_pending(&gsupc->connect_timer)) {
- LOGP(DLGSUP, LOGL_DEBUG,
- "GSUP connect: connect timer already running\n");
- osmo_timer_del(&gsupc->connect_timer);
- }
-
- if (osmo_timer_pending(&gsupc->ping_timer)) {
- LOGP(DLGSUP, 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(DLGSUP, LOGL_DEBUG, "GSUP connect: discarded stored messages\n");
-
- rc = ipa_client_conn_open(gsupc->link);
-
- if (rc >= 0) {
- LOGP(DLGSUP, LOGL_NOTICE, "GSUP connecting to %s:%d\n",
- gsupc->link->addr, gsupc->link->port);
- return 0;
- }
-
- LOGP(DLGSUP, LOGL_ERROR, "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,
- GSUP_CLIENT_RECONNECT_INTERVAL, 0);
-
- LOGP(DLGSUP, 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 gsup_client *gsupc = gsupc_;
-
- if (gsupc->is_connected)
- return;
-
- gsup_client_connect(gsupc);
-}
-
-static void client_send(struct 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_oap_register(struct gsup_client *gsupc)
-{
- struct msgb *msg_tx;
- int rc;
- rc = oap_client_register(&gsupc->oap_state, &msg_tx);
-
- if ((rc < 0) || (!msg_tx)) {
- LOGP(DLGSUP, LOGL_ERROR, "GSUP OAP set up, but cannot register.\n");
- return;
- }
-
- client_send(gsupc, IPAC_PROTO_EXT_OAP, msg_tx);
-}
-
-static void gsup_client_updown_cb(struct ipa_client_conn *link, int up)
-{
- struct gsup_client *gsupc = link->data;
-
- LOGP(DLGSUP, 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);
-
- if (gsupc->oap_state.state == OAP_INITIALIZED)
- gsup_client_oap_register(gsupc);
-
- osmo_timer_del(&gsupc->connect_timer);
- } else {
- osmo_timer_del(&gsupc->ping_timer);
-
- osmo_timer_schedule(&gsupc->connect_timer,
- GSUP_CLIENT_RECONNECT_INTERVAL, 0);
- }
-}
-
-static int gsup_client_oap_handle(struct gsup_client *gsupc, struct msgb *msg_rx)
-{
- int rc;
- struct msgb *msg_tx;
-
- /* If the oap_state is disabled, this will reject the messages. */
- rc = oap_client_handle(&gsupc->oap_state, msg_rx, &msg_tx);
- msgb_free(msg_rx);
- if (rc < 0)
- return rc;
-
- if (msg_tx)
- client_send(gsupc, IPAC_PROTO_EXT_OAP, msg_tx);
-
- return 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 gsup_client *gsupc = (struct gsup_client *)link->data;
- int rc;
- struct ipaccess_unit ipa_dev = {
- /* see gsup_client_create() on const vs non-const */
- .unit_name = (char*)gsupc->unit_name,
- };
-
- OSMO_ASSERT(ipa_dev.unit_name);
-
- msg->l2h = &hh->data[0];
-
- rc = ipaccess_bts_handle_ccm(link, &ipa_dev, msg);
-
- if (rc < 0) {
- LOGP(DLGSUP, 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(DLGSUP, 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))
- goto invalid;
-
- msg->l2h = &he->data[0];
-
- if (he->proto == IPAC_PROTO_EXT_GSUP) {
- OSMO_ASSERT(gsupc->read_cb != NULL);
- gsupc->read_cb(gsupc, msg);
- /* expecting read_cb() to free msg */
- } else if (he->proto == IPAC_PROTO_EXT_OAP) {
- return gsup_client_oap_handle(gsupc, msg);
- /* gsup_client_oap_handle frees msg */
- } else
- goto invalid;
-
- return 0;
-
-invalid:
- LOGP(DLGSUP, 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 gsup_client *gsupc = gsupc_;
-
- LOGP(DLGSUP, 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(DLGSUP, 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 gsup_client *gsupc)
-{
- osmo_timer_setup(&gsupc->ping_timer, ping_timer_cb, gsupc);
-
- gsupc->got_ipa_pong = 0;
- osmo_timer_schedule(&gsupc->ping_timer, GSUP_CLIENT_PING_INTERVAL, 0);
- LOGP(DLGSUP, LOGL_DEBUG, "GSUP sending PING\n");
- gsup_client_send_ping(gsupc);
-}
-
-struct gsup_client *gsup_client_create(const char *unit_name,
- const char *ip_addr,
- unsigned int tcp_port,
- gsup_client_read_cb_t read_cb,
- struct oap_client_config *oapc_config)
-{
- struct gsup_client *gsupc;
- int rc;
-
- gsupc = talloc_zero(tall_bsc_ctx, struct gsup_client);
- OSMO_ASSERT(gsupc);
-
- /* struct ipaccess_unit has a non-const unit_name, so let's copy to be
- * able to have a non-const unit_name here as well. To not taint the
- * public gsup_client API, let's store it in a const char* anyway. */
- gsupc->unit_name = talloc_strdup(gsupc, unit_name);
- OSMO_ASSERT(gsupc->unit_name);
-
- /* a NULL oapc_config will mark oap_state disabled. */
- rc = oap_client_init(oapc_config, &gsupc->oap_state);
- if (rc != 0)
- goto failed;
-
- 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;
-
- osmo_timer_setup(&gsupc->connect_timer, connect_timer_cb, gsupc);
-
- rc = gsup_client_connect(gsupc);
-
- if (rc < 0)
- goto failed;
-
- gsupc->read_cb = read_cb;
-
- return gsupc;
-
-failed:
- gsup_client_destroy(gsupc);
- return NULL;
-}
-
-void gsup_client_destroy(struct 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 gsup_client_send(struct gsup_client *gsupc, struct msgb *msg)
-{
- if (!gsupc || !gsupc->is_connected) {
- LOGP(DGPRS, LOGL_ERROR, "GSUP not connected, unable to send %s\n", msgb_hexdump(msg));
- msgb_free(msg);
- return -ENOTCONN;
- }
-
- client_send(gsupc, IPAC_PROTO_EXT_GSUP, msg);
-
- return 0;
-}
-
-struct msgb *gsup_client_msgb_alloc(void)
-{
- return msgb_alloc_headroom(4000, 64, __func__);
-}
diff --git a/src/libcommon/gsup_test_client.c b/src/libcommon/gsup_test_client.c
deleted file mode 100644
index b6a8d6b7d..000000000
--- a/src/libcommon/gsup_test_client.c
+++ /dev/null
@@ -1,298 +0,0 @@
-#include <string.h>
-#include <stdio.h>
-#include <errno.h>
-#include <signal.h>
-
-#include <osmocom/core/linuxlist.h>
-#include <osmocom/core/msgb.h>
-#include <osmocom/core/select.h>
-#include <osmocom/core/application.h>
-#include <osmocom/core/utils.h>
-#include <osmocom/gsm/gsup.h>
-
-#include <openbsc/gsup_client.h>
-#include <openbsc/debug.h>
-
-static struct gsup_client *g_gc;
-
-
-/***********************************************************************
- * IMSI Operation
- ***********************************************************************/
-static LLIST_HEAD(g_imsi_ops);
-
-struct imsi_op_stats {
- uint32_t num_alloc;
- uint32_t num_released;
- uint32_t num_rx_success;
- uint32_t num_rx_error;
- uint32_t num_timeout;
-};
-
-enum imsi_op_type {
- IMSI_OP_SAI,
- IMSI_OP_LU,
- IMSI_OP_ISD,
- _NUM_IMSI_OP
-};
-
-static const struct value_string imsi_op_names[] = {
- { IMSI_OP_SAI, "SAI" },
- { IMSI_OP_LU, "LU" },
- { IMSI_OP_ISD, "ISD" },
- { 0, NULL }
-};
-
-static struct imsi_op_stats imsi_op_stats[_NUM_IMSI_OP];
-
-struct imsi_op {
- struct llist_head list;
- char imsi[17];
- enum imsi_op_type type;
- struct osmo_timer_list timer;
-};
-
-static struct imsi_op *imsi_op_find(const char *imsi,
- enum imsi_op_type type)
-{
- struct imsi_op *io;
-
- llist_for_each_entry(io, &g_imsi_ops, list) {
- if (!strcmp(io->imsi, imsi) && io->type == type)
- return io;
- }
- return NULL;
-}
-
-static void imsi_op_timer_cb(void *data);
-
-static struct imsi_op *imsi_op_alloc(void *ctx, const char *imsi,
- enum imsi_op_type type)
-{
- struct imsi_op *io;
-
- if (imsi_op_find(imsi, type))
- return NULL;
-
- io = talloc_zero(ctx, struct imsi_op);
- osmo_strlcpy(io->imsi, imsi, sizeof(io->imsi));
- io->type = type;
- osmo_timer_setup(&io->timer, imsi_op_timer_cb, io);
- llist_add(&io->list, &g_imsi_ops);
- imsi_op_stats[type].num_alloc++;
-
- return io;
-}
-
-static void imsi_op_release(struct imsi_op *io)
-{
- osmo_timer_del(&io->timer);
- llist_del(&io->list);
- imsi_op_stats[io->type].num_released++;
- talloc_free(io);
-}
-
-static void imsi_op_timer_cb(void *data)
-{
- struct imsi_op *io = data;
- printf("%s: Timer expiration\n", io->imsi);
- imsi_op_stats[io->type].num_timeout++;
- imsi_op_release(io);
-}
-
-/* allocate + generate + send Send-Auth-Info */
-int req_auth_info(const char *imsi)
-{
- struct imsi_op *io = imsi_op_alloc(g_gc, imsi, IMSI_OP_SAI);
- struct osmo_gsup_message gsup = {0};
- struct msgb *msg = msgb_alloc_headroom(1200, 200, __func__);
-
- osmo_strlcpy(gsup.imsi, io->imsi, sizeof(gsup.imsi));
- gsup.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST;
-
- osmo_gsup_encode(msg, &gsup);
-
- return gsup_client_send(g_gc, msg);
-}
-
-/* allocate + generate + send Send-Auth-Info */
-int req_loc_upd(const char *imsi)
-{
- struct imsi_op *io = imsi_op_alloc(g_gc, imsi, IMSI_OP_LU);
- struct osmo_gsup_message gsup = {0};
- struct msgb *msg = msgb_alloc_headroom(1200, 200, __func__);
-
- osmo_strlcpy(gsup.imsi, io->imsi, sizeof(gsup.imsi));
- gsup.message_type = OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST;
-
- osmo_gsup_encode(msg, &gsup);
-
- return gsup_client_send(g_gc, msg);
-}
-
-int resp_isd(struct imsi_op *io)
-{
- struct osmo_gsup_message gsup = {0};
- struct msgb *msg = msgb_alloc_headroom(1200, 200, __func__);
-
- osmo_strlcpy(gsup.imsi, io->imsi, sizeof(gsup.imsi));
- gsup.message_type = OSMO_GSUP_MSGT_INSERT_DATA_RESULT;
-
- osmo_gsup_encode(msg, &gsup);
-
- imsi_op_release(io);
-
- return gsup_client_send(g_gc, msg);
-}
-
-/* receive an incoming GSUP message */
-static void imsi_op_rx_gsup(struct imsi_op *io, const struct osmo_gsup_message *gsup)
-{
- int is_error = 0;
-
- if (OSMO_GSUP_IS_MSGT_ERROR(gsup->message_type)) {
- imsi_op_stats[io->type].num_rx_error++;
- is_error = 1;
- } else
- imsi_op_stats[io->type].num_rx_success++;
-
- switch (io->type) {
- case IMSI_OP_SAI:
- printf("%s; SAI Response%s\n", io->imsi, is_error ? ": ERROR" : "");
- /* now that we have auth tuples, request LU */
- req_loc_upd(io->imsi);
- imsi_op_release(io);
- break;
- case IMSI_OP_LU:
- printf("%s; LU Response%s\n", io->imsi, is_error ? ": ERROR" : "");
- imsi_op_release(io);
- break;
- case IMSI_OP_ISD:
- printf("%s; ISD Request%s\n", io->imsi, is_error ? ": ERROR" : "");
- resp_isd(io);
- break;
- default:
- printf("%s: Unknown\n", io->imsi);
- imsi_op_release(io);
- break;
- }
-}
-
-static int op_type_by_gsup_msgt(enum osmo_gsup_message_type msg_type)
-{
- switch (msg_type) {
- case OSMO_GSUP_MSGT_SEND_AUTH_INFO_RESULT:
- case OSMO_GSUP_MSGT_SEND_AUTH_INFO_ERROR:
- return IMSI_OP_SAI;
- case OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT:
- case OSMO_GSUP_MSGT_UPDATE_LOCATION_ERROR:
- return IMSI_OP_LU;
- case OSMO_GSUP_MSGT_INSERT_DATA_REQUEST:
- return IMSI_OP_ISD;
- default:
- printf("Unknown GSUP msg_type %u\n", msg_type);
- return -1;
- }
-}
-
-static int gsupc_read_cb(struct gsup_client *gsupc, struct msgb *msg)
-{
- struct osmo_gsup_message gsup_msg = {0};
- struct imsi_op *io;
- int rc;
-
- DEBUGP(DGPRS, "Rx GSUP %s\n", osmo_hexdump(msgb_l2(msg), msgb_l2len(msg)));
-
- rc = osmo_gsup_decode(msgb_l2(msg), msgb_l2len(msg), &gsup_msg);
- if (rc < 0)
- return rc;
-
- if (!gsup_msg.imsi[0])
- return -1;
-
- rc = op_type_by_gsup_msgt(gsup_msg.message_type);
- if (rc < 0)
- return rc;
-
- switch (rc) {
- case IMSI_OP_SAI:
- case IMSI_OP_LU:
- io = imsi_op_find(gsup_msg.imsi, rc);
- if (!io)
- return -1;
- break;
- case IMSI_OP_ISD:
- /* ISD is an inbound transaction */
- io = imsi_op_alloc(g_gc, gsup_msg.imsi, IMSI_OP_ISD);
- break;
- }
-
- imsi_op_rx_gsup(io, &gsup_msg);
- msgb_free(msg);
-
- return 0;
-}
-
-static void print_report(void)
-{
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(imsi_op_stats); i++) {
- struct imsi_op_stats *st = &imsi_op_stats[i];
- const char *name = get_value_string(imsi_op_names, i);
- printf("%s: %u alloc, %u released, %u success, %u error , %u tout\n",
- name, st->num_alloc, st->num_released, st->num_rx_success,
- st->num_rx_error, st->num_timeout);
- }
-}
-
-static void sig_cb(int sig)
-{
- switch (sig) {
- case SIGINT:
- print_report();
- exit(0);
- break;
- }
-}
-
-void *tall_bsc_ctx = NULL;
-
-/* default categories */
-static struct log_info_cat default_categories[] = {
-};
-
-static const struct log_info gsup_test_client_log_info = {
- .cat = default_categories,
- .num_cat = ARRAY_SIZE(default_categories),
-};
-
-int main(int argc, char **argv)
-{
- unsigned long long i;
- char *server_host = "127.0.0.1";
- uint16_t server_port = OSMO_GSUP_PORT;
-
- osmo_init_logging(&gsup_test_client_log_info);
-
- g_gc = gsup_client_create("GSUPTEST", server_host, server_port,
- gsupc_read_cb, NULL);
-
-
- signal(SIGINT, sig_cb);
-
- for (i = 0; i < 10000; i++) {
- unsigned long long imsi = 901790000000000 + i;
- char imsi_buf[17];
- snprintf(imsi_buf, sizeof(imsi_buf), "%015llu", imsi);
- req_auth_info(imsi_buf);
- osmo_select_main(0);
- }
-
- while (1) {
- osmo_select_main(0);
- }
-
- print_report();
- exit(0);
-}
diff --git a/src/libfilter/bsc_msg_filter.c b/src/libfilter/bsc_msg_filter.c
index 115d376cb..338db7298 100644
--- a/src/libfilter/bsc_msg_filter.c
+++ b/src/libfilter/bsc_msg_filter.c
@@ -28,12 +28,14 @@
#include <openbsc/gsm_data.h>
#include <openbsc/debug.h>
#include <openbsc/ipaccess.h>
+#include <openbsc/common_cs.h>
#include <osmocom/core/talloc.h>
#include <osmocom/gsm/gsm0808.h>
#include <osmocom/gsm/protocol/gsm_08_08.h>
#include <osmocom/gsm/protocol/gsm_04_11.h>
+#include <osmocom/gsm/gsm48.h>
int bsc_filter_barr_find(struct rb_root *root, const char *imsi, int *cm, int *lu)
{
diff --git a/src/libmsc/Makefile.am b/src/libmsc/Makefile.am
deleted file mode 100644
index 908353042..000000000
--- a/src/libmsc/Makefile.am
+++ /dev/null
@@ -1,74 +0,0 @@
-AM_CPPFLAGS = \
- $(all_includes) \
- -I$(top_srcdir)/include \
- -I$(top_builddir) \
- $(NULL)
-
-AM_CFLAGS = \
- -Wall \
- $(LIBOSMOCORE_CFLAGS) \
- $(LIBOSMOVTY_CFLAGS) \
- $(LIBOSMOABIS_CFLAGS) \
- $(COVERAGE_CFLAGS) \
- $(LIBCRYPTO_CFLAGS) \
- $(LIBSMPP34_CFLAGS) \
- $(LIBASN1C_CFLAGS) \
- $(LIBOSMOSIGTRAN_CFLAGS) \
- $(NULL)
-
-noinst_HEADERS = \
- meas_feed.h \
- $(NULL)
-
-noinst_LIBRARIES = \
- libmsc.a \
- $(NULL)
-
-libmsc_a_SOURCES = \
- a_iface.c \
- a_iface_bssap.c \
- auth.c \
- msc_vty.c \
- db.c \
- gsm_04_08.c \
- gsm_04_11.c \
- gsm_04_80.c \
- gsm_subscriber.c \
- mncc.c \
- mncc_builtin.c \
- mncc_sock.c \
- msc_ifaces.c \
- rrlp.c \
- silent_call.c \
- sms_queue.c \
- ussd.c \
- vty_interface_layer3.c \
- transaction.c \
- osmo_msc.c \
- ctrl_commands.c \
- meas_feed.c \
- subscr_conn.c \
- $(NULL)
-if BUILD_IU
-libmsc_a_SOURCES += \
- iucs.c \
- iucs_ranap.c \
- $(NULL)
-else
-libmsc_a_SOURCES += \
- iu_dummy.c \
- $(NULL)
-endif
-
-if BUILD_SMPP
-noinst_HEADERS += \
- smpp_smsc.h \
- $(NULL)
-
-libmsc_a_SOURCES += \
- smpp_smsc.c \
- smpp_openbsc.c \
- smpp_vty.c \
- smpp_utils.c \
- $(NULL)
-endif
diff --git a/src/libmsc/a_iface.c b/src/libmsc/a_iface.c
deleted file mode 100644
index 25d3970f0..000000000
--- a/src/libmsc/a_iface.c
+++ /dev/null
@@ -1,578 +0,0 @@
-/* (C) 2017 by sysmocom s.f.m.c. GmbH
- * All Rights Reserved
- *
- * Author: Philipp Maier
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <osmocom/core/utils.h>
-#include <osmocom/core/msgb.h>
-#include <osmocom/core/logging.h>
-#include <osmocom/sigtran/sccp_helpers.h>
-#include <osmocom/sigtran/sccp_sap.h>
-#include <osmocom/sigtran/osmo_ss7.h>
-#include <osmocom/sigtran/protocol/m3ua.h>
-#include <osmocom/gsm/gsm0808.h>
-#include <osmocom/gsm/protocol/gsm_08_08.h>
-#include <osmocom/gsm/protocol/gsm_04_08.h>
-#include <osmocom/gsm/gsm0808_utils.h>
-#include <openbsc/debug.h>
-#include <openbsc/msc_ifaces.h>
-#include <openbsc/a_iface.h>
-#include <openbsc/a_iface_bssap.h>
-#include <openbsc/transaction.h>
-#include <osmocom/legacy_mgcp/mgcpgw_client.h>
-#include <osmocom/core/byteswap.h>
-#include <osmocom/sccp/sccp_types.h>
-#include <openbsc/a_reset.h>
-#include <openbsc/osmo_msc.h>
-
-/* A pointer to the GSM network we work with. By the current paradigm,
- * there can only be one gsm_network per MSC. The pointer is set once
- * when calling a_init() */
-static struct gsm_network *gsm_network = NULL;
-
-/* A struct to track currently active connections. We need that information
- * to handle failure sitautions. In case of a problem, we must know which
- * connections are currently open and which BSC is responsible. We also need
- * the data to perform our connection checks (a_reset). All other logic will
- * look at the connection ids and addresses that are supplied by the
- * primitives */
-struct bsc_conn {
- struct llist_head list;
- uint32_t conn_id; /* Connection identifier */
-};
-
-/* Internal list with connections we currently maintain. This
- * list is of type struct bsc_conn (see above) */
-static LLIST_HEAD(active_connections);
-
-/* Record info of a new active connection in the active connection list */
-static void record_bsc_con(const void *ctx, uint32_t conn_id)
-{
- struct bsc_conn *conn;
-
- conn = talloc_zero(ctx, struct bsc_conn);
- OSMO_ASSERT(conn);
-
- conn->conn_id = conn_id;
-
- llist_add_tail(&conn->list, &active_connections);
-}
-
-/* Delete info of a closed connection from the active connection list */
-void a_delete_bsc_con(uint32_t conn_id)
-{
- struct bsc_conn *conn;
- struct bsc_conn *conn_temp;
-
- LOGP(DMSC, LOGL_DEBUG,
- "Removing connection from active sccp-connection list (conn_id=%i)\n",
- conn_id);
-
- llist_for_each_entry_safe(conn, conn_temp, &active_connections, list) {
- if (conn->conn_id == conn_id) {
- llist_del(&conn->list);
- talloc_free(conn);
- }
- }
-}
-
-/* Check if a specified connection id has an active SCCP connection */
-static bool check_connection_active(uint32_t conn_id)
-{
- struct bsc_conn *conn;
-
- /* Find the address for the current connection id */
- llist_for_each_entry(conn, &active_connections, list) {
- if (conn->conn_id == conn_id) {
- return true;
- }
- }
-
- return false;
-}
-
-/* Get the reset context for a specifiec calling (BSC) address */
-static struct a_reset_ctx *get_reset_ctx_by_sccp_addr(const struct osmo_sccp_addr *addr)
-{
- struct bsc_context *bsc_ctx;
-
- if (!addr)
- return NULL;
-
- llist_for_each_entry(bsc_ctx, &gsm_network->a.bscs, list) {
- if (memcmp(&bsc_ctx->bsc_addr, addr, sizeof(*addr)) == 0)
- return bsc_ctx->reset;
- }
-
- LOGP(DMSC, LOGL_ERROR, "The calling BSC (%s) is unknown to this MSC ...\n",
- osmo_sccp_addr_dump(addr));
- return NULL;
-}
-
-/* Send DTAP message via A-interface */
-int a_iface_tx_dtap(struct msgb *msg)
-{
- struct gsm_subscriber_connection *conn;
- struct msgb *msg_resp;
-
- /* FIXME: Set this to some meaninful value! */
- uint8_t link_id = 0x00;
- OSMO_ASSERT(msg);
- conn = (struct gsm_subscriber_connection *)msg->dst;
- OSMO_ASSERT(conn);
- OSMO_ASSERT(conn->a.scu);
-
- LOGP(DMSC, LOGL_DEBUG, "Passing DTAP message from MSC to BSC (conn_id=%i)\n", conn->a.conn_id);
-
- msg->l3h = msg->data;
- msg_resp = gsm0808_create_dtap(msg, link_id);
- if (!msg_resp) {
- LOGP(DMSC, LOGL_ERROR, "Unable to generate BSSMAP DTAP message!\n");
- return -EINVAL;
- } else
- LOGP(DMSC, LOGL_DEBUG, "Massage will be sent as BSSMAP DTAP message!\n");
-
- LOGP(DMSC, LOGL_DEBUG, "N-DATA.req(%u, %s)\n", conn->a.conn_id, osmo_hexdump(msg_resp->data, msg_resp->len));
- return osmo_sccp_tx_data_msg(conn->a.scu, conn->a.conn_id, msg_resp);
-}
-
-/* Send Cipher mode command via A-interface */
-int a_iface_tx_cipher_mode(const struct gsm_subscriber_connection *conn,
- int cipher, const const uint8_t *key, int len, int include_imeisv)
-{
- /* TODO generalize for A- and Iu interfaces, don't name after 08.08 */
- struct msgb *msg_resp;
- struct gsm0808_encrypt_info ei;
-
- OSMO_ASSERT(conn);
-
- LOGP(DMSC, LOGL_DEBUG, "Passing Cipher mode command message from MSC to BSC (conn_id=%i)\n", conn->a.conn_id);
- uint8_t crm = 0x01;
- uint8_t *crm_ptr = NULL;
-
- /* Setup encryption information */
- if (len > ENCRY_INFO_KEY_MAXLEN || !key) {
- LOGP(DMSC, LOGL_ERROR,
- "Cipher mode command message could not be generated due to invalid key! (conn_id=%i)\n",
- conn->a.conn_id);
- return -EINVAL;
- } else {
- memcpy(&ei.key, key, len);
- ei.key_len = len;
- }
-
- if (include_imeisv)
- crm_ptr = &crm;
-
- ei.perm_algo[0] = (uint8_t) (1 << cipher);
- ei.perm_algo_len = 1;
-
- msg_resp = gsm0808_create_cipher(&ei, crm_ptr);
- LOGP(DMSC, LOGL_DEBUG, "N-DATA.req(%u, %s)\n", conn->a.conn_id, osmo_hexdump(msg_resp->data, msg_resp->len));
-
- return osmo_sccp_tx_data_msg(conn->a.scu, conn->a.conn_id, msg_resp);
-}
-
-/* Page a subscriber via A-interface */
-int a_iface_tx_paging(const char *imsi, uint32_t tmsi, uint16_t lac)
-{
- struct bsc_context *bsc_ctx;
- struct gsm0808_cell_id_list cil;
- struct msgb *msg;
- int page_count = 0;
-
- OSMO_ASSERT(imsi);
-
- cil.id_discr = CELL_IDENT_LAC;
- cil.id_list_lac[0] = lac;
- cil.id_list_len = 1;
-
- /* Deliver paging request to all known BSCs */
- llist_for_each_entry(bsc_ctx, &gsm_network->a.bscs, list) {
- if (a_reset_conn_ready(bsc_ctx->reset)) {
- LOGP(DMSC, LOGL_DEBUG,
- "Passing paging message from MSC %s to BSC %s (imsi=%s, tmsi=0x%08x, lac=%u)\n",
- osmo_sccp_addr_dump(&bsc_ctx->msc_addr),
- osmo_sccp_addr_dump(&bsc_ctx->bsc_addr), imsi, tmsi, lac);
- msg = gsm0808_create_paging(imsi, &tmsi, &cil, NULL);
- osmo_sccp_tx_unitdata_msg(bsc_ctx->sccp_user,
- &bsc_ctx->msc_addr, &bsc_ctx->bsc_addr, msg);
- page_count++;
- } else {
- LOGP(DMSC, LOGL_DEBUG,
- "Connection down, dropping paging from MSC %s to BSC %s (imsi=%s, tmsi=0x%08x, lac=%u)\n",
- osmo_sccp_addr_dump(&bsc_ctx->msc_addr),
- osmo_sccp_addr_dump(&bsc_ctx->bsc_addr), imsi, tmsi, lac);
- }
- }
-
- if (page_count <= 0)
- LOGP(DMSC, LOGL_ERROR, "Could not deliver paging because none of the associated BSCs is available!\n");
-
- return page_count;
-}
-
-/* Convert speech version field */
-static uint8_t convert_Abis_sv_to_A_sv(int speech_ver)
-{
- /* The speech versions that are transmitted in the Bearer capability
- * information element, that is transmitted on the Abis interfece
- * use a different encoding than the permitted speech version
- * identifier, that is signalled in the channel type element on the A
- * interface. (See also 3GPP TS 48.008, 3.2.2.1 and 3GPP TS 24.008,
- * 10.5.103 */
-
- switch (speech_ver) {
- case GSM48_BCAP_SV_FR:
- return GSM0808_PERM_FR1;
- break;
- case GSM48_BCAP_SV_HR:
- return GSM0808_PERM_HR1;
- break;
- case GSM48_BCAP_SV_EFR:
- return GSM0808_PERM_FR2;
- break;
- case GSM48_BCAP_SV_AMR_F:
- return GSM0808_PERM_FR3;
- break;
- case GSM48_BCAP_SV_AMR_H:
- return GSM0808_PERM_HR3;
- break;
- case GSM48_BCAP_SV_AMR_OFW:
- return GSM0808_PERM_FR4;
- break;
- case GSM48_BCAP_SV_AMR_OHW:
- return GSM0808_PERM_HR4;
- break;
- case GSM48_BCAP_SV_AMR_FW:
- return GSM0808_PERM_FR5;
- break;
- case GSM48_BCAP_SV_AMR_OH:
- return GSM0808_PERM_HR6;
- break;
- }
-
- /* If nothing matches, tag the result as invalid */
- LOGP(DMSC, LOGL_ERROR, "Invalid permitted speech version / rate detected, discarding.\n");
- return 0xFF;
-}
-
-/* Convert speech preference field */
-static uint8_t convert_Abis_prev_to_A_pref(int radio)
-{
- /* The Radio channel requirement field that is transmitted in the
- * Bearer capability information element, that is transmitted on the
- * Abis interfece uses a different encoding than the Channel rate and
- * type field that is signalled in the channel type element on the A
- * interface. (See also 3GPP TS 48.008, 3.2.2.1 and 3GPP TS 24.008,
- * 10.5.102 */
-
- switch (radio) {
- case GSM48_BCAP_RRQ_FR_ONLY:
- return GSM0808_SPEECH_FULL_BM;
- case GSM48_BCAP_RRQ_DUAL_FR:
- return GSM0808_SPEECH_FULL_PREF;
- case GSM48_BCAP_RRQ_DUAL_HR:
- return GSM0808_SPEECH_HALF_PREF;
- }
-
- LOGP(DMSC, LOGL_ERROR, "Invalid speech version / rate combination preference, defaulting to full rate.\n");
- return GSM0808_SPEECH_FULL_BM;
-}
-
-/* Assemble the channel type field */
-static int enc_channel_type(struct gsm0808_channel_type *ct, const struct gsm_mncc_bearer_cap *bc)
-{
- unsigned int i;
- uint8_t sv;
- unsigned int count = 0;
- bool only_gsm_hr = true;
-
- OSMO_ASSERT(ct);
- OSMO_ASSERT(bc);
-
- ct->ch_indctr = GSM0808_CHAN_SPEECH;
-
- for (i = 0; i < ARRAY_SIZE(bc->speech_ver); i++) {
- if (bc->speech_ver[i] == -1)
- break;
- sv = convert_Abis_sv_to_A_sv(bc->speech_ver[i]);
- if (sv != 0xFF) {
- /* Detect if something else than
- * GSM HR V1 is supported */
- if (sv == GSM0808_PERM_HR2 ||
- sv == GSM0808_PERM_HR3 || sv == GSM0808_PERM_HR4 || sv == GSM0808_PERM_HR6)
- only_gsm_hr = false;
-
- ct->perm_spch[count] = sv;
- count++;
- }
- }
- ct->perm_spch_len = count;
-
- if (only_gsm_hr)
- /* Note: We must avoid the usage of GSM HR1 as this
- * codec only offers very poor audio quality. If the
- * MS only supports GSM HR1 (and full rate), and has
- * a preference for half rate. Then we will ignore the
- * preference and assume a preference for full rate. */
- ct->ch_rate_type = GSM0808_SPEECH_FULL_BM;
- else
- ct->ch_rate_type = convert_Abis_prev_to_A_pref(bc->radio);
-
- if (count)
- return 0;
- else
- return -EINVAL;
-}
-
-/* Assemble the speech codec field */
-static int enc_speech_codec_list(struct gsm0808_speech_codec_list *scl, const struct gsm0808_channel_type *ct)
-{
- unsigned int i;
- int rc;
-
- memset(scl, 0, sizeof(*scl));
- for (i = 0; i < ct->perm_spch_len; i++) {
- rc = gsm0808_speech_codec_from_chan_type(&scl->codec[i], ct->perm_spch[i]);
- if (rc != 0)
- return -EINVAL;
- }
- scl->len = i;
-
- return 0;
-}
-
-/* Send assignment request via A-interface */
-int a_iface_tx_assignment(const struct gsm_trans *trans)
-{
- struct gsm_subscriber_connection *conn;
- struct gsm0808_channel_type ct;
- struct gsm0808_speech_codec_list scl;
- uint32_t *ci_ptr = NULL;
- struct msgb *msg;
- struct sockaddr_storage rtp_addr;
- struct sockaddr_in rtp_addr_in;
- int rc;
-
- OSMO_ASSERT(trans);
- conn = trans->conn;
- OSMO_ASSERT(conn);
-
- LOGP(DMSC, LOGL_ERROR, "Sending assignment command to BSC (conn_id %u)\n", conn->a.conn_id);
-
- /* Channel type */
- rc = enc_channel_type(&ct, &trans->bearer_cap);
- if (rc < 0) {
- LOGP(DMSC, LOGL_ERROR, "Faild to generate channel type -- assignment not sent!\n");
- return -EINVAL;
- }
-
- /* Speech codec list */
- rc = enc_speech_codec_list(&scl, &ct);
- if (rc < 0) {
- LOGP(DMSC, LOGL_ERROR, "Faild to generate Speech codec list -- assignment not sent!\n");
- return -EINVAL;
- }
-
- /* Package RTP-Address data */
- memset(&rtp_addr_in, 0, sizeof(rtp_addr_in));
- rtp_addr_in.sin_family = AF_INET;
- rtp_addr_in.sin_port = osmo_htons(conn->rtp.port_subscr);
- rtp_addr_in.sin_addr.s_addr = osmo_htonl(mgcpgw_client_remote_addr_n(gsm_network->mgcpgw.client));
-
- memset(&rtp_addr, 0, sizeof(rtp_addr));
- memcpy(&rtp_addr, &rtp_addr_in, sizeof(rtp_addr_in));
-
- msg = gsm0808_create_ass(&ct, NULL, &rtp_addr, &scl, ci_ptr);
-
- LOGP(DMSC, LOGL_DEBUG, "N-DATA.req(%u, %s)\n", conn->a.conn_id, osmo_hexdump(msg->data, msg->len));
- return osmo_sccp_tx_data_msg(conn->a.scu, conn->a.conn_id, msg);
-}
-
-/* Send clear command via A-interface */
-int a_iface_tx_clear_cmd(struct gsm_subscriber_connection *conn)
-{
- struct msgb *msg;
-
- LOGP(DMSC, LOGL_NOTICE, "Sending clear command to BSC (conn_id=%u)\n", conn->a.conn_id);
-
- msg = gsm0808_create_clear_command(GSM0808_CAUSE_CALL_CONTROL);
- return osmo_sccp_tx_data_msg(conn->a.scu, conn->a.conn_id, msg);
-}
-
-/* Callback function: Close all open connections */
-static void a_reset_cb(const void *priv)
-{
- struct msgb *msg;
- struct bsc_context *bsc_ctx = (struct bsc_context*) priv;
-
- /* Skip if the A interface is not properly initalized yet */
- if (!gsm_network)
- return;
-
- /* Clear all now orphaned subscriber connections */
- a_clear_all(bsc_ctx->sccp_user, &bsc_ctx->bsc_addr);
-
- /* Send reset to the remote BSC */
- LOGP(DMSC, LOGL_NOTICE, "Sending RESET to BSC %s\n", osmo_sccp_addr_dump(&bsc_ctx->bsc_addr));
- msg = gsm0808_create_reset();
- osmo_sccp_tx_unitdata_msg(bsc_ctx->sccp_user, &bsc_ctx->msc_addr,
- &bsc_ctx->bsc_addr, msg);
-}
-
-/* Add a new BSC connection to our internal list with known BSCs */
-static void add_bsc(const struct osmo_sccp_addr *msc_addr, const struct osmo_sccp_addr *bsc_addr,
- struct osmo_sccp_user *scu)
-{
- struct bsc_context *bsc_ctx;
-
- OSMO_ASSERT(bsc_addr);
- OSMO_ASSERT(msc_addr);
- OSMO_ASSERT(scu);
-
- /* Check if we already know this BSC, if yes, skip adding it. */
- if (get_reset_ctx_by_sccp_addr(bsc_addr))
- return;
-
- LOGP(DMSC, LOGL_NOTICE, "Adding new BSC connection for BSC %s...\n", osmo_sccp_addr_dump(bsc_addr));
-
- /* Generate and fill up a new bsc context */
- bsc_ctx = talloc_zero(gsm_network, struct bsc_context);
- OSMO_ASSERT(bsc_ctx);
- memcpy(&bsc_ctx->bsc_addr, bsc_addr, sizeof(*bsc_addr));
- memcpy(&bsc_ctx->msc_addr, msc_addr, sizeof(*msc_addr));
- bsc_ctx->sccp_user = scu;
- llist_add_tail(&bsc_ctx->list, &gsm_network->a.bscs);
-
- /* Start reset procedure to make the new connection active */
- bsc_ctx->reset = a_reset_alloc(bsc_ctx, osmo_sccp_addr_dump(bsc_addr), a_reset_cb, bsc_ctx);
-}
-
-/* Callback function, called by the SSCP stack when data arrives */
-static int sccp_sap_up(struct osmo_prim_hdr *oph, void *_scu)
-{
- struct osmo_sccp_user *scu = _scu;
- struct osmo_scu_prim *scu_prim = (struct osmo_scu_prim *)oph;
- int rc = 0;
- struct a_conn_info a_conn_info;
- memset(&a_conn_info, 0, sizeof(a_conn_info));
- a_conn_info.network = gsm_network;
- a_conn_info.reset = NULL;
-
- switch (OSMO_PRIM_HDR(&scu_prim->oph)) {
- case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_INDICATION):
- /* Handle inbound connection indication */
- add_bsc(&scu_prim->u.connect.called_addr, &scu_prim->u.connect.calling_addr, scu);
- a_conn_info.conn_id = scu_prim->u.connect.conn_id;
- a_conn_info.msc_addr = &scu_prim->u.connect.called_addr;
- a_conn_info.bsc_addr = &scu_prim->u.connect.calling_addr;
- a_conn_info.reset = get_reset_ctx_by_sccp_addr(&scu_prim->u.unitdata.calling_addr);
-
- if (a_reset_conn_ready(a_conn_info.reset) == false) {
- rc = osmo_sccp_tx_disconn(scu, a_conn_info.conn_id, a_conn_info.msc_addr,
- SCCP_RETURN_CAUSE_UNQUALIFIED);
- break;
- }
-
- osmo_sccp_tx_conn_resp(scu, scu_prim->u.connect.conn_id, &scu_prim->u.connect.called_addr, NULL, 0);
- if (msgb_l2len(oph->msg) > 0) {
- LOGP(DMSC, LOGL_DEBUG, "N-CONNECT.ind(%u, %s)\n",
- scu_prim->u.connect.conn_id, osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg)));
- rc = sccp_rx_dt(scu, &a_conn_info, oph->msg);
- } else
- LOGP(DMSC, LOGL_DEBUG, "N-CONNECT.ind(%u)\n", scu_prim->u.connect.conn_id);
-
- record_bsc_con(scu, scu_prim->u.connect.conn_id);
- break;
-
- case OSMO_PRIM(OSMO_SCU_PRIM_N_DATA, PRIM_OP_INDICATION):
- /* Handle incoming connection oriented data */
- a_conn_info.conn_id = scu_prim->u.data.conn_id;
- LOGP(DMSC, LOGL_DEBUG, "N-DATA.ind(%u, %s)\n",
- scu_prim->u.data.conn_id, osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg)));
- sccp_rx_dt(scu, &a_conn_info, oph->msg);
- break;
-
- case OSMO_PRIM(OSMO_SCU_PRIM_N_UNITDATA, PRIM_OP_INDICATION):
- /* Handle inbound UNITDATA */
- add_bsc(&scu_prim->u.unitdata.called_addr, &scu_prim->u.unitdata.calling_addr, scu);
- a_conn_info.msc_addr = &scu_prim->u.unitdata.called_addr;
- a_conn_info.bsc_addr = &scu_prim->u.unitdata.calling_addr;
- a_conn_info.reset = get_reset_ctx_by_sccp_addr(&scu_prim->u.unitdata.calling_addr);
- DEBUGP(DMSC, "N-UNITDATA.ind(%s)\n", osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg)));
- sccp_rx_udt(scu, &a_conn_info, oph->msg);
- break;
-
- default:
- LOGP(DMSC, LOGL_ERROR, "Unhandled SIGTRAN primitive: %u:%u\n", oph->primitive, oph->operation);
- break;
- }
-
- return rc;
-}
-
-/* Clear all subscriber connections on a specified BSC */
-void a_clear_all(struct osmo_sccp_user *scu, const struct osmo_sccp_addr *bsc_addr)
-{
- struct gsm_subscriber_connection *conn;
- struct gsm_subscriber_connection *conn_temp;
- struct gsm_network *network = gsm_network;
-
- OSMO_ASSERT(scu);
- OSMO_ASSERT(bsc_addr);
-
- llist_for_each_entry_safe(conn, conn_temp, &network->subscr_conns, entry) {
- /* Clear only A connections and connections that actually
- * belong to the specified BSC */
- if (conn->via_ran == RAN_GERAN_A && memcmp(bsc_addr, &conn->a.bsc_addr, sizeof(conn->a.bsc_addr)) == 0) {
- LOGP(DMSC, LOGL_NOTICE, "Dropping orphaned subscriber connection (conn_id %i)\n",
- conn->a.conn_id);
- msc_clear_request(conn, GSM48_CC_CAUSE_SWITCH_CONG);
-
- /* If there is still an SCCP connection active, remove it now */
- if (check_connection_active(conn->a.conn_id)) {
- osmo_sccp_tx_disconn(scu, conn->a.conn_id, bsc_addr,
- SCCP_RELEASE_CAUSE_END_USER_ORIGINATED);
- a_delete_bsc_con(conn->a.conn_id);
- }
- }
- }
-}
-
-/* Initalize A interface connection between to MSC and BSC */
-int a_init(struct osmo_sccp_instance *sccp, struct gsm_network *network)
-{
- OSMO_ASSERT(sccp);
- OSMO_ASSERT(network);
-
- /* FIXME: Remove hardcoded parameters, use parameters in parameter list */
- LOGP(DMSC, LOGL_NOTICE, "Initalizing SCCP connection to stp...\n");
-
- /* Set GSM network variable, there can only be
- * one network by design */
- if (gsm_network != NULL) {
- OSMO_ASSERT(gsm_network == network);
- } else
- gsm_network = network;
-
- /* SCCP Protocol stack */
- osmo_sccp_user_bind(sccp, "OsmoMSC-A", sccp_sap_up, SCCP_SSN_BSSAP);
-
- return 0;
-}
diff --git a/src/libmsc/a_iface_bssap.c b/src/libmsc/a_iface_bssap.c
deleted file mode 100644
index 4ed5f944f..000000000
--- a/src/libmsc/a_iface_bssap.c
+++ /dev/null
@@ -1,703 +0,0 @@
-/* (C) 2017 by Sysmocom s.f.m.c. GmbH
- * All Rights Reserved
- *
- * Author: Philipp Maier
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <osmocom/core/utils.h>
-#include <osmocom/core/msgb.h>
-#include <osmocom/core/logging.h>
-#include <osmocom/sigtran/sccp_helpers.h>
-#include <osmocom/sccp/sccp_types.h>
-#include <osmocom/gsm/gsm0808.h>
-#include <osmocom/gsm/gsm0808_utils.h>
-#include <openbsc/debug.h>
-#include <openbsc/gsm_data.h>
-#include <openbsc/a_iface_bssap.h>
-#include <openbsc/a_iface.h>
-#include <openbsc/osmo_msc.h>
-#include <osmocom/core/byteswap.h>
-#include <openbsc/a_reset.h>
-
-#define IP_V4_ADDR_LEN 4
-
-/*
- * Helper functions to lookup and allocate subscribers
- */
-
-/* Allocate a new subscriber connection */
-static struct gsm_subscriber_connection *subscr_conn_allocate_a(const struct a_conn_info *a_conn_info,
- struct gsm_network *network,
- uint16_t lac, struct osmo_sccp_user *scu, int conn_id)
-{
- struct gsm_subscriber_connection *conn;
-
- LOGP(DMSC, LOGL_NOTICE, "Allocating A-Interface subscriber conn: lac %i, conn_id %i\n", lac, conn_id);
-
- conn = talloc_zero(network, struct gsm_subscriber_connection);
- if (!conn)
- return NULL;
-
- conn->network = network;
- conn->via_ran = RAN_GERAN_A;
- conn->lac = lac;
-
- conn->a.conn_id = conn_id;
- conn->a.scu = scu;
-
- /* Also backup the calling address of the BSC, this allows us to
- * identify later which BSC is responsible for this subscriber connection */
- memcpy(&conn->a.bsc_addr, a_conn_info->bsc_addr, sizeof(conn->a.bsc_addr));
-
- llist_add_tail(&conn->entry, &network->subscr_conns);
- LOGP(DMSC, LOGL_NOTICE, "A-Interface subscriber connection successfully allocated!\n");
- return conn;
-}
-
-/* Return an existing A subscriber connection record for the given
- * connection IDs, or return NULL if not found. */
-static struct gsm_subscriber_connection *subscr_conn_lookup_a(const struct gsm_network *network, int conn_id)
-{
- struct gsm_subscriber_connection *conn;
-
- OSMO_ASSERT(network);
-
- DEBUGP(DMSC, "Looking for A subscriber: conn_id %i\n", conn_id);
-
- /* FIXME: log_subscribers() is defined in iucs.c as static inline, if
- * maybe this function should be public to reach it from here? */
- /* log_subscribers(network); */
-
- llist_for_each_entry(conn, &network->subscr_conns, entry) {
- if (conn->via_ran == RAN_GERAN_A && conn->a.conn_id == conn_id) {
- DEBUGP(DIUCS, "Found A subscriber for conn_id %i\n", conn_id);
- return conn;
- }
- }
- DEBUGP(DMSC, "No A subscriber found for conn_id %i\n", conn_id);
- return NULL;
-}
-
-/*
- * BSSMAP handling for UNITDATA
- */
-
-/* Endpoint to handle BSSMAP reset */
-static void bssmap_rx_reset(struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg)
-{
- LOGP(DMSC, LOGL_NOTICE, "Rx RESET from BSC %s, sending RESET ACK\n", osmo_sccp_addr_dump(a_conn_info->bsc_addr));
- osmo_sccp_tx_unitdata_msg(scu, a_conn_info->msc_addr, a_conn_info->bsc_addr, gsm0808_create_reset_ack());
-
- /* Make sure all orphand subscriber connections will be cleard */
- a_clear_all(scu, a_conn_info->bsc_addr);
-
- msgb_free(msg);
-}
-
-/* Endpoint to handle BSSMAP reset acknowlegement */
-static void bssmap_rx_reset_ack(const struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info,
- struct msgb *msg)
-{
- if (a_conn_info->reset == NULL) {
- LOGP(DMSC, LOGL_ERROR, "Received RESET ACK from an unknown BSC %s, ignoring...\n",
- osmo_sccp_addr_dump(a_conn_info->bsc_addr));
- goto fail;
- }
-
- LOGP(DMSC, LOGL_NOTICE, "Received RESET ACK from BSC %s\n",
- osmo_sccp_addr_dump(a_conn_info->bsc_addr));
-
- /* Confirm that we managed to get the reset ack message
- * towards the connection reset logic */
- a_reset_ack_confirm(a_conn_info->reset);
-
-fail:
- msgb_free(msg);
-}
-
-/* Handle UNITDATA BSSMAP messages */
-static void bssmap_rcvmsg_udt(struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg)
-{
- /* Note: When in the MSC role, RESET ACK is the only valid message that
- * can be received via UNITDATA */
-
- if (msgb_l3len(msg) < 1) {
- LOGP(DMSC, LOGL_NOTICE, "Error: No data received -- discarding message!\n");
- return;
- }
-
- LOGP(DMSC, LOGL_NOTICE, "Rx BSC UDT BSSMAP %s\n", gsm0808_bssmap_name(msg->l3h[0]));
-
- switch (msg->l3h[0]) {
- case BSS_MAP_MSG_RESET:
- bssmap_rx_reset(scu, a_conn_info, msg);
- break;
- case BSS_MAP_MSG_RESET_ACKNOWLEDGE:
- bssmap_rx_reset_ack(scu, a_conn_info, msg);
- break;
- default:
- LOGP(DMSC, LOGL_NOTICE, "Unimplemented message format: %s -- message discarded!\n",
- gsm0808_bssmap_name(msg->l3h[0]));
- msgb_free(msg);
- }
-}
-
-/* Receive incoming connection less data messages via sccp */
-void sccp_rx_udt(struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg)
-{
- /* Note: The only valid message type that can be received
- * via UNITDATA are BSS Management messages */
- struct bssmap_header *bs;
-
- OSMO_ASSERT(scu);
- OSMO_ASSERT(a_conn_info);
- OSMO_ASSERT(msg);
-
- LOGP(DMSC, LOGL_NOTICE, "Rx BSC UDT: %s\n", osmo_hexdump(msgb_l2(msg), msgb_l2len(msg)));
-
- if (msgb_l2len(msg) < sizeof(*bs)) {
- LOGP(DMSC, LOGL_ERROR, "Error: Header is too short -- discarding message!\n");
- msgb_free(msg);
- return;
- }
-
- bs = (struct bssmap_header *)msgb_l2(msg);
- if (bs->length < msgb_l2len(msg) - sizeof(*bs)) {
- LOGP(DMSC, LOGL_ERROR, "Error: Message is too short -- discarding message!\n");
- msgb_free(msg);
- return;
- }
-
- switch (bs->type) {
- case BSSAP_MSG_BSS_MANAGEMENT:
- msg->l3h = &msg->l2h[sizeof(struct bssmap_header)];
- bssmap_rcvmsg_udt(scu, a_conn_info, msg);
- break;
- default:
- LOGP(DMSC, LOGL_ERROR,
- "Error: Unimplemented message type: %s -- message discarded!\n", gsm0808_bssmap_name(bs->type));
- msgb_free(msg);
- }
-}
-
-/*
- * BSSMAP handling for connection oriented data
- */
-
-/* Endpoint to handle BSSMAP clear request */
-static int bssmap_rx_clear_rqst(struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg)
-{
- struct gsm_network *network = a_conn_info->network;
- struct tlv_parsed tp;
- int rc;
- struct msgb *msg_resp;
- uint8_t cause;
- struct gsm_subscriber_connection *conn;
-
- LOGP(DMSC, LOGL_NOTICE, "BSC requested to clear connection (conn_id=%i)\n", a_conn_info->conn_id);
-
- tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 1, msgb_l3len(msg) - 1, 0, 0);
- if (!TLVP_PRESENT(&tp, GSM0808_IE_CAUSE)) {
- LOGP(DMSC, LOGL_ERROR, "Cause code is missing -- discarding message!\n");
- goto fail;
- }
- cause = TLVP_VAL(&tp, GSM0808_IE_CAUSE)[0];
-
- /* Respond with clear command */
- msg_resp = gsm0808_create_clear_command(GSM0808_CAUSE_CALL_CONTROL);
- rc = osmo_sccp_tx_data_msg(scu, a_conn_info->conn_id, msg_resp);
-
- /* If possible, inform the MSC about the clear request */
- conn = subscr_conn_lookup_a(network, a_conn_info->conn_id);
- if (!conn)
- goto fail;
- msc_clear_request(conn, cause);
-
- msgb_free(msg);
- return rc;
-
-fail:
- msgb_free(msg);
- return -EINVAL;
-}
-
-/* Endpoint to handle BSSMAP clear complete */
-static int bssmap_rx_clear_complete(struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg)
-{
- int rc;
-
- LOGP(DMSC, LOGL_NOTICE, "Releasing connection (conn_id=%i)\n", a_conn_info->conn_id);
- rc = osmo_sccp_tx_disconn(scu, a_conn_info->conn_id,
- a_conn_info->msc_addr, SCCP_RELEASE_CAUSE_END_USER_ORIGINATED);
-
- /* Remove the record from the list with active connections. */
- a_delete_bsc_con(a_conn_info->conn_id);
-
- msgb_free(msg);
- return rc;
-}
-
-/* Endpoint to handle layer 3 complete messages */
-static int bssmap_rx_l3_compl(struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg)
-{
- struct tlv_parsed tp;
- struct {
- uint8_t ident;
- struct gsm48_loc_area_id lai;
- uint16_t ci;
- } __attribute__ ((packed)) lai_ci;
- uint16_t mcc;
- uint16_t mnc;
- uint16_t lac;
- uint8_t data_length;
- const uint8_t *data;
- int rc;
-
- struct gsm_network *network = a_conn_info->network;
- struct gsm_subscriber_connection *conn;
-
- LOGP(DMSC, LOGL_NOTICE, "BSC has completed layer 3 connection (conn_id=%i)\n", a_conn_info->conn_id);
-
- tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 1, msgb_l3len(msg) - 1, 0, 0);
- if (!TLVP_PRESENT(&tp, GSM0808_IE_CELL_IDENTIFIER)) {
- LOGP(DMSC, LOGL_ERROR, "Mandatory CELL IDENTIFIER not present -- discarding message!\n");
- goto fail;
- }
- if (!TLVP_PRESENT(&tp, GSM0808_IE_LAYER_3_INFORMATION)) {
- LOGP(DMSC, LOGL_ERROR, "Mandatory LAYER 3 INFORMATION not present -- discarding message!\n");
- goto fail;
- }
-
- /* Parse Cell ID element */
- /* FIXME: Encapsulate this in a parser/generator function inside
- * libosmocore, add support for all specified cell identification
- * discriminators (see 3GPP ts 3.2.2.17 Cell Identifier) */
- data_length = TLVP_LEN(&tp, GSM0808_IE_CELL_IDENTIFIER);
- data = TLVP_VAL(&tp, GSM0808_IE_CELL_IDENTIFIER);
- if (sizeof(lai_ci) != data_length) {
- LOGP(DMSC, LOGL_ERROR,
- "Unable to parse element CELL IDENTIFIER (wrong field length) -- discarding message!\n");
- goto fail;
- }
- memcpy(&lai_ci, data, sizeof(lai_ci));
- if (lai_ci.ident != CELL_IDENT_WHOLE_GLOBAL) {
- LOGP(DMSC, LOGL_ERROR,
- "Unable to parse element CELL IDENTIFIER (wrong cell identification discriminator) -- discarding message!\n");
- goto fail;
- }
- if (gsm48_decode_lai(&lai_ci.lai, &mcc, &mnc, &lac) != 0) {
- LOGP(DMSC, LOGL_ERROR,
- "Unable to parse element CELL IDENTIFIER (lai decoding failed) -- discarding message!\n");
- goto fail;
- }
-
- /* Parse Layer 3 Information element */
- /* FIXME: This is probably to hackish, compiler also complains "assignment discards ‘const’ qualifier..." */
- msg->l3h = TLVP_VAL(&tp, GSM0808_IE_LAYER_3_INFORMATION);
- msg->tail = msg->l3h + TLVP_LEN(&tp, GSM0808_IE_LAYER_3_INFORMATION);
-
- /* Create new subscriber context */
- conn = subscr_conn_allocate_a(a_conn_info, network, lac, scu, a_conn_info->conn_id);
-
- /* Handover location update to the MSC code */
- /* msc_compl_l3() takes ownership of dtap_msg
- * message buffer */
- rc = msc_compl_l3(conn, msg, 0);
- if (rc == MSC_CONN_ACCEPT) {
- LOGP(DMSC, LOGL_NOTICE, "User has been accepted by MSC.\n");
- return 0;
- } else if (rc == MSC_CONN_REJECT)
- LOGP(DMSC, LOGL_NOTICE, "User has been rejected by MSC.\n");
- else
- LOGP(DMSC, LOGL_NOTICE, "User has been rejected by MSC (unknown error)\n");
-
- return -EINVAL;
-
-fail:
- msgb_free(msg);
- return -EINVAL;
-}
-
-/* Endpoint to handle BSSMAP classmark update */
-static int bssmap_rx_classmark_upd(struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg)
-{
- struct gsm_network *network = a_conn_info->network;
- struct gsm_subscriber_connection *conn;
- struct tlv_parsed tp;
- const uint8_t *cm2 = NULL;
- const uint8_t *cm3 = NULL;
- uint8_t cm2_len = 0;
- uint8_t cm3_len = 0;
-
- conn = subscr_conn_lookup_a(network, a_conn_info->conn_id);
- if (!conn)
- goto fail;
-
- LOGP(DMSC, LOGL_NOTICE, "BSC sends clasmark update (conn_id=%i)\n", conn->a.conn_id);
-
- tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 1, msgb_l3len(msg) - 1, 0, 0);
- if (!TLVP_PRESENT(&tp, GSM0808_IE_CLASSMARK_INFORMATION_T2)) {
- LOGP(DMSC, LOGL_ERROR, "Mandatory Classmark Information Type 2 not present -- discarding message!\n");
- goto fail;
- }
-
- cm2 = TLVP_VAL(&tp, GSM0808_IE_CLASSMARK_INFORMATION_T2);
- cm2_len = TLVP_LEN(&tp, GSM0808_IE_CLASSMARK_INFORMATION_T2);
-
- if (TLVP_PRESENT(&tp, GSM0808_IE_CLASSMARK_INFORMATION_T3)) {
- cm3 = TLVP_VAL(&tp, GSM0808_IE_CLASSMARK_INFORMATION_T3);
- cm3_len = TLVP_LEN(&tp, GSM0808_IE_CLASSMARK_INFORMATION_T3);
- }
-
- /* Inform MSC about the classmark change */
- msc_classmark_chg(conn, cm2, cm2_len, cm3, cm3_len);
-
- msgb_free(msg);
- return 0;
-
-fail:
- msgb_free(msg);
- return -EINVAL;
-}
-
-/* Endpoint to handle BSSMAP cipher mode complete */
-static int bssmap_rx_ciph_compl(const struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info,
- struct msgb *msg)
-{
- /* FIXME: The field GSM0808_IE_LAYER_3_MESSAGE_CONTENTS is optional by
- * means of the specification. So there can be messages without L3 info.
- * In this case, the code will crash becrause msc_cipher_mode_compl()
- * is not able to deal with msg = NULL and apperently
- * msc_cipher_mode_compl() was never meant to be used without L3 data.
- * This needs to be discussed further! */
-
- struct gsm_network *network = a_conn_info->network;
- struct gsm_subscriber_connection *conn;
- struct tlv_parsed tp;
- uint8_t alg_id = 1;
-
- conn = subscr_conn_lookup_a(network, a_conn_info->conn_id);
- if (!conn)
- goto fail;
-
- LOGP(DMSC, LOGL_NOTICE, "BSC sends cipher mode complete (conn_id=%i)\n", conn->a.conn_id);
-
- tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 1, msgb_l3len(msg) - 1, 0, 0);
-
- if (TLVP_PRESENT(&tp, GSM0808_IE_CHOSEN_ENCR_ALG)) {
- alg_id = TLVP_VAL(&tp, GSM0808_IE_CHOSEN_ENCR_ALG)[0] - 1;
- }
-
- if (TLVP_PRESENT(&tp, GSM0808_IE_LAYER_3_MESSAGE_CONTENTS)) {
- msg->l3h = TLVP_VAL(&tp, GSM0808_IE_LAYER_3_MESSAGE_CONTENTS);
- msg->tail = msg->l3h + TLVP_LEN(&tp, GSM0808_IE_LAYER_3_MESSAGE_CONTENTS);
- } else {
- msgb_free(msg);
- msg = NULL;
- }
-
- /* Hand over cipher mode complete message to the MSC,
- * msc_cipher_mode_compl() takes ownership for msg */
- msc_cipher_mode_compl(conn, msg, alg_id);
-
- return 0;
-fail:
- msgb_free(msg);
- return -EINVAL;
-}
-
-/* Endpoint to handle BSSMAP cipher mode reject */
-static int bssmap_rx_ciph_rej(const struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg)
-{
- struct gsm_network *network = a_conn_info->network;
- struct gsm_subscriber_connection *conn;
- struct tlv_parsed tp;
- uint8_t cause;
-
- conn = subscr_conn_lookup_a(network, a_conn_info->conn_id);
- if (!conn)
- goto fail;
-
- LOGP(DMSC, LOGL_NOTICE, "BSC sends cipher mode reject (conn_id=%i)\n", conn->a.conn_id);
-
- tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 1, msgb_l3len(msg) - 1, 0, 0);
- if (!TLVP_PRESENT(&tp, BSS_MAP_MSG_CIPHER_MODE_REJECT)) {
- LOGP(DMSC, LOGL_ERROR, "Cause code is missing -- discarding message!\n");
- goto fail;
- }
-
- cause = TLVP_VAL(&tp, BSS_MAP_MSG_CIPHER_MODE_REJECT)[0];
- LOGP(DMSC, LOGL_NOTICE, "Cipher mode rejection cause: %i\n", cause);
-
- /* FIXME: Can we do something meaningful here? e.g. report to the
- * msc code somehow that the cipher mode command has failed. */
-
- msgb_free(msg);
- return 0;
-fail:
- msgb_free(msg);
- return -EINVAL;
-}
-
-/* Endpoint to handle BSSMAP assignment failure */
-static int bssmap_rx_ass_fail(const struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg)
-{
- struct gsm_network *network = a_conn_info->network;
- struct gsm_subscriber_connection *conn;
- struct tlv_parsed tp;
- uint8_t cause;
- uint8_t *rr_cause_ptr = NULL;
- uint8_t rr_cause;
-
- conn = subscr_conn_lookup_a(network, a_conn_info->conn_id);
- if (!conn)
- goto fail;
-
- LOGP(DMSC, LOGL_NOTICE, "BSC sends assignment failure message (conn_id=%i)\n", conn->a.conn_id);
-
- tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 1, msgb_l3len(msg) - 1, 0, 0);
- if (!TLVP_PRESENT(&tp, GSM0808_IE_CAUSE)) {
- LOGP(DMSC, LOGL_ERROR, "Cause code is missing -- discarding message!\n");
- goto fail;
- }
- cause = TLVP_VAL(&tp, GSM0808_IE_CAUSE)[0];
-
- if (TLVP_PRESENT(&tp, GSM0808_IE_RR_CAUSE)) {
- rr_cause = TLVP_VAL(&tp, GSM0808_IE_RR_CAUSE)[0];
- rr_cause_ptr = &rr_cause;
- }
-
- /* FIXME: In AoIP, the Assignment failure will carry also an optional
- * Codec List (BSS Supported) element. It has to be discussed if we
- * can ignore this element. If not, The msc_assign_fail() function
- * call has to change. However msc_assign_fail() does nothing in the
- * end. So probably we can just leave it as it is. Even for AoIP */
-
- /* Inform the MSC about the assignment failure event */
- msc_assign_fail(conn, cause, rr_cause_ptr);
-
- msgb_free(msg);
- return 0;
-fail:
- msgb_free(msg);
- return -EINVAL;
-}
-
-/* Endpoint to handle sapi "n" reject */
-static int bssmap_rx_sapi_n_rej(const struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info,
- struct msgb *msg)
-{
- struct gsm_network *network = a_conn_info->network;
- struct gsm_subscriber_connection *conn;
- struct tlv_parsed tp;
- uint8_t dlci;
-
- conn = subscr_conn_lookup_a(network, a_conn_info->conn_id);
- if (!conn)
- goto fail;
-
- LOGP(DMSC, LOGL_NOTICE, "BSC sends sapi \"n\" reject message (conn_id=%i)\n", conn->a.conn_id);
-
- /* Note: The MSC code seems not to care about the cause code, but by
- * the specification it is mandatory, so we check its presence. See
- * also 3GPP TS 48.008 3.2.1.34 SAPI "n" REJECT */
- tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 1, msgb_l3len(msg) - 1, 0, 0);
- if (!TLVP_PRESENT(&tp, GSM0808_IE_CAUSE)) {
- LOGP(DMSC, LOGL_ERROR, "Cause code is missing -- discarding message!\n");
- goto fail;
- }
-
- tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 1, msgb_l3len(msg) - 1, 0, 0);
- if (!TLVP_PRESENT(&tp, GSM0808_IE_DLCI)) {
- LOGP(DMSC, LOGL_ERROR, "DLCI is missing -- discarding message!\n");
- goto fail;
- }
- dlci = TLVP_VAL(&tp, GSM0808_IE_DLCI)[0];
-
- /* Inform the MSC about the sapi "n" reject event */
- msc_sapi_n_reject(conn, dlci);
-
- msgb_free(msg);
- return 0;
-fail:
- msgb_free(msg);
- return -EINVAL;
-}
-
-/* Endpoint to handle assignment complete */
-static int bssmap_rx_ass_compl(const struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info,
- struct msgb *msg)
-{
- struct gsm_network *network = a_conn_info->network;
- struct gsm_subscriber_connection *conn;
- struct mgcpgw_client *mgcp;
- struct tlv_parsed tp;
- struct sockaddr_storage rtp_addr;
- struct sockaddr_in *rtp_addr_in;
- int rc;
-
- conn = subscr_conn_lookup_a(network, a_conn_info->conn_id);
- if (!conn)
- goto fail;
-
- mgcp = conn->network->mgcpgw.client;
- OSMO_ASSERT(mgcp);
-
- LOGP(DMSC, LOGL_NOTICE, "BSC sends assignment complete message (conn_id=%i)\n", conn->a.conn_id);
-
- tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 1, msgb_l3len(msg) - 1, 0, 0);
-
- if (!TLVP_PRESENT(&tp, GSM0808_IE_AOIP_TRASP_ADDR)) {
- LOGP(DMSC, LOGL_ERROR, "AoIP transport identifier missing -- discarding message!\n");
- goto fail;
- }
-
- /* Decode AoIP transport address element */
- rc = gsm0808_dec_aoip_trasp_addr(&rtp_addr, TLVP_VAL(&tp, GSM0808_IE_AOIP_TRASP_ADDR),
- TLVP_LEN(&tp, GSM0808_IE_AOIP_TRASP_ADDR));
- if (rc < 0) {
- LOGP(DMSC, LOGL_ERROR, "Unable to decode aoip transport address.\n");
- goto fail;
- }
-
- /* use address / port supplied with the AoIP
- * transport address element */
- if (rtp_addr.ss_family == AF_INET) {
- rtp_addr_in = (struct sockaddr_in *)&rtp_addr;
- conn->rtp.port_subscr = osmo_ntohs(rtp_addr_in->sin_port);
- /* FIXME: We also get the IP-Address of the remote (e.g. BTS)
- * end with the response. Currently we just ignore that address.
- * Instead we expect that our local MGCP gateway and the code
- * controlling it, magically knows the IP of the remote end. */
- } else {
- LOGP(DMSC, LOGL_ERROR, "Unsopported addressing scheme. (supports only IPV4)\n");
- goto fail;
- }
-
- /* FIXME: Seems to be related to authentication or,
- encryption. Is this really in the right place? */
- msc_rx_sec_mode_compl(conn);
-
- msgb_free(msg);
- return 0;
-fail:
- msgb_free(msg);
- return -EINVAL;
-}
-
-/* Handle incoming connection oriented BSSMAP messages */
-static int rx_bssmap(struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg)
-{
- if (msgb_l3len(msg) < 1) {
- LOGP(DMSC, LOGL_NOTICE, "Error: No data received -- discarding message!\n");
- msgb_free(msg);
- return -1;
- }
-
- LOGP(DMSC, LOGL_NOTICE, "Rx MSC DT1 BSSMAP %s\n", gsm0808_bssmap_name(msg->l3h[0]));
-
- switch (msg->l3h[0]) {
- case BSS_MAP_MSG_CLEAR_RQST:
- return bssmap_rx_clear_rqst(scu, a_conn_info, msg);
- break;
- case BSS_MAP_MSG_CLEAR_COMPLETE:
- return bssmap_rx_clear_complete(scu, a_conn_info, msg);
- break;
- case BSS_MAP_MSG_COMPLETE_LAYER_3:
- return bssmap_rx_l3_compl(scu, a_conn_info, msg);
- break;
- case BSS_MAP_MSG_CLASSMARK_UPDATE:
- return bssmap_rx_classmark_upd(scu, a_conn_info, msg);
- break;
- case BSS_MAP_MSG_CIPHER_MODE_COMPLETE:
- return bssmap_rx_ciph_compl(scu, a_conn_info, msg);
- break;
- case BSS_MAP_MSG_CIPHER_MODE_REJECT:
- return bssmap_rx_ciph_rej(scu, a_conn_info, msg);
- break;
- case BSS_MAP_MSG_ASSIGMENT_FAILURE:
- return bssmap_rx_ass_fail(scu, a_conn_info, msg);
- break;
- case BSS_MAP_MSG_SAPI_N_REJECT:
- return bssmap_rx_sapi_n_rej(scu, a_conn_info, msg);
- break;
- case BSS_MAP_MSG_ASSIGMENT_COMPLETE:
- return bssmap_rx_ass_compl(scu, a_conn_info, msg);
- break;
- default:
- LOGP(DMSC, LOGL_ERROR, "Unimplemented msg type: %s\n", gsm0808_bssmap_name(msg->l3h[0]));
- msgb_free(msg);
- return -EINVAL;
- }
-
- return -EINVAL;
-}
-
-/* Endpoint to handle regular BSSAP DTAP messages */
-static int rx_dtap(const struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg)
-{
- struct gsm_network *network = a_conn_info->network;
- struct gsm_subscriber_connection *conn;
-
- conn = subscr_conn_lookup_a(network, a_conn_info->conn_id);
- if (!conn) {
- msgb_free(msg);
- return -EINVAL;
- }
-
- LOGP(DMSC, LOGL_NOTICE, "BSC sends layer 3 dtap (conn_id=%i)\n", conn->a.conn_id);
-
- /* msc_dtap expects the dtap payload in l3h */
- msg->l3h = msg->l2h + 3;
-
- /* Forward dtap payload into the msc,
- * msc_dtap() takes ownership for msg */
- msc_dtap(conn, conn->a.conn_id, msg);
-
- return 0;
-}
-
-/* Handle incoming connection oriented messages */
-int sccp_rx_dt(struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg)
-{
- OSMO_ASSERT(scu);
- OSMO_ASSERT(a_conn_info);
- OSMO_ASSERT(msg);
-
- LOGP(DMSC, LOGL_NOTICE, "Rx BSC DT: %s\n", osmo_hexdump(msgb_l2(msg), msgb_l2len(msg)));
-
- if (msgb_l2len(msg) < sizeof(struct bssmap_header)) {
- LOGP(DMSC, LOGL_NOTICE, "The header is too short -- discarding message!\n");
- msgb_free(msg);
- }
-
- switch (msg->l2h[0]) {
- case BSSAP_MSG_BSS_MANAGEMENT:
- msg->l3h = &msg->l2h[sizeof(struct bssmap_header)];
- return rx_bssmap(scu, a_conn_info, msg);
- break;
- case BSSAP_MSG_DTAP:
- return rx_dtap(scu, a_conn_info, msg);
- break;
- default:
- LOGP(DMSC, LOGL_ERROR, "Unimplemented BSSAP msg type: %s\n", gsm0808_bssap_name(msg->l2h[0]));
- msgb_free(msg);
- return -EINVAL;
- }
-
- return -EINVAL;
-}
diff --git a/src/libmsc/auth.c b/src/libmsc/auth.c
deleted file mode 100644
index 9064ce6c4..000000000
--- a/src/libmsc/auth.c
+++ /dev/null
@@ -1,42 +0,0 @@
-/* Authentication related functions */
-
-/*
- * (C) 2010 by Sylvain Munaut <tnt@246tNt.com>
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <openbsc/db.h>
-#include <openbsc/debug.h>
-#include <openbsc/auth.h>
-#include <openbsc/gsm_data.h>
-
-#include <osmocom/gsm/comp128.h>
-#include <osmocom/core/utils.h>
-
-#include <openssl/rand.h>
-
-#include <stdlib.h>
-
-const struct value_string auth_action_names[] = {
- OSMO_VALUE_STRING(AUTH_ERROR),
- OSMO_VALUE_STRING(AUTH_NOT_AVAIL),
- OSMO_VALUE_STRING(AUTH_DO_AUTH_THEN_CIPH),
- OSMO_VALUE_STRING(AUTH_DO_CIPH),
- OSMO_VALUE_STRING(AUTH_DO_AUTH),
- { 0, NULL }
-};
diff --git a/src/libmsc/ctrl_commands.c b/src/libmsc/ctrl_commands.c
deleted file mode 100644
index 9d1f0d4fa..000000000
--- a/src/libmsc/ctrl_commands.c
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * (C) 2014 by Holger Hans Peter Freyther
- * (C) 2014 by sysmocom s.f.m.c. GmbH
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <osmocom/ctrl/control_cmd.h>
-#include <osmocom/core/utils.h>
-#include <openbsc/gsm_data.h>
-#include <openbsc/gsm_subscriber.h>
-#include <openbsc/db.h>
-#include <openbsc/debug.h>
-#include <openbsc/vlr.h>
-
-#include <stdbool.h>
-
-static struct gsm_network *msc_ctrl_net = NULL;
-
-static int verify_subscriber_modify(struct ctrl_cmd *cmd, const char *value, void *d)
-{
- return 0;
-}
-
-static int set_subscriber_modify(struct ctrl_cmd *cmd, void *data)
-{
- cmd->reply = "Command moved to osmo-hlr, no longer available here";
- return CTRL_CMD_ERROR;
-}
-
-CTRL_CMD_DEFINE_WO(subscriber_modify, "subscriber-modify-v1");
-
-static int set_subscriber_delete(struct ctrl_cmd *cmd, void *data)
-{
- cmd->reply = "Command moved to osmo-hlr, no longer available here";
- return CTRL_CMD_ERROR;
-}
-CTRL_CMD_DEFINE_WO_NOVRF(subscriber_delete, "subscriber-delete-v1");
-
-static int get_subscriber_list(struct ctrl_cmd *cmd, void *d)
-{
- struct vlr_subscr *vsub;
-
- if (!msc_ctrl_net) {
- cmd->reply = "MSC CTRL commands not initialized";
- return CTRL_CMD_ERROR;
- }
-
- if (!msc_ctrl_net->vlr) {
- cmd->reply = "VLR not initialized";
- return CTRL_CMD_ERROR;
- }
-
- cmd->reply = talloc_strdup(cmd, "");
-
- llist_for_each_entry(vsub, &msc_ctrl_net->vlr->subscribers, list) {
- cmd->reply = talloc_asprintf_append(cmd->reply, "%s,%s\n",
- vsub->imsi, vsub->msisdn);
- }
- printf("%s\n", cmd->reply); /* <-- what? */
- return CTRL_CMD_REPLY;
-}
-CTRL_CMD_DEFINE_RO(subscriber_list, "subscriber-list-active-v1");
-
-int msc_ctrl_cmds_install(struct gsm_network *net)
-{
- int rc = 0;
- msc_ctrl_net = net;
-
- 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;
-}
diff --git a/src/libmsc/db.c b/src/libmsc/db.c
deleted file mode 100644
index 28e978213..000000000
--- a/src/libmsc/db.c
+++ /dev/null
@@ -1,851 +0,0 @@
-/* Simple HLR/VLR database backend using dbi */
-/* (C) 2008 by Jan Luebbe <jluebbe@debian.org>
- * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
- * (C) 2009 by Harald Welte <laforge@gnumonks.org>
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <stdint.h>
-#include <inttypes.h>
-#include <libgen.h>
-#include <stdio.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <dbi/dbi.h>
-
-#include <openbsc/gsm_data.h>
-#include <openbsc/gsm_subscriber.h>
-#include <openbsc/gsm_04_11.h>
-#include <openbsc/db.h>
-#include <openbsc/debug.h>
-#include <openbsc/vlr.h>
-
-#include <osmocom/gsm/protocol/gsm_23_003.h>
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/statistics.h>
-#include <osmocom/core/rate_ctr.h>
-#include <osmocom/core/utils.h>
-
-#include <openssl/rand.h>
-
-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;
- const char *text, *daddr;
- const unsigned char *user_data;
- char buf[32];
- char *quoted;
- dbi_result result2;
- const char *extension;
-
- if (!sms)
- return NULL;
-
- sms->id = dbi_result_get_ulonglong(result, "id");
-
- /* find extension by id, assuming that the subscriber still exists in
- * the db */
- sender_id = dbi_result_get_ulonglong(result, "sender_id");
- snprintf(buf, sizeof(buf), "%llu", sender_id);
-
- dbi_conn_quote_string_copy(conn, buf, &quoted);
- result2 = dbi_conn_queryf(conn,
- "SELECT extension FROM Subscriber "
- "WHERE id = %s ", quoted);
- free(quoted);
- extension = dbi_result_get_string(result2, "extension");
- if (extension)
- osmo_strlcpy(sms->src.addr, extension, sizeof(sms->src.addr));
- dbi_result_free(result2);
- /* got the extension */
-
- 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)
- osmo_strlcpy(sms->dst.addr, daddr, sizeof(sms->dst.addr));
-
- 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)
- osmo_strlcpy(sms->text, text, sizeof(sms->text));
- 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;
- } else {
- dbi_result_free(result);
- }
-
- /* 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;
- int db_rev = 0;
-
- /* Make a query */
- 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;
- }
-
- /* Fetch the DB schema revision */
- rev_s = dbi_result_get_string(result, "value");
- if (!rev_s) {
- dbi_result_free(result);
- return -EINVAL;
- }
-
- if (!strcmp(rev_s, SCHEMA_REVISION)) {
- /* Everything is fine */
- dbi_result_free(result);
- return 0;
- }
-
- db_rev = atoi(rev_s);
- dbi_result_free(result);
-
- /* Incremental migration waterfall */
- switch (db_rev) {
- case 2:
- if (update_db_revision_2())
- goto error;
- case 3:
- if (update_db_revision_3())
- goto error;
-
- /* The end of waterfall */
- break;
- default:
- LOGP(DDB, LOGL_FATAL,
- "Invalid database schema revision '%d'.\n", db_rev);
- return -EINVAL;
- }
-
- return 0;
-
-error:
- LOGP(DDB, LOGL_FATAL, "Failed to update database "
- "from schema revision '%d'.\n", db_rev);
- return -EINVAL;
-}
-
-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;
-}
-
-/* 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)
- osmo_strlcpy(sms->dst.addr, daddr, sizeof(sms->dst.addr));
- sms->receiver = vlr_subscr_find_by_msisdn(net->vlr, 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)
- osmo_strlcpy(sms->src.addr, saddr, sizeof(sms->src.addr));
-
- 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)
- osmo_strlcpy(sms->text, text, sizeof(sms->text));
- 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;
-}
-
-struct gsm_sms *db_sms_get_next_unsent(struct gsm_network *net,
- unsigned long long min_sms_id,
- unsigned int max_failed)
-{
- dbi_result result;
- struct gsm_sms *sms;
-
- result = dbi_conn_queryf(conn,
- "SELECT * FROM SMS"
- " WHERE sent IS NULL"
- " AND id >= %llu"
- " AND deliver_attempts <= %u"
- " ORDER BY id LIMIT 1",
- min_sms_id, max_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 vlr_subscr *vsub,
- unsigned int max_failed)
-{
- struct gsm_network *net = vsub->vlr->user_ctx;
- dbi_result result;
- struct gsm_sms *sms;
-
- if (!vsub->lu_complete)
- return NULL;
-
- result = dbi_conn_queryf(conn,
- "SELECT * FROM SMS"
- " WHERE sent IS NULL"
- " AND dest_addr=%s"
- " AND deliver_attempts <= %u"
- " ORDER BY id LIMIT 1",
- vsub->msisdn, max_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;
-}
-
-struct gsm_sms *db_sms_get_next_unsent_rr_msisdn(struct gsm_network *net,
- const char *last_msisdn,
- unsigned int max_failed)
-{
- dbi_result result;
- struct gsm_sms *sms;
-
- result = dbi_conn_queryf(conn,
- "SELECT * FROM SMS"
- " WHERE sent IS NULL"
- " AND dest_addr > '%s'"
- " AND deliver_attempts <= %u"
- " ORDER BY dest_addr, id LIMIT 1",
- last_msisdn, max_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;
-}
-
-/* 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;
-}
-
-/* Drop all pending SMS to or from the given extension */
-int db_sms_delete_by_msisdn(const char *msisdn)
-{
- dbi_result result;
- if (!msisdn || !*msisdn)
- return 0;
- result = dbi_conn_queryf(conn,
- "DELETE FROM SMS WHERE src_addr=%s OR dest_addr=%s",
- msisdn, msisdn);
- if (!result) {
- LOGP(DDB, LOGL_ERROR,
- "Failed to delete SMS for %s\n", msisdn);
- return -1;
- }
- 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;
-}
diff --git a/src/libmsc/gsm_04_08.c b/src/libmsc/gsm_04_08.c
deleted file mode 100644
index be23c6472..000000000
--- a/src/libmsc/gsm_04_08.c
+++ /dev/null
@@ -1,3482 +0,0 @@
-/* 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-2016 by Harald Welte <laforge@gnumonks.org>
- * (C) 2008-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdbool.h>
-#include <errno.h>
-#include <time.h>
-#include <netinet/in.h>
-#include <regex.h>
-#include <sys/types.h>
-#include <openssl/rand.h>
-
-#include "bscconfig.h"
-
-#include <openbsc/auth.h>
-#include <openbsc/db.h>
-#include <openbsc/debug.h>
-#include <openbsc/gsm_data.h>
-#include <openbsc/gsm_subscriber.h>
-#include <openbsc/gsm_04_11.h>
-#include <openbsc/gsm_04_08.h>
-#include <openbsc/gsm_04_80.h>
-#include <openbsc/abis_rsl.h>
-#include <openbsc/chan_alloc.h>
-#include <openbsc/paging.h>
-#include <openbsc/signal.h>
-#include <osmocom/abis/trau_frame.h>
-#include <openbsc/trau_mux.h>
-#include <openbsc/rtp_proxy.h>
-#include <openbsc/transaction.h>
-#include <openbsc/ussd.h>
-#include <openbsc/silent_call.h>
-#include <openbsc/bsc_api.h>
-#include <openbsc/osmo_msc.h>
-#include <openbsc/handover.h>
-#include <openbsc/mncc_int.h>
-#include <osmocom/abis/e1_input.h>
-#include <osmocom/core/bitvec.h>
-#include <openbsc/vlr.h>
-#include <openbsc/msc_ifaces.h>
-
-#include <osmocom/gsm/gsm48.h>
-#include <osmocom/gsm/gsm0480.h>
-#include <osmocom/gsm/gsm_utils.h>
-#include <osmocom/gsm/protocol/gsm_04_08.h>
-#include <osmocom/core/msgb.h>
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/utils.h>
-#include <osmocom/gsm/tlv.h>
-#include <osmocom/crypt/auth.h>
-#ifdef BUILD_IU
-#include <osmocom/ranap/iu_client.h>
-#endif
-
-#include <openbsc/msc_ifaces.h>
-#include <openbsc/a_iface.h>
-
-#include <assert.h>
-
-
-void *tall_locop_ctx;
-void *tall_authciphop_ctx;
-
-static int gsm0408_loc_upd_acc(struct gsm_subscriber_connection *conn,
- uint32_t send_tmsi);
-static int gsm48_tx_simple(struct gsm_subscriber_connection *conn,
- uint8_t pdisc, uint8_t msg_type);
-
-struct gsm_lai {
- uint16_t mcc;
- uint16_t mnc;
- uint16_t lac;
-};
-
-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);
- }
-
- return msc_tx_dtap(conn, msg);
-}
-
-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);
-}
-
-/* clear all transactions globally; used in case of MNCC socket disconnect */
-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 */
-static int gsm0408_loc_upd_rej(struct gsm_subscriber_connection *conn, uint8_t cause)
-{
- struct msgb *msg;
-
- msg = gsm48_create_loc_upd_rej(cause);
- if (!msg) {
- LOGP(DMM, LOGL_ERROR, "Failed to create msg for LOCATION UPDATING REJECT.\n");
- return -1;
- }
-
- LOGP(DMM, LOGL_INFO, "Subscriber %s: LOCATION UPDATING REJECT\n",
- vlr_subscr_name(conn->vsub));
-
- 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,
- uint32_t send_tmsi)
-{
- struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 LOC UPD ACC");
- struct gsm48_hdr *gh;
- struct gsm48_loc_area_id *lai;
- uint8_t *mid;
-
- 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, conn->network->country_code,
- conn->network->network_code,
- conn->lac);
-
- if (send_tmsi == GSM_RESERVED_TMSI) {
- /* we did not allocate a TMSI to the MS, so we need to
- * include the IMSI in order for the MS to delete any
- * old TMSI that might still be allocated */
- uint8_t mi[10];
- int len;
- len = gsm48_generate_mid_from_imsi(mi, conn->vsub->imsi);
- mid = msgb_put(msg, len);
- memcpy(mid, mi, len);
- DEBUGP(DMM, "-> %s LOCATION UPDATE ACCEPT\n",
- vlr_subscr_name(conn->vsub));
- } else {
- /* Include the TMSI, which means that the MS will send a
- * TMSI REALLOCATION COMPLETE, and we should wait for
- * that until T3250 expiration */
- mid = msgb_put(msg, GSM48_MID_TMSI_LEN);
- gsm48_generate_mid_from_tmsi(mid, send_tmsi);
- DEBUGP(DMM, "-> %s LOCATION UPDATE ACCEPT (TMSI = 0x%08x)\n",
- vlr_subscr_name(conn->vsub),
- send_tmsi);
- }
- /* TODO: Follow-on proceed */
- /* TODO: CTS permission */
- /* TODO: Equivalent PLMNs */
- /* TODO: Emergency Number List */
- /* TODO: Per-MS T3312 */
-
-
- 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_name("GSM 04.08 ID REQ");
- struct gsm48_hdr *gh;
-
- 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);
- uint8_t mi_type = gh->data[1] & GSM_MI_TYPE_MASK;
- char mi_string[GSM48_MI_SIZE];
-
- if (!conn->vsub) {
- LOGP(DMM, LOGL_ERROR,
- "Rx MM Identity Response: invalid: no subscriber\n");
- return -EINVAL;
- }
-
- 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);
-
- return vlr_subscr_rx_id_resp(conn->vsub, gh->data+1, gh->data[0]);
-}
-
-/* FIXME: to libosmogsm */
-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.
- * Keep this function non-static for direct invocation by unit tests. */
-int mm_rx_loc_upd_req(struct gsm_subscriber_connection *conn, struct msgb *msg)
-{
- static const enum subscr_conn_from conn_from_lu = SUBSCR_CONN_FROM_LU;
- struct gsm_network *net = conn->network;
- struct gsm48_hdr *gh = msgb_l3(msg);
- struct gsm48_loc_upd_req *lu;
- uint8_t mi_type;
- char mi_string[GSM48_MI_SIZE];
- enum vlr_lu_type vlr_lu_type = VLR_LU_TYPE_REGULAR;
- uint32_t tmsi;
- char *imsi;
- struct osmo_location_area_id old_lai, new_lai;
- struct osmo_fsm_inst *lu_fsm;
- bool is_utran;
- int rc;
-
- 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);
-
- rc = msc_create_conn_fsm(conn, mi_string);
- if (rc)
- /* logging already happened in msc_create_conn_fsm() */
- return rc;
-
- conn->classmark.classmark1 = lu->classmark1;
- conn->classmark.classmark1_set = true;
-
- DEBUGP(DMM, "LOCATION UPDATING REQUEST: MI(%s)=%s type=%s\n",
- 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:
- rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_TYPE_NORMAL]);
- vlr_lu_type = VLR_LU_TYPE_REGULAR;
- break;
- case GSM48_LUPD_IMSI_ATT:
- rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_TYPE_ATTACH]);
- vlr_lu_type = VLR_LU_TYPE_IMSI_ATTACH;
- break;
- case GSM48_LUPD_PERIODIC:
- rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_TYPE_PERIODIC]);
- vlr_lu_type = VLR_LU_TYPE_PERIODIC;
- break;
- }
-
- /* TODO: 10.5.1.6 MS Classmark for UMTS / Classmark 2 */
- /* TODO: 10.5.3.14 Aditional update parameters (CS fallback calls) */
- /* TODO: 10.5.7.8 Device properties */
- /* TODO: 10.5.1.15 MS network feature support */
-
- switch (mi_type) {
- case GSM_MI_TYPE_IMSI:
- tmsi = GSM_RESERVED_TMSI;
- imsi = mi_string;
- break;
- case GSM_MI_TYPE_TMSI:
- tmsi = tmsi_from_string(mi_string);
- imsi = NULL;
- break;
- default:
- DEBUGPC(DMM, "unknown mobile identity type\n");
- tmsi = GSM_RESERVED_TMSI;
- imsi = NULL;
- break;
- }
-
- gsm48_decode_lai(&lu->lai, &old_lai.plmn.mcc,
- &old_lai.plmn.mnc, &old_lai.lac);
- new_lai.plmn.mcc = conn->network->country_code;
- new_lai.plmn.mnc = conn->network->network_code;
- new_lai.lac = conn->lac;
- DEBUGP(DMM, "LU/new-LAC: %u/%u\n", old_lai.lac, new_lai.lac);
-
- is_utran = (conn->via_ran == RAN_UTRAN_IU);
- lu_fsm = vlr_loc_update(conn->conn_fsm,
- SUBSCR_CONN_E_ACCEPTED,
- SUBSCR_CONN_E_CN_CLOSE,
- (void*)&conn_from_lu,
- net->vlr, conn, vlr_lu_type, tmsi, imsi,
- &old_lai, &new_lai,
- is_utran || conn->network->authentication_required,
- is_utran? VLR_CIPH_A5_3
- : conn->network->a5_encryption,
- classmark_is_r99(&conn->classmark),
- is_utran,
- net->vlr->cfg.assign_tmsi);
- if (!lu_fsm) {
- DEBUGP(DRR, "%s: Can't start LU FSM\n", mi_string);
- return 0;
- }
-
- /* From vlr_loc_update() we expect an implicit dispatch of
- * VLR_ULA_E_UPDATE_LA, and thus we expect msc_vlr_subscr_assoc() to
- * already have been called and completed. Has an error occured? */
-
- if (!conn->vsub || conn->vsub->lu_fsm != lu_fsm) {
- LOGP(DRR, LOGL_ERROR,
- "%s: internal error during Location Updating attempt\n",
- mi_string);
- return -EIO;
- }
-
- return 0;
-}
-
-/* Turn int into semi-octet representation: 98 => 0x89 */
-/* FIXME: libosmocore/libosmogsm */
-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_name("GSM 04.08 MM INF");
- struct gsm48_hdr *gh;
- struct gsm_network *net = conn->network;
- 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;
-
- 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 (net->tz.override) {
- /* Convert tz.hr and tz.mn to units */
- if (net->tz.hr < 0) {
- tzunits = ((net->tz.hr/-1)*4);
- tzunits = tzunits + (net->tz.mn/15);
- ptr8[7] = bcdify(tzunits);
- /* Set negative time */
- ptr8[7] |= 0x08;
- }
- else {
- tzunits = net->tz.hr*4;
- tzunits = tzunits + (net->tz.mn/15);
- ptr8[7] = bcdify(tzunits);
- }
- /* Convert DST value */
- if (net->tz.dst >= 0 && net->tz.dst <= 2)
- dst = net->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);
-}
-
-/*! Send an Authentication Request to MS on the given subscriber connection
- * according to 3GPP/ETSI TS 24.008, Section 9.2.2.
- * \param[in] conn Subscriber connection to send on.
- * \param[in] rand Random challenge token to send, must be 16 bytes long.
- * \param[in] autn r99: In case of UMTS mutual authentication, AUTN token to
- * send; must be 16 bytes long, or pass NULL for plain GSM auth.
- * \param[in] key_seq auth tuple's sequence number.
- */
-int gsm48_tx_mm_auth_req(struct gsm_subscriber_connection *conn, uint8_t *rand,
- uint8_t *autn, int key_seq)
-{
- struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 AUTH REQ");
- 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_nospc(rand, 16));
- if (autn)
- DEBUGP(DMM, " AUTH REQ (autn = %s)\n", osmo_hexdump_nospc(autn, 16));
-
- gh->proto_discr = GSM48_PDISC_MM;
- gh->msg_type = GSM48_MT_MM_AUTH_REQ;
-
- ar->key_seq = key_seq;
-
- /* 16 bytes RAND parameters */
- osmo_static_assert(sizeof(ar->rand) == 16, sizeof_auth_req_r99_rand);
- if (rand)
- memcpy(ar->rand, rand, 16);
-
-
- /* 16 bytes AUTN */
- if (autn)
- msgb_tlv_put(msg, GSM48_IE_AUTN, 16, autn);
-
- 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);
-}
-
-static int msc_vlr_tx_cm_serv_acc(void *msc_conn_ref);
-static int msc_vlr_tx_cm_serv_rej(void *msc_conn_ref, enum vlr_proc_arq_result result);
-
-static int cm_serv_reuse_conn(struct gsm_subscriber_connection *conn, const uint8_t *mi_lv)
-{
- uint8_t mi_type;
- char mi_string[GSM48_MI_SIZE];
- uint32_t tmsi;
-
- gsm48_mi_to_string(mi_string, sizeof(mi_string), mi_lv+1, mi_lv[0]);
- mi_type = mi_lv[1] & GSM_MI_TYPE_MASK;
-
- switch (mi_type) {
- case GSM_MI_TYPE_IMSI:
- if (vlr_subscr_matches_imsi(conn->vsub, mi_string))
- goto accept_reuse;
- break;
- case GSM_MI_TYPE_TMSI:
- tmsi = osmo_load32be(mi_lv+2);
- if (vlr_subscr_matches_tmsi(conn->vsub, tmsi))
- goto accept_reuse;
- break;
- case GSM_MI_TYPE_IMEI:
- if (vlr_subscr_matches_imei(conn->vsub, mi_string))
- goto accept_reuse;
- break;
- default:
- break;
- }
-
- LOGP(DMM, LOGL_ERROR, "%s: CM Service Request with mismatching mobile identity: %s %s\n",
- vlr_subscr_name(conn->vsub), gsm48_mi_type_name(mi_type), mi_string);
- msc_vlr_tx_cm_serv_rej(conn, VLR_PR_ARQ_RES_ILLEGAL_SUBSCR);
- return -EINVAL;
-
-accept_reuse:
- DEBUGP(DMM, "%s: re-using already accepted connection\n",
- vlr_subscr_name(conn->vsub));
- conn->received_cm_service_request = true;
- return conn->network->vlr->ops.tx_cm_serv_acc(conn);
-}
-
-/*
- * 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 conn and accept
- *
- * Keep this function non-static for direct invocation by unit tests.
- */
-int gsm48_rx_mm_serv_req(struct gsm_subscriber_connection *conn, struct msgb *msg)
-{
- static const enum subscr_conn_from conn_from_cm_service_req =
- SUBSCR_CONN_FROM_CM_SERVICE_REQ;
- struct gsm_network *net = conn->network;
- uint8_t mi_type;
- char mi_string[GSM48_MI_SIZE];
-
- 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);
- struct osmo_location_area_id lai;
- bool is_utran;
- int rc;
-
- lai.plmn.mcc = conn->network->country_code;
- lai.plmn.mnc = conn->network->network_code;
- lai.lac = conn->lac;
-
- DEBUGP(DMM, "<- CM SERVICE REQUEST ");
- if (msg->data_len < sizeof(struct gsm48_service_request*)) {
- DEBUGPC(DMM, "wrong sized message\n");
- return msc_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 msc_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);
- } 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);
- } else {
- DEBUGPC(DMM, "mi_type is not expected: %d\n", mi_type);
- return msc_gsm48_tx_mm_serv_rej(conn,
- GSM48_REJECT_INCORRECT_MESSAGE);
- }
-
- osmo_signal_dispatch(SS_SUBSCR, S_SUBSCR_IDENTITY, (classmark2 + classmark2_len));
- memcpy(conn->classmark.classmark2, classmark2, classmark2_len);
- conn->classmark.classmark2_len = classmark2_len;
-
- if (conn->conn_fsm) {
- if (msc_subscr_conn_is_accepted(conn))
- return cm_serv_reuse_conn(conn, mi-1);
- LOGP(DMM, LOGL_ERROR, "%s: connection already in use\n",
- vlr_subscr_name(conn->vsub));
- msc_vlr_tx_cm_serv_rej(conn, VLR_PR_ARQ_RES_UNKNOWN_ERROR);
- return -EINVAL;
- }
-
- rc = msc_create_conn_fsm(conn, mi_string);
- if (rc) {
- msc_vlr_tx_cm_serv_rej(conn, VLR_PR_ARQ_RES_UNKNOWN_ERROR);
- /* logging already happened in msc_create_conn_fsm() */
- return rc;
- }
-
- is_utran = (conn->via_ran == RAN_UTRAN_IU);
- vlr_proc_acc_req(conn->conn_fsm,
- SUBSCR_CONN_E_ACCEPTED,
- SUBSCR_CONN_E_CN_CLOSE,
- (void*)&conn_from_cm_service_req,
- net->vlr, conn,
- VLR_PR_ARQ_T_CM_SERV_REQ, mi-1, &lai,
- is_utran || conn->network->authentication_required,
- is_utran? VLR_CIPH_A5_3
- : conn->network->a5_encryption,
- classmark_is_r99(&conn->classmark),
- is_utran);
-
- return 0;
-}
-
-static int gsm48_rx_mm_imsi_detach_ind(struct gsm_subscriber_connection *conn, struct msgb *msg)
-{
- struct gsm_network *network = conn->network;
- 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 vlr_subscr *vsub = NULL;
-
- gsm48_mi_to_string(mi_string, sizeof(mi_string), idi->mi, idi->mi_len);
- DEBUGP(DMM, "IMSI DETACH INDICATION: MI(%s)=%s\n",
- gsm48_mi_type_name(mi_type), mi_string);
-
- rate_ctr_inc(&network->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_TYPE_DETACH]);
-
- switch (mi_type) {
- case GSM_MI_TYPE_TMSI:
- vsub = vlr_subscr_find_by_tmsi(network->vlr,
- tmsi_from_string(mi_string));
- break;
- case GSM_MI_TYPE_IMSI:
- vsub = vlr_subscr_find_by_imsi(network->vlr, mi_string);
- break;
- case GSM_MI_TYPE_IMEI:
- case GSM_MI_TYPE_IMEISV:
- /* no sim card... FIXME: what to do ? */
- LOGP(DMM, LOGL_ERROR, "MI(%s)=%s: unimplemented mobile identity type\n",
- gsm48_mi_type_name(mi_type), mi_string);
- break;
- default:
- LOGP(DMM, LOGL_ERROR, "MI(%s)=%s: unknown mobile identity type\n",
- gsm48_mi_type_name(mi_type), mi_string);
- break;
- }
-
- /* TODO? We used to remember the subscriber's classmark1 here and
- * stored it in the old sqlite db, but now we store it in a conn that
- * will be discarded anyway: */
- conn->classmark.classmark1 = idi->classmark1;
-
- if (!vsub) {
- LOGP(DMM, LOGL_ERROR, "IMSI DETACH for unknown subscriber MI(%s)=%s\n",
- gsm48_mi_type_name(mi_type), mi_string);
- } else {
- LOGP(DMM, LOGL_INFO, "IMSI DETACH for %s\n", vlr_subscr_name(vsub));
- vlr_subscr_rx_imsi_detach(vsub);
- osmo_signal_dispatch(SS_SUBSCR, S_SUBSCR_DETACHED, vsub);
- vlr_subscr_put(vsub);
- }
-
- msc_subscr_conn_close(conn, 0);
- 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;
-}
-
-static int parse_gsm_auth_resp(uint8_t *res, uint8_t *res_len,
- 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;
-
- if (msgb_l3len(msg) < sizeof(*gh) + sizeof(*ar)) {
- LOGP(DMM, LOGL_ERROR,
- "%s: MM AUTHENTICATION RESPONSE:"
- " l3 length invalid: %u\n",
- vlr_subscr_name(conn->vsub), msgb_l3len(msg));
- return -EINVAL;
- }
-
- *res_len = sizeof(ar->sres);
- memcpy(res, ar->sres, sizeof(ar->sres));
- return 0;
-}
-
-static int parse_umts_auth_resp(uint8_t *res, uint8_t *res_len,
- struct gsm_subscriber_connection *conn,
- struct msgb *msg)
-{
- struct gsm48_hdr *gh;
- uint8_t *data;
- uint8_t iei;
- uint8_t ie_len;
- unsigned int data_len;
-
- /* First parse the GSM part */
- if (parse_gsm_auth_resp(res, res_len, conn, msg))
- return -EINVAL;
- OSMO_ASSERT(*res_len == 4);
-
- /* Then add the extended res part */
- gh = msgb_l3(msg);
- data = gh->data + sizeof(struct gsm48_auth_resp);
- data_len = msgb_l3len(msg) - (data - (uint8_t*)msgb_l3(msg));
-
- if (data_len < 3) {
- LOGP(DMM, LOGL_ERROR,
- "%s: MM AUTHENTICATION RESPONSE:"
- " l3 length invalid: %u\n",
- vlr_subscr_name(conn->vsub), msgb_l3len(msg));
- return -EINVAL;
- }
-
- iei = data[0];
- ie_len = data[1];
- if (iei != GSM48_IE_AUTH_RES_EXT) {
- LOGP(DMM, LOGL_ERROR,
- "%s: MM R99 AUTHENTICATION RESPONSE:"
- " expected IEI 0x%02x, got 0x%02x\n",
- vlr_subscr_name(conn->vsub),
- GSM48_IE_AUTH_RES_EXT, iei);
- return -EINVAL;
- }
-
- if (ie_len > 12) {
- LOGP(DMM, LOGL_ERROR,
- "%s: MM R99 AUTHENTICATION RESPONSE:"
- " extended Auth Resp IE 0x%02x is too large: %u bytes\n",
- vlr_subscr_name(conn->vsub), GSM48_IE_AUTH_RES_EXT, ie_len);
- return -EINVAL;
- }
-
- *res_len += ie_len;
- memcpy(res + 4, &data[2], ie_len);
- return 0;
-}
-
-/* Chapter 9.2.3: Authentication Response */
-static int gsm48_rx_mm_auth_resp(struct gsm_subscriber_connection *conn, struct msgb *msg)
-{
- uint8_t res[16];
- uint8_t res_len;
- int rc;
- bool is_r99;
-
- if (!conn->vsub) {
- LOGP(DMM, LOGL_ERROR,
- "MM AUTHENTICATION RESPONSE: invalid: no subscriber\n");
- msc_subscr_conn_close(conn, GSM_CAUSE_AUTH_FAILED);
- return -EINVAL;
- }
-
- if (msgb_l3len(msg) >
- sizeof(struct gsm48_hdr) + sizeof(struct gsm48_auth_resp)) {
- rc = parse_umts_auth_resp(res, &res_len, conn, msg);
- is_r99 = true;
- } else {
- rc = parse_gsm_auth_resp(res, &res_len, conn, msg);
- is_r99 = false;
- }
-
- if (rc) {
- msc_subscr_conn_close(conn, GSM_CAUSE_AUTH_FAILED);
- return -EINVAL;
- }
-
- DEBUGP(DMM, "%s: MM %s AUTHENTICATION RESPONSE (%s = %s)\n",
- vlr_subscr_name(conn->vsub),
- is_r99 ? "R99" : "GSM", is_r99 ? "res" : "sres",
- osmo_hexdump_nospc(res, res_len));
-
- return vlr_subscr_rx_auth_resp(conn->vsub, is_r99,
- conn->via_ran == RAN_UTRAN_IU,
- res, res_len);
-}
-
-static int gsm48_rx_mm_auth_fail(struct gsm_subscriber_connection *conn, struct msgb *msg)
-{
- struct gsm48_hdr *gh = msgb_l3(msg);
- uint8_t cause;
- uint8_t auts_tag;
- uint8_t auts_len;
- uint8_t *auts;
-
- if (!conn->vsub) {
- LOGP(DMM, LOGL_ERROR,
- "MM R99 AUTHENTICATION FAILURE: invalid: no subscriber\n");
- msc_subscr_conn_close(conn, GSM_CAUSE_AUTH_FAILED);
- return -EINVAL;
- }
-
- if (msgb_l3len(msg) < sizeof(*gh) + 1) {
- LOGP(DMM, LOGL_ERROR,
- "%s: MM R99 AUTHENTICATION FAILURE:"
- " l3 length invalid: %u\n",
- vlr_subscr_name(conn->vsub), msgb_l3len(msg));
- msc_subscr_conn_close(conn, GSM_CAUSE_AUTH_FAILED);
- return -EINVAL;
- }
-
- cause = gh->data[0];
-
- if (cause != GSM48_REJECT_SYNCH_FAILURE) {
- LOGP(DMM, LOGL_INFO,
- "%s: MM R99 AUTHENTICATION FAILURE: cause 0x%0x\n",
- vlr_subscr_name(conn->vsub), cause);
- vlr_subscr_rx_auth_fail(conn->vsub, NULL);
- return 0;
- }
-
- /* This is a Synch Failure procedure, which should pass an AUTS to
- * resynchronize the sequence nr with the HLR. Expecting exactly one
- * TLV with 14 bytes of AUTS. */
-
- if (msgb_l3len(msg) < sizeof(*gh) + 1 + 2) {
- LOGP(DMM, LOGL_INFO,
- "%s: MM R99 AUTHENTICATION FAILURE:"
- " invalid Synch Failure: missing AUTS IE\n",
- vlr_subscr_name(conn->vsub));
- msc_subscr_conn_close(conn, GSM_CAUSE_AUTH_FAILED);
- return -EINVAL;
- }
-
- auts_tag = gh->data[1];
- auts_len = gh->data[2];
- auts = &gh->data[3];
-
- if (auts_tag != GSM48_IE_AUTS
- || auts_len != 14) {
- LOGP(DMM, LOGL_INFO,
- "%s: MM R99 AUTHENTICATION FAILURE:"
- " invalid Synch Failure:"
- " expected AUTS IE 0x%02x of 14 bytes,"
- " got IE 0x%02x of %u bytes\n",
- vlr_subscr_name(conn->vsub),
- GSM48_IE_AUTS, auts_tag, auts_len);
- msc_subscr_conn_close(conn, GSM_CAUSE_AUTH_FAILED);
- return -EINVAL;
- }
-
- if (msgb_l3len(msg) < sizeof(*gh) + 1 + 2 + auts_len) {
- LOGP(DMM, LOGL_INFO,
- "%s: MM R99 AUTHENTICATION FAILURE:"
- " invalid Synch Failure msg: message truncated (%u)\n",
- vlr_subscr_name(conn->vsub), msgb_l3len(msg));
- msc_subscr_conn_close(conn, GSM_CAUSE_AUTH_FAILED);
- return -EINVAL;
- }
-
- /* We have an AUTS IE with exactly 14 bytes of AUTS and the msgb is
- * large enough. */
-
- DEBUGP(DMM, "%s: MM R99 AUTHENTICATION SYNCH (AUTS = %s)\n",
- vlr_subscr_name(conn->vsub), osmo_hexdump_nospc(auts, 14));
-
- return vlr_subscr_rx_auth_fail(conn->vsub, auts);
-}
-
-static int gsm48_rx_mm_tmsi_reall_compl(struct gsm_subscriber_connection *conn)
-{
- DEBUGP(DMM, "TMSI Reallocation Completed. Subscriber: %s\n",
- vlr_subscr_name(conn->vsub));
- if (!conn->vsub) {
- LOGP(DMM, LOGL_ERROR,
- "Rx MM TMSI Reallocation Complete: invalid: no subscriber\n");
- return -EINVAL;
- }
- return vlr_subscr_rx_tmsi_reall_compl(conn->vsub);
-}
-
-/* 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 (gsm48_hdr_msg_type(gh)) {
- case GSM48_MT_MM_LOC_UPD_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:
- rc = gsm48_rx_mm_tmsi_reall_compl(conn);
- 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;
- case GSM48_MT_MM_AUTH_FAIL:
- rc = gsm48_rx_mm_auth_fail(conn, msg);
- break;
- default:
- LOGP(DMM, LOGL_NOTICE, "Unknown GSM 04.08 MM msg type 0x%02x\n",
- gh->msg_type);
- break;
- }
-
- return rc;
-}
-
-static uint8_t *gsm48_cm2_get_mi(uint8_t *classmark2_lv, unsigned int tot_len)
-{
- /* Check the size for the classmark */
- if (tot_len < 1 + *classmark2_lv)
- return NULL;
-
- uint8_t *mi_lv = classmark2_lv + *classmark2_lv + 1;
- if (tot_len < 2 + *classmark2_lv + mi_lv[0])
- return NULL;
-
- return mi_lv;
-}
-
-/* Receive a PAGING RESPONSE message from the MS */
-static int gsm48_rx_rr_pag_resp(struct gsm_subscriber_connection *conn, struct msgb *msg)
-{
- static const enum subscr_conn_from conn_from_paging_resp =
- SUBSCR_CONN_FROM_PAGING_RESP;
- struct gsm_network *net = conn->network;
- struct gsm48_hdr *gh = msgb_l3(msg);
- struct gsm48_pag_resp *resp;
- uint8_t *classmark2_lv = gh->data + 1;
- uint8_t *mi_lv;
- uint8_t mi_type;
- char mi_string[GSM48_MI_SIZE];
- int rc = 0;
- struct osmo_location_area_id lai;
- bool is_utran;
-
- lai.plmn.mcc = conn->network->country_code;
- lai.plmn.mnc = conn->network->network_code;
- lai.lac = conn->lac;
-
- 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);
-
- mi_lv = gsm48_cm2_get_mi(classmark2_lv, msgb_l3len(msg) - sizeof(*gh));
- if (!mi_lv) {
- /* FIXME */
- return -1;
- }
-
- rc = msc_create_conn_fsm(conn, mi_string);
- if (rc)
- /* logging already happened in msc_create_conn_fsm() */
- return rc;
-
- memcpy(conn->classmark.classmark2, classmark2_lv+1, *classmark2_lv);
- conn->classmark.classmark2_len = *classmark2_lv;
-
- is_utran = (conn->via_ran == RAN_UTRAN_IU);
- vlr_proc_acc_req(conn->conn_fsm,
- SUBSCR_CONN_E_ACCEPTED,
- SUBSCR_CONN_E_CN_CLOSE,
- (void*)&conn_from_paging_resp,
- net->vlr, conn,
- VLR_PR_ARQ_T_PAGING_RESP, mi_lv, &lai,
- is_utran || conn->network->authentication_required,
- is_utran? VLR_CIPH_A5_3
- : conn->network->a5_encryption,
- classmark_is_r99(&conn->classmark),
- is_utran);
-
- return 0;
-}
-
-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\n",
- apdu_id_flags, apdu_len, osmo_hexdump(apdu_data, apdu_len));
-
- /* we're not using the app info blob anywhere, so ignore. */
-#if 0
- return db_apdu_blob_store(conn->subscr, apdu_id_flags, apdu_len, apdu_data);
-#else
- return 0;
-#endif
-}
-
-/* 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 %s GSM 04.08 RR "
- "message\n", gsm48_rr_msg_name(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_name("GSM 04.08 APP INF");
- struct gsm48_hdr *gh;
-
- 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);
-}
-
-/* FIXME: this count_statistics is a state machine behaviour. we should convert
- * the complete call control into a state machine. Afterwards we can move this
- * code into state transitions.
- */
-static void count_statistics(struct gsm_trans *trans, int new_state)
-{
- int old_state = trans->cc.state;
- struct rate_ctr_group *msc = trans->net->msc_ctrs;
-
- if (old_state == new_state)
- return;
-
- /* state incoming */
- switch (new_state) {
- case GSM_CSTATE_ACTIVE:
- osmo_counter_inc(trans->net->active_calls);
- rate_ctr_inc(&msc->ctr[MSC_CTR_CALL_ACTIVE]);
- break;
- }
-
- /* state outgoing */
- switch (old_state) {
- case GSM_CSTATE_ACTIVE:
- osmo_counter_dec(trans->net->active_calls);
- if (new_state == GSM_CSTATE_DISCONNECT_REQ ||
- new_state == GSM_CSTATE_DISCONNECT_IND)
- rate_ctr_inc(&msc->ctr[MSC_CTR_CALL_COMPLETE]);
- else
- rate_ctr_inc(&msc->ctr[MSC_CTR_CALL_INCOMPLETE]);
- break;
- }
-}
-
-/* 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));
-
- count_statistics(trans, state);
- trans->cc.state = state;
-}
-
-static int gsm48_cc_tx_status(struct gsm_trans *trans, void *arg)
-{
- struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC STATUS");
- 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_name("GSM 04.08 TX SIMPLE");
- struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
-
- 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;
-
- DEBUGP(DMNCC, "transmit message %s\n", get_mncc_name(msg_type));
-
-#if BEFORE_MSCSPLIT
- /* Re-enable this log output once we can obtain this information via
- * A-interface, see OS#2391. */
- 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,
- vlr_subscr_msisdn_or_name(trans->vsub),
- get_mncc_name(msg_type));
- else
- DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) "
- "Sending '%s' to MNCC.\n",
- vlr_subscr_msisdn_or_name(trans->vsub),
- get_mncc_name(msg_type));
- else
- DEBUGP(DCC, "(bts - trx - ts - ti -- sub -) "
- "Sending '%s' to MNCC.\n", get_mncc_name(msg_type));
-#else
- DEBUGP(DCC, "Sending '%s' to MNCC.\n", get_mncc_name(msg_type));
-#endif
-
- 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);
-
- /* Make sure call also gets released on the mgcp side */
- msc_call_release(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);
-}
-
-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);
-
- switch (event) {
- case GSM_PAGING_SUCCEEDED:
- DEBUGP(DCC, "Paging subscr %s succeeded!\n",
- vlr_subscr_msisdn_or_name(transt->vsub));
- OSMO_ASSERT(conn);
- /* Assign conn */
- 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",
- vlr_subscr_msisdn_or_name(transt->vsub));
- /* 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;
-}
-
-/* bridge channels of two transactions */
-static int tch_bridge(struct gsm_network *net, struct gsm_mncc_bridge *bridge)
-{
- struct gsm_trans *trans1 = trans_find_by_callref(net, bridge->callref[0]);
- struct gsm_trans *trans2 = trans_find_by_callref(net, bridge->callref[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(LOG_CTX_VLR_SUBSCR, trans1->vsub);
-
- return msc_call_bridge(trans1, trans2);
-}
-
-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);
- }
-
-}
-
-/* disconnect both calls from the bridge */
-static inline void disconnect_bridge(struct gsm_network *net,
- struct gsm_mncc_bridge *bridge, int err)
-{
- struct gsm_trans *trans0 = trans_find_by_callref(net, bridge->callref[0]);
- struct gsm_trans *trans1 = trans_find_by_callref(net, bridge->callref[1]);
- struct gsm_mncc mx_rel;
- if (!trans0 || !trans1)
- return;
-
- DEBUGP(DCC, "Failed to bridge TCH for calls %x <-> %x :: %s \n",
- trans0->callref, trans1->callref, strerror(err));
-
- memset(&mx_rel, 0, sizeof(struct gsm_mncc));
- mncc_set_cause(&mx_rel, GSM48_CAUSE_LOC_INN_NET,
- GSM48_CC_CAUSE_CHAN_UNACCEPT);
-
- mx_rel.callref = trans0->callref;
- gsm48_cc_tx_disconnect(trans0, &mx_rel);
-
- mx_rel.callref = trans1->callref;
- gsm48_cc_tx_disconnect(trans1, &mx_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);
- osmo_timer_setup(&trans->cc.timer, gsm48_cc_timeout, 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 = gsm48_hdr_msg_type(gh);
- 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;
-
- 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;
- osmo_strlcpy(setup.calling.number, trans->vsub->msisdn, sizeof(setup.calling.number));
- osmo_strlcpy(setup.imsi, trans->vsub->imsi, sizeof(setup.imsi));
-
- /* 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);
-
- /* Create a copy of the bearer capability
- * in the transaction struct, so we can use
- * this information later */
- memcpy(&trans->bearer_cap,&setup.bearer_cap,
- sizeof(trans->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",
- vlr_subscr_name(trans->vsub), trans->vsub->msisdn,
- setup.called.number);
-
- rate_ctr_inc(&trans->net->msc_ctrs->ctr[MSC_CTR_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_name("GSM 04.08 CC STUP");
- 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->vsub,
- 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);
-
- rate_ctr_inc(&trans->net->msc_ctrs->ctr[MSC_CTR_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;
- int rc;
-
- 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;
-
- 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);
-
- /* Create a copy of the bearer capability
- * in the transaction struct, so we can use
- * this information later */
- memcpy(&trans->bearer_cap,&call_conf.bearer_cap,
- sizeof(trans->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 */
- osmo_strlcpy(call_conf.imsi, trans->vsub->imsi, sizeof(call_conf.imsi));
-
- new_cc_state(trans, GSM_CSTATE_MO_TERM_CALL_CONF);
-
- /* Assign call (if not done yet) */
- if (trans->assignment_done == false) {
- rc = msc_call_assignment(trans);
- trans->assignment_done = true;
- }
- else
- rc = 0;
-
- /* don't continue, if there were problems with
- * the call assignment. */
- if (rc)
- return rc;
-
- return mncc_recvmsg(trans->net, trans, MNCC_CALL_CONF_IND,
- &call_conf);
-}
-
-static int gsm48_cc_tx_call_proc_and_assign(struct gsm_trans *trans, void *arg)
-{
- struct gsm_mncc *proceeding = arg;
- struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC PROC");
- struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
- int rc;
-
- 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);
-
- rc = gsm48_conn_sendmsg(msg, trans->conn, trans);
- if (rc)
- return rc;
-
- /* Assign call (if not done yet) */
- if (trans->assignment_done == false) {
- rc = msc_call_assignment(trans);
- trans->assignment_done = true;
- }
- else
- rc = 0;
-
- return rc;
-}
-
-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_name("GSM 04.08 CC ALERT");
- 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_name("GSM 04.08 CC PROGRESS");
- 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_name("GSN 04.08 CC CON");
- 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;
- osmo_strlcpy(connect.connected.number, trans->vsub->msisdn, sizeof(connect.connected.number));
- osmo_strlcpy(connect.imsi, trans->vsub->imsi, sizeof(connect.imsi));
-
- /* 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);
- rate_ctr_inc(&trans->net->msc_ctrs->ctr[MSC_CTR_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);
- rate_ctr_inc(&trans->net->msc_ctrs->ctr[MSC_CTR_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_name("GSM 04.08 CC CON ACK");
- 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_name("GSM 04.08 CC DISC");
- 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_name("GSM 04.08 CC REL");
- 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_name("GSM 04.08 CC REL COMPL");
- 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_name("GSM 04.08 CC FAC");
- 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_name("GSM 04.08 CC HLD ACK");
- 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_name("GSM 04.08 CC HLD REJ");
- 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_name("GSM 04.08 CC RETR ACK");
- 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_name("GSM 04.08 CC RETR REJ");
- 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_name("GSM 04.08 DTMF ACK");
- 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_name("GSM 04.08 DTMF REJ");
- 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_name("GSM 04.08 DTMF STP ACK");
- 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);
-
- /* Create a copy of the bearer capability
- * in the transaction struct, so we can use
- * this information later */
- memcpy(&trans->bearer_cap,&modify.bearer_cap,
- sizeof(trans->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_name("GSM 04.08 CC MOD");
- 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);
-
- /* Create a copy of the bearer capability
- * in the transaction struct, so we can use
- * this information later */
- memcpy(&trans->bearer_cap,&modify.bearer_cap,
- sizeof(trans->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_name("GSM 04.08 CC MOD COMPL");
- 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);
-
- /* Create a copy of the bearer capability
- * in the transaction struct, so we can use
- * this information later */
- memcpy(&trans->bearer_cap,&modify.bearer_cap,
- sizeof(trans->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_name("GSM 04.08 CC MOD REJ");
- 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_name("GSM 04.08 CC NOT");
- 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(&notify, 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(&notify.notify, gh->data);
-
- return mncc_recvmsg(trans->net, trans, MNCC_NOTIFY_IND, &notify);
-}
-
-static int gsm48_cc_tx_userinfo(struct gsm_trans *trans, void *arg)
-{
- struct gsm_mncc *user = arg;
- struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 USR INFO");
- 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 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)
-{
- int msg_type;
-
- /* FIXME This has to be set to some meaningful value.
- * Possible options are:
- * GSM_TCHF_FRAME, GSM_TCHF_FRAME_EFR,
- * GSM_TCHH_FRAME, GSM_TCH_FRAME_AMR
- * (0 if unknown) */
- msg_type = GSM_TCHF_FRAME;
-
- uint32_t addr = mgcpgw_client_remote_addr_n(net->mgcpgw.client);
- uint16_t port = trans->conn->rtp.port_cn;
-
- /* FIXME: This has to be set to some meaningful value,
- * before the MSC-Split, this value was pulled from
- * lchan->abis_ip.rtp_payload */
- uint32_t payload_type = 0;
-
- return mncc_recv_rtp(net, trans->callref, cmd,
- addr,
- port,
- payload_type,
- 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_trans *trans;
- int rc;
-
- /* 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(LOG_CTX_VLR_SUBSCR, trans->vsub);
- 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;
- }
-
- trans->conn->mncc_rtp_bridge = 1;
-
- /* When we call msc_call_assignment() we will trigger, depending
- * on the RAN type the call assignment on the A or Iu interface.
- * msc_call_assignment() also takes care about sending the CRCX
- * command to the MGCP-GW. The CRCX will return the port number,
- * where the PBX (e.g. Asterisk) will send its RTP stream to. We
- * have to return this port number back to the MNCC by sending
- * it back with the TCH_RTP_CREATE message. To make sure that
- * this message is sent AFTER the response to CRCX from the
- * MGCP-GW has arrived, we need will instruct msc_call_assignment()
- * to take care of this by setting trans->tch_rtp_create to true.
- * This will make sure that gsm48_tch_rtp_create() (below) is
- * called as soon as the local port number has become known. */
- trans->tch_rtp_create = true;
-
- /* Assign call (if not done yet) */
- if (trans->assignment_done == false) {
- rc = msc_call_assignment(trans);
- trans->assignment_done = true;
- }
- else
- rc = 0;
-
- return rc;
-}
-
-/* Trigger TCH_RTP_CREATE acknowledgement */
-int gsm48_tch_rtp_create(struct gsm_trans *trans)
-{
- /* This function is called as soon as the port, on which the
- * mgcp-gw expects the incoming RTP stream from the remote
- * end (e.g. Asterisk) is known. */
-
- struct gsm_subscriber_connection *conn = trans->conn;
- struct gsm_network *network = conn->network;
-
- mncc_recv_rtp_sock(network, trans, MNCC_RTP_CREATE);
- return 0;
-}
-
-static int tch_rtp_connect(struct gsm_network *net, void *arg)
-{
- 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(LOG_CTX_VLR_SUBSCR, trans->vsub);
- 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;
- }
-
- msc_call_connect(trans,rtp->port,rtp->ip);
- 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_and_assign},
- {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},
-};
-
-#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_mncc *data = arg, rel;
-
- DEBUGP(DMNCC, "receive message %s\n", get_mncc_name(msg_type));
-
- /* handle special messages */
- switch(msg_type) {
- case MNCC_BRIDGE:
- rc = tch_bridge(net, arg);
- if (rc < 0)
- disconnect_bridge(net, arg, -rc);
- return rc;
- 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 MNCC_FRAME_DROP:
- case MNCC_FRAME_RECV:
- case GSM_TCHF_FRAME:
- case GSM_TCHF_FRAME_EFR:
- case GSM_TCHH_FRAME:
- case GSM_TCH_FRAME_AMR:
- LOGP(DMNCC, LOGL_ERROR, "RTP streams must be handled externally; %s not supported.\n",
- get_mncc_name(msg_type));
- return -ENOTSUP;
- }
-
- 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 vlr_subscr *vsub;
-
- 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])
- vsub = vlr_subscr_find_by_msisdn(net->vlr,
- data->called.number);
- else
- vsub = vlr_subscr_find_by_imsi(net->vlr, data->imsi);
-
- /* update the subscriber we deal with */
- log_set_context(LOG_CTX_VLR_SUBSCR, vsub);
-
- /* If subscriber is not found */
- if (!vsub) {
- 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 (!vsub->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);
- vlr_subscr_put(vsub);
- /* 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, vsub, GSM48_PDISC_CC, 0xff, data->callref);
- if (!trans) {
- DEBUGP(DCC, "No memory for trans.\n");
- vlr_subscr_put(vsub);
- /* Ressource unavailable */
- mncc_release_ind(net, NULL, data->callref,
- GSM48_CAUSE_LOC_PRN_S_LU,
- GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
- return -ENOMEM;
- }
-
- /* Find conn */
- conn = connection_for_subscr(vsub);
-
- /* If subscriber has no conn */
- if (!conn) {
- /* find transaction with this subscriber already paging */
- llist_for_each_entry(transt, &net->trans_list, entry) {
- /* Transaction of our conn? */
- if (transt == trans ||
- transt->vsub != vsub)
- 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), vsub->lac);
- vlr_subscr_put(vsub);
- trans_free(trans);
- return 0;
- }
- /* store setup information until paging succeeds */
- memcpy(&trans->cc.msg, data, sizeof(struct gsm_mncc));
-
- /* Request a channel */
- trans->paging_request = subscr_request_conn(
- vsub,
- setup_trig_pag_evt,
- trans,
- "MNCC: establish call");
- if (!trans->paging_request) {
- LOGP(DCC, LOGL_ERROR, "Failed to allocate paging token.\n");
- vlr_subscr_put(vsub);
- trans_free(trans);
- return 0;
- }
- vlr_subscr_put(vsub);
- return 0;
- }
-
- /* Assign conn */
- trans->conn = msc_subscr_conn_get(conn);
- vlr_subscr_put(vsub);
- } else {
- /* update the subscriber we deal with */
- log_set_context(LOG_CTX_VLR_SUBSCR, trans->vsub);
- }
-
- if (trans->conn)
- conn = trans->conn;
-
- /* if paging did not respond yet */
- if (!conn) {
- DEBUGP(DCC, "(sub %s) "
- "Received '%s' from MNCC in paging state\n",
- vlr_subscr_msisdn_or_name(trans->vsub),
- 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, "(ti %02x sub %s) "
- "Received '%s' from MNCC in state %d (%s)\n",
- trans->transaction_id,
- vlr_subscr_msisdn_or_name(trans->conn->vsub),
- 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 = gsm48_hdr_msg_type(gh);
- uint8_t transaction_id = gsm48_hdr_trans_id_flip_ti(gh);
- 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;
- }
-
- if (!conn->vsub) {
- LOGP(DCC, LOGL_ERROR, "Invalid conn: no subscriber\n");
- return -EINVAL;
- }
-
- /* Find transaction */
- trans = trans_find_by_id(conn, GSM48_PDISC_CC, transaction_id);
-
-#if BEFORE_MSCSPLIT
- /* Re-enable this log output once we can obtain this information via
- * A-interface, see OS#2391. */
- 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, vlr_subscr_msisdn_or_name(conn->vsub),
- gsm48_cc_msg_name(msg_type), trans?(trans->cc.state):0,
- gsm48_cc_state_name(trans?(trans->cc.state):0));
-#endif
-
- /* Create transaction */
- if (!trans) {
- DEBUGP(DCC, "Unknown transaction ID %x, "
- "creating new trans.\n", transaction_id);
- /* Create transaction */
- trans = trans_alloc(conn->network, conn->vsub,
- 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 = msc_subscr_conn_get(conn);
- cm_service_request_concludes(conn, msg);
- }
-
- /* 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->vsub);
-
- rc = datastatelist[i].rout(trans, msg);
-
- msc_subscr_conn_communicating(conn);
- return rc;
-}
-
-static bool msg_is_initially_permitted(const struct gsm48_hdr *hdr)
-{
- uint8_t pdisc = gsm48_hdr_pdisc(hdr);
- uint8_t msg_type = gsm48_hdr_msg_type(hdr);
-
- switch (pdisc) {
- case GSM48_PDISC_MM:
- switch (msg_type) {
- case GSM48_MT_MM_LOC_UPD_REQUEST:
- case GSM48_MT_MM_CM_SERV_REQ:
- case GSM48_MT_MM_AUTH_RESP:
- case GSM48_MT_MM_AUTH_FAIL:
- case GSM48_MT_MM_ID_RESP:
- case GSM48_MT_MM_TMSI_REALL_COMPL:
- case GSM48_MT_MM_IMSI_DETACH_IND:
- return true;
- default:
- break;
- }
- break;
- case GSM48_PDISC_RR:
- switch (msg_type) {
- case GSM48_MT_RR_CIPH_M_COMPL:
- case GSM48_MT_RR_PAG_RESP:
- return true;
- default:
- break;
- }
- break;
- default:
- break;
- }
-
- return false;
-}
-
-void cm_service_request_concludes(struct gsm_subscriber_connection *conn,
- struct msgb *msg)
-{
-
- /* If a CM Service Request was received before, this is the request the
- * conn was opened for. No need to wait for further messages. */
-
- if (!conn->received_cm_service_request)
- return;
-
- if (log_check_level(DMM, LOGL_DEBUG)) {
- struct gsm48_hdr *gh = msgb_l3(msg);
- uint8_t pdisc = gsm48_hdr_pdisc(gh);
- uint8_t msg_type = gsm48_hdr_msg_type(gh);
-
- DEBUGP(DMM, "%s: rx msg %s:"
- " received_cm_service_request changes to false\n",
- vlr_subscr_name(conn->vsub),
- gsm48_pdisc_msgtype_name(pdisc, msg_type));
- }
- conn->received_cm_service_request = false;
-}
-
-
-/* Main entry point for GSM 04.08/44.008 Layer 3 data (e.g. from the BSC). */
-int gsm0408_dispatch(struct gsm_subscriber_connection *conn, struct msgb *msg)
-{
- struct gsm48_hdr *gh = msgb_l3(msg);
- uint8_t pdisc = gsm48_hdr_pdisc(gh);
- int rc = 0;
-
- OSMO_ASSERT(conn);
- OSMO_ASSERT(msg);
-
- LOGP(DRLL, LOGL_DEBUG, "Dispatching 04.08 message %s (0x%x:0x%x)\n",
- gsm48_pdisc_msgtype_name(pdisc, gsm48_hdr_msg_type(gh)),
- pdisc, gsm48_hdr_msg_type(gh));
-
- if (!msc_subscr_conn_is_accepted(conn)
- && !msg_is_initially_permitted(gh)) {
- LOGP(DRLL, LOGL_ERROR,
- "subscr %s: Message not permitted for initial conn: %s\n",
- vlr_subscr_name(conn->vsub),
- gsm48_pdisc_msgtype_name(pdisc, gsm48_hdr_msg_type(gh)));
- return -EACCES;
- }
-
- if (conn->vsub && conn->vsub->cs.attached_via_ran != conn->via_ran) {
- LOGP(DMM, LOGL_ERROR,
- "%s: Illegal situation: RAN type mismatch:"
- " attached via %s, received message via %s\n",
- vlr_subscr_name(conn->vsub),
- ran_type_name(conn->vsub->cs.attached_via_ran),
- ran_type_name(conn->via_ran));
- return -EACCES;
- }
-
-#if 0
- if (silent_call_reroute(conn, msg))
- return silent_call_rx(conn, msg);
-#endif
-
- switch (pdisc) {
- case GSM48_PDISC_CC:
- 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:
- 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);
- rc = -ENOTSUP;
- break;
- case GSM48_PDISC_NC_SS:
- rc = handle_rcv_ussd(conn, msg);
- break;
- default:
- LOGP(DRLL, LOGL_NOTICE, "Unknown "
- "GSM 04.08 discriminator 0x%02x\n", pdisc);
- rc = -EINVAL;
- break;
- }
-
- return rc;
-}
-
-/***********************************************************************
- * VLR integration
- ***********************************************************************/
-
-/* VLR asks us to send an authentication request */
-static int msc_vlr_tx_auth_req(void *msc_conn_ref, struct gsm_auth_tuple *at,
- bool send_autn)
-{
- struct gsm_subscriber_connection *conn = msc_conn_ref;
- return gsm48_tx_mm_auth_req(conn, at->vec.rand,
- send_autn? at->vec.autn : NULL,
- at->key_seq);
-}
-
-/* VLR asks us to send an authentication reject */
-static int msc_vlr_tx_auth_rej(void *msc_conn_ref)
-{
- struct gsm_subscriber_connection *conn = msc_conn_ref;
- return gsm48_tx_mm_auth_rej(conn);
-}
-
-/* VLR asks us to transmit an Identity Request of given type */
-static int msc_vlr_tx_id_req(void *msc_conn_ref, uint8_t mi_type)
-{
- struct gsm_subscriber_connection *conn = msc_conn_ref;
- return mm_tx_identity_req(conn, mi_type);
-}
-
-/* VLR asks us to transmit a Location Update Accept */
-static int msc_vlr_tx_lu_acc(void *msc_conn_ref, uint32_t send_tmsi)
-{
- struct gsm_subscriber_connection *conn = msc_conn_ref;
- return gsm0408_loc_upd_acc(conn, send_tmsi);
-}
-
-/* VLR asks us to transmit a Location Update Reject */
-static int msc_vlr_tx_lu_rej(void *msc_conn_ref, uint8_t cause)
-{
- struct gsm_subscriber_connection *conn = msc_conn_ref;
- return gsm0408_loc_upd_rej(conn, cause);
-}
-
-/* VLR asks us to transmit a CM Service Accept */
-static int msc_vlr_tx_cm_serv_acc(void *msc_conn_ref)
-{
- struct gsm_subscriber_connection *conn = msc_conn_ref;
- return msc_gsm48_tx_mm_serv_ack(conn);
-}
-
-static int msc_vlr_tx_common_id(void *msc_conn_ref)
-{
- struct gsm_subscriber_connection *conn = msc_conn_ref;
- return msc_tx_common_id(conn);
-}
-
-/* VLR asks us to transmit a CM Service Reject */
-static int msc_vlr_tx_cm_serv_rej(void *msc_conn_ref, enum vlr_proc_arq_result result)
-{
- uint8_t cause;
- struct gsm_subscriber_connection *conn = msc_conn_ref;
- conn->received_cm_service_request = false;
-
- switch (result) {
- default:
- case VLR_PR_ARQ_RES_NONE:
- case VLR_PR_ARQ_RES_SYSTEM_FAILURE:
- case VLR_PR_ARQ_RES_UNKNOWN_ERROR:
- cause = GSM48_REJECT_NETWORK_FAILURE;
- break;
- case VLR_PR_ARQ_RES_ILLEGAL_SUBSCR:
- cause = GSM48_REJECT_LOC_NOT_ALLOWED;
- break;
- case VLR_PR_ARQ_RES_UNIDENT_SUBSCR:
- cause = GSM48_REJECT_INVALID_MANDANTORY_INF;
- break;
- case VLR_PR_ARQ_RES_ROAMING_NOTALLOWED:
- cause = GSM48_REJECT_ROAMING_NOT_ALLOWED;
- break;
- case VLR_PR_ARQ_RES_ILLEGAL_EQUIP:
- cause = GSM48_REJECT_ILLEGAL_MS;
- break;
- case VLR_PR_ARQ_RES_TIMEOUT:
- cause = GSM48_REJECT_CONGESTION;
- break;
- };
-
- return msc_gsm48_tx_mm_serv_rej(conn, cause);
-}
-
-/* VLR asks us to start using ciphering */
-static int msc_vlr_set_ciph_mode(void *msc_conn_ref,
- enum vlr_ciph ciph,
- bool retrieve_imeisv)
-{
- struct gsm_subscriber_connection *conn = msc_conn_ref;
- struct vlr_subscr *vsub;
- struct gsm_auth_tuple *tuple;
-
- if (!conn || !conn->vsub) {
- LOGP(DMM, LOGL_ERROR, "Cannot send Ciphering Mode Command to"
- " NULL conn/subscriber");
- return -EINVAL;
- }
-
- vsub = conn->vsub;
- tuple = vsub->last_tuple;
-
- if (!tuple) {
- LOGP(DMM, LOGL_ERROR, "subscr %s: Cannot send Ciphering Mode"
- " Command: no auth tuple available\n",
- vlr_subscr_name(vsub));
- return -EINVAL;
- }
-
- switch (conn->via_ran) {
- case RAN_GERAN_A:
- DEBUGP(DMM, "-> CIPHER MODE COMMAND %s\n",
- vlr_subscr_name(conn->vsub));
- return a_iface_tx_cipher_mode(conn, ciph, tuple->vec.kc, 8,
- retrieve_imeisv);
- case RAN_UTRAN_IU:
-#ifdef BUILD_IU
- DEBUGP(DMM, "-> SECURITY MODE CONTROL %s\n",
- vlr_subscr_name(conn->vsub));
- return ranap_iu_tx_sec_mode_cmd(conn->iu.ue_ctx, &tuple->vec, 0, 1);
-#else
- LOGP(DMM, LOGL_ERROR, "Cannot send Security Mode Control over RAN_UTRAN_IU,"
- " built without Iu support\n");
- return -ENOTSUP;
-#endif
-
- default:
- break;
- }
- LOGP(DMM, LOGL_ERROR,
- "%s: cannot start ciphering, unknown RAN type %d\n",
- vlr_subscr_name(conn->vsub), conn->via_ran);
- return -ENOTSUP;
-}
-
-void msc_rx_sec_mode_compl(struct gsm_subscriber_connection *conn)
-{
- struct vlr_ciph_result vlr_res = {};
-
- if (!conn || !conn->vsub) {
- LOGP(DMM, LOGL_ERROR,
- "Rx Security Mode Complete for invalid conn\n");
- return;
- }
-
- DEBUGP(DMM, "<- SECURITY MODE COMPLETE %s\n",
- vlr_subscr_name(conn->vsub));
-
- vlr_res.cause = VLR_CIPH_COMPL;
- vlr_subscr_rx_ciph_res(conn->vsub, &vlr_res);
-}
-
-/* VLR informs us that the subscriber data has somehow been modified */
-static void msc_vlr_subscr_update(struct vlr_subscr *subscr)
-{
- /* FIXME */
-}
-
-/* VLR informs us that the subscriber has been associated with a conn */
-static void msc_vlr_subscr_assoc(void *msc_conn_ref,
- struct vlr_subscr *vsub)
-{
- struct gsm_subscriber_connection *conn = msc_conn_ref;
- OSMO_ASSERT(!conn->vsub);
- conn->vsub = vlr_subscr_get(vsub);
- conn->vsub->cs.attached_via_ran = conn->via_ran;
-}
-
-/* operations that we need to implement for libvlr */
-static const struct vlr_ops msc_vlr_ops = {
- .tx_auth_req = msc_vlr_tx_auth_req,
- .tx_auth_rej = msc_vlr_tx_auth_rej,
- .tx_id_req = msc_vlr_tx_id_req,
- .tx_lu_acc = msc_vlr_tx_lu_acc,
- .tx_lu_rej = msc_vlr_tx_lu_rej,
- .tx_cm_serv_acc = msc_vlr_tx_cm_serv_acc,
- .tx_cm_serv_rej = msc_vlr_tx_cm_serv_rej,
- .set_ciph_mode = msc_vlr_set_ciph_mode,
- .tx_common_id = msc_vlr_tx_common_id,
- .subscr_update = msc_vlr_subscr_update,
- .subscr_assoc = msc_vlr_subscr_assoc,
-};
-
-/* Allocate net->vlr so that the VTY may configure the VLR's data structures */
-int msc_vlr_alloc(struct gsm_network *net)
-{
- net->vlr = vlr_alloc(net, &msc_vlr_ops);
- if (!net->vlr)
- return -ENOMEM;
- net->vlr->user_ctx = net;
- return 0;
-}
-
-/* Launch the VLR, i.e. its GSUP connection */
-int msc_vlr_start(struct gsm_network *net)
-{
- OSMO_ASSERT(net->vlr);
- return vlr_start("MSC", net->vlr, net->gsup_server_addr_str,
- net->gsup_server_port);
-}
diff --git a/src/libmsc/gsm_04_11.c b/src/libmsc/gsm_04_11.c
deleted file mode 100644
index 6949ac51f..000000000
--- a/src/libmsc/gsm_04_11.c
+++ /dev/null
@@ -1,1074 +0,0 @@
-/* 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 <daniel@totalueberwachung.de>
- * (C) 2009 by Harald Welte <laforge@gnumonks.org>
- * (C) 2010-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
- * (C) 2010 by On-Waves
- * (C) 2011 by Andreas Eversberg <jolly@eversberg.eu>
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <time.h>
-#include <netinet/in.h>
-
-#include "bscconfig.h"
-
-#include <osmocom/core/msgb.h>
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/utils.h>
-
-#include <osmocom/gsm/gsm_utils.h>
-#include <osmocom/gsm/gsm0411_utils.h>
-#include <osmocom/gsm/protocol/gsm_04_11.h>
-
-#include <openbsc/debug.h>
-#include <openbsc/gsm_data.h>
-#include <openbsc/db.h>
-#include <openbsc/gsm_subscriber.h>
-#include <openbsc/gsm_04_08.h>
-#include <openbsc/abis_rsl.h>
-#include <openbsc/signal.h>
-#include <openbsc/db.h>
-#include <openbsc/transaction.h>
-#include <openbsc/paging.h>
-#include <openbsc/bsc_rll.h>
-#include <openbsc/chan_alloc.h>
-#include <openbsc/msc_ifaces.h>
-#include <openbsc/osmo_msc.h>
-#include <openbsc/vlr.h>
-
-#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)
- vlr_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 vlr_subscr *receiver,
- struct vlr_subscr *sender,
- int dcs, const char *text)
-{
- struct gsm_sms *sms = sms_alloc();
-
- if (!sms)
- return NULL;
-
- sms->receiver = vlr_subscr_get(receiver);
- osmo_strlcpy(sms->text, text, sizeof(sms->text));
-
- osmo_strlcpy(sms->src.addr, sender->msisdn, sizeof(sms->src.addr));
- 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;
- osmo_strlcpy(sms->dst.addr, receiver->msisdn, sizeof(sms->dst.addr));
- /* 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 msc_tx_dtap(conn, msg);
-}
-
-/* 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, bool *deferred)
-{
- 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, deferred);
- if (rc == GSM411_RP_CAUSE_MO_NUM_UNASSIGNED)
- goto try_local;
- if (rc < 0) {
- LOGP(DLSMS, LOGL_ERROR, "%s: SMS delivery error: %d.",
- vlr_subscr_name(conn->vsub), rc);
- rc = GSM411_RP_CAUSE_MO_TEMP_FAIL;
- /* rc will be logged by gsm411_send_rp_error() */
- rate_ctr_inc(&conn->bts->network->msc_ctrs->ctr[
- MSC_CTR_SMS_DELIVER_UNKNOWN_ERROR]);
- }
- return rc;
- }
-
-try_local:
-#endif
-
- /* determine gsms->receiver based on dialled number */
- gsms->receiver = vlr_subscr_find_by_msisdn(conn->network->vlr,
- gsms->dst.addr);
- if (!gsms->receiver) {
-#ifdef BUILD_SMPP
- /* Avoid a second look-up */
- if (smpp_first) {
- rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_SMS_NO_RECEIVER]);
- return GSM411_RP_CAUSE_MO_NUM_UNASSIGNED;
- }
-
- rc = smpp_try_deliver(gsms, conn, deferred);
- if (rc == GSM411_RP_CAUSE_MO_NUM_UNASSIGNED) {
- rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_SMS_NO_RECEIVER]);
- } else if (rc < 0) {
- LOGP(DLSMS, LOGL_ERROR, "%s: SMS delivery error: %d.",
- vlr_subscr_name(conn->vsub), rc);
- rc = GSM411_RP_CAUSE_MO_TEMP_FAIL;
- /* rc will be logged by gsm411_send_rp_error() */
- rate_ctr_inc(&conn->bts->network->msc_ctrs->ctr[
- MSC_CTR_SMS_DELIVER_UNKNOWN_ERROR]);
- }
-#else
- rc = GSM411_RP_CAUSE_MO_NUM_UNASSIGNED;
- rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_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_trans *trans, struct msgb *msg,
- uint32_t gsm411_msg_ref, bool *deferred)
-{
- struct gsm_subscriber_connection *conn = trans->conn;
- uint8_t *smsp = msgb_sms(msg);
- struct gsm_sms *gsms;
- unsigned int sms_alphabet;
- uint8_t sms_mti, sms_vpf;
- 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;
-
- rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_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_vpf = (*smsp & 0x18) >> 3;
- gsms->status_rep_req = (*smsp & 0x20);
- gsms->ud_hdr_ind = (*smsp & 0x40);
- /*
- * Not evaluating MMS (More Messages to Send) because the
- * lchan stays open anyway.
- * Not evaluating RP (Reply Path) because we're not aware of its
- * benefits.
- */
-
- smsp++;
- gsms->msg_ref = *smsp++;
-
- gsms->gsm411.transaction_id = trans->transaction_id;
- gsms->gsm411.msg_ref = gsm411_msg_ref;
-
- /* 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;
- }
- }
-
- osmo_strlcpy(gsms->src.addr, conn->vsub->msisdn, sizeof(gsms->src.addr));
-
- 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",
- vlr_subscr_name(conn->vsub), 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, deferred);
-out:
- if (!deferred)
- 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);
-}
-
-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);
-}
-
-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)
-{
- bool deferred = false;
- 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, msg, rph->msg_ref, &deferred);
- if (rc == 0 && !deferred)
- 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->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",
- vlr_subscr_name(trans->conn->vsub), 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);
- rate_ctr_inc(&net->msc_ctrs->ctr[MSC_CTR_SMS_RP_ERR_MEM]);
- } else {
- send_signal(S_SMS_UNKNOWN_ERROR, trans, sms, 0);
- rate_ctr_inc(&net->msc_ctrs->ctr[MSC_CTR_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 = gsm48_hdr_trans_id_flip_ti(gh);
- struct gsm_trans *trans;
- int new_trans = 0;
- int rc = 0;
-
- if (!conn->vsub)
- return -EIO;
- /* FIXME: send some error message */
-
- DEBUGP(DLSMS, "receiving data (trans_id=%x, msg_type=%d)\n", transaction_id, msg_type);
- 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->network, conn->vsub,
- 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 = msc_subscr_conn_get(conn);
-
- new_trans = 1;
- cm_service_request_concludes(conn, msg);
- }
-
- /* 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);
- }
- }
-
- msc_subscr_conn_communicating(conn);
-
- 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 conn. We also assume that the caller ensured this conn 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->next_rp_ref);
- int transaction_id;
- int rc;
-
- transaction_id =
- trans_assign_trans_id(conn->network, conn->vsub,
- 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, "%s()\n", __func__);
-
- /* FIXME: allocate transaction with message reference */
- trans = trans_alloc(conn->network, conn->vsub,
- 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 = msc_subscr_conn_get(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");
-
- rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_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 vlr_subscr *vsub,
- struct gsm_sms *sms)
-{
- struct gsm_subscriber_connection *conn;
- void *res;
-
- /* check if we already have an open conn to the subscriber.
- * if yes, send the SMS this way */
- conn = connection_for_subscr(vsub);
- if (conn) {
- LOGP(DLSMS, LOGL_DEBUG, "Sending SMS via already open connection %p to %s\n",
- conn, vlr_subscr_name(vsub));
- return gsm411_send_sms(conn, sms);
- }
-
- /* if not, we have to start paging */
- LOGP(DLSMS, LOGL_DEBUG, "Sending SMS: no connection open, start paging %s\n",
- vlr_subscr_name(vsub));
- res = subscr_request_conn(vsub, paging_cb_send_sms, sms, "send 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;
- }
-}
-
-/* Process incoming SAPI N-REJECT from BSC */
-void gsm411_sapi_n_reject(struct gsm_subscriber_connection *conn)
-{
- struct gsm_network *net;
- struct gsm_trans *trans, *tmp;
-
- net = conn->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);
- }
-}
-
diff --git a/src/libmsc/gsm_04_80.c b/src/libmsc/gsm_04_80.c
deleted file mode 100644
index bec1d26f4..000000000
--- a/src/libmsc/gsm_04_80.c
+++ /dev/null
@@ -1,155 +0,0 @@
-/* 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 <laforge@gnumonks.org>
- * (C) 2008, 2009, 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
- * (C) 2009 by Mike Haben <michael.haben@btinternet.com>
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-
-#include <openbsc/debug.h>
-#include <openbsc/gsm_data.h>
-#include <openbsc/gsm_04_08.h>
-#include <openbsc/gsm_04_80.h>
-#include <openbsc/msc_ifaces.h>
-
-#include <osmocom/gsm/gsm0480.h>
-#include <osmocom/gsm/gsm_utils.h>
-#include <osmocom/core/msgb.h>
-#include <osmocom/gsm/tlv.h>
-
-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 ss_request *req)
-{
- struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 USSD RSP");
- 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 msc_tx_dtap(conn, msg);
-}
-
-int gsm0480_send_ussd_reject(struct gsm_subscriber_connection *conn,
- const struct msgb *in_msg,
- const struct ss_request *req)
-{
- struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 USSD REJ");
- 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 msc_tx_dtap(conn, msg);
-}
-
-int msc_send_ussd_notify(struct gsm_subscriber_connection *conn, int level, const char *text)
-{
- struct msgb *msg = gsm0480_create_ussd_notify(level, text);
- if (!msg)
- return -1;
- return msc_tx_dtap(conn, msg);
-}
-
-int msc_send_ussd_release_complete(struct gsm_subscriber_connection *conn)
-{
- struct msgb *msg = gsm0480_create_ussd_release_complete();
- if (!msg)
- return -1;
- return msc_tx_dtap(conn, msg);
-}
diff --git a/src/libmsc/gsm_subscriber.c b/src/libmsc/gsm_subscriber.c
deleted file mode 100644
index 09540c16c..000000000
--- a/src/libmsc/gsm_subscriber.c
+++ /dev/null
@@ -1,190 +0,0 @@
-/* The concept of a subscriber for the MSC, roughly HLR/VLR functionality */
-
-/* (C) 2008 by Harald Welte <laforge@gnumonks.org>
- * (C) 2009,2013 by Holger Hans Peter Freyther <zecke@selfish.org>
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include "../../bscconfig.h"
-
-#include <unistd.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <assert.h>
-#include <time.h>
-#include <stdbool.h>
-
-#include <osmocom/core/talloc.h>
-
-#include <osmocom/vty/vty.h>
-
-#ifdef BUILD_IU
-#include <osmocom/ranap/iu_client.h>
-#else
-#include <openbsc/iu_dummy.h>
-#endif
-
-#include <openbsc/gsm_subscriber.h>
-#include <openbsc/gsm_04_08.h>
-#include <openbsc/debug.h>
-#include <openbsc/paging.h>
-#include <openbsc/signal.h>
-#include <openbsc/db.h>
-#include <openbsc/chan_alloc.h>
-#include <openbsc/vlr.h>
-#include <openbsc/osmo_msc.h>
-#include <openbsc/msc_ifaces.h>
-#include <openbsc/a_iface.h>
-
-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 vlr_subscr *vsub = param;
- struct paging_signal_data sig_data;
-
- OSMO_ASSERT(vsub);
- OSMO_ASSERT(hooknum == GSM_HOOK_RR_PAGING);
- OSMO_ASSERT(!(conn && (conn->vsub != vsub)));
- OSMO_ASSERT(!((event == GSM_PAGING_SUCCEEDED) && !conn));
-
- LOGP(DPAG, LOGL_DEBUG, "Paging %s for %s (event=%d)\n",
- event == GSM_PAGING_SUCCEEDED ? "success" : "failure",
- vlr_subscr_name(vsub), event);
-
- if (!vsub->cs.is_paging) {
- LOGP(DPAG, LOGL_ERROR,
- "Paging Response received for subscriber"
- " that is not paging.\n");
- return -EINVAL;
- }
-
- if (event == GSM_PAGING_SUCCEEDED)
- msc_stop_paging(vsub);
-
- /* Inform parts of the system we don't know */
- sig_data.vsub = vsub;
- 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, &vsub->cs.requests, entry) {
- llist_del(&request->entry);
- if (request->cbfn) {
- LOGP(DPAG, LOGL_DEBUG, "Calling paging cbfn.\n");
- request->cbfn(hooknum, event, msg, data, request->param);
- } else
- LOGP(DPAG, LOGL_DEBUG, "Paging without action.\n");
- talloc_free(request);
- }
-
- /* balanced with the moment we start paging */
- vsub->cs.is_paging = false;
- vlr_subscr_put(vsub);
- return 0;
-}
-
-int msc_paging_request(struct vlr_subscr *vsub)
-{
- /* The subscriber was last seen in subscr->lac. Find out which
- * BSCs/RNCs are responsible and send them a paging request via open
- * SCCP connections (if any). */
- /* TODO Implementing only RNC paging, since this is code on the iu branch.
- * Need to add BSC paging at some point. */
- switch (vsub->cs.attached_via_ran) {
- case RAN_GERAN_A:
- return a_iface_tx_paging(vsub->imsi, vsub->tmsi, vsub->lac);
- case RAN_UTRAN_IU:
- return ranap_iu_page_cs(vsub->imsi,
- vsub->tmsi == GSM_RESERVED_TMSI?
- NULL : &vsub->tmsi,
- vsub->lac);
- default:
- break;
- }
-
- LOGP(DPAG, LOGL_ERROR, "%s: Cannot page, subscriber not attached\n",
- vlr_subscr_name(vsub));
- return -EINVAL;
-}
-
-/*! \brief Start a paging request for vsub, call cbfn(param) when done.
- * \param vsub subscriber to page.
- * \param cbfn function to call when the conn is established.
- * \param param caller defined param to pass to cbfn().
- * \param label human readable label of the request kind used for logging.
- */
-struct subscr_request *subscr_request_conn(struct vlr_subscr *vsub,
- gsm_cbfn *cbfn, void *param,
- const char *label)
-{
- int rc;
- struct subscr_request *request;
-
- /* Start paging.. we know it is async so we can do it before */
- if (!vsub->cs.is_paging) {
- LOGP(DMM, LOGL_DEBUG, "Subscriber %s not paged yet, start paging.\n",
- vlr_subscr_name(vsub));
- rc = msc_paging_request(vsub);
- if (rc <= 0) {
- LOGP(DMM, LOGL_ERROR, "Subscriber %s paging failed: %d\n",
- vlr_subscr_name(vsub), rc);
- return NULL;
- }
- /* reduced on the first paging callback */
- vlr_subscr_get(vsub);
- vsub->cs.is_paging = true;
- } else {
- LOGP(DMM, LOGL_DEBUG, "Subscriber %s already paged.\n",
- vlr_subscr_name(vsub));
- }
-
- /* TODO: Stop paging in case of memory allocation failure */
- request = talloc_zero(vsub, struct subscr_request);
- if (!request)
- return NULL;
-
- request->cbfn = cbfn;
- request->param = param;
- llist_add_tail(&request->entry, &vsub->cs.requests);
- return request;
-}
-
-void subscr_remove_request(struct subscr_request *request)
-{
- llist_del(&request->entry);
- talloc_free(request);
-}
-
-struct gsm_subscriber_connection *connection_for_subscr(struct vlr_subscr *vsub)
-{
- struct gsm_network *net = vsub->vlr->user_ctx;
- struct gsm_subscriber_connection *conn;
-
- llist_for_each_entry(conn, &net->subscr_conns, entry) {
- if (conn->vsub == vsub)
- return conn;
- }
-
- return NULL;
-}
diff --git a/src/libmsc/iu_dummy.c b/src/libmsc/iu_dummy.c
deleted file mode 100644
index e9d335e2e..000000000
--- a/src/libmsc/iu_dummy.c
+++ /dev/null
@@ -1,93 +0,0 @@
-/* Trivial switch-off of external Iu dependencies,
- * allowing to run full unit tests even when built without Iu support. */
-
-/*
- * (C) 2016,2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
- *
- * Author: Neels Hofmeyr <nhofmeyr@sysmocom.de>
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include "../../bscconfig.h"
-#ifndef BUILD_IU
-
-#include <openbsc/iu_dummy.h>
-
-#include <osmocom/core/logging.h>
-#include <osmocom/vty/logging.h>
-#include <osmocom/core/msgb.h>
-
-struct msgb;
-struct ranap_ue_conn_ctx;
-struct gsm_auth_tuple;
-struct RANAP_Cause;
-struct osmo_auth_vector;
-
-int ranap_iu_tx(struct msgb *msg, uint8_t sapi)
-{
- LOGP(DLGLOBAL, LOGL_INFO, "iu_tx() dummy called, NOT transmitting %d bytes: %s\n",
- msg->len, osmo_hexdump(msg->data, msg->len));
- return 0;
-}
-
-int ranap_iu_tx_sec_mode_cmd(struct ranap_ue_conn_ctx *uectx, struct osmo_auth_vector *vec,
- int send_ck)
-{
- LOGP(DLGLOBAL, LOGL_INFO, "iu_tx_sec_mode_cmd() dummy called, NOT transmitting Security Mode Command\n");
- return 0;
-}
-
-int ranap_iu_page_cs(const char *imsi, const uint32_t *tmsi, uint16_t lac)
-{
- LOGP(DLGLOBAL, LOGL_INFO, "iu_page_cs() dummy called, NOT paging\n");
- return 23;
-}
-
-int ranap_iu_page_ps(const char *imsi, const uint32_t *ptmsi, uint16_t lac, uint8_t rac)
-{
- LOGP(DLGLOBAL, LOGL_INFO, "iu_page_ps() dummy called, NOT paging\n");
- return 0;
-}
-
-struct msgb *ranap_new_msg_rab_assign_voice(uint8_t rab_id, uint32_t rtp_ip,
- uint16_t rtp_port,
- bool use_x213_nsap)
-{
- LOGP(DLGLOBAL, LOGL_INFO, "ranap_new_msg_rab_assign_voice() dummy called, NOT composing RAB Assignment\n");
- return NULL;
-}
-
-int ranap_iu_rab_act(struct ranap_ue_conn_ctx *ue_ctx, struct msgb *msg)
-{
- LOGP(DLGLOBAL, LOGL_INFO, "iu_rab_act() dummy called, NOT activating RAB\n");
- return 0;
-}
-
-int ranap_iu_tx_common_id(struct ranap_ue_conn_ctx *uectx, const char *imsi)
-{
- LOGP(DLGLOBAL, LOGL_INFO, "iu_tx_common_id() dummy called, NOT sending CommonID\n");
- return 0;
-}
-
-int ranap_iu_tx_release(struct ranap_ue_conn_ctx *ctx, const struct RANAP_Cause *cause)
-{
- LOGP(DLGLOBAL, LOGL_INFO, "iu_tx_release() dummy called, NOT sending Release\n");
- return 0;
-}
-
-#endif
diff --git a/src/libmsc/iucs.c b/src/libmsc/iucs.c
deleted file mode 100644
index 04b9ece7d..000000000
--- a/src/libmsc/iucs.c
+++ /dev/null
@@ -1,189 +0,0 @@
-/* Code to manage MSC subscriber connections over IuCS interface */
-
-/*
- * (C) 2016,2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
- *
- * Author: Neels Hofmeyr <nhofmeyr@sysmocom.de>
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <inttypes.h>
-
-#include <osmocom/core/logging.h>
-#include <osmocom/ranap/iu_client.h>
-#include <openbsc/debug.h>
-
-#include <openbsc/gsm_data.h>
-#include <openbsc/gsm_subscriber.h>
-#include <openbsc/osmo_msc.h>
-#include <openbsc/vlr.h>
-
-/* For A-interface see libbsc/bsc_api.c subscr_con_allocate() */
-static struct gsm_subscriber_connection *subscr_conn_allocate_iu(struct gsm_network *network,
- struct ranap_ue_conn_ctx *ue,
- uint16_t lac)
-{
- struct gsm_subscriber_connection *conn;
-
- DEBUGP(DIUCS, "Allocating IuCS subscriber conn: lac %d, conn_id %" PRIx32 "\n",
- lac, ue->conn_id);
-
- conn = talloc_zero(network, struct gsm_subscriber_connection);
- if (!conn)
- return NULL;
-
- conn->network = network;
- conn->via_ran = RAN_UTRAN_IU;
- conn->iu.ue_ctx = ue;
- conn->iu.ue_ctx->rab_assign_addr_enc = network->iu.rab_assign_addr_enc;
- conn->lac = lac;
-
- llist_add_tail(&conn->entry, &network->subscr_conns);
- return conn;
-}
-
-static int same_ue_conn(struct ranap_ue_conn_ctx *a, struct ranap_ue_conn_ctx *b)
-{
- if (a == b)
- return 1;
- return (a->conn_id == b->conn_id);
-}
-
-static inline void log_subscribers(struct gsm_network *network)
-{
- if (!log_check_level(DIUCS, LOGL_DEBUG))
- return;
-
- struct gsm_subscriber_connection *conn;
- int i = 0;
- llist_for_each_entry(conn, &network->subscr_conns, entry) {
- DEBUGP(DIUCS, "%3d: %s", i, vlr_subscr_name(conn->vsub));
- switch (conn->via_ran) {
- case RAN_UTRAN_IU:
- DEBUGPC(DIUCS, " Iu");
- if (conn->iu.ue_ctx) {
- DEBUGPC(DIUCS, " conn_id %d",
- conn->iu.ue_ctx->conn_id
- );
- }
- break;
- case RAN_GERAN_A:
- DEBUGPC(DIUCS, " A");
- /* TODO log A-interface connection details */
- break;
- case RAN_UNKNOWN:
- DEBUGPC(DIUCS, " ?");
- break;
- default:
- DEBUGPC(DIUCS, " invalid");
- break;
- }
- DEBUGPC(DIUCS, "\n");
- i++;
- }
- DEBUGP(DIUCS, "subscribers registered: %d\n", i);
-}
-
-/* Return an existing IuCS subscriber connection record for the given
- * connection IDs, or return NULL if not found. */
-struct gsm_subscriber_connection *subscr_conn_lookup_iu(
- struct gsm_network *network,
- struct ranap_ue_conn_ctx *ue)
-{
- struct gsm_subscriber_connection *conn;
-
- DEBUGP(DIUCS, "Looking for IuCS subscriber: conn_id %" PRIx32 "\n",
- ue->conn_id);
- log_subscribers(network);
-
- llist_for_each_entry(conn, &network->subscr_conns, entry) {
- if (conn->via_ran != RAN_UTRAN_IU)
- continue;
- if (!same_ue_conn(conn->iu.ue_ctx, ue))
- continue;
- DEBUGP(DIUCS, "Found IuCS subscriber for conn_id %" PRIx32 "\n",
- ue->conn_id);
- return conn;
- }
- DEBUGP(DIUCS, "No IuCS subscriber found for conn_id %" PRIx32 "\n",
- ue->conn_id);
- return NULL;
-}
-
-/* Receive MM/CC/... message from IuCS (SCCP user SAP).
- * msg->dst must reference a struct ranap_ue_conn_ctx, which identifies the peer that
- * sent the msg.
- *
- * For A-interface see libbsc/bsc_api.c gsm0408_rcvmsg(). */
-int gsm0408_rcvmsg_iucs(struct gsm_network *network, struct msgb *msg,
- uint16_t *lac)
-{
- int rc;
- struct ranap_ue_conn_ctx *ue_ctx;
- struct gsm_subscriber_connection *conn;
-
- ue_ctx = (struct ranap_ue_conn_ctx*)msg->dst;
-
- /* TODO: are there message types that could allow us to skip this
- * search? */
- conn = subscr_conn_lookup_iu(network, ue_ctx);
-
- if (conn && lac && (conn->lac != *lac)) {
- LOGP(DIUCS, LOGL_ERROR, "IuCS subscriber has changed LAC"
- " within the same connection, discarding connection:"
- " %s from LAC %d to %d\n",
- vlr_subscr_name(conn->vsub), conn->lac, *lac);
- /* Deallocate conn with previous LAC */
- msc_subscr_conn_close(conn, GSM_CAUSE_INV_MAND_INFO);
- /* At this point we could be tolerant and allocate a new
- * connection, but changing the LAC within the same connection
- * is shifty. Rather cancel everything. */
- return -1;
- }
-
- if (conn) {
- /* Make sure we don't receive RR over IuCS; otherwise all
- * messages handled by gsm0408_dispatch() are of interest (CC,
- * MM, SMS, NS_SS, maybe even MM_GPRS and SM_GPRS). */
- struct gsm48_hdr *gh = msgb_l3(msg);
- uint8_t pdisc = gh->proto_discr & 0x0f;
- OSMO_ASSERT(pdisc != GSM48_PDISC_RR);
-
- msc_dtap(conn, ue_ctx->conn_id, msg);
- rc = 0;
- } else {
- /* allocate a new connection */
-
- if (!lac) {
- LOGP(DIUCS, LOGL_ERROR, "New IuCS subscriber"
- " but no LAC available. Expecting an InitialUE"
- " message containing a LAI IE."
- " Dropping connection.\n");
- return -1;
- }
-
- conn = subscr_conn_allocate_iu(network, ue_ctx, *lac);
- if (!conn)
- abort();
-
- /* ownership of conn hereby goes to the MSC: */
- rc = msc_compl_l3(conn, msg, 0);
- }
-
- return rc;
-}
diff --git a/src/libmsc/iucs_ranap.c b/src/libmsc/iucs_ranap.c
deleted file mode 100644
index 45de1caca..000000000
--- a/src/libmsc/iucs_ranap.c
+++ /dev/null
@@ -1,104 +0,0 @@
-/* Implementation of RANAP messages to/from an MSC via an Iu-CS interface.
- * This keeps direct RANAP dependencies out of libmsc. */
-
-/* (C) 2016 by sysmocom s.m.f.c. GmbH <info@sysmocom.de>
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include "../../bscconfig.h"
-
-#ifdef BUILD_IU
-
-#include <osmocom/core/logging.h>
-
-#include <osmocom/ranap/ranap_ies_defs.h>
-#include <osmocom/ranap/iu_client.h>
-
-#include <openbsc/debug.h>
-#include <openbsc/gsm_data.h>
-#include <openbsc/gsm_subscriber.h>
-#include <openbsc/iucs.h>
-#include <openbsc/vlr.h>
-#include <openbsc/iucs_ranap.h>
-#include <openbsc/osmo_msc.h>
-
-/* To continue authorization after a Security Mode Complete */
-int gsm0408_authorize(struct gsm_subscriber_connection *conn);
-
-static int iucs_rx_rab_assign(struct gsm_subscriber_connection *conn,
- RANAP_RAB_SetupOrModifiedItemIEs_t *setup_ies)
-{
- uint8_t rab_id;
- RANAP_RAB_SetupOrModifiedItem_t *item = &setup_ies->raB_SetupOrModifiedItem;
-
- rab_id = item->rAB_ID.buf[0];
-
- LOGP(DIUCS, LOGL_NOTICE,
- "Received RAB assignment event for %s rab_id=%hhd\n",
- vlr_subscr_name(conn->vsub), rab_id);
-
- return 0;
-}
-
-int iucs_rx_sec_mode_compl(struct gsm_subscriber_connection *conn,
- RANAP_SecurityModeCompleteIEs_t *ies)
-{
- OSMO_ASSERT(conn->via_ran == RAN_UTRAN_IU);
-
- /* TODO evalute ies */
-
- msc_rx_sec_mode_compl(conn);
- return 0;
-}
-
-int iucs_rx_ranap_event(struct gsm_network *network,
- struct ranap_ue_conn_ctx *ue_ctx, int type, void *data)
-{
- struct gsm_subscriber_connection *conn;
-
- conn = subscr_conn_lookup_iu(network, ue_ctx);
-
- if (!conn) {
- LOGP(DRANAP, LOGL_ERROR, "Cannot find subscriber for IU event %u\n", type);
- return -1;
- }
-
- switch (type) {
- case RANAP_IU_EVENT_IU_RELEASE:
- case RANAP_IU_EVENT_LINK_INVALIDATED:
- LOGP(DIUCS, LOGL_INFO, "IuCS release for %s\n",
- vlr_subscr_name(conn->vsub));
- msc_subscr_conn_close(conn, 0);
- return 0;
-
- case RANAP_IU_EVENT_SECURITY_MODE_COMPLETE:
- LOGP(DIUCS, LOGL_INFO, "IuCS security mode complete for %s\n",
- vlr_subscr_name(conn->vsub));
- return iucs_rx_sec_mode_compl(conn,
- (RANAP_SecurityModeCompleteIEs_t*)data);
- case RANAP_IU_EVENT_RAB_ASSIGN:
- return iucs_rx_rab_assign(conn,
- (RANAP_RAB_SetupOrModifiedItemIEs_t*)data);
- default:
- LOGP(DIUCS, LOGL_NOTICE, "Unknown message received:"
- " RANAP event: %i\n", type);
- return -1;
- }
-}
-
-#endif /* BUILD_IU */
diff --git a/src/libmsc/meas_feed.c b/src/libmsc/meas_feed.c
deleted file mode 100644
index 1e7b4cd51..000000000
--- a/src/libmsc/meas_feed.c
+++ /dev/null
@@ -1,168 +0,0 @@
-/* UDP-Feed of measurement reports */
-
-#include <unistd.h>
-
-#include <sys/socket.h>
-
-#include <osmocom/core/msgb.h>
-#include <osmocom/core/socket.h>
-#include <osmocom/core/write_queue.h>
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/utils.h>
-
-#include <osmocom/vty/command.h>
-#include <osmocom/vty/vty.h>
-
-#include <openbsc/meas_rep.h>
-#include <openbsc/signal.h>
-#include <openbsc/gsm_subscriber.h>
-#include <openbsc/meas_feed.h>
-#include <openbsc/vty.h>
-#include <openbsc/vlr.h>
-
-#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 vlr_subscr *vsub;
-
- /* ignore measurements as long as we don't know who it is */
- if (!mr->lchan || !mr->lchan->conn || !mr->lchan->conn->vsub)
- return 0;
-
- vsub = mr->lchan->conn->vsub;
-
- 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 */
- osmo_strlcpy(mfm->imsi, vsub->imsi, sizeof(mfm->imsi));
- osmo_strlcpy(mfm->name, vsub->name, sizeof(mfm->name));
- osmo_strlcpy(mfm->scenario, g_mfs.scenario, sizeof(mfm->scenario));
-
- /* 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)
-{
- osmo_strlcpy(g_mfs.scenario, name, sizeof(g_mfs.scenario));
-}
-
-const char *meas_feed_scenario_get(void)
-{
- return g_mfs.scenario;
-}
diff --git a/src/libmsc/meas_feed.h b/src/libmsc/meas_feed.h
deleted file mode 100644
index 782a9616c..000000000
--- a/src/libmsc/meas_feed.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef _INT_MEAS_FEED_H
-#define _INT_MEAS_FEED_H
-
-#include <stdint.h>
-
-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 */
diff --git a/src/libmsc/mncc.c b/src/libmsc/mncc.c
deleted file mode 100644
index 8110eadca..000000000
--- a/src/libmsc/mncc.c
+++ /dev/null
@@ -1,107 +0,0 @@
-/* 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 <laforge@gnumonks.org>
- * (C) 2009 by Andreas Eversberg <Andreas.Eversberg@versatel.de>
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/utils.h>
-
-#include <openbsc/gsm_04_08.h>
-#include <openbsc/debug.h>
-#include <openbsc/mncc.h>
-#include <openbsc/gsm_data.h>
-#include <openbsc/transaction.h>
-#include <openbsc/rtp_proxy.h>
-
-
-static const struct value_string mncc_names[] = {
- { MNCC_SETUP_REQ, "MNCC_SETUP_REQ" },
- { MNCC_SETUP_IND, "MNCC_SETUP_IND" },
- { MNCC_SETUP_RSP, "MNCC_SETUP_RSP" },
- { MNCC_SETUP_CNF, "MNCC_SETUP_CNF" },
- { MNCC_SETUP_COMPL_REQ, "MNCC_SETUP_COMPL_REQ" },
- { MNCC_SETUP_COMPL_IND, "MNCC_SETUP_COMPL_IND" },
- { MNCC_CALL_CONF_IND, "MNCC_CALL_CONF_IND" },
- { MNCC_CALL_PROC_REQ, "MNCC_CALL_PROC_REQ" },
- { MNCC_PROGRESS_REQ, "MNCC_PROGRESS_REQ" },
- { MNCC_ALERT_REQ, "MNCC_ALERT_REQ" },
- { MNCC_ALERT_IND, "MNCC_ALERT_IND" },
- { MNCC_NOTIFY_REQ, "MNCC_NOTIFY_REQ" },
- { MNCC_NOTIFY_IND, "MNCC_NOTIFY_IND" },
- { MNCC_DISC_REQ, "MNCC_DISC_REQ" },
- { MNCC_DISC_IND, "MNCC_DISC_IND" },
- { MNCC_REL_REQ, "MNCC_REL_REQ" },
- { MNCC_REL_IND, "MNCC_REL_IND" },
- { MNCC_REL_CNF, "MNCC_REL_CNF" },
- { MNCC_FACILITY_REQ, "MNCC_FACILITY_REQ" },
- { MNCC_FACILITY_IND, "MNCC_FACILITY_IND" },
- { MNCC_START_DTMF_IND, "MNCC_START_DTMF_IND" },
- { MNCC_START_DTMF_RSP, "MNCC_START_DTMF_RSP" },
- { MNCC_START_DTMF_REJ, "MNCC_START_DTMF_REJ" },
- { MNCC_STOP_DTMF_IND, "MNCC_STOP_DTMF_IND" },
- { MNCC_STOP_DTMF_RSP, "MNCC_STOP_DTMF_RSP" },
- { MNCC_MODIFY_REQ, "MNCC_MODIFY_REQ" },
- { MNCC_MODIFY_IND, "MNCC_MODIFY_IND" },
- { MNCC_MODIFY_RSP, "MNCC_MODIFY_RSP" },
- { MNCC_MODIFY_CNF, "MNCC_MODIFY_CNF" },
- { MNCC_MODIFY_REJ, "MNCC_MODIFY_REJ" },
- { MNCC_HOLD_IND, "MNCC_HOLD_IND" },
- { MNCC_HOLD_CNF, "MNCC_HOLD_CNF" },
- { MNCC_HOLD_REJ, "MNCC_HOLD_REJ" },
- { MNCC_RETRIEVE_IND, "MNCC_RETRIEVE_IND" },
- { MNCC_RETRIEVE_CNF, "MNCC_RETRIEVE_CNF" },
- { MNCC_RETRIEVE_REJ, "MNCC_RETRIEVE_REJ" },
- { MNCC_USERINFO_REQ, "MNCC_USERINFO_REQ" },
- { MNCC_USERINFO_IND, "MNCC_USERINFO_IND" },
- { MNCC_REJ_REQ, "MNCC_REJ_REQ" },
- { MNCC_REJ_IND, "MNCC_REJ_IND" },
- { MNCC_BRIDGE, "MNCC_BRIDGE" },
- { MNCC_FRAME_RECV, "MNCC_FRAME_RECV" },
- { MNCC_FRAME_DROP, "MNCC_FRAME_DROP" },
- { MNCC_LCHAN_MODIFY, "MNCC_LCHAN_MODIFY" },
- { MNCC_RTP_CREATE, "MNCC_RTP_CREATE" },
- { MNCC_RTP_CONNECT, "MNCC_RTP_CONNECT" },
- { MNCC_RTP_FREE, "MNCC_RTP_FREE" },
- { GSM_TCHF_FRAME, "GSM_TCHF_FRAME" },
- { GSM_TCHF_FRAME_EFR, "GSM_TCHF_FRAME_EFR" },
- { GSM_TCHH_FRAME, "GSM_TCHH_FRAME" },
- { GSM_TCH_FRAME_AMR, "GSM_TCH_FRAME_AMR" },
- { GSM_BAD_FRAME, "GSM_BAD_FRAME" },
- { 0, NULL },
-};
-
-const char *get_mncc_name(int value)
-{
- return get_value_string(mncc_names, value);
-}
-
-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;
-}
-
diff --git a/src/libmsc/mncc_builtin.c b/src/libmsc/mncc_builtin.c
deleted file mode 100644
index ac6e7345d..000000000
--- a/src/libmsc/mncc_builtin.c
+++ /dev/null
@@ -1,383 +0,0 @@
-/* mncc_builtin.c - default, minimal built-in MNCC Application for
- * standalone bsc_hack (network-in-the-box mode) */
-
-/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org>
- * (C) 2009 by Andreas Eversberg <Andreas.Eversberg@versatel.de>
- * 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 <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-
-#include <openbsc/gsm_04_08.h>
-#include <openbsc/debug.h>
-#include <openbsc/mncc.h>
-#include <openbsc/mncc_int.h>
-#include <osmocom/core/talloc.h>
-#include <openbsc/gsm_data.h>
-#include <openbsc/transaction.h>
-#include <openbsc/rtp_proxy.h>
-
-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;
-}
-
-/* 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;
- 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;
- struct gsm_mncc_bridge bridge = { .msg_type = MNCC_BRIDGE };
-
- /* 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 */
- bridge.callref[0] = call->callref;
- bridge.callref[1] = call->remote_ref;
- DEBUGP(DMNCC, "(call %x) Bridging with remote.\n", call->callref);
-
- /* 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, &bridge);
- } 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;
-}
-
-/* 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)) {
- LOGP(DMNCC, LOGL_ERROR, "(call %x) Received data frame, which is not supported.\n",
- call->callref);
- 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 */
- 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:
- rc = mncc_tx_to_cc(net, MNCC_START_DTMF_REJ, data);
- break;
- case MNCC_STOP_DTMF_IND:
- rc = mncc_tx_to_cc(net, MNCC_STOP_DTMF_RSP, data);
- 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;
-}
diff --git a/src/libmsc/mncc_sock.c b/src/libmsc/mncc_sock.c
deleted file mode 100644
index 0c696f2d9..000000000
--- a/src/libmsc/mncc_sock.c
+++ /dev/null
@@ -1,317 +0,0 @@
-/* mncc_sock.c: Tie the MNCC interface to a unix domain socket */
-
-/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org>
- * (C) 2009 by Andreas Eversberg <Andreas.Eversberg@versatel.de>
- * (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 <stdio.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <assert.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/select.h>
-#include <osmocom/core/socket.h>
-#include <osmocom/gsm/protocol/gsm_04_08.h>
-
-#include <openbsc/debug.h>
-#include <openbsc/mncc.h>
-#include <openbsc/gsm_data.h>
-
-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;
-}
-
-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);
-
- 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, const char *sock_path)
-{
- 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;
-
- bfd->fd = osmo_sock_unix_init(SOCK_SEQPACKET, 0, sock_path,
- OSMO_SOCK_F_BIND);
- if (bfd->fd < 0) {
- LOGP(DMNCC, LOGL_ERROR, "Could not create unix socket: %s: %s\n",
- sock_path, strerror(errno));
- talloc_free(state);
- return -1;
- }
-
- 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;
-
- LOGP(DMNCC, LOGL_NOTICE, "MNCC socket at %s\n", sock_path);
- return 0;
-}
diff --git a/src/libmsc/msc_ifaces.c b/src/libmsc/msc_ifaces.c
deleted file mode 100644
index 161a10019..000000000
--- a/src/libmsc/msc_ifaces.c
+++ /dev/null
@@ -1,423 +0,0 @@
-/* Implementation for MSC decisions which interface to send messages out on. */
-
-/* (C) 2016 by sysmocom s.m.f.c GmbH <info@sysmocom.de>
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <osmocom/core/logging.h>
-
-#include <openbsc/debug.h>
-#include <openbsc/gsm_data.h>
-#include <openbsc/msc_ifaces.h>
-#include <openbsc/gsm_subscriber.h>
-#include <openbsc/transaction.h>
-#include <osmocom/legacy_mgcp/mgcp.h>
-#include <osmocom/legacy_mgcp/mgcpgw_client.h>
-#include <openbsc/vlr.h>
-#include <openbsc/a_iface.h>
-
-#include "../../bscconfig.h"
-
-#ifdef BUILD_IU
-#include <osmocom/ranap/iu_client.h>
-extern struct msgb *ranap_new_msg_rab_assign_voice(uint8_t rab_id,
- uint32_t rtp_ip,
- uint16_t rtp_port,
- bool use_x213_nsap);
-#else
-#include <openbsc/iu_dummy.h>
-#endif /* BUILD_IU */
-
-static int msc_tx(struct gsm_subscriber_connection *conn, struct msgb *msg)
-{
- if (!conn)
- return -EINVAL;
- if (!msg)
- return -EINVAL;
-
- DEBUGP(DMSC, "msc_tx %u bytes to %s via %s\n",
- msg->len, vlr_subscr_name(conn->vsub),
- ran_type_name(conn->via_ran));
- switch (conn->via_ran) {
- case RAN_GERAN_A:
- msg->dst = conn;
- return a_iface_tx_dtap(msg);
-
- case RAN_UTRAN_IU:
- msg->dst = conn->iu.ue_ctx;
- return ranap_iu_tx(msg, 0);
-
- default:
- LOGP(DMSC, LOGL_ERROR,
- "msc_tx(): conn->via_ran invalid (%d)\n",
- conn->via_ran);
- return -1;
- }
-}
-
-
-int msc_tx_dtap(struct gsm_subscriber_connection *conn,
- struct msgb *msg)
-{
- return msc_tx(conn, msg);
-}
-
-
-/* 9.2.5 CM service accept */
-int msc_gsm48_tx_mm_serv_ack(struct gsm_subscriber_connection *conn)
-{
- struct msgb *msg;
- struct gsm48_hdr *gh;
-
- if (!conn)
- return -EINVAL;
-
- msg = gsm48_msgb_alloc_name("GSM 04.08 SERV ACC");
-
- gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
- gh->proto_discr = GSM48_PDISC_MM;
- gh->msg_type = GSM48_MT_MM_CM_SERV_ACC;
-
- DEBUGP(DMM, "-> CM SERVICE ACCEPT %s\n",
- vlr_subscr_name(conn->vsub));
-
- return msc_tx_dtap(conn, msg);
-}
-
-/* 9.2.6 CM service reject */
-int msc_gsm48_tx_mm_serv_rej(struct gsm_subscriber_connection *conn,
- enum gsm48_reject_value value)
-{
- struct msgb *msg;
-
- if (!conn)
- return -EINVAL;
-
- conn->received_cm_service_request = false;
-
- 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 msc_tx_dtap(conn, msg);
-}
-
-int msc_tx_common_id(struct gsm_subscriber_connection *conn)
-{
- if (!conn)
- return -EINVAL;
-
- /* Common ID is only sent over IuCS */
- if (conn->via_ran != RAN_UTRAN_IU) {
- LOGP(DMM, LOGL_INFO,
- "%s: Asked to transmit Common ID, but skipping"
- " because this is not on UTRAN\n",
- vlr_subscr_name(conn->vsub));
- return 0;
- }
-
- DEBUGP(DIUCS, "%s: tx CommonID %s\n",
- vlr_subscr_name(conn->vsub), conn->vsub->imsi);
- return ranap_iu_tx_common_id(conn->iu.ue_ctx, conn->vsub->imsi);
-}
-
-static int iu_rab_act_cs(struct ranap_ue_conn_ctx *uectx, uint8_t rab_id,
- uint32_t rtp_ip, uint16_t rtp_port)
-{
-#ifdef BUILD_IU
- struct msgb *msg;
- bool use_x213_nsap;
- uint32_t conn_id = uectx->conn_id;
-
- use_x213_nsap = (uectx->rab_assign_addr_enc == RANAP_NSAP_ADDR_ENC_X213);
-
- LOGP(DIUCS, LOGL_DEBUG, "Assigning RAB: conn_id=%u, rab_id=%d,"
- " rtp=%x:%u, use_x213_nsap=%d\n", conn_id, rab_id, rtp_ip,
- rtp_port, use_x213_nsap);
-
- msg = ranap_new_msg_rab_assign_voice(rab_id, rtp_ip, rtp_port,
- use_x213_nsap);
- msg->l2h = msg->data;
-
- if (ranap_iu_rab_act(uectx, msg))
- LOGP(DIUCS, LOGL_ERROR, "Failed to send RAB Assignment:"
- " conn_id=%d rab_id=%d rtp=%x:%u\n",
- conn_id, rab_id, rtp_ip, rtp_port);
- return 0;
-#else
- LOGP(DMSC, LOGL_ERROR, "Cannot send Iu RAB Assignment: built without Iu support\n");
- return -ENOTSUP;
-#endif
-}
-
-static void mgcp_response_rab_act_cs_crcx(struct mgcp_response *r, void *priv)
-{
- struct gsm_trans *trans = priv;
- struct gsm_subscriber_connection *conn = trans->conn;
- uint32_t rtp_ip;
- int rc;
-
- if (r->head.response_code != 200) {
- LOGP(DMGCP, LOGL_ERROR,
- "MGCPGW response yields error: %d %s\n",
- r->head.response_code, r->head.comment);
- goto rab_act_cs_error;
- }
-
- rc = mgcp_response_parse_params(r);
- if (rc) {
- LOGP(DMGCP, LOGL_ERROR,
- "Cannot parse MGCP response, for %s\n",
- vlr_subscr_name(trans->vsub));
- goto rab_act_cs_error;
- }
-
- conn->rtp.port_cn = r->audio_port;
-
- rtp_ip = mgcpgw_client_remote_addr_n(conn->network->mgcpgw.client);
-
- if (trans->conn->via_ran == RAN_UTRAN_IU) {
- /* Assign a voice channel via RANAP on 3G */
- if (iu_rab_act_cs(conn->iu.ue_ctx, conn->iu.rab_id, rtp_ip, conn->rtp.port_subscr))
- goto rab_act_cs_error;
- } else if (trans->conn->via_ran == RAN_GERAN_A) {
- /* Assign a voice channel via A on 2G */
- if (a_iface_tx_assignment(trans))
- goto rab_act_cs_error;
- } else
- goto rab_act_cs_error;
-
- /* Respond back to MNCC (if requested) */
- if (trans->tch_rtp_create) {
- if (gsm48_tch_rtp_create(trans))
- goto rab_act_cs_error;
- }
- return;
-
-rab_act_cs_error:
- /* FIXME abort call, invalidate conn, ... */
- LOGP(DMSC, LOGL_ERROR, "%s: failure during assignment\n",
- vlr_subscr_name(trans->vsub));
- return;
-}
-
-int msc_call_assignment(struct gsm_trans *trans)
-{
- struct gsm_subscriber_connection *conn;
- struct mgcpgw_client *mgcp;
- struct msgb *msg;
- uint16_t bts_base;
-
- if (!trans)
- return -EINVAL;
- if (!trans->conn)
- return -EINVAL;
-
- conn = trans->conn;
- mgcp = conn->network->mgcpgw.client;
-
-#ifdef BUILD_IU
- /* FIXME: HACK. where to scope the RAB Id? At the conn / subscriber / ranap_ue_conn_ctx? */
- static uint8_t next_iu_rab_id = 1;
- if (conn->via_ran == RAN_UTRAN_IU)
- conn->iu.rab_id = next_iu_rab_id ++;
-#endif
-
- conn->rtp.mgcp_rtp_endpoint =
- mgcpgw_client_next_endpoint(conn->network->mgcpgw.client);
-
- /* This will calculate the port we assign to the BTS via AoIP
- * assignment command (or rab-assignment on 3G) The BTS will send
- * its RTP traffic to that port on the MGCPGW side. The MGCPGW only
- * gets the endpoint ID via the CRCX. It will do the same calculation
- * on his side too to get knowledge of the rtp port. */
- bts_base = mgcpgw_client_conf_actual(mgcp)->bts_base;
- conn->rtp.port_subscr = bts_base + 2 * conn->rtp.mgcp_rtp_endpoint;
-
- /* Establish the RTP stream first as looping back to the originator.
- * The MDCX will patch through to the counterpart. TODO: play a ring
- * tone instead. */
- msg = mgcp_msg_crcx(mgcp, conn->rtp.mgcp_rtp_endpoint,
- conn->rtp.mgcp_rtp_endpoint, MGCP_CONN_LOOPBACK);
- return mgcpgw_client_tx(mgcp, msg, mgcp_response_rab_act_cs_crcx, trans);
-}
-
-static void mgcp_response_bridge_mdcx(struct mgcp_response *r, void *priv);
-
-static void mgcp_bridge(struct gsm_trans *from, struct gsm_trans *to,
- enum bridge_state state,
- enum mgcp_connection_mode mode)
-{
- struct gsm_subscriber_connection *conn1 = from->conn;
- struct gsm_subscriber_connection *conn2 = to->conn;
- struct mgcpgw_client *mgcp = conn1->network->mgcpgw.client;
- const char *ip;
- struct msgb *msg;
-
- OSMO_ASSERT(mgcp);
-
- from->bridge.peer = to;
- from->bridge.state = state;
-
- /* Loop back to the same MGCP GW */
- ip = mgcpgw_client_remote_addr_str(mgcp);
-
- msg = mgcp_msg_mdcx(mgcp,
- conn1->rtp.mgcp_rtp_endpoint,
- ip, conn2->rtp.port_cn,
- mode);
- if (mgcpgw_client_tx(mgcp, msg, mgcp_response_bridge_mdcx, from))
- LOGP(DMGCP, LOGL_ERROR,
- "Failed to send MDCX message for %s\n",
- vlr_subscr_name(from->vsub));
-}
-
-static void mgcp_response_bridge_mdcx(struct mgcp_response *r, void *priv)
-{
- struct gsm_trans *trans = priv;
- struct gsm_trans *peer = trans->bridge.peer;
-
- switch (trans->bridge.state) {
- case BRIDGE_STATE_LOOPBACK_PENDING:
- trans->bridge.state = BRIDGE_STATE_LOOPBACK_ESTABLISHED;
-
- switch (peer->bridge.state) {
- case BRIDGE_STATE_LOOPBACK_PENDING:
- /* Wait until the other is done as well. */
- return;
- case BRIDGE_STATE_LOOPBACK_ESTABLISHED:
- /* Now that both are in loopback, switch both to
- * forwarding. */
- mgcp_bridge(trans, peer, BRIDGE_STATE_BRIDGE_PENDING,
- MGCP_CONN_RECV_SEND);
- mgcp_bridge(peer, trans, BRIDGE_STATE_BRIDGE_PENDING,
- MGCP_CONN_RECV_SEND);
- break;
- default:
- LOGP(DMGCP, LOGL_ERROR,
- "Unexpected bridge state: %d for %s\n",
- trans->bridge.state, vlr_subscr_name(trans->vsub));
- break;
- }
- break;
-
- case BRIDGE_STATE_BRIDGE_PENDING:
- trans->bridge.state = BRIDGE_STATE_BRIDGE_ESTABLISHED;
- break;
-
- default:
- LOGP(DMGCP, LOGL_ERROR,
- "Unexpected bridge state: %d for %s\n",
- trans->bridge.state, vlr_subscr_name(trans->vsub));
- break;
- }
-}
-
-int msc_call_connect(struct gsm_trans *trans, uint16_t port, uint32_t ip)
-{
- /* With this function we inform the MGCP-GW where (ip/port) it
- * has to send its outgoing voic traffic. The receiving end will
- * usually be a PBX (e.g. Asterisk). The IP-Address we tell, will
- * not only be used to direct the traffic, it will also be used
- * as a filter to make sure only RTP packets from the right
- * remote end will reach the BSS. This is also the reason why
- * inbound audio will not work until this step is performed */
-
- /* NOTE: This function is used when msc_call_bridge(), is not
- * applicable. This is usually the case when an external MNCC
- * is in use */
-
- struct gsm_subscriber_connection *conn;
- struct mgcpgw_client *mgcp;
- struct msgb *msg;
-
- if (!trans)
- return -EINVAL;
- if (!trans->conn)
- return -EINVAL;
- if (!trans->conn->network)
- return -EINVAL;
- if (!trans->conn->network->mgcpgw.client)
- return -EINVAL;
-
- mgcp = trans->conn->network->mgcpgw.client;
-
- struct in_addr ip_addr;
- ip_addr.s_addr = ntohl(ip);
-
- conn = trans->conn;
-
- msg = mgcp_msg_mdcx(mgcp,
- conn->rtp.mgcp_rtp_endpoint,
- inet_ntoa(ip_addr), port, MGCP_CONN_RECV_SEND);
- if (mgcpgw_client_tx(mgcp, msg, NULL, trans))
- LOGP(DMGCP, LOGL_ERROR,
- "Failed to send MDCX message for %s\n",
- vlr_subscr_name(trans->vsub));
-
- return 0;
-}
-
-int msc_call_bridge(struct gsm_trans *trans1, struct gsm_trans *trans2)
-{
- if (!trans1)
- return -EINVAL;
- if (!trans2)
- return -EINVAL;
-
- /* First setup as loopback and configure the counterparts' endpoints,
- * so that when transmission starts the originating addresses are
- * already known to be valid. The mgcp callback will continue. */
- mgcp_bridge(trans1, trans2, BRIDGE_STATE_LOOPBACK_PENDING,
- MGCP_CONN_LOOPBACK);
- mgcp_bridge(trans2, trans1, BRIDGE_STATE_LOOPBACK_PENDING,
- MGCP_CONN_LOOPBACK);
-
- return 0;
-}
-
-void msc_call_release(struct gsm_trans *trans)
-{
- struct msgb *msg;
- struct gsm_subscriber_connection *conn;
- struct mgcpgw_client *mgcp;
-
- if (!trans)
- return;
- if (!trans->conn)
- return;
- if (!trans->conn->network)
- return;
-
- conn = trans->conn;
- mgcp = conn->network->mgcpgw.client;
-
- /* Send DLCX */
- msg = mgcp_msg_dlcx(mgcp, conn->rtp.mgcp_rtp_endpoint,
- conn->rtp.mgcp_rtp_endpoint);
- if (mgcpgw_client_tx(mgcp, msg, NULL, NULL))
- LOGP(DMGCP, LOGL_ERROR,
- "Failed to send DLCX message for %s\n",
- vlr_subscr_name(trans->vsub));
-
- /* Release endpoint id */
- mgcpgw_client_release_endpoint(conn->rtp.mgcp_rtp_endpoint, mgcp);
-}
diff --git a/src/libmsc/msc_vty.c b/src/libmsc/msc_vty.c
deleted file mode 100644
index 50679aa01..000000000
--- a/src/libmsc/msc_vty.c
+++ /dev/null
@@ -1,162 +0,0 @@
-/* MSC interface to quagga VTY */
-/* (C) 2016 by sysmocom s.m.f.c. GmbH <info@sysmocom.de>
- * Based on OpenBSC interface to quagga VTY (libmsc/vty_interface_layer3.c)
- * (C) 2009 by Harald Welte <laforge@gnumonks.org>
- * (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 <http://www.gnu.org/licenses/>.
- *
- */
-
-/* NOTE: I would have liked to call this the MSC_NODE instead of the MSC_NODE,
- * but MSC_NODE already exists to configure a remote MSC for osmo-bsc. */
-
-#include "../../bscconfig.h"
-
-#include <inttypes.h>
-
-#include <osmocom/vty/command.h>
-#ifdef BUILD_IU
-#include <osmocom/ranap/iu_client.h>
-#endif
-
-#include <openbsc/vty.h>
-#include <openbsc/gsm_data.h>
-#include <openbsc/gsm_subscriber.h>
-#include <openbsc/vlr.h>
-
-static struct cmd_node msc_node = {
- MSC_NODE,
- "%s(config-msc)# ",
- 1,
-};
-
-DEFUN(cfg_msc, cfg_msc_cmd,
- "msc", "Configure MSC options")
-{
- vty->node = MSC_NODE;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_msc_assign_tmsi, cfg_msc_assign_tmsi_cmd,
- "assign-tmsi",
- "Assign TMSI during Location Updating.\n")
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- gsmnet->vlr->cfg.assign_tmsi = true;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_msc_no_assign_tmsi, cfg_msc_no_assign_tmsi_cmd,
- "no assign-tmsi",
- NO_STR "Assign TMSI during Location Updating.\n")
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- gsmnet->vlr->cfg.assign_tmsi = false;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_msc_cs7_instance_a,
- cfg_msc_cs7_instance_a_cmd,
- "cs7-instance-a <0-15>",
- "Set SS7 to be used by the A-Interface.\n" "SS7 instance reference number\n")
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- gsmnet->a.cs7_instance = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_msc_cs7_instance_iu,
- cfg_msc_cs7_instance_iu_cmd,
- "cs7-instance-iu <0-15>",
- "Set SS7 to be used by the Iu-Interface.\n" "SS7 instance reference number\n")
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- gsmnet->iu.cs7_instance = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
-static int config_write_msc(struct vty *vty)
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
-
- vty_out(vty, "msc%s", VTY_NEWLINE);
- vty_out(vty, " %sassign-tmsi%s",
- gsmnet->vlr->cfg.assign_tmsi? "" : "no ", VTY_NEWLINE);
-
- vty_out(vty, " cs7-instance-a %u%s", gsmnet->a.cs7_instance,
- VTY_NEWLINE);
- vty_out(vty, " cs7-instance-iu %u%s", gsmnet->iu.cs7_instance,
- VTY_NEWLINE);
-
- mgcpgw_client_config_write(vty, " ");
-#ifdef BUILD_IU
- ranap_iu_vty_config_write(vty, " ");
-#endif
-
- 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, " 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);
- if (gsmnet->tz.override != 0) {
- if (gsmnet->tz.dst)
- vty_out(vty, " timezone %d %d %d%s",
- gsmnet->tz.hr, gsmnet->tz.mn, gsmnet->tz.dst,
- VTY_NEWLINE);
- else
- vty_out(vty, " timezone %d %d%s",
- gsmnet->tz.hr, gsmnet->tz.mn, VTY_NEWLINE);
- }
- if (gsmnet->t3212 == 0)
- vty_out(vty, " no periodic location update%s", VTY_NEWLINE);
- else
- vty_out(vty, " periodic location update %u%s",
- gsmnet->t3212 * 6, VTY_NEWLINE);
-
- return CMD_SUCCESS;
-}
-
-void msc_vty_init(struct gsm_network *msc_network)
-{
- common_cs_vty_init(msc_network, config_write_net);
-
- install_element(CONFIG_NODE, &cfg_msc_cmd);
- install_node(&msc_node, config_write_msc);
- vty_install_default(MSC_NODE);
- install_element(MSC_NODE, &cfg_msc_assign_tmsi_cmd);
- install_element(MSC_NODE, &cfg_msc_no_assign_tmsi_cmd);
- install_element(MSC_NODE, &cfg_msc_cs7_instance_a_cmd);
- install_element(MSC_NODE, &cfg_msc_cs7_instance_iu_cmd);
-
- mgcpgw_client_vty_init(msc_network, MSC_NODE, &msc_network->mgcpgw.conf);
-#ifdef BUILD_IU
- ranap_iu_vty_init(MSC_NODE, &msc_network->iu.rab_assign_addr_enc);
-#endif
-}
diff --git a/src/libmsc/osmo_msc.c b/src/libmsc/osmo_msc.c
deleted file mode 100644
index 4d24f22a6..000000000
--- a/src/libmsc/osmo_msc.c
+++ /dev/null
@@ -1,387 +0,0 @@
-/* main MSC management code... */
-
-/*
- * (C) 2010,2013 by Holger Hans Peter Freyther <zecke@selfish.org>
- * (C) 2010 by On-Waves
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <openbsc/osmo_msc.h>
-#include <openbsc/bsc_api.h>
-#include <openbsc/debug.h>
-#include <openbsc/transaction.h>
-#include <openbsc/db.h>
-#include <openbsc/vlr.h>
-#include <openbsc/osmo_msc.h>
-#include <openbsc/a_iface.h>
-
-#include <openbsc/gsm_04_11.h>
-
-#include "../../bscconfig.h"
-#ifdef BUILD_IU
-#include <osmocom/ranap/iu_client.h>
-#else
-#include <openbsc/iu_dummy.h>
-#endif
-
-/* Receive a SAPI-N-REJECT from BSC */
-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 void subscr_conn_bump(struct gsm_subscriber_connection *conn)
-{
- if (!conn)
- return;
- if (!conn->conn_fsm)
- return;
- if (!(conn->conn_fsm->state == SUBSCR_CONN_S_ACCEPTED
- || conn->conn_fsm->state == SUBSCR_CONN_S_COMMUNICATING)) {
- DEBUGP(DMM, "%s: bump: conn still being established (%s)\n",
- vlr_subscr_name(conn->vsub),
- osmo_fsm_inst_state_name(conn->conn_fsm));
- return;
- }
- osmo_fsm_inst_dispatch(conn->conn_fsm, SUBSCR_CONN_E_BUMP, NULL);
-}
-
-/* receive a Level 3 Complete message and return MSC_CONN_ACCEPT or
- * MSC_CONN_REJECT */
-int msc_compl_l3(struct gsm_subscriber_connection *conn,
- struct msgb *msg, uint16_t chosen_channel)
-{
- msc_subscr_conn_get(conn);
- gsm0408_dispatch(conn, msg);
-
- /* Bump whether the conn wants to be closed */
- subscr_conn_bump(conn);
-
- /* If this should be kept, the conn->conn_fsm has placed a use_count */
- msc_subscr_conn_put(conn);
-
- /* Always return acceptance, because even if the conn was not accepted,
- * we assumed ownership of it and the caller shall not interfere with
- * that. We may even already have discarded the conn. */
- return MSC_CONN_ACCEPT;
-
-#if 0
- /*
- * If this is a silent call we want the channel to remain open as long as
- * possible and this is why we accept this connection regardless of any
- * pending transaction or ongoing operation.
- */
- if (conn->silent_call)
- return MSC_CONN_ACCEPT;
- if (conn->loc_operation || conn->sec_operation || conn->anch_operation)
- return MSC_CONN_ACCEPT;
- if (trans_has_conn(conn))
- return MSC_CONN_ACCEPT;
-
- LOGP(DRR, LOGL_INFO, "MSC Complete L3: Rejecting connection.\n");
- return MSC_CONN_REJECT;
-#endif
-}
-
-/* Receive a DTAP message from BSC */
-void msc_dtap(struct gsm_subscriber_connection *conn, uint8_t link_id, struct msgb *msg)
-{
- msc_subscr_conn_get(conn);
- gsm0408_dispatch(conn, msg);
-
- /* Bump whether the conn wants to be closed */
- subscr_conn_bump(conn);
- msc_subscr_conn_put(conn);
-}
-
-/* Receive an ASSIGNMENT COMPLETE from BSC */
-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");
-}
-
-/* Receive an ASSIGNMENT FAILURE from BSC */
-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");
-}
-
-/* Receive a CLASSMARK CHANGE from BSC */
-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)
-{
- if (cm2 && cm2_len) {
- if (cm2_len > sizeof(conn->classmark.classmark2)) {
- LOGP(DRR, LOGL_NOTICE, "%s: classmark2 is %u bytes, truncating at %zu bytes\n",
- vlr_subscr_name(conn->vsub), cm2_len, sizeof(conn->classmark.classmark2));
- cm2_len = sizeof(conn->classmark.classmark2);
- }
- conn->classmark.classmark2_len = cm2_len;
- memcpy(conn->classmark.classmark2, cm2, cm2_len);
- }
- if (cm3 && cm3_len) {
- if (cm3_len > sizeof(conn->classmark.classmark3)) {
- LOGP(DRR, LOGL_NOTICE, "%s: classmark3 is %u bytes, truncating at %zu bytes\n",
- vlr_subscr_name(conn->vsub), cm3_len, sizeof(conn->classmark.classmark3));
- cm3_len = sizeof(conn->classmark.classmark3);
- }
- conn->classmark.classmark3_len = cm3_len;
- memcpy(conn->classmark.classmark3, cm3, cm3_len);
- }
-}
-
-/* Receive a CIPHERING MODE COMPLETE from BSC */
-void msc_cipher_mode_compl(struct gsm_subscriber_connection *conn,
- struct msgb *msg, uint8_t alg_id)
-{
- struct gsm48_hdr *gh = msgb_l3(msg);
- unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
- struct tlv_parsed tp;
- uint8_t mi_type;
- char imeisv[GSM48_MI_SIZE] = "";
- struct vlr_ciph_result ciph_res = { .cause = VLR_CIPH_REJECT };
-
- if (!gh) {
- LOGP(DRR, LOGL_ERROR, "invalid: msgb without l3 header\n");
- return;
- }
-
- if (!conn) {
- LOGP(DRR, LOGL_ERROR,
- "invalid: rx Ciphering Mode Complete on NULL conn\n");
- return;
- }
- if (!conn->vsub) {
- LOGP(DRR, LOGL_ERROR,
- "invalid: rx Ciphering Mode Complete for NULL subscr\n");
- return;
- }
-
- DEBUGP(DRR, "%s: CIPHERING MODE COMPLETE\n",
- vlr_subscr_name(conn->vsub));
-
- tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0);
-
- /* bearer capability */
- if (TLVP_PRESENT(&tp, GSM48_IE_MOBILE_ID)) {
- mi_type = TLVP_VAL(&tp, GSM48_IE_MOBILE_ID)[0] & GSM_MI_TYPE_MASK;
- if (mi_type == GSM_MI_TYPE_IMEISV
- && TLVP_LEN(&tp, GSM48_IE_MOBILE_ID) > 0) {
- gsm48_mi_to_string(imeisv, sizeof(imeisv),
- TLVP_VAL(&tp, GSM48_IE_MOBILE_ID),
- TLVP_LEN(&tp, GSM48_IE_MOBILE_ID));
- ciph_res.imeisv = imeisv;
- }
- }
-
- ciph_res.cause = VLR_CIPH_COMPL;
- vlr_subscr_rx_ciph_res(conn->vsub, &ciph_res);
-}
-
-struct gsm_subscriber_connection *msc_subscr_con_allocate(struct gsm_network *network)
-{
- struct gsm_subscriber_connection *conn;
-
- conn = talloc_zero(network, struct gsm_subscriber_connection);
- if (!conn)
- return NULL;
-
- conn->network = network;
- llist_add_tail(&conn->entry, &network->subscr_conns);
- return conn;
-}
-
-void msc_subscr_cleanup(struct vlr_subscr *vsub)
-{
- if (!vsub)
- return;
- vsub->lu_fsm = NULL;
-}
-
-void msc_subscr_con_cleanup(struct gsm_subscriber_connection *conn)
-{
- if (!conn)
- return;
-
- if (conn->vsub) {
- DEBUGP(DRLL, "subscr %s: Freeing subscriber connection\n",
- vlr_subscr_name(conn->vsub));
- msc_subscr_cleanup(conn->vsub);
- vlr_subscr_put(conn->vsub);
- conn->vsub = NULL;
- } else
- DEBUGP(DRLL, "Freeing subscriber connection"
- " with NULL subscriber\n");
-
- if (!conn->conn_fsm)
- return;
-
- osmo_fsm_inst_term(conn->conn_fsm,
- (conn->conn_fsm->state == SUBSCR_CONN_S_RELEASED)
- ? OSMO_FSM_TERM_REGULAR
- : OSMO_FSM_TERM_ERROR,
- NULL);
-}
-
-void msc_subscr_con_free(struct gsm_subscriber_connection *conn)
-{
- if (!conn)
- return;
-
- msc_subscr_con_cleanup(conn);
-
- llist_del(&conn->entry);
- talloc_free(conn);
-}
-
-/* Receive a CLEAR REQUEST from BSC */
-int msc_clear_request(struct gsm_subscriber_connection *conn, uint32_t cause)
-{
- msc_subscr_conn_close(conn, cause);
- return 1;
-}
-
-/* MSC-level operations to be called by libbsc in NITB */
-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_cipher_mode_compl,
- .conn_cleanup = msc_subscr_con_cleanup,
-};
-
-struct bsc_api *msc_bsc_api() {
- return &msc_handler;
-}
-
-static void msc_subscr_conn_release_all(struct gsm_subscriber_connection *conn, uint32_t cause)
-{
- if (conn->in_release)
- return;
- conn->in_release = true;
-
- /* If we're closing in a middle of a trans, we need to clean up */
- trans_conn_closed(conn);
-
- switch (conn->via_ran) {
- case RAN_UTRAN_IU:
- ranap_iu_tx_release(conn->iu.ue_ctx, NULL);
- /* FIXME: keep the conn until the Iu Release Outcome is
- * received from the UE, or a timeout expires. For now, the log
- * says "unknown UE" for each release outcome. */
- break;
- case RAN_GERAN_A:
- a_iface_tx_clear_cmd(conn);
- break;
- default:
- LOGP(DMM, LOGL_ERROR, "%s: Unknown RAN type, cannot tx release/clear\n",
- vlr_subscr_name(conn->vsub));
- break;
- }
-}
-
-/* If the conn->conn_fsm is still present, dispatch SUBSCR_CONN_E_CN_CLOSE
- * event to gracefully terminate the connection. If the conn_fsm is already
- * cleared, call msc_subscr_conn_release_all() to take release actions.
- * \param cause a GSM_CAUSE_* constant, e.g. GSM_CAUSE_AUTH_FAILED.
- */
-void msc_subscr_conn_close(struct gsm_subscriber_connection *conn,
- uint32_t cause)
-{
- if (!conn)
- return;
- if (conn->in_release) {
- DEBUGP(DMM, "msc_subscr_conn_close(vsub=%s, cause=%u):"
- " already dispatching release, ignore.\n",
- vlr_subscr_name(conn->vsub), cause);
- return;
- }
- if (!conn->conn_fsm) {
- DEBUGP(DMM, "msc_subscr_conn_close(vsub=%s, cause=%u): no conn fsm,"
- " releasing directly without release event.\n",
- vlr_subscr_name(conn->vsub), cause);
- /* In case of an IMSI Detach, we don't have conn_fsm. Release
- * anyway to ensure a timely Iu Release / BSSMAP Clear. */
- msc_subscr_conn_release_all(conn, cause);
- return;
- }
- if (conn->conn_fsm->state == SUBSCR_CONN_S_RELEASED) {
- DEBUGP(DMM, "msc_subscr_conn_close(vsub=%s, cause=%u):"
- " conn fsm already releasing, ignore.\n",
- vlr_subscr_name(conn->vsub), cause);
- return;
- }
- osmo_fsm_inst_dispatch(conn->conn_fsm, SUBSCR_CONN_E_CN_CLOSE, &cause);
-}
-
-/* increment the ref-count. Needs to be called by every user */
-struct gsm_subscriber_connection *
-_msc_subscr_conn_get(struct gsm_subscriber_connection *conn,
- const char *file, int line)
-{
- OSMO_ASSERT(conn);
-
- if (conn->in_release)
- return NULL;
-
- conn->use_count++;
- LOGPSRC(DREF, LOGL_DEBUG, file, line,
- "%s: MSC conn use + 1 == %u\n",
- vlr_subscr_name(conn->vsub), conn->use_count);
-
- return conn;
-}
-
-/* decrement the ref-count. Once it reaches zero, we release */
-void _msc_subscr_conn_put(struct gsm_subscriber_connection *conn,
- const char *file, int line)
-{
- OSMO_ASSERT(conn);
-
- if (conn->use_count == 0) {
- LOGPSRC(DREF, LOGL_ERROR, file, line,
- "%s: MSC conn use - 1 failed: is already 0\n",
- vlr_subscr_name(conn->vsub));
- return;
- }
-
- conn->use_count--;
- LOGPSRC(DREF, LOGL_DEBUG, file, line,
- "%s: MSC conn use - 1 == %u\n",
- vlr_subscr_name(conn->vsub), conn->use_count);
-
- if (conn->use_count == 0)
- msc_subscr_con_free(conn);
-}
-
-void msc_stop_paging(struct vlr_subscr *vsub)
-{
- DEBUGP(DPAG, "Paging can stop for %s\n", vlr_subscr_name(vsub));
- /* tell BSCs and RNCs to stop paging? How? */
-}
diff --git a/src/libmsc/rrlp.c b/src/libmsc/rrlp.c
deleted file mode 100644
index cd3da066b..000000000
--- a/src/libmsc/rrlp.c
+++ /dev/null
@@ -1,104 +0,0 @@
-/* Radio Resource LCS (Location) Protocol, GMS TS 04.31 */
-
-/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-
-
-#include <openbsc/gsm_04_08.h>
-#include <openbsc/signal.h>
-#include <openbsc/gsm_subscriber.h>
-#include <openbsc/chan_alloc.h>
-
-/* 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->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 vlr_subscr *vsub;
- struct gsm_subscriber_connection *conn;
-
- switch (signal) {
- case S_SUBSCR_ATTACHED:
- /* A subscriber has attached. */
- vsub = signal_data;
- conn = connection_for_subscr(vsub);
- 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);
-}
diff --git a/src/libmsc/silent_call.c b/src/libmsc/silent_call.c
deleted file mode 100644
index 7af7a8055..000000000
--- a/src/libmsc/silent_call.c
+++ /dev/null
@@ -1,165 +0,0 @@
-/* GSM silent call feature */
-
-/*
- * (C) 2009 by Harald Welte <laforge@gnumonks.org>
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <stdlib.h>
-#include <unistd.h>
-#include <errno.h>
-
-#include <osmocom/core/msgb.h>
-#include <openbsc/signal.h>
-#include <openbsc/debug.h>
-#include <openbsc/paging.h>
-#include <openbsc/gsm_data.h>
-#include <openbsc/gsm_subscriber.h>
-#include <openbsc/abis_rsl.h>
-#include <openbsc/chan_alloc.h>
-#include <openbsc/osmo_msc.h>
-
-/* 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:
-#if BEFORE_MSCSPLIT
- /* Re-enable this log output once we can obtain this information via
- * A-interface, see OS#2391. */
- DEBUGPC(DLSMS, "success, using Timeslot %u on ARFCN %u\n",
- conn->lchan->ts->nr, conn->lchan->ts->trx->arfcn);
-#endif
- conn->silent_call = 1;
- msc_subscr_conn_get(conn);
- /* 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;
-}
-
-#if 0
-/* 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;
-}
-#endif
-
-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 },
-};
-
-#if 0
-/* 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 = gsm48_hdr_pdisc(gh);
- uint8_t msg_type = gsm48_hdr_msg_type(gh);
- 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 == msg_type)
- return 0;
- }
-
- /* otherwise, reroute */
- LOGP(DLSMS, LOGL_INFO, "Rerouting L3 message from a silent call.\n");
- return 1;
-}
-#endif
-
-
-/* initiate a silent call with a given subscriber */
-int gsm_silent_call_start(struct vlr_subscr *vsub, void *data, int type)
-{
- struct subscr_request *req;
-
- /* FIXME the VTY command allows selecting a silent call channel type.
- * This doesn't apply to the situation after MSCSPLIT with an
- * A-interface. */
- req = subscr_request_conn(vsub, paging_cb_silent, data,
- "establish silent call");
- return req != NULL;
-}
-
-/* end a silent call with a given subscriber */
-int gsm_silent_call_stop(struct vlr_subscr *vsub)
-{
- struct gsm_subscriber_connection *conn;
-
- conn = connection_for_subscr(vsub);
- if (!conn)
- return -EINVAL;
-
- /* did we actually establish a silent call for this guy? */
- if (!conn->silent_call)
- return -EINVAL;
-
-#if BEFORE_MSCSPLIT
- /* Re-enable this log output once we can obtain this information via
- * A-interface, see OS#2391. */
- DEBUGPC(DLSMS, "Stopping silent call using Timeslot %u on ARFCN %u\n",
- conn->lchan->ts->nr, conn->lchan->ts->trx->arfcn);
-#endif
-
- conn->silent_call = 0;
- msc_subscr_conn_put(conn);
-
- return 0;
-}
diff --git a/src/libmsc/smpp_openbsc.c b/src/libmsc/smpp_openbsc.c
deleted file mode 100644
index 24a465373..000000000
--- a/src/libmsc/smpp_openbsc.c
+++ /dev/null
@@ -1,765 +0,0 @@
-/* OpenBSC SMPP 3.4 interface, SMSC-side implementation */
-
-/* (C) 2012-2013 by Harald Welte <laforge@gnumonks.org>
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <stdint.h>
-#include <errno.h>
-
-#include <smpp34.h>
-#include <smpp34_structs.h>
-#include <smpp34_params.h>
-
-#include <osmocom/core/utils.h>
-#include <osmocom/core/msgb.h>
-#include <osmocom/core/logging.h>
-#include <osmocom/core/talloc.h>
-#include <osmocom/gsm/protocol/gsm_04_11.h>
-#include <osmocom/gsm/protocol/smpp34_osmocom.h>
-
-#include <openbsc/gsm_subscriber.h>
-#include <openbsc/debug.h>
-#include <openbsc/db.h>
-#include <openbsc/gsm_04_11.h>
-#include <openbsc/gsm_data.h>
-#include <openbsc/signal.h>
-#include <openbsc/transaction.h>
-#include <openbsc/gsm_subscriber.h>
-#include <openbsc/chan_alloc.h>
-#include <openbsc/vlr.h>
-
-#include "smpp_smsc.h"
-
-/*! \brief find vlr_subscr for a given SMPP NPI/TON/Address */
-static struct vlr_subscr *subscr_by_dst(struct gsm_network *net,
- uint8_t npi, uint8_t ton,
- const char *addr)
-{
- struct vlr_subscr *vsub = NULL;
-
- switch (npi) {
- case NPI_Land_Mobile_E212:
- vsub = vlr_subscr_find_by_imsi(net->vlr, addr);
- break;
- case NPI_ISDN_E163_E164:
- case NPI_Private:
- vsub = vlr_subscr_find_by_msisdn(net->vlr, addr);
- break;
- default:
- LOGP(DSMPP, LOGL_NOTICE, "Unsupported NPI: %u\n", npi);
- break;
- }
-
- log_set_context(LOG_CTX_VLR_SUBSCR, vsub);
- return vsub;
-}
-
-/*! \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 vlr_subscr *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");
- vlr_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");
- vlr_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;
- osmo_strlcpy(sms->dst.addr, dest->msisdn, sizeof(sms->dst.addr));
-
- /* fill in the source address */
- sms->src.ton = submit->source_addr_ton;
- sms->src.npi = submit->source_addr_npi;
- osmo_strlcpy(sms->src.addr, (char *)submit->source_addr,
- sizeof(sms->src.addr));
-
- 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 vlr_subscr *vsub,
- 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,
- vsub->imsi, smpp_avail_status);
- } else {
- smpp_tx_alert(esme, TON_Network_Specific,
- NPI_ISDN_E163_E164,
- vsub->msisdn, smpp_avail_status);
- }
- }
-}
-
-
-/*! \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->vsub) {
- /* 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->vsub, 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 vlr_subscr *vsub = 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, vsub, 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);
-}
-
-#if BEFORE_MSCSPLIT
-/* We currently have no lchan information. Re-add after A-interface, see OS#2390. */
-/* 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) /* Save Timing Offset field = MS Timing Offset + 63 */
- append_tlv_u8(req_tlv, TLVID_osmo_ta, mr->ms_timing_offset + 63);
-
- 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->vsub) {
- struct vlr_subscr *vsub = lchan->conn->vsub;
- size_t imei_len = strlen(vsub->imei);
- if (imei_len)
- append_tlv(req_tlv, TLVID_osmo_imei,
- (uint8_t *)vsub->imei, imei_len+1);
- }
-}
-#endif
-
-struct {
- uint32_t smpp_status_code;
- uint8_t gsm411_cause;
-} smpp_to_gsm411_err_array[] = {
-
- /* Seems like most phones don't care about the failure cause,
- * although some will display a different notification for
- * GSM411_RP_CAUSE_MO_NUM_UNASSIGNED
- * Some provoke a display of "Try again later"
- * while others a more definitive "Message sending failed"
- */
-
- { ESME_RSYSERR, GSM411_RP_CAUSE_MO_DEST_OUT_OF_ORDER },
- { ESME_RINVDSTADR, GSM411_RP_CAUSE_MO_NUM_UNASSIGNED },
- { ESME_RMSGQFUL, GSM411_RP_CAUSE_MO_CONGESTION },
- { ESME_RINVSRCADR, GSM411_RP_CAUSE_MO_SMS_REJECTED },
- { ESME_RINVMSGID, GSM411_RP_CAUSE_INV_TRANS_REF }
-};
-
-static int smpp_to_gsm411_err(uint32_t smpp_status_code, int *gsm411_cause)
-{
- int i;
- for (i = 0; i < ARRAY_SIZE(smpp_to_gsm411_err_array); i++) {
- if (smpp_to_gsm411_err_array[i].smpp_status_code != smpp_status_code)
- continue;
- *gsm411_cause = smpp_to_gsm411_err_array[i].gsm411_cause;
- return 0;
- }
- return -1;
-}
-
-static void smpp_cmd_free(struct osmo_smpp_cmd *cmd)
-{
- osmo_timer_del(&cmd->response_timer);
- llist_del(&cmd->list);
- vlr_subscr_put(cmd->vsub);
- sms_free(cmd->sms);
- talloc_free(cmd);
-}
-
-void smpp_cmd_flush_pending(struct osmo_esme *esme)
-{
- struct osmo_smpp_cmd *cmd, *next;
-
- llist_for_each_entry_safe(cmd, next, &esme->smpp_cmd_list, list)
- smpp_cmd_free(cmd);
-}
-
-void smpp_cmd_ack(struct osmo_smpp_cmd *cmd)
-{
- struct gsm_subscriber_connection *conn;
- struct gsm_trans *trans;
-
- conn = connection_for_subscr(cmd->vsub);
- if (!conn) {
- LOGP(DSMPP, LOGL_ERROR, "No connection to subscriber anymore\n");
- return;
- }
-
- trans = trans_find_by_id(conn, GSM48_PDISC_SMS,
- cmd->sms->gsm411.transaction_id);
- if (!trans) {
- LOGP(DSMPP, LOGL_ERROR, "GSM transaction %u is gone\n",
- cmd->sms->gsm411.transaction_id);
- return;
- }
-
- gsm411_send_rp_ack(trans, cmd->sms->gsm411.msg_ref);
- smpp_cmd_free(cmd);
-}
-
-void smpp_cmd_err(struct osmo_smpp_cmd *cmd, uint32_t status)
-{
- struct gsm_subscriber_connection *conn;
- struct gsm_trans *trans;
- int gsm411_cause;
-
- conn = connection_for_subscr(cmd->vsub);
- if (!conn) {
- LOGP(DSMPP, LOGL_ERROR, "No connection to subscriber anymore\n");
- return;
- }
-
- trans = trans_find_by_id(conn, GSM48_PDISC_SMS,
- cmd->sms->gsm411.transaction_id);
- if (!trans) {
- LOGP(DSMPP, LOGL_ERROR, "GSM transaction %u is gone\n",
- cmd->sms->gsm411.transaction_id);
- return;
- }
-
- if (smpp_to_gsm411_err(status, &gsm411_cause) < 0)
- gsm411_cause = GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER;
-
- gsm411_send_rp_error(trans, cmd->sms->gsm411.msg_ref, gsm411_cause);
-
- smpp_cmd_free(cmd);
-}
-
-static void smpp_deliver_sm_cb(void *data)
-{
- smpp_cmd_err(data, ESME_RSYSERR);
-}
-
-static int smpp_cmd_enqueue(struct osmo_esme *esme,
- struct vlr_subscr *vsub, struct gsm_sms *sms,
- uint32_t sequence_number, bool *deferred)
-{
- struct osmo_smpp_cmd *cmd;
-
- cmd = talloc_zero(esme, struct osmo_smpp_cmd);
- if (!cmd)
- return -1;
-
- cmd->sequence_nr = sequence_number;
- cmd->sms = sms;
- cmd->vsub = vlr_subscr_get(vsub);
-
- /* FIXME: No predefined value for this response_timer as specified by
- * SMPP 3.4 specs, section 7.2. Make this configurable? Don't forget
- * lchan keeps busy until we get a reply to this SMPP command. Too high
- * value may exhaust resources.
- */
- osmo_timer_setup(&cmd->response_timer, smpp_deliver_sm_cb, cmd);
- osmo_timer_schedule(&cmd->response_timer, 5, 0);
- llist_add_tail(&cmd->list, &esme->smpp_cmd_list);
- *deferred = true;
-
- return 0;
-}
-
-struct osmo_smpp_cmd *smpp_cmd_find_by_seqnum(struct osmo_esme *esme,
- uint32_t sequence_nr)
-{
- struct osmo_smpp_cmd *cmd;
-
- llist_for_each_entry(cmd, &esme->smpp_cmd_list, list) {
- if (cmd->sequence_nr == sequence_nr)
- return cmd;
- }
- return NULL;
-}
-
-static int deliver_to_esme(struct osmo_esme *esme, struct gsm_sms *sms,
- struct gsm_subscriber_connection *conn,
- bool *deferred)
-{
- struct deliver_sm_t deliver;
- int mode, ret;
- uint8_t dcs;
-
- 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->vsub->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->vsub->msisdn);
- }
-
- 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 BEFORE_MSCSPLIT
- /* We currently have no lchan information. Re-add after A-interface, see OS#2390. */
- if (esme->acl && esme->acl->osmocom_ext && conn->lchan)
- append_osmo_tlvs(&deliver.tlv, conn->lchan);
-#endif
-
- ret = smpp_tx_deliver(esme, &deliver);
- if (ret < 0)
- return ret;
-
- return smpp_cmd_enqueue(esme, conn->vsub, sms,
- deliver.sequence_number, deferred);
-}
-
-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, bool *deferred)
-{
- 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 GSM411_RP_CAUSE_MO_NUM_UNASSIGNED;
-
- return deliver_to_esme(esme, sms, conn, deferred);
-}
-
-struct smsc *smsc_from_vty(struct vty *v)
-{
- /* FIXME: this is ugly */
- return g_smsc;
-}
-
-/*! \brief Allocate the OpenBSC SMPP interface struct and init VTY. */
-int smpp_openbsc_alloc_init(void *ctx)
-{
- g_smsc = smpp_smsc_alloc_init(ctx);
- if (!g_smsc) {
- LOGP(DSMPP, LOGL_FATAL, "Cannot allocate smsc struct\n");
- return -1;
- }
- return smpp_vty_init();
-}
-
-/*! \brief Launch the OpenBSC SMPP interface with the parameters set from VTY.
- */
-int smpp_openbsc_start(struct gsm_network *net)
-{
- int rc;
- g_smsc->priv = net;
-
- /* If a VTY configuration has taken place, the values have been stored
- * in the smsc struct. Otherwise, use the defaults (NULL -> any, 0 ->
- * default SMPP port, see smpp_smsc_bind()). */
- rc = smpp_smsc_start(g_smsc, g_smsc->bind_addr, g_smsc->listen_port);
- if (rc < 0)
- return rc;
-
- rc = osmo_signal_register_handler(SS_SMS, smpp_sms_cb, g_smsc);
- if (rc < 0)
- return rc;
- rc = osmo_signal_register_handler(SS_SUBSCR, smpp_subscr_cb, g_smsc);
- if (rc < 0)
- return rc;
-
- return 0;
-}
-
diff --git a/src/libmsc/smpp_smsc.c b/src/libmsc/smpp_smsc.c
deleted file mode 100644
index 48a119261..000000000
--- a/src/libmsc/smpp_smsc.c
+++ /dev/null
@@ -1,1030 +0,0 @@
-/* SMPP 3.4 interface, SMSC-side implementation */
-/* (C) 2012 by Harald Welte <laforge@gnumonks.org>
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <stdint.h>
-#include <errno.h>
-#include <limits.h>
-
-#include <sys/socket.h>
-#include <netinet/in.h>
-
-#include <smpp34.h>
-#include <smpp34_structs.h>
-#include <smpp34_params.h>
-
-#include <osmocom/core/utils.h>
-#include <osmocom/core/socket.h>
-#include <osmocom/core/msgb.h>
-#include <osmocom/core/logging.h>
-#include <osmocom/core/write_queue.h>
-#include <osmocom/core/talloc.h>
-
-#include "smpp_smsc.h"
-
-#include <openbsc/debug.h>
-#include <openbsc/gsm_data.h>
-
-/*! \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);
- }
- smpp_cmd_flush_pending(esme);
- 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;
- struct osmo_smpp_cmd *cmd;
- 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;
- }
-
- cmd = smpp_cmd_find_by_seqnum(esme, deliver_r.sequence_number);
- if (!cmd) {
- LOGP(DSMPP, LOGL_ERROR, "[%s] Rx DELIVER-SM RESP !? (%s)\n",
- esme->system_id, get_value_string(smpp_status_strs,
- deliver_r.command_status));
- return -1;
- }
-
- if (deliver_r.command_status == ESME_ROK)
- smpp_cmd_ack(cmd);
- else
- smpp_cmd_err(cmd, deliver_r.command_status);
-
- 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 Command "
- "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;
- }
-
- INIT_LLIST_HEAD(&esme->smpp_cmd_list);
- 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 allocate and initialize an smsc struct from talloc context ctx. */
-struct smsc *smpp_smsc_alloc_init(void *ctx)
-{
- struct smsc *smsc = talloc_zero(ctx, struct smsc);
-
- 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;
-
- return smsc;
-}
-
-/*! \brief Set the SMPP address and port without binding. */
-int smpp_smsc_conf(struct smsc *smsc, const char *bind_addr, uint16_t port)
-{
- talloc_free((void*)smsc->bind_addr);
- smsc->bind_addr = NULL;
- if (bind_addr) {
- smsc->bind_addr = talloc_strdup(smsc, bind_addr);
- if (!smsc->bind_addr)
- return -ENOMEM;
- }
- smsc->listen_port = port;
- return 0;
-}
-
-/*! \brief Bind to given address and port and accept connections.
- * \param[in] bind_addr Local IP address, may be NULL for any.
- * \param[in] port TCP port number, may be 0 for default SMPP (2775).
- */
-int smpp_smsc_start(struct smsc *smsc, const char *bind_addr, uint16_t port)
-{
- int rc;
-
- /* default port for SMPP */
- if (!port)
- port = 2775;
-
- smpp_smsc_stop(smsc);
-
- LOGP(DSMPP, LOGL_NOTICE, "SMPP at %s %d\n",
- bind_addr? bind_addr : "0.0.0.0", port);
-
- rc = osmo_sock_init_ofd(&smsc->listen_ofd, AF_UNSPEC, SOCK_STREAM,
- IPPROTO_TCP, bind_addr, port,
- OSMO_SOCK_F_BIND);
- if (rc < 0)
- return rc;
-
- /* store new address and port */
- rc = smpp_smsc_conf(smsc, bind_addr, port);
- if (rc)
- smpp_smsc_stop(smsc);
- return rc;
-}
-
-/*! \brief Change a running connection to a different address/port, and upon
- * error switch back to the running configuration. */
-int smpp_smsc_restart(struct smsc *smsc, const char *bind_addr, uint16_t port)
-{
- int rc;
-
- rc = smpp_smsc_start(smsc, bind_addr, port);
- if (rc)
- /* if there is an error, try to re-bind to the old port */
- return smpp_smsc_start(smsc, smsc->bind_addr, smsc->listen_port);
- return 0;
-}
-
-/*! /brief Close SMPP connection. */
-void smpp_smsc_stop(struct smsc *smsc)
-{
- if (smsc->listen_ofd.fd > 0) {
- close(smsc->listen_ofd.fd);
- smsc->listen_ofd.fd = 0;
- osmo_fd_unregister(&smsc->listen_ofd);
- }
-}
diff --git a/src/libmsc/smpp_smsc.h b/src/libmsc/smpp_smsc.h
deleted file mode 100644
index 4bee59b6b..000000000
--- a/src/libmsc/smpp_smsc.h
+++ /dev/null
@@ -1,166 +0,0 @@
-#ifndef _SMPP_SMSC_H
-#define _SMPP_SMSC_H
-
-#include <sys/socket.h>
-#include <netinet/in.h>
-
-#include <osmocom/core/utils.h>
-#include <osmocom/core/msgb.h>
-#include <osmocom/core/write_queue.h>
-#include <osmocom/core/timer.h>
-
-#include <smpp34.h>
-#include <smpp34_structs.h>
-#include <smpp34_params.h>
-
-#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;
-
- struct llist_head smpp_cmd_list;
-
- 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 osmo_smpp_cmd {
- struct llist_head list;
- struct vlr_subscr *vsub;
- struct gsm_sms *sms;
- uint32_t sequence_nr;
- struct osmo_timer_list response_timer;
-};
-
-struct osmo_smpp_cmd *smpp_cmd_find_by_seqnum(struct osmo_esme *esme,
- uint32_t sequence_number);
-void smpp_cmd_ack(struct osmo_smpp_cmd *cmd);
-void smpp_cmd_err(struct osmo_smpp_cmd *cmd, uint32_t status);
-void smpp_cmd_flush_pending(struct osmo_esme *esme);
-
-struct smsc {
- struct osmo_fd listen_ofd;
- struct llist_head esme_list;
- struct llist_head acl_list;
- struct llist_head route_list;
- const char *bind_addr;
- 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);
-
-struct smsc *smpp_smsc_alloc_init(void *ctx);
-int smpp_smsc_conf(struct smsc *smsc, const char *bind_addr, uint16_t port);
-int smpp_smsc_start(struct smsc *smsc, const char *bind_addr, uint16_t port);
-int smpp_smsc_restart(struct smsc *smsc, const char *bind_addr, uint16_t port);
-void smpp_smsc_stop(struct smsc *smsc);
-
-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, bool *deferred);
-#endif
diff --git a/src/libmsc/smpp_utils.c b/src/libmsc/smpp_utils.c
deleted file mode 100644
index d0850d8c1..000000000
--- a/src/libmsc/smpp_utils.c
+++ /dev/null
@@ -1,62 +0,0 @@
-
-/* (C) 2012-2013 by Harald Welte <laforge@gnumonks.org>
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "smpp_smsc.h"
-#include <openbsc/debug.h>
-
-
-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;
-
-}
diff --git a/src/libmsc/smpp_vty.c b/src/libmsc/smpp_vty.c
deleted file mode 100644
index 13467f182..000000000
--- a/src/libmsc/smpp_vty.c
+++ /dev/null
@@ -1,612 +0,0 @@
-/* SMPP vty interface */
-
-/* (C) 2012 by Harald Welte <laforge@gnumonks.org>
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <ctype.h>
-#include <string.h>
-#include <errno.h>
-#include <netdb.h>
-#include <sys/socket.h>
-
-#include <osmocom/vty/command.h>
-#include <osmocom/vty/buffer.h>
-#include <osmocom/vty/vty.h>
-
-#include <osmocom/core/linuxlist.h>
-#include <osmocom/core/utils.h>
-#include <osmocom/core/talloc.h>
-
-#include <openbsc/vty.h>
-
-#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;
-}
-
-static int smpp_local_tcp(struct vty *vty,
- const char *bind_addr, uint16_t port)
-{
- struct smsc *smsc = smsc_from_vty(vty);
- int is_running = smsc->listen_ofd.fd > 0;
- int same_bind_addr;
- int rc;
-
- /* If it is not up yet, don't rebind, just set values. */
- if (!is_running) {
- rc = smpp_smsc_conf(smsc, bind_addr, port);
- if (rc < 0) {
- vty_out(vty, "%% Cannot configure new address:port%s",
- VTY_NEWLINE);
- return CMD_WARNING;
- }
- return CMD_SUCCESS;
- }
-
- rc = smpp_smsc_restart(smsc, bind_addr, port);
- if (rc < 0) {
- vty_out(vty, "%% Cannot bind to new port %s:%u nor to"
- " old port %s:%u%s",
- bind_addr? bind_addr : "0.0.0.0",
- port,
- smsc->bind_addr? smsc->bind_addr : "0.0.0.0",
- smsc->listen_port,
- VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- same_bind_addr = (bind_addr == smsc->bind_addr)
- || (bind_addr && smsc->bind_addr
- && (strcmp(bind_addr, smsc->bind_addr) == 0));
-
- if (!same_bind_addr || port != smsc->listen_port) {
- vty_out(vty, "%% Cannot bind to new port %s:%u, staying on"
- " old port %s:%u%s",
- bind_addr? bind_addr : "0.0.0.0",
- port,
- smsc->bind_addr? smsc->bind_addr : "0.0.0.0",
- smsc->listen_port,
- VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- 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]);
- return smpp_local_tcp(vty, smsc->bind_addr, port);
-}
-
-DEFUN(cfg_smpp_addr_port, cfg_smpp_addr_port_cmd,
- "local-tcp-ip A.B.C.D <1-65535>",
- "Set the local IP address and TCP port on which we listen for SMPP\n"
- "Local IP address\n"
- "TCP port number")
-{
- const char *bind_addr = argv[0];
- uint16_t port = atoi(argv[1]);
- return smpp_local_tcp(vty, bind_addr, port);
-}
-
-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);
- if (smsc->bind_addr)
- vty_out(vty, " local-tcp-ip %s %u%s", smsc->bind_addr,
- smsc->listen_port, VTY_NEWLINE);
- else
- 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 address 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 address 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_addr_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;
-}
diff --git a/src/libmsc/sms_queue.c b/src/libmsc/sms_queue.c
deleted file mode 100644
index fe7a608be..000000000
--- a/src/libmsc/sms_queue.c
+++ /dev/null
@@ -1,578 +0,0 @@
-/* SMS queue to continously attempt to deliver SMS */
-/*
- * (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-/**
- * 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 <limits.h>
-
-#include <openbsc/sms_queue.h>
-#include <openbsc/chan_alloc.h>
-#include <openbsc/db.h>
-#include <openbsc/debug.h>
-#include <openbsc/gsm_data.h>
-#include <openbsc/gsm_04_11.h>
-#include <openbsc/gsm_subscriber.h>
-#include <openbsc/signal.h>
-#include <openbsc/vlr.h>
-
-#include <osmocom/core/talloc.h>
-
-#include <osmocom/vty/vty.h>
-
-/*
- * One pending SMS that we wait for.
- */
-struct gsm_sms_pending {
- struct llist_head entry;
-
- struct vlr_subscr *vsub;
- 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;
-
- char last_msisdn[GSM_EXTENSION_LENGTH+1];
-};
-
-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 vlr_subscr *vsub)
-{
- struct gsm_sms_pending *pending;
-
- llist_for_each_entry(pending, &smsq->pending_sms, entry) {
- if (pending->vsub == vsub)
- return pending;
- }
-
- return NULL;
-}
-
-static int sms_subscriber_is_pending(struct gsm_sms_queue *smsq,
- struct vlr_subscr *vsub)
-{
- return sms_subscriber_find_pending(smsq, vsub) != 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->vsub = vlr_subscr_get(sms->receiver);
- pending->sms_id = sms->id;
- return pending;
-}
-
-static void sms_pending_free(struct gsm_sms_pending *pending)
-{
- vlr_subscr_put(pending->vsub);
- llist_del(&pending->entry);
- talloc_free(pending);
-}
-
-static void sms_pending_resend(struct gsm_sms_pending *pending)
-{
- struct gsm_network *net = pending->vsub->vlr->user_ctx;
- struct gsm_sms_queue *smsq;
- LOGP(DLSMS, LOGL_DEBUG,
- "Scheduling resend of SMS %llu.\n", pending->sms_id);
-
- pending->resend = 1;
-
- smsq = 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_network *net = pending->vsub->vlr->user_ctx;
- struct gsm_sms_queue *smsq;
-
- LOGP(DLSMS, LOGL_NOTICE, "Sending SMS %llu failed %d times.\n",
- pending->sms_id, pending->failed_attempts);
-
- smsq = 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);
- }
- }
-}
-
-/* Find the next pending SMS by cycling through the recipients. We could also
- * cycle through the pending SMS, but that might cause us to keep trying to
- * send SMS to the same few subscribers repeatedly while not servicing other
- * subscribers for a long time. By walking the list of recipient MSISDNs, we
- * ensure that all subscribers get their fair time to receive SMS. */
-struct gsm_sms *smsq_take_next_sms(struct gsm_network *net,
- char *last_msisdn,
- size_t last_msisdn_buflen)
-{
- struct gsm_sms *sms;
- int wrapped = 0;
- int sanity = 100;
- char started_with_msisdn[last_msisdn_buflen];
-
- osmo_strlcpy(started_with_msisdn, last_msisdn,
- sizeof(started_with_msisdn));
-
- while (wrapped < 2 && (--sanity)) {
- /* If we wrapped around and passed the first msisdn, we're
- * through the entire SMS DB; end it. */
- if (wrapped && strcmp(last_msisdn, started_with_msisdn) >= 0)
- break;
-
- sms = db_sms_get_next_unsent_rr_msisdn(net, last_msisdn, 9);
- if (!sms) {
- last_msisdn[0] = '\0';
- wrapped ++;
- continue;
- }
-
- /* Whatever happens, next time around service another recipient
- */
- osmo_strlcpy(last_msisdn, sms->dst.addr, last_msisdn_buflen);
-
- /* Is the subscriber attached? If not, go to next SMS */
- if (!sms->receiver || !sms->receiver->lu_complete)
- continue;
-
- return sms;
- }
-
- DEBUGP(DLSMS, "SMS queue: no SMS to be sent\n");
- return NULL;
-}
-
-/**
- * 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 = smsq_take_next_sms(smsq->network, smsq->last_msisdn,
- sizeof(smsq->last_msisdn));
- if (!sms) {
- LOGP(DLSMS, LOGL_DEBUG, "Sending SMS done (%d attempted)\n",
- attempted);
- break;
- }
-
- rounds += 1;
- LOGP(DLSMS, LOGL_DEBUG, "Sending SMS round %d\n", rounds);
-
- /*
- * 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) {
- LOGP(DLSMS, LOGL_DEBUG, "Sending SMS done (loop) (%d attempted)\n",
- attempted);
- 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 vlr_subscr *vsub)
-{
- struct gsm_network *net = vsub->vlr->user_ctx;
- struct gsm_sms_queue *smsq = 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, vsub));
-
- /* check for more messages for this subscriber */
- sms = db_sms_get_unsent_for_subscr(vsub, UINT_MAX);
- if (!sms)
- goto no_pending_sms;
-
- /* The sms should not 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(net->sms_queue);
-}
-
-/*
- * Kick off the queue again.
- */
-int sms_queue_trigger(struct gsm_sms_queue *smsq)
-{
- LOGP(DLSMS, LOGL_DEBUG, "Triggering SMS queue\n");
- 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;
- osmo_timer_setup(&sms->push_queue, sms_submit_pending, sms);
- osmo_timer_setup(&sms->resend_pending, sms_resend_pending, sms);
-
- sms_submit_pending(sms);
-
- return 0;
-}
-
-static int sub_ready_for_sm(struct gsm_network *net, struct vlr_subscr *vsub)
-{
- 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, vsub);
- if (pending) {
- LOGP(DMSC, LOGL_NOTICE,
- "Pending paging while subscriber %llu attached.\n",
- vsub->id);
- return 0;
- }
-
- conn = connection_for_subscr(vsub);
- if (!conn)
- return -1;
-
- /* Now try to deliver any pending SMS to this sub */
- sms = db_sms_get_unsent_for_subscr(vsub, UINT_MAX);
- 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 vlr_subscr *vsub = signal_data;
-
- if (signal != S_SUBSCR_ATTACHED)
- return 0;
-
- /* this is readyForSM */
- return sub_ready_for_sm(handler_data, vsub);
-}
-
-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 vlr_subscr *vsub;
-
- /* 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;
- vsub = vlr_subscr_get(pending->vsub);
- sms_pending_free(pending);
- /* Attempt to send another SMS to this subscriber */
- sms_send_next(vsub);
- vlr_subscr_put(vsub);
- 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->vsub->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->vsub->id);
- sms_pending_free(pending);
- }
-
- smsq->pending = 0;
- return 0;
-}
diff --git a/src/libmsc/subscr_conn.c b/src/libmsc/subscr_conn.c
deleted file mode 100644
index bcab8e48c..000000000
--- a/src/libmsc/subscr_conn.c
+++ /dev/null
@@ -1,359 +0,0 @@
-/* MSC subscriber connection implementation */
-
-/*
- * (C) 2016 by sysmocom s.m.f.c. <info@sysmocom.de>
- * All Rights Reserved
- *
- * Author: Neels Hofmeyr
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <osmocom/core/logging.h>
-#include <osmocom/core/fsm.h>
-#include <osmocom/core/signal.h>
-
-#include <openbsc/osmo_msc.h>
-#include <openbsc/vlr.h>
-#include <openbsc/debug.h>
-#include <openbsc/transaction.h>
-#include <openbsc/signal.h>
-#include <openbsc/a_iface.h>
-
-#define SUBSCR_CONN_TIMEOUT 5 /* seconds */
-
-static const struct value_string subscr_conn_fsm_event_names[] = {
- OSMO_VALUE_STRING(SUBSCR_CONN_E_INVALID),
- OSMO_VALUE_STRING(SUBSCR_CONN_E_START),
- OSMO_VALUE_STRING(SUBSCR_CONN_E_ACCEPTED),
- OSMO_VALUE_STRING(SUBSCR_CONN_E_COMMUNICATING),
- OSMO_VALUE_STRING(SUBSCR_CONN_E_BUMP),
- OSMO_VALUE_STRING(SUBSCR_CONN_E_MO_CLOSE),
- OSMO_VALUE_STRING(SUBSCR_CONN_E_CN_CLOSE),
- { 0, NULL }
-};
-
-const struct value_string subscr_conn_from_names[] = {
- OSMO_VALUE_STRING(SUBSCR_CONN_FROM_INVALID),
- OSMO_VALUE_STRING(SUBSCR_CONN_FROM_LU),
- OSMO_VALUE_STRING(SUBSCR_CONN_FROM_CM_SERVICE_REQ),
- OSMO_VALUE_STRING(SUBSCR_CONN_FROM_PAGING_RESP),
- { 0, NULL }
-};
-
-static void paging_event(struct gsm_subscriber_connection *conn,
- enum gsm_paging_event pe)
-{
- subscr_paging_dispatch(GSM_HOOK_RR_PAGING, pe, NULL, conn, conn->vsub);
-}
-
-void subscr_conn_fsm_init(struct osmo_fsm_inst *fi, uint32_t event, void *data)
-{
- OSMO_ASSERT(event == SUBSCR_CONN_E_START);
- osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_NEW,
- SUBSCR_CONN_TIMEOUT, 0);
-}
-
-void subscr_conn_fsm_new(struct osmo_fsm_inst *fi, uint32_t event, void *data)
-{
- struct gsm_subscriber_connection *conn = fi->priv;
- enum subscr_conn_from from = SUBSCR_CONN_FROM_INVALID;
- bool success;
-
- if (data) {
- from = *(enum subscr_conn_from*)data;
- LOGPFSM(fi, "%s\n", subscr_conn_from_name(from));
- }
-
- /* If accepted, transition the state, all other cases mean failure. */
- switch (event) {
- case SUBSCR_CONN_E_ACCEPTED:
- osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_ACCEPTED,
- SUBSCR_CONN_TIMEOUT, 0);
- break;
-
- case SUBSCR_CONN_E_MO_CLOSE:
- case SUBSCR_CONN_E_CN_CLOSE:
- if (data)
- LOGPFSM(fi, "Close event, cause %u\n",
- *(uint32_t*)data);
- /* will release further below, see
- * 'if (fi->state != SUBSCR_CONN_S_ACCEPTED)' */
- break;
-
- default:
- LOGPFSML(fi, LOGL_ERROR,
- "Unexpected event: %d %s\n", event,
- osmo_fsm_event_name(fi->fsm, event));
- break;
- }
-
- success = (fi->state == SUBSCR_CONN_S_ACCEPTED);
-
- if (from == SUBSCR_CONN_FROM_LU)
- rate_ctr_inc(&conn->network->msc_ctrs->ctr[
- success ? MSC_CTR_LOC_UPDATE_COMPLETED
- : MSC_CTR_LOC_UPDATE_FAILED]);
-
- /* signal paging success or failure in case this was a paging */
- if (from == SUBSCR_CONN_FROM_PAGING_RESP)
- paging_event(conn,
- success ? GSM_PAGING_SUCCEEDED
- : GSM_PAGING_EXPIRED);
-
- /* FIXME rate counters */
- /*rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_COMPLETED]);*/
-
- /* On failure, discard the conn */
- if (!success) {
- /* TODO: on MO_CLOSE or CN_CLOSE, first go to RELEASING and
- * await BSC/RNC confirmation? */
- osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_RELEASED, 0, 0);
- return;
- }
-
- if (from == SUBSCR_CONN_FROM_CM_SERVICE_REQ) {
- conn->received_cm_service_request = true;
- LOGPFSML(fi, LOGL_DEBUG, "received_cm_service_request = true\n");
- }
-
- osmo_fsm_inst_dispatch(fi, SUBSCR_CONN_E_BUMP, data);
-}
-
-static void subscr_conn_fsm_bump(struct osmo_fsm_inst *fi, uint32_t event, void *data)
-{
- struct gsm_subscriber_connection *conn = fi->priv;
- struct gsm_trans *trans;
-
- if (conn->silent_call) {
- LOGPFSML(fi, LOGL_DEBUG, "bump: silent call still active\n");
- return;
- }
-
- if (conn->received_cm_service_request) {
- LOGPFSML(fi, LOGL_DEBUG, "bump: still awaiting first request after a CM Service Request\n");
- return;
- }
-
- if (conn->vsub && !llist_empty(&conn->vsub->cs.requests)) {
- struct subscr_request *sr;
- if (!log_check_level(fi->fsm->log_subsys, LOGL_DEBUG)) {
- llist_for_each_entry(sr, &conn->vsub->cs.requests, entry) {
- LOGPFSML(fi, LOGL_DEBUG, "bump: still active: %s\n",
- sr->label);
- }
- }
- return;
- }
-
- if ((trans = trans_has_conn(conn))) {
- LOGPFSML(fi, LOGL_DEBUG,
- "bump: connection still has active transaction: %s\n",
- gsm48_pdisc_name(trans->protocol));
- return;
- }
-
- LOGPFSML(fi, LOGL_DEBUG, "bump: releasing conn\n");
- osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_RELEASED, 0, 0);
-}
-
-static void subscr_conn_fsm_accepted_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
-{
- struct gsm_subscriber_connection *conn = fi->priv;
- osmo_signal_dispatch(SS_SUBSCR, S_SUBSCR_ATTACHED, conn->vsub);
-}
-
-static void subscr_conn_fsm_accepted(struct osmo_fsm_inst *fi, uint32_t event, void *data)
-{
- switch (event) {
- case SUBSCR_CONN_E_COMMUNICATING:
- osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_COMMUNICATING, 0, 0);
- return;
-
- case SUBSCR_CONN_E_BUMP:
- subscr_conn_fsm_bump(fi, event, data);
- return;
-
- default:
- break;
- }
- /* Whatever unexpected happens in the accepted state, it means release.
- * Even if an unexpected event is passed, the safest thing to do is
- * discard the conn. We don't expect another SUBSCR_CONN_E_ACCEPTED. */
- osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_RELEASED, 0, 0);
-}
-
-static void subscr_conn_fsm_communicating(struct osmo_fsm_inst *fi, uint32_t event, void *data)
-{
- switch (event) {
- case SUBSCR_CONN_E_COMMUNICATING:
- /* no-op */
- return;
-
- case SUBSCR_CONN_E_BUMP:
- subscr_conn_fsm_bump(fi, event, data);
- return;
-
- default:
- break;
- }
- /* Whatever unexpected happens in the accepted state, it means release.
- * Even if an unexpected event is passed, the safest thing to do is
- * discard the conn. We don't expect another SUBSCR_CONN_E_ACCEPTED. */
- osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_RELEASED, 0, 0);
-}
-
-static void subscr_conn_fsm_cleanup(struct osmo_fsm_inst *fi,
- enum osmo_fsm_term_cause cause)
-{
- struct gsm_subscriber_connection *conn = fi->priv;
- fi->priv = NULL;
-
- if (!conn)
- return;
- conn->conn_fsm = NULL;
- msc_subscr_conn_close(conn, cause);
- msc_subscr_conn_put(conn);
-}
-
-int subscr_conn_fsm_timeout(struct osmo_fsm_inst *fi)
-{
- struct gsm_subscriber_connection *conn = fi->priv;
- if (conn)
- vlr_subscr_conn_timeout(conn->vsub);
- osmo_fsm_inst_dispatch(fi, SUBSCR_CONN_E_CN_CLOSE, NULL);
- return 0;
-}
-
-static void subscr_conn_fsm_release(struct osmo_fsm_inst *fi, uint32_t prev_state)
-{
- osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
-}
-
-#define S(x) (1 << (x))
-
-static const struct osmo_fsm_state subscr_conn_fsm_states[] = {
- [SUBSCR_CONN_S_INIT] = {
- .name = OSMO_STRINGIFY(SUBSCR_CONN_S_INIT),
- .in_event_mask = S(SUBSCR_CONN_E_START),
- .out_state_mask = S(SUBSCR_CONN_S_NEW),
- .action = subscr_conn_fsm_init,
- },
- [SUBSCR_CONN_S_NEW] = {
- .name = OSMO_STRINGIFY(SUBSCR_CONN_S_NEW),
- .in_event_mask = S(SUBSCR_CONN_E_ACCEPTED) |
- S(SUBSCR_CONN_E_MO_CLOSE) |
- S(SUBSCR_CONN_E_CN_CLOSE),
- .out_state_mask = S(SUBSCR_CONN_S_ACCEPTED) |
- S(SUBSCR_CONN_S_RELEASED),
- .action = subscr_conn_fsm_new,
- },
- [SUBSCR_CONN_S_ACCEPTED] = {
- .name = OSMO_STRINGIFY(SUBSCR_CONN_S_ACCEPTED),
- /* allow everything to release for any odd behavior */
- .in_event_mask = S(SUBSCR_CONN_E_COMMUNICATING) |
- S(SUBSCR_CONN_E_BUMP) |
- S(SUBSCR_CONN_E_ACCEPTED) |
- S(SUBSCR_CONN_E_MO_CLOSE) |
- S(SUBSCR_CONN_E_CN_CLOSE),
- .out_state_mask = S(SUBSCR_CONN_S_RELEASED) |
- S(SUBSCR_CONN_S_COMMUNICATING),
- .onenter = subscr_conn_fsm_accepted_enter,
- .action = subscr_conn_fsm_accepted,
- },
- [SUBSCR_CONN_S_COMMUNICATING] = {
- .name = OSMO_STRINGIFY(SUBSCR_CONN_S_COMMUNICATING),
- /* allow everything to release for any odd behavior */
- .in_event_mask = S(SUBSCR_CONN_E_BUMP) |
- S(SUBSCR_CONN_E_ACCEPTED) |
- S(SUBSCR_CONN_E_COMMUNICATING) |
- S(SUBSCR_CONN_E_MO_CLOSE) |
- S(SUBSCR_CONN_E_CN_CLOSE),
- .out_state_mask = S(SUBSCR_CONN_S_RELEASED),
- .action = subscr_conn_fsm_communicating,
- },
- [SUBSCR_CONN_S_RELEASED] = {
- .name = OSMO_STRINGIFY(SUBSCR_CONN_S_RELEASED),
- .onenter = subscr_conn_fsm_release,
- },
-};
-
-static struct osmo_fsm subscr_conn_fsm = {
- .name = "Subscr_Conn",
- .states = subscr_conn_fsm_states,
- .num_states = ARRAY_SIZE(subscr_conn_fsm_states),
- .allstate_event_mask = 0,
- .allstate_action = NULL,
- .log_subsys = DMM,
- .event_names = subscr_conn_fsm_event_names,
- .cleanup = subscr_conn_fsm_cleanup,
- .timer_cb = subscr_conn_fsm_timeout,
-};
-
-int msc_create_conn_fsm(struct gsm_subscriber_connection *conn, const char *id)
-{
- struct osmo_fsm_inst *fi;
- OSMO_ASSERT(conn);
-
- if (conn->conn_fsm) {
- LOGP(DMM, LOGL_ERROR,
- "%s: Error: connection already in use\n", id);
- return -EINVAL;
- }
-
- /* Allocate the FSM not with the subscr_conn. Semantically it would
- * make sense, but in subscr_conn_fsm_cleanup(), we want to discard the
- * subscriber connection. If the FSM is freed along with the subscriber
- * connection, then in _osmo_fsm_inst_term() the osmo_fsm_inst_free()
- * that follows the cleanup() call would run into a double free. */
- fi = osmo_fsm_inst_alloc(&subscr_conn_fsm, conn->network,
- msc_subscr_conn_get(conn),
- LOGL_DEBUG, id);
-
- if (!fi) {
- LOGP(DMM, LOGL_ERROR,
- "%s: Failed to allocate subscr conn master FSM\n", id);
- return -ENOMEM;
- }
- conn->conn_fsm = fi;
- osmo_fsm_inst_dispatch(conn->conn_fsm, SUBSCR_CONN_E_START, NULL);
- return 0;
-}
-
-bool msc_subscr_conn_is_accepted(struct gsm_subscriber_connection *conn)
-{
- if (!conn)
- return false;
- if (!conn->vsub)
- return false;
- if (!conn->conn_fsm)
- return false;
- if (!(conn->conn_fsm->state == SUBSCR_CONN_S_ACCEPTED
- || conn->conn_fsm->state == SUBSCR_CONN_S_COMMUNICATING))
- return false;
- return true;
-}
-
-void msc_subscr_conn_communicating(struct gsm_subscriber_connection *conn)
-{
- OSMO_ASSERT(conn);
- osmo_fsm_inst_dispatch(conn->conn_fsm, SUBSCR_CONN_E_COMMUNICATING,
- NULL);
-}
-
-void msc_subscr_conn_init(void)
-{
- osmo_fsm_register(&subscr_conn_fsm);
-}
diff --git a/src/libmsc/transaction.c b/src/libmsc/transaction.c
deleted file mode 100644
index 7289a8f11..000000000
--- a/src/libmsc/transaction.c
+++ /dev/null
@@ -1,214 +0,0 @@
-/* GSM 04.07 Transaction handling */
-
-/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <openbsc/transaction.h>
-#include <openbsc/gsm_data.h>
-#include <openbsc/mncc.h>
-#include <openbsc/debug.h>
-#include <osmocom/core/talloc.h>
-#include <openbsc/gsm_04_08.h>
-#include <openbsc/mncc.h>
-#include <openbsc/paging.h>
-#include <openbsc/osmo_msc.h>
-#include <openbsc/vlr.h>
-
-void *tall_trans_ctx;
-
-void _gsm48_cc_trans_free(struct gsm_trans *trans);
-
-/*! Find a transaction in connection for given protocol + transaction ID
- * \param[in] conn Connection in whihc we want to find transaction
- * \param[in] proto Protocol of transaction
- * \param[in] trans_id Transaction ID of transaction
- * \returns Matching transaction, if any
- */
-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->network;
- struct vlr_subscr *vsub = conn->vsub;
-
- llist_for_each_entry(trans, &net->trans_list, entry) {
- if (trans->vsub == vsub &&
- trans->protocol == proto &&
- trans->transaction_id == trans_id)
- return trans;
- }
- return NULL;
-}
-
-/*! Find a transaction by call reference
- * \param[in] net Network in which we should search
- * \param[in] callref Call Reference of transaction
- * \returns Matching transaction, if any
- */
-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;
-}
-
-/*! Allocate a new transaction and add it to network list
- * \param[in] net Netwokr in which we allocate transaction
- * \param[in] subscr Subscriber for which we allocate transaction
- * \param[in] protocol Protocol (CC/SMS/...)
- * \param[in] callref Call Reference
- * \returns Transaction
- */
-struct gsm_trans *trans_alloc(struct gsm_network *net,
- struct vlr_subscr *vsub,
- uint8_t protocol, uint8_t trans_id,
- uint32_t callref)
-{
- struct gsm_trans *trans;
-
- DEBUGP(DCC, "subscr=%p, net=%p\n", vsub, net);
-
- trans = talloc_zero(tall_trans_ctx, struct gsm_trans);
- if (!trans)
- return NULL;
-
- trans->vsub = vlr_subscr_get(vsub);
-
- trans->protocol = protocol;
- trans->transaction_id = trans_id;
- trans->callref = callref;
-
- trans->net = net;
- llist_add_tail(&trans->entry, &net->trans_list);
-
- return trans;
-}
-
-/*! Release a transaction
- * \param[in] trans Transaction to be released
- */
-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->vsub) {
- vlr_subscr_put(trans->vsub);
- trans->vsub = NULL;
- }
-
- llist_del(&trans->entry);
-
- if (trans->conn)
- msc_subscr_conn_put(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
- * \param[in] net GSM network
- * \param[in] subscr Subscriber for which to find ID
- * \param[in] protocol Protocol for whihc to find ID
- * \param[in] ti_flag FIXME
- */
-int trans_assign_trans_id(struct gsm_network *net, struct vlr_subscr *vsub,
- 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->vsub != vsub ||
- 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;
-}
-
-/*! Check if we have any transaction for given connection
- * \param[in] conn Connection to check
- * \returns 1 in case there is a transaction, 0 otherwise
- */
-struct gsm_trans *trans_has_conn(const struct gsm_subscriber_connection *conn)
-{
- struct gsm_trans *trans;
-
- llist_for_each_entry(trans, &conn->network->trans_list, entry)
- if (trans->conn == conn)
- return trans;
-
- return NULL;
-}
-
-/*! Free all transactions associated with a connection, presumably when the
- * conn is being closed. The transaction code will inform the CC or SMS
- * facilities, which will then send the necessary release indications.
- * \param[in] conn Connection that is going to be closed.
- */
-void trans_conn_closed(struct gsm_subscriber_connection *conn)
-{
- struct gsm_trans *trans;
-
- /* As part of the CC REL_IND the remote leg might be released and this
- * will trigger another call to trans_free. This is something the llist
- * macro can not handle and we need to re-iterate the list every time.
- */
-restart:
- llist_for_each_entry(trans, &conn->network->trans_list, entry) {
- if (trans->conn == conn) {
- trans_free(trans);
- goto restart;
- }
- }
-}
diff --git a/src/libmsc/ussd.c b/src/libmsc/ussd.c
deleted file mode 100644
index 81a356690..000000000
--- a/src/libmsc/ussd.c
+++ /dev/null
@@ -1,103 +0,0 @@
-/* Network-specific handling of mobile-originated USSDs. */
-
-/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
- * (C) 2008, 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
- * (C) 2009 by Mike Haben <michael.haben@btinternet.com>
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-/* This module defines the network-specific handling of mobile-originated
- USSD messages. */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-
-#include <openbsc/gsm_04_80.h>
-#include <openbsc/gsm_subscriber.h>
-#include <openbsc/debug.h>
-#include <openbsc/osmo_msc.h>
-#include <openbsc/vlr.h>
-
-/* 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 ss_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 ss_request req;
- struct gsm48_hdr *gh;
-
- /* TODO: Use subscriber_connection ref-counting if we ever want
- * to keep the connection alive due ot ongoing USSD exchange.
- * As we answer everytying synchronously so far, there's no need
- * yet */
-
- cm_service_request_concludes(conn, msg);
-
- memset(&req, 0, sizeof(req));
- gh = msgb_l3(msg);
- rc = gsm0480_decode_ss_request(gh, msgb_l3len(msg), &req);
- if (!rc) {
- DEBUGP(DMM, "Unhandled SS\n");
- rc = gsm0480_send_ussd_reject(conn, msg, &req);
- return rc;
- }
-
- /* Interrogation or releaseComplete? */
- if (req.ussd_text[0] == '\0' || req.ussd_text[0] == 0xFF) {
- if (req.ss_code > 0) {
- /* Assume interrogateSS or modification of it and reject */
- rc = gsm0480_send_ussd_reject(conn, msg, &req);
- return rc;
- }
- /* Still assuming a Release-Complete and returning */
- return 0;
- }
-
- msc_subscr_conn_communicating(conn);
- if (!strcmp(USSD_TEXT_OWN_NUMBER, (const char *)req.ussd_text)) {
- DEBUGP(DMM, "USSD: Own number requested\n");
- rc = send_own_number(conn, msg, &req);
- } else {
- DEBUGP(DMM, "Unhandled USSD %s\n", req.ussd_text);
- rc = gsm0480_send_ussd_reject(conn, msg, &req);
- }
-
- return rc;
-}
-
-/* A network-specific handler function */
-static int send_own_number(struct gsm_subscriber_connection *conn, const struct msgb *msg, const struct ss_request *req)
-{
- char *own_number = conn->vsub->msisdn;
- char response_string[GSM_EXTENSION_LENGTH + 20];
-
- DEBUGP(DMM, "%s: MSISDN = %s\n", vlr_subscr_name(conn->vsub),
- own_number);
-
- /* 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);
-}
diff --git a/src/libmsc/vty_interface_layer3.c b/src/libmsc/vty_interface_layer3.c
deleted file mode 100644
index d1bf6b36d..000000000
--- a/src/libmsc/vty_interface_layer3.c
+++ /dev/null
@@ -1,885 +0,0 @@
-/* OpenBSC interface to quagga VTY */
-/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
- * (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 <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <stdlib.h>
-#include <limits.h>
-#include <unistd.h>
-#include <time.h>
-#include <inttypes.h>
-
-#include <osmocom/vty/command.h>
-#include <osmocom/vty/buffer.h>
-#include <osmocom/vty/vty.h>
-
-#include <arpa/inet.h>
-
-#include <osmocom/core/linuxlist.h>
-#include <openbsc/gsm_data.h>
-#include <openbsc/gsm_subscriber.h>
-#include <openbsc/bsc_subscriber.h>
-#include <openbsc/silent_call.h>
-#include <openbsc/gsm_04_11.h>
-#include <osmocom/abis/e1_input.h>
-#include <openbsc/abis_nm.h>
-#include <osmocom/gsm/gsm_utils.h>
-#include <osmocom/core/utils.h>
-#include <openbsc/db.h>
-#include <osmocom/core/talloc.h>
-#include <openbsc/signal.h>
-#include <openbsc/debug.h>
-#include <openbsc/vty.h>
-#include <openbsc/gsm_04_80.h>
-#include <openbsc/chan_alloc.h>
-#include <openbsc/sms_queue.h>
-#include <openbsc/mncc_int.h>
-#include <openbsc/handover.h>
-#include <openbsc/vlr.h>
-
-#include <osmocom/vty/logging.h>
-
-#include <openbsc/osmo_msc.h>
-
-#include "meas_feed.h"
-
-extern struct gsm_network *gsmnet_from_vty(struct vty *v);
-
-static void subscr_dump_full_vty(struct vty *vty, struct vlr_subscr *vsub)
-{
- int reqs;
- struct llist_head *entry;
- char expire_time[200];
-
- if (strlen(vsub->name))
- vty_out(vty, " Name: '%s'%s", vsub->name, VTY_NEWLINE);
- if (strlen(vsub->msisdn))
- vty_out(vty, " Extension: %s%s", vsub->msisdn,
- VTY_NEWLINE);
- vty_out(vty, " LAC: %d/0x%x%s",
- vsub->lac, vsub->lac, VTY_NEWLINE);
- vty_out(vty, " IMSI: %s%s", vsub->imsi, VTY_NEWLINE);
- if (vsub->tmsi != GSM_RESERVED_TMSI)
- vty_out(vty, " TMSI: %08X%s", vsub->tmsi,
- VTY_NEWLINE);
- if (vsub->tmsi_new != GSM_RESERVED_TMSI)
- vty_out(vty, " new TMSI: %08X%s", vsub->tmsi_new,
- VTY_NEWLINE);
-
-#if 0
- /* TODO: add this to vlr_subscr? */
- if (vsub->auth_info.auth_algo != AUTH_ALGO_NONE) {
- struct gsm_auth_info *i = &vsub->auth_info;
- vty_out(vty, " A3A8 algorithm id: %d%s",
- i->auth_algo, VTY_NEWLINE);
- vty_out(vty, " A3A8 Ki: %s%s",
- osmo_hexdump(i->a3a8_ki, i->a3a8_ki_len),
- VTY_NEWLINE);
- }
-#endif
-
- if (vsub->last_tuple) {
- struct gsm_auth_tuple *t = vsub->last_tuple;
- vty_out(vty, " A3A8 last tuple (used %d times):%s",
- t->use_count, VTY_NEWLINE);
- vty_out(vty, " seq # : %d%s",
- t->key_seq, VTY_NEWLINE);
- vty_out(vty, " RAND : %s%s",
- osmo_hexdump(t->vec.rand, sizeof(t->vec.rand)),
- VTY_NEWLINE);
- vty_out(vty, " SRES : %s%s",
- osmo_hexdump(t->vec.sres, sizeof(t->vec.sres)),
- VTY_NEWLINE);
- vty_out(vty, " Kc : %s%s",
- osmo_hexdump(t->vec.kc, sizeof(t->vec.kc)),
- VTY_NEWLINE);
- }
-
- /* print the expiration time of a subscriber */
- strftime(expire_time, sizeof(expire_time),
- "%a, %d %b %Y %T %z", localtime(&vsub->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, &vsub->cs.requests)
- reqs += 1;
- vty_out(vty, " Paging: %s paging for %d requests%s",
- vsub->cs.is_paging ? "is" : "not", reqs, VTY_NEWLINE);
- vty_out(vty, " Use count: %u%s", vsub->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_network *gsmnet = gsmnet_from_vty(vty);
- struct vlr_subscr *vsub;
- int count = 0;
-
- llist_for_each_entry(vsub, &gsmnet->vlr->subscribers, list) {
- if (++count > 100) {
- vty_out(vty, "%% More than %d subscribers in cache,"
- " stopping here.%s", count-1, VTY_NEWLINE);
- break;
- }
- vty_out(vty, " Subscriber:%s", VTY_NEWLINE);
- subscr_dump_full_vty(vty, vsub);
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN(sms_send_pend,
- sms_send_pend_cmd,
- "sms send pending",
- "SMS related commands\n" "SMS Sending related commands\n"
- "Send all pending SMS")
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- struct gsm_sms *sms;
- unsigned long long sms_id = 0;
-
- while (1) {
- sms = db_sms_get_next_unsent(gsmnet, sms_id, UINT_MAX);
- if (!sms)
- break;
-
- if (sms->receiver)
- gsm411_send_sms_subscr(sms->receiver, sms);
-
- sms_id = sms->id + 1;
- }
-
- return CMD_SUCCESS;
-}
-
-static int _send_sms_str(struct vlr_subscr *receiver,
- struct vlr_subscr *sender,
- char *str, uint8_t tp_pid)
-{
- struct gsm_network *net = receiver->vlr->user_ctx;
- 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;
- }
- LOGP(DLSMS, LOGL_DEBUG, "SMS stored in DB\n");
-
- sms_free(sms);
- sms_queue_trigger(net->sms_queue);
- return CMD_SUCCESS;
-}
-
-static struct vlr_subscr *get_vsub_by_argv(struct gsm_network *gsmnet,
- const char *type,
- const char *id)
-{
- if (!strcmp(type, "extension") || !strcmp(type, "msisdn"))
- return vlr_subscr_find_by_msisdn(gsmnet->vlr, id);
- else if (!strcmp(type, "imsi") || !strcmp(type, "id"))
- return vlr_subscr_find_by_imsi(gsmnet->vlr, id);
- else if (!strcmp(type, "tmsi"))
- return vlr_subscr_find_by_tmsi(gsmnet->vlr, atoi(id));
-
- return NULL;
-}
-#define SUBSCR_TYPES "(extension|imsi|tmsi|id)"
-#define SUBSCR_HELP "Operations on a Subscriber\n" \
- "Identify subscriber by extension (phone number)\n" \
- "Identify subscriber by IMSI\n" \
- "Identify subscriber by TMSI\n" \
- "Identify subscriber by 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 vlr_subscr *vsub = get_vsub_by_argv(gsmnet, argv[0],
- argv[1]);
-
- if (!vsub) {
- vty_out(vty, "%% No subscriber found for %s %s%s",
- argv[0], argv[1], VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- subscr_dump_full_vty(vty, vsub);
-
- vlr_subscr_put(vsub);
-
- 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")
-{
- vty_out(vty, "%% 'subscriber create' now needs to be done at osmo-hlr%s",
- VTY_NEWLINE);
- return CMD_WARNING;
-}
-
-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 vlr_subscr *vsub;
- struct gsm_sms *sms;
-
- vsub = get_vsub_by_argv(gsmnet, argv[0], argv[1]);
- if (!vsub) {
- vty_out(vty, "%% No subscriber found for %s %s%s",
- argv[0], argv[1], VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- sms = db_sms_get_unsent_for_subscr(vsub, UINT_MAX);
- if (sms)
- gsm411_send_sms_subscr(sms->receiver, sms);
-
- vlr_subscr_put(vsub);
-
- 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 vlr_subscr *vsub = get_vsub_by_argv(gsmnet, argv[0], argv[1]);
- struct vlr_subscr *sender = get_vsub_by_argv(gsmnet, argv[2], argv[3]);
- char *str;
- int rc;
-
- if (!vsub) {
- 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(vsub, sender, str, 0);
- talloc_free(str);
-
-err:
- if (sender)
- vlr_subscr_put(sender);
-
- if (vsub)
- vlr_subscr_put(vsub);
-
- 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 vlr_subscr *vsub = get_vsub_by_argv(gsmnet, argv[0], argv[1]);
- struct vlr_subscr *sender = get_vsub_by_argv(gsmnet, argv[2], argv[3]);
- char *str;
- int rc;
-
- if (!vsub) {
- 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(vsub, sender, str, 64);
- talloc_free(str);
-
-err:
- if (sender)
- vlr_subscr_put(sender);
-
- if (vsub)
- vlr_subscr_put(vsub);
-
- 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 vlr_subscr *vsub = get_vsub_by_argv(gsmnet, argv[0], argv[1]);
- int rc, type;
-
- if (!vsub) {
- 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(vsub, vty, type);
- if (rc <= 0) {
- vty_out(vty, "%% Subscriber not attached%s",
- VTY_NEWLINE);
- vlr_subscr_put(vsub);
- return CMD_WARNING;
- }
-
- vlr_subscr_put(vsub);
-
- 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 vlr_subscr *vsub = get_vsub_by_argv(gsmnet, argv[0], argv[1]);
- int rc;
-
- if (!vsub) {
- vty_out(vty, "%% No subscriber found for %s %s%s",
- argv[0], argv[1], VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- rc = gsm_silent_call_stop(vsub);
- if (rc < 0) {
- vlr_subscr_put(vsub);
- return CMD_WARNING;
- }
-
- vlr_subscr_put(vsub);
-
- 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 vlr_subscr *vsub = get_vsub_by_argv(gsmnet, argv[0], argv[1]);
- int level;
-
- if (!vsub) {
- 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) {
- vlr_subscr_put(vsub);
- return CMD_WARNING;
- }
-
- conn = connection_for_subscr(vsub);
- if (!conn) {
- vty_out(vty, "%% An active connection is required for %s %s%s",
- argv[0], argv[1], VTY_NEWLINE);
- vlr_subscr_put(vsub);
- talloc_free(text);
- return CMD_WARNING;
- }
-
- msc_send_ussd_notify(conn, level, text);
- msc_send_ussd_release_complete(conn);
-
- vlr_subscr_put(vsub);
- talloc_free(text);
- return CMD_SUCCESS;
-}
-
-DEFUN(ena_subscr_expire,
- ena_subscr_expire_cmd,
- "subscriber " SUBSCR_TYPES " ID expire",
- SUBSCR_HELP "Expire the subscriber Now\n")
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- struct vlr_subscr *vsub = get_vsub_by_argv(gsmnet, argv[0],
- argv[1]);
-
- if (!vsub) {
- vty_out(vty, "%% No subscriber found for %s %s%s",
- argv[0], argv[1], VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- if (vsub->lu_complete) {
- vsub->lu_complete = false;
- vlr_subscr_put(vsub);
- vty_out(vty, "%% VLR released subscriber %s%s",
- vlr_subscr_name(vsub), VTY_NEWLINE);
- }
-
- if (vsub->use_count > 1)
- vty_out(vty, "%% Subscriber %s is still in use,"
- " should be released soon%s",
- vlr_subscr_name(vsub), VTY_NEWLINE);
-
- vlr_subscr_put(vsub);
- 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")
-{
- vty_out(vty, "%% 'subscriber a3a8' is no longer supported.%s"
- "%% This is now up to osmo-hlr.%s",
- VTY_NEWLINE, VTY_NEWLINE);
- return CMD_WARNING;
-}
-
-DEFUN(subscriber_update,
- subscriber_update_cmd,
- "subscriber " SUBSCR_TYPES " ID update",
- SUBSCR_HELP "Update the subscriber data from the dabase.\n")
-{
- vty_out(vty, "%% 'subscriber update' is no longer supported.%s",
- VTY_NEWLINE);
- return CMD_WARNING;
-}
-
-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 success%s", 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);
-
- vty_out(vty, "Location Update : %lu attach, %lu normal, %lu periodic%s",
- net->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_TYPE_ATTACH].current,
- net->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_TYPE_NORMAL].current,
- net->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_TYPE_PERIODIC].current,
- VTY_NEWLINE);
- vty_out(vty, "IMSI Detach Indications : %lu%s",
- net->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_TYPE_DETACH].current,
- VTY_NEWLINE);
- vty_out(vty, "Location Updating Results: %lu completed, %lu failed%s",
- net->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_COMPLETED].current,
- net->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_FAILED].current,
- VTY_NEWLINE);
- vty_out(vty, "Handover : %lu attempted, %lu no_channel, %lu timeout, "
- "%lu completed, %lu failed%s",
- net->msc_ctrs->ctr[BSC_CTR_HANDOVER_ATTEMPTED].current,
- net->msc_ctrs->ctr[BSC_CTR_HANDOVER_NO_CHANNEL].current,
- net->msc_ctrs->ctr[BSC_CTR_HANDOVER_TIMEOUT].current,
- net->msc_ctrs->ctr[BSC_CTR_HANDOVER_COMPLETED].current,
- net->msc_ctrs->ctr[BSC_CTR_HANDOVER_FAILED].current,
- VTY_NEWLINE);
- vty_out(vty, "SMS MO : %lu submitted, %lu no receiver%s",
- net->msc_ctrs->ctr[MSC_CTR_SMS_SUBMITTED].current,
- net->msc_ctrs->ctr[MSC_CTR_SMS_NO_RECEIVER].current,
- VTY_NEWLINE);
- vty_out(vty, "SMS MT : %lu delivered, %lu no memory, %lu other error%s",
- net->msc_ctrs->ctr[MSC_CTR_SMS_DELIVERED].current,
- net->msc_ctrs->ctr[MSC_CTR_SMS_RP_ERR_MEM].current,
- net->msc_ctrs->ctr[MSC_CTR_SMS_RP_ERR_OTHER].current,
- VTY_NEWLINE);
- vty_out(vty, "MO Calls : %lu setup, %lu connect ack%s",
- net->msc_ctrs->ctr[MSC_CTR_CALL_MO_SETUP].current,
- net->msc_ctrs->ctr[MSC_CTR_CALL_MO_CONNECT_ACK].current,
- VTY_NEWLINE);
- vty_out(vty, "MT Calls : %lu setup, %lu connect%s",
- net->msc_ctrs->ctr[MSC_CTR_CALL_MT_SETUP].current,
- net->msc_ctrs->ctr[MSC_CTR_CALL_MT_CONNECT].current,
- 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 vlr_subscr *vlr_subscr;
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- struct log_target *tgt = osmo_log_vty2tgt(vty);
- const char *imsi = argv[0];
-
- if (!tgt)
- return CMD_WARNING;
-
- vlr_subscr = vlr_subscr_find_by_imsi(gsmnet->vlr, imsi);
-
- if (!vlr_subscr) {
- vty_out(vty, "%%no subscriber with IMSI(%s)%s",
- argv[0], VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- log_set_filter_vlr_subscr(tgt, vlr_subscr);
- return CMD_SUCCESS;
-}
-
-static struct cmd_node hlr_node = {
- HLR_NODE,
- "%s(config-hlr)# ",
- 1,
-};
-
-DEFUN(cfg_hlr, cfg_hlr_cmd,
- "hlr", "Configure connection to the HLR")
-{
- vty->node = HLR_NODE;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_hlr_remote_ip, cfg_hlr_remote_ip_cmd, "remote-ip A.B.C.D",
- "Remote GSUP address of the HLR\n"
- "Remote GSUP address (default: " MSC_HLR_REMOTE_IP_DEFAULT ")")
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- talloc_free((void*)gsmnet->gsup_server_addr_str);
- gsmnet->gsup_server_addr_str = talloc_strdup(gsmnet, argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_hlr_remote_port, cfg_hlr_remote_port_cmd, "remote-port <1-65535>",
- "Remote GSUP port of the HLR\n"
- "Remote GSUP port (default: " OSMO_STRINGIFY(MSC_HLR_REMOTE_PORT_DEFAULT) ")")
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- gsmnet->gsup_server_port = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
-static int config_write_hlr(struct vty *vty)
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
-
- vty_out(vty, "hlr%s", VTY_NEWLINE);
- vty_out(vty, " remote-ip %s%s",
- gsmnet->gsup_server_addr_str, VTY_NEWLINE);
- vty_out(vty, " remote-port %u%s",
- gsmnet->gsup_server_port, 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_expire_cmd);
- install_element(ENABLE_NODE, &ena_subscr_a3a8_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_hlr_cmd);
- install_node(&hlr_node, config_write_hlr);
- install_element(HLR_NODE, &cfg_hlr_remote_ip_cmd);
- install_element(HLR_NODE, &cfg_hlr_remote_port_cmd);
-
- return 0;
-}
diff --git a/src/libvlr/Makefile.am b/src/libvlr/Makefile.am
deleted file mode 100644
index 17ad4111f..000000000
--- a/src/libvlr/Makefile.am
+++ /dev/null
@@ -1,19 +0,0 @@
-AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)
-AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) \
- $(COVERAGE_CFLAGS) $(LIBCRYPTO_CFLAGS)
-
-noinst_HEADERS = \
- vlr_access_req_fsm.h \
- vlr_auth_fsm.h \
- vlr_core.h \
- vlr_lu_fsm.h \
- $(NULL)
-
-noinst_LIBRARIES = libvlr.a
-
-libvlr_a_SOURCES = \
- vlr.c \
- vlr_access_req_fsm.c \
- vlr_auth_fsm.c \
- vlr_lu_fsm.c \
- $(NULL)
diff --git a/src/libvlr/vlr.c b/src/libvlr/vlr.c
deleted file mode 100644
index 22898b119..000000000
--- a/src/libvlr/vlr.c
+++ /dev/null
@@ -1,1115 +0,0 @@
-/* Osmocom Visitor Location Register (VLR) code base */
-
-/* (C) 2016 by Harald Welte <laforge@gnumonks.org>
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <osmocom/core/linuxlist.h>
-#include <osmocom/core/fsm.h>
-#include <osmocom/core/utils.h>
-#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
-#include <osmocom/gsm/gsup.h>
-#include <osmocom/gsm/apn.h>
-#include <openbsc/gsm_subscriber.h>
-#include <openbsc/gsup_client.h>
-#include <openbsc/vlr.h>
-#include <openbsc/gprs_sgsn.h>
-#include <openbsc/gprs_utils.h>
-#include <openbsc/debug.h>
-
-#include <openssl/rand.h>
-
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <limits.h>
-
-#include "vlr_core.h"
-#include "vlr_auth_fsm.h"
-#include "vlr_lu_fsm.h"
-#include "vlr_access_req_fsm.h"
-
-#define SGSN_SUBSCR_MAX_RETRIES 3
-#define SGSN_SUBSCR_RETRY_INTERVAL 10
-
-/***********************************************************************
- * Convenience functions
- ***********************************************************************/
-
-const struct value_string vlr_ciph_names[] = {
- OSMO_VALUE_STRING(VLR_CIPH_NONE),
- OSMO_VALUE_STRING(VLR_CIPH_A5_1),
- OSMO_VALUE_STRING(VLR_CIPH_A5_2),
- OSMO_VALUE_STRING(VLR_CIPH_A5_3),
- { 0, NULL }
-};
-
-uint32_t vlr_timer(struct vlr_instance *vlr, uint32_t timer)
-{
- uint32_t tidx = 0xffffffff;
-
- switch (timer) {
- case 3270:
- tidx = VLR_T_3270;
- break;
- case 3260:
- tidx = VLR_T_3260;
- break;
- case 3250:
- tidx = VLR_T_3250;
- break;
- }
-
- OSMO_ASSERT(tidx < sizeof(vlr->cfg.timer));
- return vlr->cfg.timer[tidx];
-}
-
-struct vlr_subscr *_vlr_subscr_find_by_imsi(struct vlr_instance *vlr,
- const char *imsi,
- const char *file, int line)
-{
- struct vlr_subscr *vsub;
-
- if (!imsi || !*imsi)
- return NULL;
-
- llist_for_each_entry(vsub, &vlr->subscribers, list) {
- if (vlr_subscr_matches_imsi(vsub, imsi))
- return _vlr_subscr_get(vsub, file, line);
- }
- return NULL;
-}
-
-struct vlr_subscr *_vlr_subscr_find_by_tmsi(struct vlr_instance *vlr,
- uint32_t tmsi,
- const char *file, int line)
-{
- struct vlr_subscr *vsub;
-
- if (tmsi == GSM_RESERVED_TMSI)
- return NULL;
-
- llist_for_each_entry(vsub, &vlr->subscribers, list) {
- if (vlr_subscr_matches_tmsi(vsub, tmsi))
- return _vlr_subscr_get(vsub, file, line);
- }
- return NULL;
-}
-
-struct vlr_subscr *_vlr_subscr_find_by_msisdn(struct vlr_instance *vlr,
- const char *msisdn,
- const char *file, int line)
-{
- struct vlr_subscr *vsub;
-
- if (!msisdn || !*msisdn)
- return NULL;
-
- llist_for_each_entry(vsub, &vlr->subscribers, list) {
- if (vlr_subscr_matches_msisdn(vsub, msisdn))
- return _vlr_subscr_get(vsub, file, line);
- }
- return NULL;
-}
-
-/* Transmit GSUP message to HLR */
-static int vlr_tx_gsup_message(struct vlr_instance *vlr,
- struct osmo_gsup_message *gsup_msg)
-{
- struct msgb *msg = gsup_client_msgb_alloc();
-
- osmo_gsup_encode(msg, gsup_msg);
-
- if (!vlr->gsup_client) {
- LOGP(DVLR, LOGL_NOTICE, "GSUP link is down, cannot "
- "send GSUP: %s\n", msgb_hexdump(msg));
- msgb_free(msg);
- return -ENOTSUP;
- }
-
- LOGP(DVLR, LOGL_DEBUG, "GSUP tx: %s\n",
- osmo_hexdump_nospc(msg->data, msg->len));
-
- return gsup_client_send(vlr->gsup_client, msg);
-}
-
-/* Transmit GSUP message for subscriber to HLR, using IMSI from subscriber */
-static int vlr_subscr_tx_gsup_message(struct vlr_subscr *vsub,
- struct osmo_gsup_message *gsup_msg)
-{
- struct vlr_instance *vlr = vsub->vlr;
-
- if (strlen(gsup_msg->imsi) == 0)
- osmo_strlcpy(gsup_msg->imsi, vsub->imsi, sizeof(gsup_msg->imsi));
-
- return vlr_tx_gsup_message(vlr, gsup_msg);
-}
-
-/* Transmit GSUP error in response to original message */
-static int vlr_tx_gsup_error_reply(struct vlr_instance *vlr,
- struct osmo_gsup_message *gsup_orig,
- enum gsm48_gmm_cause cause)
-{
- struct osmo_gsup_message gsup_reply = {0};
-
- osmo_strlcpy(gsup_reply.imsi, gsup_orig->imsi, sizeof(gsup_reply.imsi));
- gsup_reply.cause = cause;
- gsup_reply.message_type =
- OSMO_GSUP_TO_MSGT_ERROR(gsup_orig->message_type);
-
- return vlr_tx_gsup_message(vlr, &gsup_reply);
-}
-
-struct vlr_subscr *_vlr_subscr_get(struct vlr_subscr *sub, const char *file, int line)
-{
- if (!sub)
- return NULL;
- OSMO_ASSERT(sub->use_count < INT_MAX);
- sub->use_count++;
- LOGPSRC(DREF, LOGL_DEBUG, file, line,
- "VLR subscr %s usage increases to: %d\n",
- vlr_subscr_name(sub), sub->use_count);
- return sub;
-}
-
-struct vlr_subscr *_vlr_subscr_put(struct vlr_subscr *sub, const char *file, int line)
-{
- if (!sub)
- return NULL;
- sub->use_count--;
- LOGPSRC(DREF, sub->use_count >= 0? LOGL_DEBUG : LOGL_ERROR,
- file, line,
- "VLR subscr %s usage decreases to: %d\n",
- vlr_subscr_name(sub), sub->use_count);
- if (sub->use_count <= 0)
- vlr_subscr_free(sub);
- return NULL;
-}
-
-/* Allocate a new subscriber and insert it into list */
-static struct vlr_subscr *_vlr_subscr_alloc(struct vlr_instance *vlr)
-{
- struct vlr_subscr *vsub;
- int i;
-
- vsub = talloc_zero(vlr, struct vlr_subscr);
- vsub->vlr = vlr;
- vsub->tmsi = GSM_RESERVED_TMSI;
- vsub->tmsi_new = GSM_RESERVED_TMSI;
-
- for (i = 0; i < ARRAY_SIZE(vsub->auth_tuples); i++)
- vsub->auth_tuples[i].key_seq = GSM_KEY_SEQ_INVAL;
-
- INIT_LLIST_HEAD(&vsub->cs.requests);
- INIT_LLIST_HEAD(&vsub->ps.pdp_list);
-
- llist_add_tail(&vsub->list, &vlr->subscribers);
- return vsub;
-}
-
-struct vlr_subscr *vlr_subscr_alloc(struct vlr_instance *vlr)
-{
- return vlr_subscr_get(_vlr_subscr_alloc(vlr));
-}
-
-/* Send a GSUP Purge MS request.
- * TODO: this should be sent to the *previous* VLR when this VLR is "taking"
- * this subscriber, not to the HLR? */
-int vlr_subscr_purge(struct vlr_subscr *vsub)
-{
- struct osmo_gsup_message gsup_msg = {0};
-
- gsup_msg.message_type = OSMO_GSUP_MSGT_PURGE_MS_REQUEST;
-
- /* provide HLR number in case we know it */
- gsup_msg.hlr_enc_len = vsub->hlr.len;
- gsup_msg.hlr_enc = vsub->hlr.buf;
-
- return vlr_subscr_tx_gsup_message(vsub, &gsup_msg);
-}
-
-void vlr_subscr_cancel(struct vlr_subscr *vsub, enum gsm48_gmm_cause cause)
-{
- if (!vsub)
- return;
-
- if (vsub->lu_fsm) {
- if (vsub->lu_fsm->state == VLR_ULA_S_WAIT_HLR_UPD)
- osmo_fsm_inst_dispatch(vsub->lu_fsm,
- VLR_ULA_E_HLR_LU_RES,
- (void*)&cause);
- else
- osmo_fsm_inst_term(vsub->lu_fsm, OSMO_FSM_TERM_ERROR,
- 0);
- }
-
- if (vsub->proc_arq_fsm)
- osmo_fsm_inst_term(vsub->proc_arq_fsm, OSMO_FSM_TERM_ERROR, 0);
-}
-
-/* Call vlr_subscr_cancel(), then completely drop the entry from the VLR */
-void vlr_subscr_free(struct vlr_subscr *vsub)
-{
- llist_del(&vsub->list);
- DEBUGP(DREF, "freeing VLR subscr %s\n", vlr_subscr_name(vsub));
- talloc_free(vsub);
-}
-
-/* Generate a new TMSI and store in vsub->tmsi_new.
- * Search all known subscribers to ensure that the TMSI is unique. */
-int vlr_subscr_alloc_tmsi(struct vlr_subscr *vsub)
-{
- struct vlr_instance *vlr = vsub->vlr;
- uint32_t tmsi;
- int tried;
-
- for (tried = 0; tried < 100; tried++) {
- if (RAND_bytes((uint8_t *) &tmsi, sizeof(tmsi)) != 1) {
- LOGP(DVLR, LOGL_ERROR, "RAND_bytes failed\n");
- return -1;
- }
- /* throw the dice again, if the TSMI doesn't fit */
- if (tmsi == GSM_RESERVED_TMSI)
- continue;
-
- /* Section 2.4 of 23.003: MSC has two MSB 00/01/10, SGSN 11 */
- if (vlr->cfg.is_ps) {
- /* SGSN */
- tmsi |= 0xC000000;
- } else {
- /* MSC */
- if ((tmsi & 0xC0000000) == 0xC0000000)
- tmsi &= ~0xC0000000;
- }
-
- /* If this TMSI is already in use, try another one. */
- if (vlr_subscr_find_by_tmsi(vlr, tmsi))
- continue;
-
- vsub->tmsi_new = tmsi;
- return 0;
- }
-
- LOGP(DVLR, LOGL_ERROR, "subscr %s: unable to generate valid TMSI"
- " after %d tries\n", vlr_subscr_name(vsub), tried);
- return -1;
-}
-
-/* Find subscriber by IMSI, or create new subscriber if not found.
- * \param[in] vlr VLR instace.
- * \param[in] imsi IMSI string.
- * \param[out] created if non-NULL, returns whether a new entry was created. */
-struct vlr_subscr *_vlr_subscr_find_or_create_by_imsi(struct vlr_instance *vlr,
- const char *imsi,
- bool *created,
- const char *file,
- int line)
-{
- struct vlr_subscr *vsub;
- vsub = _vlr_subscr_find_by_imsi(vlr, imsi, file, line);
- if (vsub) {
- if (created)
- *created = false;
- return vsub;
- }
-
- vsub = _vlr_subscr_get(_vlr_subscr_alloc(vlr), file, line);
- if (!vsub)
- return NULL;
- vlr_subscr_set_imsi(vsub, imsi);
- LOGP(DVLR, LOGL_INFO, "New subscr, IMSI: %s\n", vsub->imsi);
- if (created)
- *created = true;
- return vsub;
-}
-
-/* Find subscriber by TMSI, or create new subscriber if not found.
- * \param[in] vlr VLR instace.
- * \param[in] tmsi TMSI.
- * \param[out] created if non-NULL, returns whether a new entry was created. */
-struct vlr_subscr *_vlr_subscr_find_or_create_by_tmsi(struct vlr_instance *vlr,
- uint32_t tmsi,
- bool *created,
- const char *file,
- int line)
-{
- struct vlr_subscr *vsub;
- vsub = _vlr_subscr_find_by_tmsi(vlr, tmsi, file, line);
- if (vsub) {
- if (created)
- *created = false;
- return vsub;
- }
-
- vsub = _vlr_subscr_get(_vlr_subscr_alloc(vlr), file, line);
- if (!vsub)
- return NULL;
- vsub->tmsi = tmsi;
- LOGP(DVLR, LOGL_INFO, "New subscr, TMSI: 0x%08x\n", vsub->tmsi);
- if (created)
- *created = true;
- return vsub;
-}
-
-void vlr_subscr_set_imsi(struct vlr_subscr *vsub, const char *imsi)
-{
- if (!vsub)
- return;
- osmo_strlcpy(vsub->imsi, imsi, sizeof(vsub->imsi));
- vsub->id = atoll(vsub->imsi);
- DEBUGP(DVLR, "set IMSI on subscriber; IMSI=%s id=%llu\n",
- vsub->imsi, vsub->id);
-}
-
-void vlr_subscr_set_imei(struct vlr_subscr *vsub, const char *imei)
-{
- if (!vsub)
- return;
- osmo_strlcpy(vsub->imei, imei, sizeof(vsub->imei));
- DEBUGP(DVLR, "set IMEI on subscriber; IMSI=%s IMEI=%s\n",
- vsub->imsi, vsub->imei);
-}
-
-void vlr_subscr_set_imeisv(struct vlr_subscr *vsub, const char *imeisv)
-{
- if (!vsub)
- return;
- osmo_strlcpy(vsub->imeisv, imeisv, sizeof(vsub->imeisv));
- DEBUGP(DVLR, "set IMEISV on subscriber; IMSI=%s IMEISV=%s\n",
- vsub->imsi, vsub->imeisv);
-}
-
-/* Safely copy the given MSISDN string to vsub->msisdn */
-void vlr_subscr_set_msisdn(struct vlr_subscr *vsub, const char *msisdn)
-{
- if (!vsub)
- return;
- osmo_strlcpy(vsub->msisdn, msisdn, sizeof(vsub->msisdn));
- DEBUGP(DVLR, "set MSISDN on subscriber; IMSI=%s MSISDN=%s\n",
- vsub->imsi, vsub->msisdn);
-}
-
-bool vlr_subscr_matches_imsi(struct vlr_subscr *vsub, const char *imsi)
-{
- return vsub && imsi && vsub->imsi[0] && !strcmp(vsub->imsi, imsi);
-}
-
-bool vlr_subscr_matches_tmsi(struct vlr_subscr *vsub, uint32_t tmsi)
-{
- return vsub && tmsi != GSM_RESERVED_TMSI
- && (vsub->tmsi == tmsi || vsub->tmsi_new == tmsi);
-}
-
-bool vlr_subscr_matches_msisdn(struct vlr_subscr *vsub, const char *msisdn)
-{
- return vsub && msisdn && vsub->msisdn[0]
- && !strcmp(vsub->msisdn, msisdn);
-}
-
-bool vlr_subscr_matches_imei(struct vlr_subscr *vsub, const char *imei)
-{
- return vsub && imei && vsub->imei[0]
- && !strcmp(vsub->imei, imei);
-}
-
-/* Send updated subscriber information to HLR */
-int vlr_subscr_changed(struct vlr_subscr *vsub)
-{
- /* FIXME */
- LOGP(DVLR, LOGL_ERROR, "Not implemented: %s\n", __func__);
- return 0;
-}
-
-/***********************************************************************
- * PDP context data
- ***********************************************************************/
-
-struct sgsn_subscriber_pdp_data *
-vlr_subscr_pdp_data_alloc(struct vlr_subscr *vsub)
-{
- struct sgsn_subscriber_pdp_data* pdata;
-
- pdata = talloc_zero(vsub, struct sgsn_subscriber_pdp_data);
-
- llist_add_tail(&pdata->list, &vsub->ps.pdp_list);
-
- return pdata;
-}
-
-static int vlr_subscr_pdp_data_clear(struct vlr_subscr *vsub)
-{
- struct sgsn_subscriber_pdp_data *pdp, *pdp2;
- int count = 0;
-
- llist_for_each_entry_safe(pdp, pdp2, &vsub->ps.pdp_list, list) {
- llist_del(&pdp->list);
- talloc_free(pdp);
- count += 1;
- }
-
- return count;
-}
-
-static struct sgsn_subscriber_pdp_data *
-vlr_subscr_pdp_data_get_by_id(struct vlr_subscr *vsub, unsigned context_id)
-{
- struct sgsn_subscriber_pdp_data *pdp;
-
- llist_for_each_entry(pdp, &vsub->ps.pdp_list, list) {
- if (pdp->context_id == context_id)
- return pdp;
- }
-
- return NULL;
-}
-
-/***********************************************************************
- * Actual Implementation
- ***********************************************************************/
-
-static int vlr_rx_gsup_unknown_imsi(struct vlr_instance *vlr,
- struct osmo_gsup_message *gsup_msg)
-{
- if (OSMO_GSUP_IS_MSGT_REQUEST(gsup_msg->message_type)) {
- vlr_tx_gsup_error_reply(vlr, gsup_msg,
- GMM_CAUSE_IMSI_UNKNOWN);
- LOGP(DVLR, LOGL_NOTICE,
- "Unknown IMSI %s, discarding GSUP request "
- "of type 0x%02x\n",
- gsup_msg->imsi, gsup_msg->message_type);
- } else if (OSMO_GSUP_IS_MSGT_ERROR(gsup_msg->message_type)) {
- LOGP(DVLR, 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(DVLR, 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;
-}
-
-static int vlr_rx_gsup_purge_no_subscr(struct vlr_instance *vlr,
- struct osmo_gsup_message *gsup_msg)
-{
- if (OSMO_GSUP_IS_MSGT_ERROR(gsup_msg->message_type)) {
- LOGGSUPP(LOGL_NOTICE, gsup_msg,
- "Purge MS has failed with cause '%s' (%d)\n",
- get_value_string(gsm48_gmm_cause_names, gsup_msg->cause),
- gsup_msg->cause);
- return -gsup_msg->cause;
- }
- LOGGSUPP(LOGL_INFO, gsup_msg, "Completing purge MS\n");
- return 0;
-}
-
-/* VLR internal call to request UpdateLocation from HLR */
-int vlr_subscr_req_lu(struct vlr_subscr *vsub, bool is_ps)
-{
- struct osmo_gsup_message gsup_msg = {0};
- int rc;
-
- gsup_msg.message_type = OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST;
- rc = vlr_subscr_tx_gsup_message(vsub, &gsup_msg);
-
- return rc;
-}
-
-/* VLR internal call to request tuples from HLR */
-int vlr_subscr_req_sai(struct vlr_subscr *vsub,
- const uint8_t *auts, const uint8_t *auts_rand)
-{
- struct osmo_gsup_message gsup_msg = {0};
-
- gsup_msg.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST;
- gsup_msg.auts = auts;
- gsup_msg.rand = auts_rand;
-
- return vlr_subscr_tx_gsup_message(vsub, &gsup_msg);
-}
-
-/* Tell HLR that authentication failure occurred */
-int vlr_subscr_tx_auth_fail_rep(struct vlr_subscr *vsub)
-{
- struct osmo_gsup_message gsup_msg = {0};
-
- gsup_msg.message_type = OSMO_GSUP_MSGT_AUTH_FAIL_REPORT;
- osmo_strlcpy(gsup_msg.imsi, vsub->imsi, sizeof(gsup_msg.imsi));
- return vlr_tx_gsup_message(vsub->vlr, &gsup_msg);
-}
-
-/* Update the subscriber with GSUP-received auth tuples */
-void vlr_subscr_update_tuples(struct vlr_subscr *vsub,
- const struct osmo_gsup_message *gsup)
-{
- unsigned int i;
- unsigned int got_tuples;
-
- if (gsup->num_auth_vectors) {
- memset(&vsub->auth_tuples, 0, sizeof(vsub->auth_tuples));
- for (i = 0; i < ARRAY_SIZE(vsub->auth_tuples); i++)
- vsub->auth_tuples[i].key_seq = GSM_KEY_SEQ_INVAL;
- }
-
- got_tuples = 0;
- for (i = 0; i < gsup->num_auth_vectors; i++) {
- size_t key_seq = i;
-
- if (key_seq >= ARRAY_SIZE(vsub->auth_tuples)) {
- LOGVSUBP(LOGL_NOTICE, vsub,
- "Skipping auth tuple wih invalid cksn %zu\n",
- key_seq);
- continue;
- }
- vsub->auth_tuples[i].vec = gsup->auth_vectors[i];
- vsub->auth_tuples[i].key_seq = key_seq;
- got_tuples ++;
- }
-
- LOGVSUBP(LOGL_DEBUG, vsub, "Received %u auth tuples\n", got_tuples);
-
- if (!got_tuples) {
- /* FIXME what now? */
- // vlr_subscr_cancel(vsub, GMM_CAUSE_GSM_AUTH_UNACCEPT); ?
- }
-
- /* New tuples means last_tuple becomes invalid */
- vsub->last_tuple = NULL;
-}
-
-/* Handle SendAuthInfo Result/Error from HLR */
-static int vlr_subscr_handle_sai_res(struct vlr_subscr *vsub,
- const struct osmo_gsup_message *gsup)
-{
- struct osmo_fsm_inst *auth_fi = vsub->auth_fsm;
- void *data = (void *) gsup;
-
- switch (gsup->message_type) {
- case OSMO_GSUP_MSGT_SEND_AUTH_INFO_RESULT:
- osmo_fsm_inst_dispatch(auth_fi, VLR_AUTH_E_HLR_SAI_ACK, data);
- break;
- case OSMO_GSUP_MSGT_SEND_AUTH_INFO_ERROR:
- osmo_fsm_inst_dispatch(auth_fi, VLR_AUTH_E_HLR_SAI_NACK, data);
- break;
- default:
- return -1;
- }
-
- return 0;
-}
-
-static int decode_bcd_number_safe(char *output, int output_len,
- const uint8_t *bcd_lv, int input_len,
- int h_len)
-{
- uint8_t len;
- OSMO_ASSERT(output_len >= 1);
- *output = '\0';
- if (input_len < 1)
- return -EIO;
- len = bcd_lv[0];
- if (input_len < len)
- return -EIO;
- return gsm48_decode_bcd_number(output, output_len, bcd_lv, h_len);
-}
-
-static void vlr_subscr_gsup_insert_data(struct vlr_subscr *vsub,
- const struct osmo_gsup_message *gsup_msg)
-{
- unsigned idx;
- int rc;
-
- if (gsup_msg->msisdn_enc) {
- decode_bcd_number_safe(vsub->msisdn, sizeof(vsub->msisdn),
- gsup_msg->msisdn_enc,
- gsup_msg->msisdn_enc_len, 0);
- LOGP(DVLR, LOGL_DEBUG, "IMSI:%s has MSISDN:%s\n",
- vsub->imsi, vsub->msisdn);
- }
-
- if (gsup_msg->hlr_enc) {
- if (gsup_msg->hlr_enc_len > sizeof(vsub->hlr.buf)) {
- LOGP(DVLR, LOGL_ERROR, "HLR-Number too long (%zu)\n",
- gsup_msg->hlr_enc_len);
- vsub->hlr.len = 0;
- } else {
- memcpy(vsub->hlr.buf, gsup_msg->hlr_enc,
- gsup_msg->hlr_enc_len);
- vsub->hlr.len = gsup_msg->hlr_enc_len;
- }
- }
-
- if (gsup_msg->pdp_info_compl) {
- rc = vlr_subscr_pdp_data_clear(vsub);
- if (rc > 0)
- LOGP(DVLR, LOGL_INFO, "Cleared existing PDP info\n");
- }
-
- for (idx = 0; idx < gsup_msg->num_pdp_infos; idx++) {
- const struct osmo_gsup_pdp_info *pdp_info = &gsup_msg->pdp_infos[idx];
- size_t ctx_id = pdp_info->context_id;
- struct sgsn_subscriber_pdp_data *pdp_data;
-
- if (pdp_info->apn_enc_len >= sizeof(pdp_data->apn_str)-1) {
- LOGVSUBP(LOGL_ERROR, vsub,
- "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)) {
- LOGVSUBP(LOGL_ERROR, vsub,
- "QoS info too long (%zu)\n",
- pdp_info->qos_enc_len);
- continue;
- }
-
- LOGVSUBP(LOGL_INFO, vsub,
- "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 = vlr_subscr_pdp_data_get_by_id(vsub, ctx_id);
- if (!pdp_data) {
- pdp_data = vlr_subscr_pdp_data_alloc(vsub);
- pdp_data->context_id = ctx_id;
- }
-
- OSMO_ASSERT(pdp_data != NULL);
- pdp_data->pdp_type = pdp_info->pdp_type;
- osmo_apn_to_str(pdp_data->apn_str,
- pdp_info->apn_enc, pdp_info->apn_enc_len);
- memcpy(pdp_data->qos_subscribed, pdp_info->qos_enc, pdp_info->qos_enc_len);
- pdp_data->qos_subscribed_len = pdp_info->qos_enc_len;
- }
-}
-
-
-/* Handle InsertSubscrData Result from HLR */
-static int vlr_subscr_handle_isd_req(struct vlr_subscr *vsub,
- const struct osmo_gsup_message *gsup)
-{
- struct osmo_gsup_message gsup_reply = {0};
-
- vlr_subscr_gsup_insert_data(vsub, gsup);
- vsub->vlr->ops.subscr_update(vsub);
-
- gsup_reply.message_type = OSMO_GSUP_MSGT_INSERT_DATA_RESULT;
- return vlr_subscr_tx_gsup_message(vsub, &gsup_reply);
-}
-
-/* Handle UpdateLocation Result from HLR */
-static int vlr_subscr_handle_lu_res(struct vlr_subscr *vsub,
- const struct osmo_gsup_message *gsup)
-{
- if (!vsub->lu_fsm) {
- LOGVSUBP(LOGL_ERROR, vsub, "Rx GSUP LU Result "
- "without LU in progress\n");
- return -ENODEV;
- }
-
- /* contrary to MAP, we allow piggy-backing subscriber data onto the
- * UPDATE LOCATION RESULT, and don't mandate the use of a separate
- * nested INSERT SUBSCRIBER DATA transaction */
- vlr_subscr_gsup_insert_data(vsub, gsup);
-
- osmo_fsm_inst_dispatch(vsub->lu_fsm, VLR_ULA_E_HLR_LU_RES, NULL);
-
- return 0;
-}
-
-/* Handle UpdateLocation Result from HLR */
-static int vlr_subscr_handle_lu_err(struct vlr_subscr *vsub,
- const struct osmo_gsup_message *gsup)
-{
- if (!vsub->lu_fsm) {
- LOGVSUBP(LOGL_ERROR, vsub, "Rx GSUP LU Error "
- "without LU in progress\n");
- return -ENODEV;
- }
-
- LOGVSUBP(LOGL_DEBUG, vsub, "UpdateLocation failed; gmm_cause: %s\n",
- get_value_string(gsm48_gmm_cause_names, gsup->cause));
-
- osmo_fsm_inst_dispatch(vsub->lu_fsm, VLR_ULA_E_HLR_LU_RES,
- (void *)&gsup->cause);
-
- return 0;
-}
-
-/* Handle LOCATION CANCEL request from HLR */
-static int vlr_subscr_handle_cancel_req(struct vlr_subscr *vsub,
- struct osmo_gsup_message *gsup_msg)
-{
- struct osmo_gsup_message gsup_reply = {0};
- int is_update_procedure = !gsup_msg->cancel_type ||
- gsup_msg->cancel_type == OSMO_GSUP_CANCEL_TYPE_UPDATE;
-
- LOGVSUBP(LOGL_INFO, vsub, "Cancelling MS subscriber (%s)\n",
- is_update_procedure ?
- "update procedure" : "subscription withdraw");
-
- gsup_reply.message_type = OSMO_GSUP_MSGT_LOCATION_CANCEL_RESULT;
- vlr_subscr_tx_gsup_message(vsub, &gsup_reply);
-
- vlr_subscr_cancel(vsub, gsup_msg->cause);
-
- return 0;
-}
-
-/* Incoming handler for GSUP from HLR.
- * Keep this function non-static for direct invocation by unit tests. */
-int vlr_gsupc_read_cb(struct gsup_client *gsupc, struct msgb *msg)
-{
- struct vlr_instance *vlr = (struct vlr_instance *) gsupc->data;
- struct vlr_subscr *vsub;
- struct osmo_gsup_message gsup;
- int rc;
-
- DEBUGP(DVLR, "GSUP rx %u: %s\n", msgb_l2len(msg),
- osmo_hexdump_nospc(msgb_l2(msg), msgb_l2len(msg)));
-
- rc = osmo_gsup_decode(msgb_l2(msg), msgb_l2len(msg), &gsup);
- if (rc < 0) {
- LOGP(DVLR, LOGL_ERROR,
- "decoding GSUP message fails with error '%s' (%d)\n",
- get_value_string(gsm48_gmm_cause_names, -rc), -rc);
- return rc;
- }
-
- if (!gsup.imsi[0]) {
- LOGP(DVLR, LOGL_ERROR, "Missing IMSI in GSUP message\n");
- if (OSMO_GSUP_IS_MSGT_REQUEST(gsup.message_type))
- vlr_tx_gsup_error_reply(vlr, &gsup,
- GMM_CAUSE_INV_MAND_INFO);
- return -GMM_CAUSE_INV_MAND_INFO;
- }
-
- vsub = vlr_subscr_find_by_imsi(vlr, gsup.imsi);
- if (!vsub) {
- switch (gsup.message_type) {
- case OSMO_GSUP_MSGT_PURGE_MS_RESULT:
- case OSMO_GSUP_MSGT_PURGE_MS_ERROR:
- return vlr_rx_gsup_purge_no_subscr(vlr, &gsup);
- default:
- return vlr_rx_gsup_unknown_imsi(vlr, &gsup);
- }
- }
-
- switch (gsup.message_type) {
- case OSMO_GSUP_MSGT_SEND_AUTH_INFO_RESULT:
- case OSMO_GSUP_MSGT_SEND_AUTH_INFO_ERROR:
- rc = vlr_subscr_handle_sai_res(vsub, &gsup);
- break;
- case OSMO_GSUP_MSGT_INSERT_DATA_REQUEST:
- rc = vlr_subscr_handle_isd_req(vsub, &gsup);
- break;
- case OSMO_GSUP_MSGT_LOCATION_CANCEL_REQUEST:
- rc = vlr_subscr_handle_cancel_req(vsub, &gsup);
- break;
- case OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT:
- rc = vlr_subscr_handle_lu_res(vsub, &gsup);
- break;
- case OSMO_GSUP_MSGT_UPDATE_LOCATION_ERROR:
- rc = vlr_subscr_handle_lu_err(vsub, &gsup);
- break;
- case OSMO_GSUP_MSGT_PURGE_MS_ERROR:
- case OSMO_GSUP_MSGT_PURGE_MS_RESULT:
- case OSMO_GSUP_MSGT_DELETE_DATA_REQUEST:
- LOGVSUBP(LOGL_ERROR, vsub,
- "Rx GSUP msg_type=%d not yet implemented\n",
- gsup.message_type);
- rc = -GMM_CAUSE_MSGT_NOTEXIST_NOTIMPL;
- break;
- default:
- LOGVSUBP(LOGL_ERROR, vsub,
- "Rx GSUP msg_type=%d not valid at VLR/SGSN side\n",
- gsup.message_type);
- rc = -GMM_CAUSE_MSGT_NOTEXIST_NOTIMPL;
- break;
- }
-
- vlr_subscr_put(vsub);
- return rc;
-}
-
-/* MSC->VLR: Subscriber has provided IDENTITY RESPONSE */
-int vlr_subscr_rx_id_resp(struct vlr_subscr *vsub,
- const uint8_t *mi, size_t mi_len)
-{
- char mi_string[GSM48_MI_SIZE];
- uint8_t mi_type = mi[0] & GSM_MI_TYPE_MASK;
-
- gsm48_mi_to_string(mi_string, sizeof(mi_string), mi, mi_len);
-
- /* update the vlr_subscr with the given identity */
- switch (mi_type) {
- case GSM_MI_TYPE_IMSI:
- if (vsub->imsi[0]
- && !vlr_subscr_matches_imsi(vsub, mi_string)) {
- LOGVSUBP(LOGL_ERROR, vsub, "IMSI in ID RESP differs:"
- " %s\n", mi_string);
- } else
- vlr_subscr_set_imsi(vsub, mi_string);
- break;
- case GSM_MI_TYPE_IMEI:
- vlr_subscr_set_imei(vsub, mi_string);
- break;
- case GSM_MI_TYPE_IMEISV:
- vlr_subscr_set_imeisv(vsub, mi_string);
- break;
- }
-
- if (vsub->auth_fsm) {
- switch (mi_type) {
- case GSM_MI_TYPE_IMSI:
- osmo_fsm_inst_dispatch(vsub->auth_fsm,
- VLR_AUTH_E_MS_ID_IMSI, mi_string);
- break;
- }
- }
-
- if (vsub->lu_fsm) {
- uint32_t event = 0;
- switch (mi_type) {
- case GSM_MI_TYPE_IMSI:
- event = VLR_ULA_E_ID_IMSI;
- break;
- case GSM_MI_TYPE_IMEI:
- event = VLR_ULA_E_ID_IMEI;
- break;
- case GSM_MI_TYPE_IMEISV:
- event = VLR_ULA_E_ID_IMEISV;
- break;
- default:
- OSMO_ASSERT(0);
- break;
- }
- osmo_fsm_inst_dispatch(vsub->lu_fsm, event, mi_string);
- } else {
- LOGVSUBP(LOGL_NOTICE, vsub, "gratuitous ID RESPONSE?!?\n");
- }
-
- return 0;
-}
-
-/* MSC->VLR: Subscriber has provided IDENTITY RESPONSE */
-int vlr_subscr_rx_tmsi_reall_compl(struct vlr_subscr *vsub)
-{
- if (vsub->lu_fsm) {
- return osmo_fsm_inst_dispatch(vsub->lu_fsm,
- VLR_ULA_E_NEW_TMSI_ACK, NULL);
- } else if (vsub->proc_arq_fsm) {
- return osmo_fsm_inst_dispatch(vsub->proc_arq_fsm,
- PR_ARQ_E_TMSI_ACK, NULL);
- } else {
- LOGVSUBP(LOGL_NOTICE, vsub,
- "gratuitous TMSI REALLOC COMPL");
- return -EINVAL;
- }
-}
-
-int vlr_subscr_rx_imsi_detach(struct vlr_subscr *vsub)
-{
- /* paranoia: should any LU or PARQ FSMs still be running, stop them. */
- vlr_subscr_cancel(vsub, GMM_CAUSE_IMPL_DETACHED);
-
- vsub->imsi_detached_flag = true;
- if (vsub->lu_complete) {
- vsub->lu_complete = false;
- /* balancing the get from vlr_lu_compl_fsm_success() */
- vlr_subscr_put(vsub);
- }
- return 0;
-}
-
-/* Tear down any running FSMs due to MSC connection timeout.
- * Visit all vsub->*_fsm pointers and give them a queue to send a final reject
- * message before the entire connection is torn down.
- * \param[in] vsub subscriber to tear down
- */
-void vlr_subscr_conn_timeout(struct vlr_subscr *vsub)
-{
- if (!vsub)
- return;
-
- vlr_loc_update_conn_timeout(vsub->lu_fsm);
- vlr_parq_conn_timeout(vsub->proc_arq_fsm);
-}
-
-struct vlr_instance *vlr_alloc(void *ctx, const struct vlr_ops *ops)
-{
- struct vlr_instance *vlr = talloc_zero(ctx, struct vlr_instance);
- OSMO_ASSERT(vlr);
-
- /* Some of these are needed only on UTRAN, but in case the caller wants
- * only GERAN, she should just provide dummy callbacks. */
- OSMO_ASSERT(ops->tx_auth_req);
- OSMO_ASSERT(ops->tx_auth_rej);
- OSMO_ASSERT(ops->tx_id_req);
- OSMO_ASSERT(ops->tx_lu_acc);
- OSMO_ASSERT(ops->tx_lu_rej);
- OSMO_ASSERT(ops->tx_cm_serv_acc);
- OSMO_ASSERT(ops->tx_cm_serv_rej);
- OSMO_ASSERT(ops->set_ciph_mode);
- OSMO_ASSERT(ops->tx_common_id);
- OSMO_ASSERT(ops->subscr_update);
- OSMO_ASSERT(ops->subscr_assoc);
-
- INIT_LLIST_HEAD(&vlr->subscribers);
- INIT_LLIST_HEAD(&vlr->operations);
- memcpy(&vlr->ops, ops, sizeof(vlr->ops));
-
- /* defaults */
- vlr->cfg.check_imei_rqd = true;
-
- /* osmo_auth_fsm.c */
- osmo_fsm_register(&vlr_auth_fsm);
- /* osmo_lu_fsm.c */
- vlr_lu_fsm_init();
- /* vlr_access_request_fsm.c */
- vlr_parq_fsm_init();
-
- return vlr;
-}
-
-int vlr_start(const char *gsup_unit_name, struct vlr_instance *vlr,
- const char *gsup_server_addr_str, uint16_t gsup_server_port)
-{
- OSMO_ASSERT(vlr);
-
- vlr->gsup_client = gsup_client_create(gsup_unit_name,
- gsup_server_addr_str,
- gsup_server_port,
- &vlr_gsupc_read_cb, NULL);
- if (!vlr->gsup_client)
- return -ENOMEM;
- vlr->gsup_client->data = vlr;
-
- return 0;
-}
-
-/* MSC->VLR: Subscribre has disconnected */
-int vlr_subscr_disconnected(struct vlr_subscr *vsub)
-{
- /* This corresponds to a MAP-ABORT from MSC->VLR on a classic B
- * interface */
- osmo_fsm_inst_term(vsub->lu_fsm, OSMO_FSM_TERM_REQUEST, NULL);
- osmo_fsm_inst_term(vsub->auth_fsm, OSMO_FSM_TERM_REQUEST, NULL);
- vsub->msc_conn_ref = NULL;
-
- return 0;
-}
-
-/* MSC->VLR: Receive Authentication Failure from Subscriber */
-int vlr_subscr_rx_auth_fail(struct vlr_subscr *vsub, const uint8_t *auts)
-{
- struct vlr_auth_resp_par par = {0};
- par.auts = auts;
-
- osmo_fsm_inst_dispatch(vsub->auth_fsm, VLR_AUTH_E_MS_AUTH_FAIL, &par);
- return 0;
-}
-
-/* MSC->VLR: Receive Authentication Response from MS
- * \returns 1 in case of success, 0 in case of delay, -1 on auth error */
-int vlr_subscr_rx_auth_resp(struct vlr_subscr *vsub, bool is_r99,
- bool is_utran, const uint8_t *res, uint8_t res_len)
-{
- struct osmo_fsm_inst *auth_fi = vsub->auth_fsm;
- struct vlr_auth_resp_par par;
-
- par.is_r99 = is_r99;
- par.is_utran = is_utran;
- par.res = res;
- par.res_len = res_len;
- osmo_fsm_inst_dispatch(auth_fi, VLR_AUTH_E_MS_AUTH_RESP, (void *) &par);
-
- return 0;
-}
-
-/* MSC->VLR: Receive result of Ciphering Mode Command from MS */
-void vlr_subscr_rx_ciph_res(struct vlr_subscr *vsub, struct vlr_ciph_result *res)
-{
- if (vsub->lu_fsm && vsub->lu_fsm->state == VLR_ULA_S_WAIT_CIPH)
- osmo_fsm_inst_dispatch(vsub->lu_fsm, VLR_ULA_E_CIPH_RES, res);
- if (vsub->proc_arq_fsm
- && vsub->proc_arq_fsm->state == PR_ARQ_S_WAIT_CIPH)
- osmo_fsm_inst_dispatch(vsub->proc_arq_fsm, PR_ARQ_E_CIPH_RES,
- res);
-}
-
-/* Internal evaluation of requested ciphering mode.
- * Send set_ciph_mode() to MSC depending on the ciph_mode argument.
- * \param[in] vlr VLR instance.
- * \param[in] fi Calling FSM instance, for logging.
- * \param[in] msc_conn_ref MSC conn to send to.
- * \param[in] ciph_mode Ciphering config, to decide whether to do ciphering.
- * \returns 0 if no ciphering is needed or message was sent successfully,
- * or a negative value if ciph_mode is invalid or sending failed.
- */
-int vlr_set_ciph_mode(struct vlr_instance *vlr,
- struct osmo_fsm_inst *fi,
- void *msc_conn_ref,
- enum vlr_ciph ciph_mode,
- bool retrieve_imeisv)
-{
- switch (ciph_mode) {
- case VLR_CIPH_NONE:
- return 0;
-
- case VLR_CIPH_A5_1:
- case VLR_CIPH_A5_3:
- return vlr->ops.set_ciph_mode(msc_conn_ref,
- ciph_mode,
- retrieve_imeisv);
-
- case VLR_CIPH_A5_2:
- /* TODO policy by user config? */
- LOGPFSML(fi, LOGL_ERROR, "A5/2 ciphering is not allowed\n");
- return -EINVAL;
-
- default:
- LOGPFSML(fi, LOGL_ERROR, "unknown ciphering value: %d\n",
- ciph_mode);
- return -EINVAL;
- }
-}
-
-void log_set_filter_vlr_subscr(struct log_target *target,
- struct vlr_subscr *vlr_subscr)
-{
- struct vlr_subscr **fsub = (void*)&target->filter_data[LOG_FLT_VLR_SUBSCR];
-
- /* free the old data */
- if (*fsub) {
- vlr_subscr_put(*fsub);
- *fsub = NULL;
- }
-
- if (vlr_subscr) {
- target->filter_map |= (1 << LOG_FLT_VLR_SUBSCR);
- *fsub = vlr_subscr_get(vlr_subscr);
- } else
- target->filter_map &= ~(1 << LOG_FLT_VLR_SUBSCR);
-}
diff --git a/src/libvlr/vlr_access_req_fsm.c b/src/libvlr/vlr_access_req_fsm.c
deleted file mode 100644
index f9ed0b57d..000000000
--- a/src/libvlr/vlr_access_req_fsm.c
+++ /dev/null
@@ -1,795 +0,0 @@
-/* Osmocom Visitor Location Register (VLR): Access Request FSMs */
-
-/* (C) 2016 by Harald Welte <laforge@gnumonks.org>
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <osmocom/core/fsm.h>
-#include <osmocom/gsm/gsup.h>
-#include <openbsc/vlr.h>
-#include <openbsc/debug.h>
-
-#include "vlr_core.h"
-#include "vlr_auth_fsm.h"
-#include "vlr_lu_fsm.h"
-#include "vlr_access_req_fsm.h"
-
-#define S(x) (1 << (x))
-
-/***********************************************************************
- * Process_Access_Request_VLR, TS 29.002 Chapter 25.4.2
- ***********************************************************************/
-
-const struct value_string vlr_proc_arq_result_names[] = {
- OSMO_VALUE_STRING(VLR_PR_ARQ_RES_NONE),
- OSMO_VALUE_STRING(VLR_PR_ARQ_RES_SYSTEM_FAILURE),
- OSMO_VALUE_STRING(VLR_PR_ARQ_RES_ILLEGAL_SUBSCR),
- OSMO_VALUE_STRING(VLR_PR_ARQ_RES_UNIDENT_SUBSCR),
- OSMO_VALUE_STRING(VLR_PR_ARQ_RES_ROAMING_NOTALLOWED),
- OSMO_VALUE_STRING(VLR_PR_ARQ_RES_ILLEGAL_EQUIP),
- OSMO_VALUE_STRING(VLR_PR_ARQ_RES_UNKNOWN_ERROR),
- OSMO_VALUE_STRING(VLR_PR_ARQ_RES_TIMEOUT),
- OSMO_VALUE_STRING(VLR_PR_ARQ_RES_PASSED),
- { 0, NULL }
-};
-
-static const struct value_string proc_arq_vlr_event_names[] = {
- OSMO_VALUE_STRING(PR_ARQ_E_START),
- OSMO_VALUE_STRING(PR_ARQ_E_ID_IMSI),
- OSMO_VALUE_STRING(PR_ARQ_E_AUTH_RES),
- OSMO_VALUE_STRING(PR_ARQ_E_CIPH_RES),
- OSMO_VALUE_STRING(PR_ARQ_E_UPD_LOC_RES),
- OSMO_VALUE_STRING(PR_ARQ_E_TRACE_RES),
- OSMO_VALUE_STRING(PR_ARQ_E_IMEI_RES),
- OSMO_VALUE_STRING(PR_ARQ_E_PRES_RES),
- OSMO_VALUE_STRING(PR_ARQ_E_TMSI_ACK),
- { 0, NULL }
-};
-
-struct proc_arq_priv {
- struct vlr_instance *vlr;
- struct vlr_subscr *vsub;
- void *msc_conn_ref;
- struct osmo_fsm_inst *ul_child_fsm;
- struct osmo_fsm_inst *sub_pres_vlr_fsm;
- uint32_t parent_event_success;
- uint32_t parent_event_failure;
- void *parent_event_data;
-
- enum vlr_parq_type type;
- enum vlr_proc_arq_result result;
- bool by_tmsi;
- char imsi[16];
- uint32_t tmsi;
- struct osmo_location_area_id lai;
- bool authentication_required;
- enum vlr_ciph ciphering_required;
- bool is_r99;
- bool is_utran;
- bool implicitly_accepted_parq_by_ciphering_cmd;
-};
-
-static void assoc_par_with_subscr(struct osmo_fsm_inst *fi, struct vlr_subscr *vsub)
-{
- struct proc_arq_priv *par = fi->priv;
- struct vlr_instance *vlr = par->vlr;
-
- vsub->msc_conn_ref = par->msc_conn_ref;
- par->vsub = vsub;
- /* Tell MSC to associate this subscriber with the given
- * connection */
- vlr->ops.subscr_assoc(par->msc_conn_ref, par->vsub);
-}
-
-#define proc_arq_fsm_done(fi, res) _proc_arq_fsm_done(fi, res, __FILE__, __LINE__)
-static void _proc_arq_fsm_done(struct osmo_fsm_inst *fi,
- enum vlr_proc_arq_result res,
- const char *file, int line)
-{
- struct proc_arq_priv *par = fi->priv;
- LOGPFSMSRC(fi, file, line, "proc_arq_fsm_done(%s)\n",
- vlr_proc_arq_result_name(res));
- par->result = res;
- osmo_fsm_inst_state_chg(fi, PR_ARQ_S_DONE, 0, 0);
-}
-
-static void proc_arq_vlr_dispatch_result(struct osmo_fsm_inst *fi,
- uint32_t prev_state)
-{
- struct proc_arq_priv *par = fi->priv;
- bool success;
- int rc;
- LOGPFSM(fi, "Process Access Request result: %s\n",
- vlr_proc_arq_result_name(par->result));
-
- success = (par->result == VLR_PR_ARQ_RES_PASSED);
-
- /* It would be logical to first dispatch the success event to the
- * parent FSM, but that could start actions that send messages to the
- * MS. Rather send the CM Service Accept message first and then signal
- * success. Since messages are handled synchronously, the success event
- * will be processed before we handle new incoming data from the MS. */
-
- if (par->type == VLR_PR_ARQ_T_CM_SERV_REQ) {
- if (success
- && !par->implicitly_accepted_parq_by_ciphering_cmd) {
- rc = par->vlr->ops.tx_cm_serv_acc(par->msc_conn_ref);
- if (rc) {
- LOGPFSML(fi, LOGL_ERROR,
- "Failed to send CM Service Accept\n");
- success = false;
- }
- }
- if (!success) {
- rc = par->vlr->ops.tx_cm_serv_rej(par->msc_conn_ref,
- par->result);
- if (rc)
- LOGPFSML(fi, LOGL_ERROR,
- "Failed to send CM Service Reject\n");
- }
- }
-
- /* For VLR_PR_ARQ_T_PAGING_RESP, there is nothing to send. The conn_fsm
- * will start handling pending paging transactions. */
-
- if (!fi->proc.parent) {
- LOGPFSML(fi, LOGL_ERROR, "No parent FSM");
- return;
- }
- osmo_fsm_inst_dispatch(fi->proc.parent,
- success ? par->parent_event_success
- : par->parent_event_failure,
- par->parent_event_data);
-}
-
-void proc_arq_vlr_cleanup(struct osmo_fsm_inst *fi,
- enum osmo_fsm_term_cause cause)
-{
- struct proc_arq_priv *par = fi->priv;
- if (par->vsub && par->vsub->proc_arq_fsm == fi)
- par->vsub->proc_arq_fsm = NULL;
-}
-
-static void _proc_arq_vlr_post_imei(struct osmo_fsm_inst *fi)
-{
- struct proc_arq_priv *par = fi->priv;
- struct vlr_subscr *vsub = par->vsub;
-
- LOGPFSM(fi, "%s()\n", __func__);
-
- /* See 3GPP TS 29.002 Proc_Acc_Req_VLR3. */
- /* TODO: Identity := IMSI */
- if (0 /* TODO: TMSI reallocation at access: vlr->cfg.alloc_tmsi_arq */) {
- vlr_subscr_alloc_tmsi(vsub);
- /* TODO: forward TMSI to MS, wait for TMSI
- * REALLOC COMPLETE */
- /* TODO: Freeze old TMSI */
- osmo_fsm_inst_state_chg(fi, PR_ARQ_S_WAIT_TMSI_ACK, 0, 0);
- return;
- }
-
- proc_arq_fsm_done(fi, VLR_PR_ARQ_RES_PASSED);
-}
-
-static void _proc_arq_vlr_post_trace(struct osmo_fsm_inst *fi)
-{
- struct proc_arq_priv *par = fi->priv;
- struct vlr_subscr *vsub = par->vsub;
- struct vlr_instance *vlr = vsub->vlr;
-
- LOGPFSM(fi, "%s()\n", __func__);
-
- /* Node 3 */
- /* See 3GPP TS 29.002 Proc_Acc_Req_VLR3. */
- if (0 /* IMEI check required */) {
- /* Chck_IMEI_VLR */
- vlr->ops.tx_id_req(par->msc_conn_ref, GSM_MI_TYPE_IMEI);
- osmo_fsm_inst_state_chg(fi, PR_ARQ_S_WAIT_CHECK_IMEI,
- vlr_timer(vlr, 3270), 3270);
- } else
- _proc_arq_vlr_post_imei(fi);
-}
-
-/* After Subscriber_Present_VLR */
-static void _proc_arq_vlr_post_pres(struct osmo_fsm_inst *fi)
-{
- LOGPFSM(fi, "%s()\n", __func__);
- /* See 3GPP TS 29.002 Proc_Acc_Req_VLR3. */
- if (0 /* TODO: tracing required */) {
- /* TODO: Trace_Subscriber_Activity_VLR */
- osmo_fsm_inst_state_chg(fi, PR_ARQ_S_WAIT_TRACE_SUB, 0, 0);
- }
- _proc_arq_vlr_post_trace(fi);
-}
-
-/* After Update_Location_Child_VLR */
-static void _proc_arq_vlr_node2_post_vlr(struct osmo_fsm_inst *fi)
-{
- struct proc_arq_priv *par = fi->priv;
- struct vlr_subscr *vsub = par->vsub;
-
- LOGPFSM(fi, "%s()\n", __func__);
-
- if (!vsub->sub_dataconf_by_hlr_ind) {
- /* Set User Error: Unidentified Subscriber */
- proc_arq_fsm_done(fi, VLR_PR_ARQ_RES_UNIDENT_SUBSCR);
- return;
- }
- /* We don't feature location area specific blocking (yet). */
- if (0 /* roaming not allowed in LA */) {
- /* Set User Error: Roaming not allowed in this LA */
- proc_arq_fsm_done(fi, VLR_PR_ARQ_RES_ROAMING_NOTALLOWED);
- return;
- }
- vsub->imsi_detached_flag = false;
- if (vsub->ms_not_reachable_flag) {
- /* Start Subscriber_Present_VLR */
- osmo_fsm_inst_state_chg(fi, PR_ARQ_S_WAIT_SUB_PRES, 0, 0);
- par->sub_pres_vlr_fsm = sub_pres_vlr_fsm_start(fi, vsub,
- PR_ARQ_E_PRES_RES);
- return;
- }
- _proc_arq_vlr_post_pres(fi);
-}
-
-static void _proc_arq_vlr_node2_post_ciph(struct osmo_fsm_inst *fi)
-{
- struct proc_arq_priv *par = fi->priv;
- struct vlr_subscr *vsub = par->vsub;
-
- LOGPFSM(fi, "%s()\n", __func__);
-
- if (par->is_utran) {
- int rc;
- rc = par->vlr->ops.tx_common_id(par->msc_conn_ref);
- if (rc)
- LOGPFSML(fi, LOGL_ERROR,
- "Error while sending Common ID (%d)\n", rc);
- }
-
- vsub->conf_by_radio_contact_ind = true;
- if (vsub->loc_conf_in_hlr_ind == false) {
- /* start Update_Location_Child_VLR. WE use
- * Update_HLR_VLR instead, the differences appear
- * insignificant for now. */
- par->ul_child_fsm = upd_hlr_vlr_proc_start(fi, vsub,
- PR_ARQ_E_UPD_LOC_RES);
- osmo_fsm_inst_state_chg(fi, PR_ARQ_S_WAIT_UPD_LOC_CHILD, 0, 0);
- return;
- }
- _proc_arq_vlr_node2_post_vlr(fi);
-}
-
-static bool is_ciph_required(struct proc_arq_priv *par)
-{
- return par->ciphering_required != VLR_CIPH_NONE;
-}
-
-static void _proc_arq_vlr_node2(struct osmo_fsm_inst *fi)
-{
- struct proc_arq_priv *par = fi->priv;
- struct vlr_subscr *vsub = par->vsub;
-
- LOGPFSM(fi, "%s()\n", __func__);
-
- if (!is_ciph_required(par)) {
- _proc_arq_vlr_node2_post_ciph(fi);
- return;
- }
-
- if (vlr_set_ciph_mode(vsub->vlr, fi, par->msc_conn_ref,
- par->ciphering_required,
- vsub->vlr->cfg.retrieve_imeisv_ciphered)) {
- LOGPFSML(fi, LOGL_ERROR,
- "Failed to send Ciphering Mode Command\n");
- proc_arq_fsm_done(fi, VLR_PR_ARQ_RES_SYSTEM_FAILURE);
- return;
- }
-
- par->implicitly_accepted_parq_by_ciphering_cmd = true;
- osmo_fsm_inst_state_chg(fi, PR_ARQ_S_WAIT_CIPH, 0, 0);
-}
-
-static bool is_auth_required(struct proc_arq_priv *par)
-{
- /* The cases where the authentication procedure should be used
- * are defined in 3GPP TS 33.102 */
- /* For now we use a default value passed in to vlr_lu_fsm(). */
- return par->authentication_required
- || (par->ciphering_required != VLR_CIPH_NONE);
-}
-
-/* after the IMSI is known */
-static void proc_arq_vlr_fn_post_imsi(struct osmo_fsm_inst *fi)
-{
- struct proc_arq_priv *par = fi->priv;
- struct vlr_subscr *vsub = par->vsub;
-
- LOGPFSM(fi, "%s()\n", __func__);
-
- OSMO_ASSERT(vsub);
-
- /* TODO: Identity IMEI -> System Failure */
- if (is_auth_required(par)) {
- osmo_fsm_inst_state_chg(fi, PR_ARQ_S_WAIT_AUTH,
- 0, 0);
- vsub->auth_fsm = auth_fsm_start(vsub, fi->log_level, fi,
- PR_ARQ_E_AUTH_RES,
- par->is_r99,
- par->is_utran);
- } else {
- _proc_arq_vlr_node2(fi);
- }
-}
-
-static void proc_arq_vlr_fn_init(struct osmo_fsm_inst *fi,
- uint32_t event, void *data)
-{
- struct proc_arq_priv *par = fi->priv;
- struct vlr_instance *vlr = par->vlr;
- struct vlr_subscr *vsub = NULL;
-
- OSMO_ASSERT(event == PR_ARQ_E_START);
-
- /* Obtain_Identity_VLR */
- if (!par->by_tmsi) {
- /* IMSI was included */
- vsub = vlr_subscr_find_by_imsi(par->vlr, par->imsi);
- } else {
- /* TMSI was included */
- vsub = vlr_subscr_find_by_tmsi(par->vlr, par->tmsi);
- }
- if (vsub) {
- log_set_context(LOG_CTX_VLR_SUBSCR, vsub);
- if (vsub->proc_arq_fsm && fi != vsub->proc_arq_fsm) {
- LOGPFSML(fi, LOGL_ERROR,
- "Another proc_arq_fsm is already"
- " associated with subscr %s,"
- " terminating the other FSM.\n",
- vlr_subscr_name(vsub));
- proc_arq_fsm_done(vsub->proc_arq_fsm,
- VLR_PR_ARQ_RES_SYSTEM_FAILURE);
- }
- vsub->proc_arq_fsm = fi;
- assoc_par_with_subscr(fi, vsub);
- proc_arq_vlr_fn_post_imsi(fi);
- vlr_subscr_put(vsub);
- return;
- }
- /* No VSUB could be resolved. What now? */
-
- if (!par->by_tmsi) {
- /* We couldn't find a subscriber even by IMSI,
- * Set User Error: Unidentified Subscriber */
- proc_arq_fsm_done(fi, VLR_PR_ARQ_RES_UNIDENT_SUBSCR);
- return;
- } else {
- /* TMSI was included, are we permitted to use it? */
- if (vlr->cfg.parq_retrieve_imsi) {
- /* Obtain_IMSI_VLR */
- osmo_fsm_inst_state_chg(fi, PR_ARQ_S_WAIT_OBTAIN_IMSI,
- vlr_timer(vlr, 3270), 3270);
- return;
- } else {
- /* Set User Error: Unidentified Subscriber */
- proc_arq_fsm_done(fi, VLR_PR_ARQ_RES_UNIDENT_SUBSCR);
- return;
- }
- }
-}
-
-/* ID REQ(IMSI) has returned */
-static void proc_arq_vlr_fn_w_obt_imsi(struct osmo_fsm_inst *fi,
- uint32_t event, void *data)
-{
- struct proc_arq_priv *par = fi->priv;
- struct vlr_instance *vlr = par->vlr;
- struct vlr_subscr *vsub;
-
- OSMO_ASSERT(event == PR_ARQ_E_ID_IMSI);
-
- vsub = vlr_subscr_find_by_imsi(vlr, par->imsi);
- if (!vsub) {
- /* Set User Error: Unidentified Subscriber */
- proc_arq_fsm_done(fi, VLR_PR_ARQ_RES_UNIDENT_SUBSCR);
- return;
- }
- assoc_par_with_subscr(fi, vsub);
- proc_arq_vlr_fn_post_imsi(fi);
- vlr_subscr_put(vsub);
-}
-
-/* Authenticate_VLR has completed */
-static void proc_arq_vlr_fn_w_auth(struct osmo_fsm_inst *fi,
- uint32_t event, void *data)
-{
- enum vlr_auth_fsm_result res;
- enum vlr_proc_arq_result ret;
-
- OSMO_ASSERT(event == PR_ARQ_E_AUTH_RES);
-
- res = data ? *(enum vlr_auth_fsm_result*)data : -1;
- LOGPFSM(fi, "got %s\n", vlr_auth_fsm_result_name(res));
-
- switch (res) {
- case VLR_AUTH_RES_PASSED:
- /* Node 2 */
- _proc_arq_vlr_node2(fi);
- return;
- case VLR_AUTH_RES_ABORTED:
- /* Error */
- ret = VLR_PR_ARQ_RES_UNKNOWN_ERROR;
- break;
- case VLR_AUTH_RES_UNKNOWN_SUBSCR:
- /* Set User Error: Unidentified Subscriber */
- ret = VLR_PR_ARQ_RES_UNIDENT_SUBSCR;
- break;
- case VLR_AUTH_RES_AUTH_FAILED:
- /* Set User Error: Illegal Subscriber */
- ret = VLR_PR_ARQ_RES_ILLEGAL_SUBSCR;
- break;
- case VLR_AUTH_RES_PROC_ERR:
- /* Set User Error: System failure */
- ret = VLR_PR_ARQ_RES_SYSTEM_FAILURE;
- break;
- default:
- LOGPFSML(fi, LOGL_ERROR, "Unexpected vlr_auth_fsm_result value: %d (data=%p)\n", res, data);
- ret = VLR_PR_ARQ_RES_UNKNOWN_ERROR;
- break;
- }
-
- /* send process_access_req response to caller, enter error state */
- proc_arq_fsm_done(fi, ret);
-}
-
-static void proc_arq_vlr_fn_w_ciph(struct osmo_fsm_inst *fi,
- uint32_t event, void *data)
-{
- struct proc_arq_priv *par = fi->priv;
- struct vlr_subscr *vsub = par->vsub;
- struct vlr_ciph_result res = { .cause = VLR_CIPH_REJECT };
-
- OSMO_ASSERT(event == PR_ARQ_E_CIPH_RES);
-
- if (!data)
- LOGPFSML(fi, LOGL_ERROR, "invalid ciphering result: NULL\n");
- else
- res = *(struct vlr_ciph_result*)data;
-
- switch (res.cause) {
- case VLR_CIPH_COMPL:
- break;
- case VLR_CIPH_REJECT:
- LOGPFSM(fi, "ciphering rejected\n");
- proc_arq_fsm_done(fi, VLR_PR_ARQ_RES_ILLEGAL_SUBSCR);
- return;
- default:
- LOGPFSML(fi, LOGL_ERROR, "invalid ciphering result: %d\n",
- res.cause);
- proc_arq_fsm_done(fi, VLR_PR_ARQ_RES_ILLEGAL_SUBSCR);
- return;
- }
-
-
- if (res.imeisv) {
- LOGPFSM(fi, "got IMEISV: %s\n", res.imeisv);
- vlr_subscr_set_imeisv(vsub, res.imeisv);
- }
- _proc_arq_vlr_node2_post_ciph(fi);
-}
-
-/* Update_Location_Child_VLR has completed */
-static void proc_arq_vlr_fn_w_upd_loc(struct osmo_fsm_inst *fi,
- uint32_t event, void *data)
-{
- OSMO_ASSERT(event == PR_ARQ_E_UPD_LOC_RES);
-
- _proc_arq_vlr_node2_post_vlr(fi);
-}
-
-/* Subscriber_Present_VLR has completed */
-static void proc_arq_vlr_fn_w_pres(struct osmo_fsm_inst *fi,
- uint32_t event, void *data)
-{
- OSMO_ASSERT(event == PR_ARQ_E_PRES_RES);
-
- _proc_arq_vlr_post_pres(fi);
-}
-
-static void proc_arq_vlr_fn_w_trace(struct osmo_fsm_inst *fi,
- uint32_t event, void *data)
-{
- OSMO_ASSERT(event == PR_ARQ_E_TRACE_RES);
-
- _proc_arq_vlr_post_trace(fi);
-}
-
-/* we have received the ID RESPONSE (IMEI) */
-static void proc_arq_vlr_fn_w_imei(struct osmo_fsm_inst *fi,
- uint32_t event, void *data)
-{
- OSMO_ASSERT(event == PR_ARQ_E_IMEI_RES);
-
- _proc_arq_vlr_post_imei(fi);
-}
-
-/* MSC tells us that MS has acknowleded TMSI re-allocation */
-static void proc_arq_vlr_fn_w_tmsi(struct osmo_fsm_inst *fi,
- uint32_t event, void *data)
-{
- OSMO_ASSERT(event == PR_ARQ_E_TMSI_ACK);
-
- /* FIXME: check confirmation? unfreeze? */
- proc_arq_fsm_done(fi, VLR_PR_ARQ_RES_PASSED);
-}
-
-static const struct osmo_fsm_state proc_arq_vlr_states[] = {
- [PR_ARQ_S_INIT] = {
- .name = OSMO_STRINGIFY(PR_ARQ_S_INIT),
- .in_event_mask = S(PR_ARQ_E_START),
- .out_state_mask = S(PR_ARQ_S_DONE) |
- S(PR_ARQ_S_WAIT_OBTAIN_IMSI) |
- S(PR_ARQ_S_WAIT_AUTH) |
- S(PR_ARQ_S_WAIT_UPD_LOC_CHILD) |
- S(PR_ARQ_S_WAIT_SUB_PRES) |
- S(PR_ARQ_S_WAIT_TRACE_SUB) |
- S(PR_ARQ_S_WAIT_CHECK_IMEI) |
- S(PR_ARQ_S_WAIT_TMSI_ACK),
- .action = proc_arq_vlr_fn_init,
- },
- [PR_ARQ_S_WAIT_OBTAIN_IMSI] = {
- .name = OSMO_STRINGIFY(PR_ARQ_S_WAIT_OBTAIN_IMSI),
- .in_event_mask = S(PR_ARQ_E_ID_IMSI),
- .out_state_mask = S(PR_ARQ_S_DONE) |
- S(PR_ARQ_S_WAIT_AUTH) |
- S(PR_ARQ_S_WAIT_UPD_LOC_CHILD) |
- S(PR_ARQ_S_WAIT_SUB_PRES) |
- S(PR_ARQ_S_WAIT_TRACE_SUB) |
- S(PR_ARQ_S_WAIT_CHECK_IMEI) |
- S(PR_ARQ_S_WAIT_TMSI_ACK),
- .action = proc_arq_vlr_fn_w_obt_imsi,
- },
- [PR_ARQ_S_WAIT_AUTH] = {
- .name = OSMO_STRINGIFY(PR_ARQ_S_WAIT_AUTH),
- .in_event_mask = S(PR_ARQ_E_AUTH_RES),
- .out_state_mask = S(PR_ARQ_S_DONE) |
- S(PR_ARQ_S_WAIT_CIPH) |
- S(PR_ARQ_S_WAIT_UPD_LOC_CHILD) |
- S(PR_ARQ_S_WAIT_SUB_PRES) |
- S(PR_ARQ_S_WAIT_TRACE_SUB) |
- S(PR_ARQ_S_WAIT_CHECK_IMEI) |
- S(PR_ARQ_S_WAIT_TMSI_ACK),
- .action = proc_arq_vlr_fn_w_auth,
- },
- [PR_ARQ_S_WAIT_CIPH] = {
- .name = OSMO_STRINGIFY(PR_ARQ_S_WAIT_CIPH),
- .in_event_mask = S(PR_ARQ_E_CIPH_RES),
- .out_state_mask = S(PR_ARQ_S_DONE) |
- S(PR_ARQ_S_WAIT_UPD_LOC_CHILD) |
- S(PR_ARQ_S_WAIT_SUB_PRES) |
- S(PR_ARQ_S_WAIT_TRACE_SUB) |
- S(PR_ARQ_S_WAIT_CHECK_IMEI) |
- S(PR_ARQ_S_WAIT_TMSI_ACK),
- .action = proc_arq_vlr_fn_w_ciph,
- },
- [PR_ARQ_S_WAIT_UPD_LOC_CHILD] = {
- .name = OSMO_STRINGIFY(PR_ARQ_S_WAIT_UPD_LOC_CHILD),
- .in_event_mask = S(PR_ARQ_E_UPD_LOC_RES),
- .out_state_mask = S(PR_ARQ_S_DONE) |
- S(PR_ARQ_S_WAIT_SUB_PRES) |
- S(PR_ARQ_S_WAIT_TRACE_SUB) |
- S(PR_ARQ_S_WAIT_CHECK_IMEI) |
- S(PR_ARQ_S_WAIT_TMSI_ACK),
- .action = proc_arq_vlr_fn_w_upd_loc,
- },
- [PR_ARQ_S_WAIT_SUB_PRES] = {
- .name = OSMO_STRINGIFY(PR_ARQ_S_WAIT_SUB_PRES),
- .in_event_mask = S(PR_ARQ_E_PRES_RES),
- .out_state_mask = S(PR_ARQ_S_DONE) |
- S(PR_ARQ_S_WAIT_TRACE_SUB) |
- S(PR_ARQ_S_WAIT_CHECK_IMEI) |
- S(PR_ARQ_S_WAIT_TMSI_ACK),
- .action = proc_arq_vlr_fn_w_pres,
- },
- [PR_ARQ_S_WAIT_TRACE_SUB] = {
- .name = OSMO_STRINGIFY(PR_ARQ_S_WAIT_TRACE_SUB),
- .in_event_mask = S(PR_ARQ_E_TRACE_RES),
- .out_state_mask = S(PR_ARQ_S_DONE) |
- S(PR_ARQ_S_WAIT_CHECK_IMEI) |
- S(PR_ARQ_S_WAIT_TMSI_ACK),
- .action = proc_arq_vlr_fn_w_trace,
- },
- [PR_ARQ_S_WAIT_CHECK_IMEI] = {
- .name = OSMO_STRINGIFY(PR_ARQ_S_WAIT_CHECK_IMEI),
- .in_event_mask = S(PR_ARQ_E_IMEI_RES),
- .out_state_mask = S(PR_ARQ_S_DONE) |
- S(PR_ARQ_S_WAIT_TMSI_ACK),
- .action = proc_arq_vlr_fn_w_imei,
- },
- [PR_ARQ_S_WAIT_TMSI_ACK] = {
- .name = OSMO_STRINGIFY(PR_ARQ_S_WAIT_TMSI_ACK),
- .in_event_mask = S(PR_ARQ_E_TMSI_ACK),
- .out_state_mask = S(PR_ARQ_S_DONE),
- .action = proc_arq_vlr_fn_w_tmsi,
- },
- [PR_ARQ_S_DONE] = {
- .name = OSMO_STRINGIFY(PR_ARQ_S_DONE),
- .onenter = proc_arq_vlr_dispatch_result,
- },
-};
-
-static struct osmo_fsm proc_arq_vlr_fsm = {
- .name = "Process_Access_Request_VLR",
- .states = proc_arq_vlr_states,
- .num_states = ARRAY_SIZE(proc_arq_vlr_states),
- .allstate_event_mask = 0,
- .allstate_action = NULL,
- .log_subsys = DVLR,
- .event_names = proc_arq_vlr_event_names,
- .cleanup = proc_arq_vlr_cleanup,
-};
-
-void
-vlr_proc_acc_req(struct osmo_fsm_inst *parent,
- uint32_t parent_event_success,
- uint32_t parent_event_failure,
- void *parent_event_data,
- struct vlr_instance *vlr, void *msc_conn_ref,
- enum vlr_parq_type type, const uint8_t *mi_lv,
- const struct osmo_location_area_id *lai,
- bool authentication_required,
- enum vlr_ciph ciphering_required,
- bool is_r99, bool is_utran)
-{
- struct osmo_fsm_inst *fi;
- struct proc_arq_priv *par;
- char mi_string[GSM48_MI_SIZE];
- uint8_t mi_type;
-
- fi = osmo_fsm_inst_alloc_child(&proc_arq_vlr_fsm, parent,
- parent_event_failure);
- if (!fi)
- return;
-
- par = talloc_zero(fi, struct proc_arq_priv);
- fi->priv = par;
- par->vlr = vlr;
- par->msc_conn_ref = msc_conn_ref;
- par->type = type;
- par->lai = *lai;
- par->parent_event_success = parent_event_success;
- par->parent_event_failure = parent_event_failure;
- par->parent_event_data = parent_event_data;
- par->authentication_required = authentication_required;
- par->ciphering_required = ciphering_required;
- par->is_r99 = is_r99;
- par->is_utran = is_utran;
-
- LOGPFSM(fi, "rev=%s net=%s%s%s\n",
- is_r99 ? "R99" : "GSM",
- is_utran ? "UTRAN" : "GERAN",
- (authentication_required || ciphering_required)?
- " Auth" : " (no Auth)",
- (authentication_required || ciphering_required)?
- (ciphering_required? "+Ciph" : " (no Ciph)")
- : "");
-
- if (is_utran && !authentication_required)
- LOGPFSML(fi, LOGL_ERROR,
- "Authentication off on UTRAN network. Good luck.\n");
-
- gsm48_mi_to_string(mi_string, sizeof(mi_string), mi_lv+1, mi_lv[0]);
- mi_type = mi_lv[1] & GSM_MI_TYPE_MASK;
- switch (mi_type) {
- case GSM_MI_TYPE_IMSI:
- strncpy(par->imsi, mi_string, sizeof(par->imsi)-1);
- par->imsi[sizeof(par->imsi)-1] = '\0';
- par->by_tmsi = false;
- break;
- case GSM_MI_TYPE_TMSI:
- par->by_tmsi = true;
- par->tmsi = osmo_load32be(mi_lv+2);
- break;
- case GSM_MI_TYPE_IMEI:
- /* TODO: IMEI (emergency call) */
- default:
- /* FIXME: directly send reject? */
- proc_arq_fsm_done(fi, VLR_PR_ARQ_RES_UNIDENT_SUBSCR);
- return;
- }
-
- osmo_fsm_inst_dispatch(fi, PR_ARQ_E_START, NULL);
-}
-
-/* Gracefully terminate an FSM created by vlr_proc_acc_req() in case of
- * external timeout (i.e. from MSC). */
-void vlr_parq_conn_timeout(struct osmo_fsm_inst *fi)
-{
- if (!fi || fi->state == PR_ARQ_S_DONE)
- return;
- LOGPFSM(fi, "Connection timed out\n");
- proc_arq_fsm_done(fi, VLR_PR_ARQ_RES_TIMEOUT);
-}
-
-
-#if 0
-/***********************************************************************
- * Update_Location_Child_VLR, TS 29.002 Chapter 25.4.4
- ***********************************************************************/
-
-enum upd_loc_child_vlr_state {
- ULC_S_IDLE,
- ULC_S_WAIT_HLR_RESP,
- ULC_S_DONE,
-};
-
-enum upd_loc_child_vlr_event {
- ULC_E_START,
-};
-
-static const struct value_string upd_loc_child_vlr_event_names[] = {
- { ULC_E_START, "START" },
- { 0, NULL }
-};
-
-static void upd_loc_child_f_idle(struct osmo_fsm_inst *fi, uint32_t event,
- void *data)
-{
- OSMO_ASSERT(event == ULC_E_START);
-
- /* send update location */
-}
-
-static void upd_loc_child_f_w_hlr(struct osmo_fsm_inst *fi, uint32_t event,
- void *data)
-{
-}
-
-static const struct osmo_fsm_state upd_loc_child_vlr_states[] = {
- [ULC_S_IDLE] = {
- .in_event_mask = ,
- .out_state_mask = S(ULC_S_WAIT_HLR_RESP) |
- S(ULC_S_DONE),
- .name = "IDLE",
- .action = upd_loc_child_f_idle,
- },
- [ULC_S_WAIT_HLR_RESP] = {
- .in_event_mask = ,
- .out_state_mask = S(ULC_S_DONE),
- .name = "WAIT-HLR-RESP",
- .action = upd_loc_child_f_w_hlr,
- },
- [ULC_S_DONE] = {
- .name = "DONE",
- },
-};
-
-static struct osmo_fsm upd_loc_child_vlr_fsm = {
- .name = "Update_Location_Child_VLR",
- .states = upd_loc_child_vlr_states,
- .num_states = ARRAY_SIZE(upd_loc_child_vlr_states),
- .log_subsys = DVLR,
- .event_names = upd_loc_child_vlr_event_names,
-};
-#endif
-
-void vlr_parq_fsm_init(void)
-{
- //osmo_fsm_register(&upd_loc_child_vlr_fsm);
- osmo_fsm_register(&proc_arq_vlr_fsm);
-}
diff --git a/src/libvlr/vlr_access_req_fsm.h b/src/libvlr/vlr_access_req_fsm.h
deleted file mode 100644
index 8386da6f2..000000000
--- a/src/libvlr/vlr_access_req_fsm.h
+++ /dev/null
@@ -1,17 +0,0 @@
-#pragma once
-
-enum proc_arq_vlr_state {
- PR_ARQ_S_INIT,
- /* Waiting for Obtain_Identity_VLR (IMSI) result */
- PR_ARQ_S_WAIT_OBTAIN_IMSI,
- /* Waiting for Authenticate_VLR result */
- PR_ARQ_S_WAIT_AUTH,
- PR_ARQ_S_WAIT_CIPH,
- PR_ARQ_S_WAIT_UPD_LOC_CHILD,
- PR_ARQ_S_WAIT_SUB_PRES,
- PR_ARQ_S_WAIT_TRACE_SUB,
- PR_ARQ_S_WAIT_CHECK_IMEI,
- PR_ARQ_S_WAIT_TMSI_ACK,
- PR_ARQ_S_WAIT_CECK_CONF,
- PR_ARQ_S_DONE,
-};
diff --git a/src/libvlr/vlr_auth_fsm.c b/src/libvlr/vlr_auth_fsm.c
deleted file mode 100644
index 0eb86e749..000000000
--- a/src/libvlr/vlr_auth_fsm.c
+++ /dev/null
@@ -1,605 +0,0 @@
-/* Osmocom Visitor Location Register (VLR) Autentication FSM */
-
-/* (C) 2016 by Harald Welte <laforge@gnumonks.org>
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-
-#include <osmocom/core/fsm.h>
-#include <osmocom/core/utils.h>
-#include <osmocom/gsm/gsup.h>
-#include <openbsc/vlr.h>
-#include <openbsc/debug.h>
-
-#include "vlr_core.h"
-#include "vlr_auth_fsm.h"
-
-#define S(x) (1 << (x))
-
-static const struct value_string fsm_auth_event_names[] = {
- OSMO_VALUE_STRING(VLR_AUTH_E_START),
- OSMO_VALUE_STRING(VLR_AUTH_E_HLR_SAI_ACK),
- OSMO_VALUE_STRING(VLR_AUTH_E_HLR_SAI_NACK),
- OSMO_VALUE_STRING(VLR_AUTH_E_HLR_SAI_ABORT),
- OSMO_VALUE_STRING(VLR_AUTH_E_MS_AUTH_RESP),
- OSMO_VALUE_STRING(VLR_AUTH_E_MS_AUTH_FAIL),
- OSMO_VALUE_STRING(VLR_AUTH_E_MS_ID_IMSI),
- { 0, NULL }
-};
-
-const struct value_string vlr_auth_fsm_result_names[] = {
- OSMO_VALUE_STRING(VLR_AUTH_RES_ABORTED),
- OSMO_VALUE_STRING(VLR_AUTH_RES_UNKNOWN_SUBSCR),
- OSMO_VALUE_STRING(VLR_AUTH_RES_PROC_ERR),
- OSMO_VALUE_STRING(VLR_AUTH_RES_AUTH_FAILED),
- OSMO_VALUE_STRING(VLR_AUTH_RES_PASSED),
- {0, NULL}
-};
-
-/* private state of the auth_fsm_instance */
-struct auth_fsm_priv {
- struct vlr_subscr *vsub;
- bool by_imsi;
- bool is_r99;
- bool is_utran;
- bool auth_requested;
-
- int auth_tuple_max_use_count; /* see vlr->cfg instead */
-};
-
-/***********************************************************************
- * Utility functions
- ***********************************************************************/
-
-/* Always use either vlr_subscr_get_auth_tuple() or vlr_subscr_has_auth_tuple()
- * instead, to ensure proper use count.
- * Return an auth tuple with the lowest use_count among the auth tuples. If
- * max_use_count >= 0, return NULL if all available auth tuples have a use
- * count > max_use_count. If max_use_count is negative, return a currently
- * least used auth tuple without enforcing a maximum use count. If there are
- * no auth tuples, return NULL.
- */
-static struct gsm_auth_tuple *
-_vlr_subscr_next_auth_tuple(struct vlr_subscr *vsub, int max_use_count)
-{
- unsigned int count;
- unsigned int idx;
- struct gsm_auth_tuple *at = NULL;
- unsigned int key_seq = GSM_KEY_SEQ_INVAL;
-
- if (!vsub)
- return NULL;
-
- if (vsub->last_tuple)
- key_seq = vsub->last_tuple->key_seq;
-
- if (key_seq == GSM_KEY_SEQ_INVAL)
- /* Start with 0 after increment modulo array size */
- idx = ARRAY_SIZE(vsub->auth_tuples) - 1;
- else
- idx = key_seq;
-
- for (count = ARRAY_SIZE(vsub->auth_tuples); count > 0; count--) {
- idx = (idx + 1) % ARRAY_SIZE(vsub->auth_tuples);
-
- if (vsub->auth_tuples[idx].key_seq == GSM_KEY_SEQ_INVAL)
- continue;
-
- if (!at || vsub->auth_tuples[idx].use_count < at->use_count)
- at = &vsub->auth_tuples[idx];
- }
-
- if (!at || (max_use_count >= 0 && at->use_count > max_use_count))
- return NULL;
-
- return at;
-}
-
-/* Return an auth tuple and increment its use count. */
-static struct gsm_auth_tuple *
-vlr_subscr_get_auth_tuple(struct vlr_subscr *vsub, int max_use_count)
-{
- struct gsm_auth_tuple *at = _vlr_subscr_next_auth_tuple(vsub,
- max_use_count);
- if (!at)
- return NULL;
- at->use_count++;
- return at;
-}
-
-/* Return whether an auth tuple with the given max_use_count is available. */
-static bool vlr_subscr_has_auth_tuple(struct vlr_subscr *vsub,
- int max_use_count)
-{
- return _vlr_subscr_next_auth_tuple(vsub, max_use_count) != NULL;
-}
-
-static bool check_auth_resp(struct vlr_subscr *vsub, bool is_r99,
- bool is_utran, const uint8_t *res,
- uint8_t res_len)
-{
- struct gsm_auth_tuple *at = vsub->last_tuple;
- struct osmo_auth_vector *vec = &at->vec;
- bool check_umts;
- OSMO_ASSERT(at);
-
- LOGVSUBP(LOGL_DEBUG, vsub, "received res: %s\n",
- osmo_hexdump(res, res_len));
-
- /* RES must be present and at least 32bit */
- if (!res || res_len < sizeof(vec->sres)) {
- LOGVSUBP(LOGL_NOTICE, vsub, "AUTH RES missing or too short "
- "(%u)\n", res_len);
- goto out_false;
- }
-
- check_umts = false;
- if (is_r99 && (vec->auth_types & OSMO_AUTH_TYPE_UMTS)) {
- check_umts = true;
- /* We have a R99 capable UE and have a UMTS AKA capable USIM.
- * However, the ME may still choose to only perform GSM AKA, as
- * long as the bearer is GERAN */
- if (res_len != vec->res_len) {
- if (is_utran) {
- LOGVSUBP(LOGL_NOTICE, vsub,
- "AUTH via UTRAN but "
- "res_len(%u) != vec->res_len(%u)\n",
- res_len, vec->res_len);
- goto out_false;
- }
- check_umts = false;
- }
- }
-
- if (check_umts) {
- if (res_len != vec->res_len
- || memcmp(res, vec->res, res_len)) {
- LOGVSUBP(LOGL_INFO, vsub, "UMTS AUTH failure:"
- " mismatching res (expected res=%s)\n",
- osmo_hexdump(vec->res, vec->res_len));
- goto out_false;
- }
-
- LOGVSUBP(LOGL_INFO, vsub, "AUTH established UMTS security"
- " context\n");
- vsub->sec_ctx = VLR_SEC_CTX_UMTS;
- return true;
- } else {
- if (res_len != sizeof(vec->sres)
- || memcmp(res, vec->sres, sizeof(vec->sres))) {
- LOGVSUBP(LOGL_INFO, vsub, "GSM AUTH failure:"
- " mismatching sres (expected sres=%s)\n",
- osmo_hexdump(vec->sres, sizeof(vec->sres)));
- goto out_false;
- }
-
- LOGVSUBP(LOGL_INFO, vsub, "AUTH established GSM security"
- " context\n");
- vsub->sec_ctx = VLR_SEC_CTX_GSM;
- return true;
- }
-
-out_false:
- vsub->sec_ctx = VLR_SEC_CTX_NONE;
- return false;
-}
-
-static void auth_fsm_onenter_failed(struct osmo_fsm_inst *fi, uint32_t prev_state)
-{
- struct auth_fsm_priv *afp = fi->priv;
- struct vlr_subscr *vsub = afp->vsub;
-
- /* If authentication hasn't even started, e.g. the HLR sent no auth
- * info, then we also don't need to tell the HLR about an auth failure.
- */
- if (afp->auth_requested)
- vlr_subscr_tx_auth_fail_rep(vsub);
-}
-
-static bool is_umts_auth(struct auth_fsm_priv *afp,
- uint32_t auth_types)
-{
- if (!afp->is_r99)
- return false;
- if (!(auth_types & OSMO_AUTH_TYPE_UMTS))
- return false;
- return true;
-}
-
-/* Terminate the Auth FSM Instance and notify parent */
-static void auth_fsm_term(struct osmo_fsm_inst *fi, enum vlr_auth_fsm_result res)
-{
- struct auth_fsm_priv *afp = fi->priv;
- struct vlr_subscr *vsub = afp->vsub;
-
- LOGPFSM(fi, "Authentication terminating with result %s\n",
- vlr_auth_fsm_result_name(res));
-
- /* Do one final state transition (mostly for logging purpose) */
- if (res == VLR_AUTH_RES_PASSED)
- osmo_fsm_inst_state_chg(fi, VLR_SUB_AS_AUTHENTICATED, 0, 0);
- else
- osmo_fsm_inst_state_chg(fi, VLR_SUB_AS_AUTH_FAILED, 0, 0);
-
- /* return the result to the parent FSM */
- osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, &res);
- vsub->auth_fsm = NULL;
-}
-
-/* back-end function transmitting authentication. Caller ensures we have valid
- * tuple */
-static int _vlr_subscr_authenticate(struct osmo_fsm_inst *fi)
-{
- struct auth_fsm_priv *afp = fi->priv;
- struct vlr_subscr *vsub = afp->vsub;
- struct gsm_auth_tuple *at;
-
- /* Caller ensures we have vectors available */
- at = vlr_subscr_get_auth_tuple(vsub, afp->auth_tuple_max_use_count);
- if (!at) {
- LOGPFSML(fi, LOGL_ERROR, "A previous check ensured that an"
- " auth tuple was available, but now there is in fact"
- " none.\n");
- auth_fsm_term(fi, VLR_AUTH_RES_PROC_ERR);
- return -1;
- }
-
- LOGPFSM(fi, "got auth tuple: use_count=%d key_seq=%d\n",
- at->use_count, at->key_seq);
-
- OSMO_ASSERT(at);
-
- /* Transmit auth req to subscriber */
- afp->auth_requested = true;
- vsub->last_tuple = at;
- vsub->vlr->ops.tx_auth_req(vsub->msc_conn_ref, at,
- is_umts_auth(afp, at->vec.auth_types));
- return 0;
-}
-
-/***********************************************************************
- * FSM State Action functions
- ***********************************************************************/
-
-/* Initial State of TS 23.018 AUT_VLR */
-static void auth_fsm_needs_auth(struct osmo_fsm_inst *fi, uint32_t event, void *data)
-{
- struct auth_fsm_priv *afp = fi->priv;
- struct vlr_subscr *vsub = afp->vsub;
-
- OSMO_ASSERT(event == VLR_AUTH_E_START);
-
- /* Start off with the default max_use_count, possibly change that if we
- * need to re-use an old tuple. */
- afp->auth_tuple_max_use_count = vsub->vlr->cfg.auth_tuple_max_use_count;
-
- /* Check if we have vectors available */
- if (!vlr_subscr_has_auth_tuple(vsub, afp->auth_tuple_max_use_count)) {
- /* Obtain_Authentication_Sets_VLR */
- vlr_subscr_req_sai(vsub, NULL, NULL);
- osmo_fsm_inst_state_chg(fi, VLR_SUB_AS_NEEDS_AUTH_WAIT_AI,
- GSM_29002_TIMER_M, 0);
- } else {
- /* go straight ahead with sending auth request */
- osmo_fsm_inst_state_chg(fi, VLR_SUB_AS_WAIT_RESP,
- vlr_timer(vsub->vlr, 3260), 3260);
- _vlr_subscr_authenticate(fi);
- }
-}
-
-/* Waiting for Authentication Info from HLR */
-static void auth_fsm_wait_ai(struct osmo_fsm_inst *fi, uint32_t event,
- void *data)
-{
- struct auth_fsm_priv *afp = fi->priv;
- struct vlr_subscr *vsub = afp->vsub;
- struct osmo_gsup_message *gsup = data;
-
- if (event == VLR_AUTH_E_HLR_SAI_NACK)
- LOGPFSM(fi, "GSUP: rx Auth Info Error cause: %d: %s\n",
- gsup->cause,
- get_value_string(gsm48_gmm_cause_names, gsup->cause));
-
- /* We are in what corresponds to the
- * Wait_For_Authentication_Sets state of TS 23.018 OAS_VLR */
- if ((event == VLR_AUTH_E_HLR_SAI_ACK && !gsup->num_auth_vectors)
- || (event == VLR_AUTH_E_HLR_SAI_NACK &&
- gsup->cause != GMM_CAUSE_IMSI_UNKNOWN)
- || (event == VLR_AUTH_E_HLR_SAI_ABORT)) {
- if (vsub->vlr->cfg.auth_reuse_old_sets_on_error
- && vlr_subscr_has_auth_tuple(vsub, -1)) {
- /* To re-use an old tuple, disable the max_use_count
- * constraint. */
- afp->auth_tuple_max_use_count = -1;
- goto pass;
- }
- /* result = procedure error */
- auth_fsm_term(fi, VLR_AUTH_RES_PROC_ERR);
- return;
- }
-
- switch (event) {
- case VLR_AUTH_E_HLR_SAI_ACK:
- vlr_subscr_update_tuples(vsub, gsup);
- goto pass;
- break;
- case VLR_AUTH_E_HLR_SAI_NACK:
- auth_fsm_term(fi,
- gsup->cause == GMM_CAUSE_IMSI_UNKNOWN?
- VLR_AUTH_RES_UNKNOWN_SUBSCR
- : VLR_AUTH_RES_PROC_ERR);
- break;
- }
-
- return;
-pass:
- osmo_fsm_inst_state_chg(fi, VLR_SUB_AS_WAIT_RESP,
- vlr_timer(vsub->vlr, 3260), 3260);
- _vlr_subscr_authenticate(fi);
-}
-
-/* Waiting for Authentication Response from MS */
-static void auth_fsm_wait_auth_resp(struct osmo_fsm_inst *fi, uint32_t event,
- void *data)
-{
- struct auth_fsm_priv *afp = fi->priv;
- struct vlr_subscr *vsub = afp->vsub;
- struct vlr_instance *vlr = vsub->vlr;
- struct vlr_auth_resp_par *par = data;
- int rc;
-
- switch (event) {
- case VLR_AUTH_E_MS_AUTH_RESP:
- rc = check_auth_resp(vsub, par->is_r99, par->is_utran,
- par->res, par->res_len);
- if (rc == false) {
- if (!afp->by_imsi) {
- vlr->ops.tx_id_req(vsub->msc_conn_ref,
- GSM_MI_TYPE_IMSI);
- osmo_fsm_inst_state_chg(fi,
- VLR_SUB_AS_WAIT_ID_IMSI,
- vlr_timer(vlr, 3270), 3270);
- } else {
- auth_fsm_term(fi, VLR_AUTH_RES_AUTH_FAILED);
- }
- } else {
- auth_fsm_term(fi, VLR_AUTH_RES_PASSED);
- }
- break;
- case VLR_AUTH_E_MS_AUTH_FAIL:
- if (par->auts) {
- /* First failure, start re-sync attempt */
- vlr_subscr_req_sai(vsub, par->auts,
- vsub->last_tuple->vec.rand);
- osmo_fsm_inst_state_chg(fi,
- VLR_SUB_AS_NEEDS_AUTH_WAIT_SAI_RESYNC,
- GSM_29002_TIMER_M, 0);
- } else
- auth_fsm_term(fi, VLR_AUTH_RES_AUTH_FAILED);
- break;
- }
-}
-
-/* Waiting for Authentication Info from HLR (resync case) */
-static void auth_fsm_wait_ai_resync(struct osmo_fsm_inst *fi,
- uint32_t event, void *data)
-{
- struct auth_fsm_priv *afp = fi->priv;
- struct vlr_subscr *vsub = afp->vsub;
- struct osmo_gsup_message *gsup = data;
-
- /* We are in what corresponds to the
- * Wait_For_Authentication_Sets state of TS 23.018 OAS_VLR */
- if ((event == VLR_AUTH_E_HLR_SAI_ACK && !gsup->num_auth_vectors) ||
- (event == VLR_AUTH_E_HLR_SAI_NACK &&
- gsup->cause != GMM_CAUSE_IMSI_UNKNOWN) ||
- (event == VLR_AUTH_E_HLR_SAI_ABORT)) {
- /* result = procedure error */
- auth_fsm_term(fi, VLR_AUTH_RES_PROC_ERR);
- }
- switch (event) {
- case VLR_AUTH_E_HLR_SAI_ACK:
- vlr_subscr_update_tuples(vsub, gsup);
- goto pass;
- break;
- case VLR_AUTH_E_HLR_SAI_NACK:
- auth_fsm_term(fi,
- gsup->cause == GMM_CAUSE_IMSI_UNKNOWN?
- VLR_AUTH_RES_UNKNOWN_SUBSCR
- : VLR_AUTH_RES_PROC_ERR);
- break;
- }
-
- return;
-pass:
- osmo_fsm_inst_state_chg(fi, VLR_SUB_AS_WAIT_RESP_RESYNC,
- vlr_timer(vsub->vlr, 3260), 3260);
- _vlr_subscr_authenticate(fi);
-}
-
-/* Waiting for AUTH RESP from MS (re-sync case) */
-static void auth_fsm_wait_auth_resp_resync(struct osmo_fsm_inst *fi,
- uint32_t event, void *data)
-{
- struct auth_fsm_priv *afp = fi->priv;
- struct vlr_subscr *vsub = afp->vsub;
- struct vlr_auth_resp_par *par = data;
- struct vlr_instance *vlr = vsub->vlr;
- int rc;
-
- switch (event) {
- case VLR_AUTH_E_MS_AUTH_RESP:
- rc = check_auth_resp(vsub, par->is_r99, par->is_utran,
- par->res, par->res_len);
- if (rc == false) {
- if (!afp->by_imsi) {
- vlr->ops.tx_id_req(vsub->msc_conn_ref,
- GSM_MI_TYPE_IMSI);
- osmo_fsm_inst_state_chg(fi,
- VLR_SUB_AS_WAIT_ID_IMSI,
- vlr_timer(vlr, 3270), 3270);
- } else {
- /* Result = Aborted */
- auth_fsm_term(fi, VLR_AUTH_RES_ABORTED);
- }
- } else {
- /* Result = Pass */
- auth_fsm_term(fi, VLR_AUTH_RES_PASSED);
- }
- break;
- case VLR_AUTH_E_MS_AUTH_FAIL:
- /* Second failure: Result = Fail */
- auth_fsm_term(fi, VLR_AUTH_RES_AUTH_FAILED);
- break;
- }
-}
-
-/* AUT_VLR waiting for Obtain_IMSI_VLR result */
-static void auth_fsm_wait_imsi(struct osmo_fsm_inst *fi, uint32_t event,
- void *data)
-{
- struct auth_fsm_priv *afp = fi->priv;
- struct vlr_subscr *vsub = afp->vsub;
- const char *mi_string = data;
-
- switch (event) {
- case VLR_AUTH_E_MS_ID_IMSI:
- if (vsub->imsi[0]
- && !vlr_subscr_matches_imsi(vsub, mi_string)) {
- LOGVSUBP(LOGL_ERROR, vsub, "IMSI in ID RESP differs:"
- " %s\n", mi_string);
- } else {
- strncpy(vsub->imsi, mi_string, sizeof(vsub->imsi));
- vsub->imsi[sizeof(vsub->imsi)-1] = '\0';
- }
- /* retry with identity=IMSI */
- afp->by_imsi = true;
- osmo_fsm_inst_state_chg(fi, VLR_SUB_AS_NEEDS_AUTH, 0, 0);
- osmo_fsm_inst_dispatch(fi, VLR_AUTH_E_START, NULL);
- break;
- }
-}
-
-static const struct osmo_fsm_state auth_fsm_states[] = {
- [VLR_SUB_AS_NEEDS_AUTH] = {
- .name = OSMO_STRINGIFY(VLR_SUB_AS_NEEDS_AUTH),
- .in_event_mask = S(VLR_AUTH_E_START),
- .out_state_mask = S(VLR_SUB_AS_NEEDS_AUTH_WAIT_AI) |
- S(VLR_SUB_AS_WAIT_RESP),
- .action = auth_fsm_needs_auth,
- },
- [VLR_SUB_AS_NEEDS_AUTH_WAIT_AI] = {
- .name = OSMO_STRINGIFY(VLR_SUB_AS_NEEDS_AUTH_WAIT_AI),
- .in_event_mask = S(VLR_AUTH_E_HLR_SAI_ACK) |
- S(VLR_AUTH_E_HLR_SAI_NACK),
- .out_state_mask = S(VLR_SUB_AS_AUTH_FAILED) |
- S(VLR_SUB_AS_WAIT_RESP),
- .action = auth_fsm_wait_ai,
- },
- [VLR_SUB_AS_WAIT_RESP] = {
- .name = OSMO_STRINGIFY(VLR_SUB_AS_WAIT_RESP),
- .in_event_mask = S(VLR_AUTH_E_MS_AUTH_RESP) |
- S(VLR_AUTH_E_MS_AUTH_FAIL),
- .out_state_mask = S(VLR_SUB_AS_WAIT_ID_IMSI) |
- S(VLR_SUB_AS_AUTH_FAILED) |
- S(VLR_SUB_AS_AUTHENTICATED) |
- S(VLR_SUB_AS_NEEDS_AUTH_WAIT_SAI_RESYNC),
- .action = auth_fsm_wait_auth_resp,
- },
- [VLR_SUB_AS_NEEDS_AUTH_WAIT_SAI_RESYNC] = {
- .name = OSMO_STRINGIFY(VLR_SUB_AS_NEEDS_AUTH_WAIT_SAI_RESYNC),
- .in_event_mask = S(VLR_AUTH_E_HLR_SAI_ACK) |
- S(VLR_AUTH_E_HLR_SAI_NACK),
- .out_state_mask = S(VLR_SUB_AS_AUTH_FAILED) |
- S(VLR_SUB_AS_WAIT_RESP_RESYNC),
- .action = auth_fsm_wait_ai_resync,
- },
- [VLR_SUB_AS_WAIT_RESP_RESYNC] = {
- .name = OSMO_STRINGIFY(VLR_SUB_AS_WAIT_RESP_RESYNC),
- .in_event_mask = S(VLR_AUTH_E_MS_AUTH_RESP) |
- S(VLR_AUTH_E_MS_AUTH_FAIL),
- .out_state_mask = S(VLR_SUB_AS_AUTH_FAILED) |
- S(VLR_SUB_AS_AUTHENTICATED),
- .action = auth_fsm_wait_auth_resp_resync,
- },
- [VLR_SUB_AS_WAIT_ID_IMSI] = {
- .name = OSMO_STRINGIFY(VLR_SUB_AS_WAIT_ID_IMSI),
- .in_event_mask = S(VLR_AUTH_E_MS_ID_IMSI),
- .out_state_mask = S(VLR_SUB_AS_NEEDS_AUTH),
- .action = auth_fsm_wait_imsi,
- },
- [VLR_SUB_AS_AUTHENTICATED] = {
- .name = OSMO_STRINGIFY(VLR_SUB_AS_AUTHENTICATED),
- .in_event_mask = 0,
- .out_state_mask = 0,
- },
- [VLR_SUB_AS_AUTH_FAILED] = {
- .name = OSMO_STRINGIFY(VLR_SUB_AS_AUTH_FAILED),
- .in_event_mask = 0,
- .out_state_mask = 0,
- .onenter = auth_fsm_onenter_failed,
- },
-};
-
-struct osmo_fsm vlr_auth_fsm = {
- .name = "VLR_Authenticate",
- .states = auth_fsm_states,
- .num_states = ARRAY_SIZE(auth_fsm_states),
- .allstate_event_mask = 0,
- .allstate_action = NULL,
- .log_subsys = DVLR,
- .event_names = fsm_auth_event_names,
-};
-
-/***********************************************************************
- * User API (for SGSN/MSC code)
- ***********************************************************************/
-
-/* MSC->VLR: Start Procedure Authenticate_VLR (TS 23.012 Ch. 4.1.2.2) */
-struct osmo_fsm_inst *auth_fsm_start(struct vlr_subscr *vsub,
- uint32_t log_level,
- struct osmo_fsm_inst *parent,
- uint32_t parent_term_event,
- bool is_r99,
- bool is_utran)
-{
- struct osmo_fsm_inst *fi;
- struct auth_fsm_priv *afp;
-
- fi = osmo_fsm_inst_alloc_child(&vlr_auth_fsm, parent,
- parent_term_event);
-
-
- afp = talloc_zero(fi, struct auth_fsm_priv);
- if (!afp) {
- osmo_fsm_inst_dispatch(parent, parent_term_event, 0);
- return NULL;
- }
-
- afp->vsub = vsub;
- if (vsub->imsi[0])
- afp->by_imsi = true;
- afp->is_r99 = is_r99;
- afp->is_utran = is_utran;
- fi->priv = afp;
- vsub->auth_fsm = fi;
-
- osmo_fsm_inst_dispatch(fi, VLR_AUTH_E_START, NULL);
-
- return fi;
-}
diff --git a/src/libvlr/vlr_auth_fsm.h b/src/libvlr/vlr_auth_fsm.h
deleted file mode 100644
index 226435f83..000000000
--- a/src/libvlr/vlr_auth_fsm.h
+++ /dev/null
@@ -1,52 +0,0 @@
-#pragma once
-
-#include <osmocom/core/utils.h>
-
-/* Parameters to VLR_AUTH_E_MS_AUTH_RESP */
-struct vlr_auth_resp_par {
- bool is_r99;
- bool is_utran;
- const uint8_t *res;
- unsigned int res_len;
- const uint8_t *auts;
-};
-
-/* Result communicated back to parent FMS */
-enum vlr_auth_fsm_result {
- VLR_AUTH_RES_ABORTED,
- VLR_AUTH_RES_UNKNOWN_SUBSCR,
- VLR_AUTH_RES_PROC_ERR,
- VLR_AUTH_RES_AUTH_FAILED,
- VLR_AUTH_RES_PASSED,
-};
-
-extern const struct value_string vlr_auth_fsm_result_names[];
-static inline const char *vlr_auth_fsm_result_name(enum vlr_auth_fsm_result val)
-{
- return get_value_string(vlr_auth_fsm_result_names, val);
-}
-
-enum vlr_fsm_auth_event {
- VLR_AUTH_E_START,
- /* TS 23.018 OAS_VLR1(2): SendAuthInfo ACK from HLR */
- VLR_AUTH_E_HLR_SAI_ACK,
- /* TS 23.018 OAS_VLR1(2): SendAuthInfo NACK from HLR */
- VLR_AUTH_E_HLR_SAI_NACK,
- /* FIXME: merge with NACK? */
- VLR_AUTH_E_HLR_SAI_ABORT,
- /* Authentication Response from MS */
- VLR_AUTH_E_MS_AUTH_RESP,
- /* Authentication Failure from MS */
- VLR_AUTH_E_MS_AUTH_FAIL,
- /* Identity Response (IMSI) from MS */
- VLR_AUTH_E_MS_ID_IMSI,
-};
-
-struct osmo_fsm vlr_auth_fsm;
-
-struct osmo_fsm_inst *auth_fsm_start(struct vlr_subscr *vsub,
- uint32_t log_level,
- struct osmo_fsm_inst *parent,
- uint32_t parent_term_event,
- bool is_r99,
- bool is_utran);
diff --git a/src/libvlr/vlr_core.h b/src/libvlr/vlr_core.h
deleted file mode 100644
index 0e63c7e69..000000000
--- a/src/libvlr/vlr_core.h
+++ /dev/null
@@ -1,21 +0,0 @@
-#pragma once
-
-#include <openbsc/vlr.h>
-
-#define LOGGSUPP(level, gsup, fmt, args...) \
- LOGP(DVLR, level, "GSUP(%s) " fmt, \
- (gsup)->imsi, \
- ## args)
-
-#define LOGVSUBP(level, vsub, fmt, args...) \
- LOGP(DVLR, level, "SUBSCR(%s) " fmt, \
- vlr_subscr_name(vsub), ## args)
-
-
-const char *vlr_subscr_name(struct vlr_subscr *vsub);
-int vlr_subscr_req_lu(struct vlr_subscr *vsub, bool is_ps);
-int vlr_subscr_req_sai(struct vlr_subscr *vsub, const uint8_t *auts,
- const uint8_t *auts_rand);
-struct vlr_subscr *vlr_subscr_alloc(struct vlr_instance *vlr);
-void vlr_subscr_update_tuples(struct vlr_subscr *vsub,
- const struct osmo_gsup_message *gsup);
diff --git a/src/libvlr/vlr_lu_fsm.c b/src/libvlr/vlr_lu_fsm.c
deleted file mode 100644
index 94bea560f..000000000
--- a/src/libvlr/vlr_lu_fsm.c
+++ /dev/null
@@ -1,1449 +0,0 @@
-/* Osmocom Visitor Location Register (VLR): Location Update FSMs */
-
-/* (C) 2016 by Harald Welte <laforge@gnumonks.org>
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <osmocom/core/fsm.h>
-#include <osmocom/gsm/gsup.h>
-#include <openbsc/vlr.h>
-#include <openbsc/debug.h>
-
-#include "vlr_core.h"
-#include "vlr_auth_fsm.h"
-#include "vlr_lu_fsm.h"
-
-#define S(x) (1 << (x))
-
-#define LU_TIMEOUT_LONG 30
-
-enum vlr_fsm_result {
- VLR_FSM_RESULT_NONE,
- VLR_FSM_RESULT_SUCCESS,
- VLR_FSM_RESULT_FAILURE,
-};
-
-
-/***********************************************************************
- * Update_HLR_VLR, TS 23.012 Chapter 4.1.2.4
- ***********************************************************************/
-
-enum upd_hlr_vlr_state {
- UPD_HLR_VLR_S_INIT,
- UPD_HLR_VLR_S_WAIT_FOR_DATA,
- UPD_HLR_VLR_S_DONE,
-};
-
-enum upd_hlr_vlr_evt {
- UPD_HLR_VLR_E_START,
- UPD_HLR_VLR_E_INS_SUB_DATA,
- UPD_HLR_VLR_E_ACT_TRACE_MODE,
- UPD_HLR_VLR_E_FW_CHECK_SS_IND,
- UPD_HLR_VLR_E_UPD_LOC_ACK,
- UPD_HLR_VLR_E_UPD_LOC_NACK,
-};
-
-static const struct value_string upd_hlr_vlr_event_names[] = {
- OSMO_VALUE_STRING(UPD_HLR_VLR_E_START),
- OSMO_VALUE_STRING(UPD_HLR_VLR_E_INS_SUB_DATA),
- OSMO_VALUE_STRING(UPD_HLR_VLR_E_ACT_TRACE_MODE),
- OSMO_VALUE_STRING(UPD_HLR_VLR_E_FW_CHECK_SS_IND),
- OSMO_VALUE_STRING(UPD_HLR_VLR_E_UPD_LOC_ACK),
- OSMO_VALUE_STRING(UPD_HLR_VLR_E_UPD_LOC_NACK),
- { 0, NULL }
-};
-
-static void upd_hlr_vlr_fsm_init(struct osmo_fsm_inst *fi, uint32_t event,
- void *data)
-{
- struct vlr_subscr *vsub = fi->priv;
-
- OSMO_ASSERT(event == UPD_HLR_VLR_E_START);
-
- /* Send UpdateLocation to HLR */
- vlr_subscr_req_lu(vsub, vsub->vlr->cfg.is_ps);
- osmo_fsm_inst_state_chg(fi, UPD_HLR_VLR_S_WAIT_FOR_DATA,
- LU_TIMEOUT_LONG, 0);
-}
-
-static void upd_hlr_vlr_fsm_wait_data(struct osmo_fsm_inst *fi, uint32_t event,
- void *data)
-{
- struct vlr_subscr *vsub = fi->priv;
-
- switch (event) {
- case UPD_HLR_VLR_E_INS_SUB_DATA:
- /* FIXME: Insert_Subscr_Data_VLR */
- break;
- case UPD_HLR_VLR_E_ACT_TRACE_MODE:
- /* TODO: Activate_Tracing_VLR */
- break;
- case UPD_HLR_VLR_E_FW_CHECK_SS_IND:
- /* TODO: Forward Check SS Ind to MSC */
- break;
- case UPD_HLR_VLR_E_UPD_LOC_ACK:
- /* Inside Update_HLR_VLR after UpdateLocationAck */
- vsub->sub_dataconf_by_hlr_ind = true;
- vsub->loc_conf_in_hlr_ind = true;
- osmo_fsm_inst_state_chg(fi, UPD_HLR_VLR_S_DONE, 0, 0);
- osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
- break;
- case UPD_HLR_VLR_E_UPD_LOC_NACK:
- /* Inside Update_HLR_VLR after UpdateLocationNack */
- /* TODO: Check_User_Error_In_Serving_Network_Entity */
- vsub->sub_dataconf_by_hlr_ind = false;
- vsub->loc_conf_in_hlr_ind = false;
- osmo_fsm_inst_state_chg(fi, UPD_HLR_VLR_S_DONE, 0, 0);
- /* Data is a pointer to a gsm48_gmm_cause which we
- * simply pass through */
- osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, data);
- break;
- }
-}
-
-static const struct osmo_fsm_state upd_hlr_vlr_states[] = {
- [UPD_HLR_VLR_S_INIT] = {
- .in_event_mask = S(UPD_HLR_VLR_E_START),
- .out_state_mask = S(UPD_HLR_VLR_S_WAIT_FOR_DATA),
- .name = OSMO_STRINGIFY(UPD_HLR_VLR_S_INIT),
- .action = upd_hlr_vlr_fsm_init,
- },
- [UPD_HLR_VLR_S_WAIT_FOR_DATA] = {
- .in_event_mask = S(UPD_HLR_VLR_E_INS_SUB_DATA) |
- S(UPD_HLR_VLR_E_ACT_TRACE_MODE) |
- S(UPD_HLR_VLR_E_FW_CHECK_SS_IND) |
- S(UPD_HLR_VLR_E_UPD_LOC_ACK) |
- S(UPD_HLR_VLR_E_UPD_LOC_NACK),
- .out_state_mask = S(UPD_HLR_VLR_S_DONE),
- .name = OSMO_STRINGIFY(UPD_HLR_VLR_S_WAIT_FOR_DATA),
- .action = upd_hlr_vlr_fsm_wait_data,
- },
- [UPD_HLR_VLR_S_DONE] = {
- .name = OSMO_STRINGIFY(UPD_HLR_VLR_S_DONE),
- },
-};
-
-static struct osmo_fsm upd_hlr_vlr_fsm = {
- .name = "upd_hlr_vlr_fsm",
- .states = upd_hlr_vlr_states,
- .num_states = ARRAY_SIZE(upd_hlr_vlr_states),
- .allstate_event_mask = 0,
- .allstate_action = NULL,
- .log_subsys = DVLR,
- .event_names = upd_hlr_vlr_event_names,
-};
-
-struct osmo_fsm_inst *
-upd_hlr_vlr_proc_start(struct osmo_fsm_inst *parent,
- struct vlr_subscr *vsub,
- uint32_t parent_event)
-{
- struct osmo_fsm_inst *fi;
-
- fi = osmo_fsm_inst_alloc_child(&upd_hlr_vlr_fsm, parent,
- parent_event);
- if (!fi)
- return NULL;
-
- fi->priv = vsub;
- osmo_fsm_inst_dispatch(fi, UPD_HLR_VLR_E_START, NULL);
-
- return fi;
-}
-
-
-/***********************************************************************
- * Subscriber_Present_VLR, TS 29.002 Chapter 25.10.1
- ***********************************************************************/
-
-enum sub_pres_vlr_state {
- SUB_PRES_VLR_S_INIT,
- SUB_PRES_VLR_S_WAIT_FOR_HLR,
- SUB_PRES_VLR_S_DONE,
-};
-
-enum sub_pres_vlr_event {
- SUB_PRES_VLR_E_START,
- SUB_PRES_VLR_E_READY_SM_CNF,
- SUB_PRES_VLR_E_READY_SM_ERR,
-};
-
-static const struct value_string sub_pres_vlr_event_names[] = {
- OSMO_VALUE_STRING(SUB_PRES_VLR_E_START),
- OSMO_VALUE_STRING(SUB_PRES_VLR_E_READY_SM_CNF),
- OSMO_VALUE_STRING(SUB_PRES_VLR_E_READY_SM_ERR),
- { 0, NULL }
-};
-
-static void sub_pres_vlr_fsm_init(struct osmo_fsm_inst *fi, uint32_t event,
- void *data)
-{
- struct vlr_subscr *vsub = fi->priv;
- OSMO_ASSERT(event == SUB_PRES_VLR_E_START);
-
- if (!vsub->ms_not_reachable_flag) {
- osmo_fsm_inst_state_chg(fi, SUB_PRES_VLR_S_DONE, 0, 0);
- osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
- return;
- }
- /* FIXME: Send READY_FOR_SM via GSUP */
- osmo_fsm_inst_state_chg(fi, SUB_PRES_VLR_S_WAIT_FOR_HLR,
- LU_TIMEOUT_LONG, 0);
-}
-
-static void sub_pres_vlr_fsm_wait_hlr(struct osmo_fsm_inst *fi, uint32_t event,
- void *data)
-{
- struct vlr_subscr *vsub = fi->priv;
-
- switch (event) {
- case SUB_PRES_VLR_E_READY_SM_CNF:
- vsub->ms_not_reachable_flag = false;
- break;
- case SUB_PRES_VLR_E_READY_SM_ERR:
- break;
- }
- osmo_fsm_inst_state_chg(fi, SUB_PRES_VLR_S_DONE, 0, 0);
- osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
-}
-
-static const struct osmo_fsm_state sub_pres_vlr_states[] = {
- [SUB_PRES_VLR_S_INIT] = {
- .in_event_mask = S(SUB_PRES_VLR_E_START),
- .out_state_mask = S(SUB_PRES_VLR_S_WAIT_FOR_HLR) |
- S(SUB_PRES_VLR_S_DONE),
- .name = OSMO_STRINGIFY(SUB_PRES_VLR_S_INIT),
- .action = sub_pres_vlr_fsm_init,
- },
- [SUB_PRES_VLR_S_WAIT_FOR_HLR] = {
- .in_event_mask = S(SUB_PRES_VLR_E_READY_SM_CNF) |
- S(SUB_PRES_VLR_E_READY_SM_ERR),
- .out_state_mask = S(SUB_PRES_VLR_S_DONE),
- .name = OSMO_STRINGIFY(SUB_PRES_VLR_S_WAIT_FOR_HLR),
- .action = sub_pres_vlr_fsm_wait_hlr,
- },
- [SUB_PRES_VLR_S_DONE] = {
- .name = OSMO_STRINGIFY(SUB_PRES_VLR_S_DONE),
- },
-};
-
-static struct osmo_fsm sub_pres_vlr_fsm = {
- .name = "sub_pres_vlr_fsm",
- .states = sub_pres_vlr_states,
- .num_states = ARRAY_SIZE(sub_pres_vlr_states),
- .allstate_event_mask = 0,
- .allstate_action = NULL,
- .log_subsys = DVLR,
- .event_names = sub_pres_vlr_event_names,
-};
-
-struct osmo_fsm_inst *sub_pres_vlr_fsm_start(struct osmo_fsm_inst *parent,
- struct vlr_subscr *vsub,
- uint32_t term_event)
-{
- struct osmo_fsm_inst *fi;
-
- fi = osmo_fsm_inst_alloc_child(&sub_pres_vlr_fsm, parent,
- term_event);
- if (!fi)
- return NULL;
-
- fi->priv = vsub;
- osmo_fsm_inst_dispatch(fi, SUB_PRES_VLR_E_START, NULL);
-
- return fi;
-}
-
-/***********************************************************************
- * Location_Update_Completion_VLR, TS 23.012 Chapter 4.1.2.3
- ***********************************************************************/
-
-enum lu_compl_vlr_state {
- LU_COMPL_VLR_S_INIT,
- LU_COMPL_VLR_S_WAIT_SUB_PRES,
- LU_COMPL_VLR_S_WAIT_IMEI,
- LU_COMPL_VLR_S_WAIT_IMEI_TMSI,
- LU_COMPL_VLR_S_WAIT_TMSI_CNF,
- LU_COMPL_VLR_S_DONE,
-};
-
-enum lu_compl_vlr_event {
- LU_COMPL_VLR_E_START,
- LU_COMPL_VLR_E_SUB_PRES_COMPL,
- LU_COMPL_VLR_E_IMEI_CHECK_ACK,
- LU_COMPL_VLR_E_IMEI_CHECK_NACK,
- LU_COMPL_VLR_E_NEW_TMSI_ACK,
-};
-
-static const struct value_string lu_compl_vlr_event_names[] = {
- OSMO_VALUE_STRING(LU_COMPL_VLR_E_START),
- OSMO_VALUE_STRING(LU_COMPL_VLR_E_SUB_PRES_COMPL),
- OSMO_VALUE_STRING(LU_COMPL_VLR_E_IMEI_CHECK_ACK),
- OSMO_VALUE_STRING(LU_COMPL_VLR_E_IMEI_CHECK_NACK),
- OSMO_VALUE_STRING(LU_COMPL_VLR_E_NEW_TMSI_ACK),
- { 0, NULL }
-};
-
-struct lu_compl_vlr_priv {
- struct vlr_subscr *vsub;
- void *msc_conn_ref;
- struct osmo_fsm_inst *sub_pres_vlr_fsm;
- uint32_t parent_event_success;
- uint32_t parent_event_failure;
- void *parent_event_data;
- enum vlr_fsm_result result;
- uint8_t cause;
- bool assign_tmsi;
-};
-
-static void _vlr_lu_compl_fsm_done(struct osmo_fsm_inst *fi,
- enum vlr_fsm_result result,
- uint8_t cause)
-{
- struct lu_compl_vlr_priv *lcvp = fi->priv;
- lcvp->result = result;
- lcvp->cause = cause;
- osmo_fsm_inst_state_chg(fi, LU_COMPL_VLR_S_DONE, 0, 0);
-}
-
-static void vlr_lu_compl_fsm_success(struct osmo_fsm_inst *fi)
-{
- struct lu_compl_vlr_priv *lcvp = fi->priv;
- struct vlr_subscr *vsub = lcvp->vsub;
- if (!vsub->lu_complete) {
- vsub->lu_complete = true;
- /* Balanced by vlr_subscr_rx_imsi_detach() */
- vlr_subscr_get(vsub);
- }
- _vlr_lu_compl_fsm_done(fi, VLR_FSM_RESULT_SUCCESS, 0);
-}
-
-static void vlr_lu_compl_fsm_failure(struct osmo_fsm_inst *fi, uint8_t cause)
-{
- struct lu_compl_vlr_priv *lcvp = fi->priv;
- lcvp->vsub->vlr->ops.tx_lu_rej(lcvp->msc_conn_ref, cause);
- _vlr_lu_compl_fsm_done(fi, VLR_FSM_RESULT_FAILURE, cause);
-}
-
-static void vlr_lu_compl_fsm_dispatch_result(struct osmo_fsm_inst *fi,
- uint32_t prev_state)
-{
- struct lu_compl_vlr_priv *lcvp = fi->priv;
- if (!fi->proc.parent) {
- LOGPFSML(fi, LOGL_ERROR, "No parent FSM\n");
- return;
- }
- osmo_fsm_inst_dispatch(fi->proc.parent,
- (lcvp->result == VLR_FSM_RESULT_SUCCESS)
- ? lcvp->parent_event_success
- : lcvp->parent_event_failure,
- &lcvp->cause);
-}
-
-static void lu_compl_vlr_init(struct osmo_fsm_inst *fi, uint32_t event,
- void *data)
-{
- struct lu_compl_vlr_priv *lcvp = fi->priv;
- struct vlr_subscr *vsub = lcvp->vsub;
- struct vlr_instance *vlr;
- OSMO_ASSERT(vsub);
- vlr = vsub->vlr;
- OSMO_ASSERT(vlr);
-
- OSMO_ASSERT(event == LU_COMPL_VLR_E_START);
-
- /* TODO: National Roaming restrictions? */
- /* TODO: Roaming restriction due to unsupported feature in subscriber
- * data? */
- /* TODO: Regional subscription restriction? */
- /* TODO: Administrative restriction of subscribres' access feature? */
- /* TODO: AccessRestrictuionData parameter available? */
- /* TODO: AccessRestrictionData permits RAT? */
- /* Node 1 */
- /* TODO: Autonomous CSG supported in VPLMN and allowed by HPLMN? */
- /* TODO: Hybrid Cel / CSG Cell */
- /* Node 2 */
- vsub->la_allowed = true;
- vsub->imsi_detached_flag = false;
- /* Start Subscriber_Present_VLR Procedure */
- osmo_fsm_inst_state_chg(fi, LU_COMPL_VLR_S_WAIT_SUB_PRES,
- LU_TIMEOUT_LONG, 0);
-
- lcvp->sub_pres_vlr_fsm = sub_pres_vlr_fsm_start(fi, vsub,
- LU_COMPL_VLR_E_SUB_PRES_COMPL);
-
-}
-
-static void lu_compl_vlr_new_tmsi(struct osmo_fsm_inst *fi)
-{
- struct lu_compl_vlr_priv *lcvp = fi->priv;
- struct vlr_subscr *vsub = lcvp->vsub;
- struct vlr_instance *vlr = vsub->vlr;
-
- LOGPFSM(fi, "%s()\n", __func__);
-
- if (vlr_subscr_alloc_tmsi(vsub)) {
- vlr_lu_compl_fsm_failure(fi,
- GSM48_REJECT_SRV_OPT_TMP_OUT_OF_ORDER);
- return;
- }
-
- osmo_fsm_inst_state_chg(fi, LU_COMPL_VLR_S_WAIT_TMSI_CNF,
- vlr_timer(vlr, 3250), 3250);
-
- vlr->ops.tx_lu_acc(lcvp->msc_conn_ref, vsub->tmsi_new);
-}
-
-/* After completion of Subscriber_Present_VLR */
-static void lu_compl_vlr_wait_subscr_pres(struct osmo_fsm_inst *fi,
- uint32_t event,
- void *data)
-{
- struct lu_compl_vlr_priv *lcvp = fi->priv;
- struct vlr_subscr *vsub = lcvp->vsub;
- struct vlr_instance *vlr = vsub->vlr;
-
- OSMO_ASSERT(event == LU_COMPL_VLR_E_SUB_PRES_COMPL);
-
- lcvp->sub_pres_vlr_fsm = NULL;
-
- /* TODO: Trace_Subscriber_Activity_VLR */
-
- if (vlr->cfg.check_imei_rqd) {
- /* Check IMEI VLR */
- osmo_fsm_inst_state_chg(fi,
- lcvp->assign_tmsi ?
- LU_COMPL_VLR_S_WAIT_IMEI_TMSI
- : LU_COMPL_VLR_S_WAIT_IMEI,
- vlr_timer(vlr, 3270), 3270);
- vlr->ops.tx_id_req(lcvp->msc_conn_ref, GSM_MI_TYPE_IMEI);
- return;
- }
-
- /* Do we need to allocate a TMSI? */
- if (lcvp->assign_tmsi) {
- lu_compl_vlr_new_tmsi(fi);
- return;
- }
-
- /* Location Updating Accept */
- vlr->ops.tx_lu_acc(lcvp->msc_conn_ref, GSM_RESERVED_TMSI);
- vlr_lu_compl_fsm_success(fi);
-}
-
-/* Waiting for completion of CHECK_IMEI_VLR */
-static void lu_compl_vlr_wait_imei(struct osmo_fsm_inst *fi, uint32_t event,
- void *data)
-{
- struct lu_compl_vlr_priv *lcvp = fi->priv;
- struct vlr_subscr *vsub = lcvp->vsub;
- struct vlr_instance *vlr = vsub->vlr;
-
- switch (event) {
- case LU_COMPL_VLR_E_IMEI_CHECK_ACK:
- if (!vsub->imei[0]) {
- /* Abort: Do nothing */
- vlr_lu_compl_fsm_failure(fi,
- GSM48_REJECT_PROTOCOL_ERROR);
- return;
- }
- /* Pass */
- break;
-
- case LU_COMPL_VLR_E_IMEI_CHECK_NACK:
- vlr_lu_compl_fsm_failure(fi, GSM48_REJECT_ILLEGAL_ME);
- /* FIXME: IMEI Check Fail to VLR Application (Detach IMSI VLR) */
- return;
- }
-
- /* IMEI is available. Allocate TMSI if needed. */
- if (lcvp->assign_tmsi) {
- if (fi->state != LU_COMPL_VLR_S_WAIT_IMEI_TMSI)
- LOGPFSML(fi, LOGL_ERROR,
- "TMSI required, expected to be in state"
- " LU_COMPL_VLR_S_WAIT_IMEI_TMSI,"
- " am in %s instead\n",
- osmo_fsm_state_name(fi->fsm, fi->state));
- /* Logged an error, continue anyway. */
-
- lu_compl_vlr_new_tmsi(fi);
-
- /* Wait for TMSI ack */
- return;
- }
-
- /* No TMSI needed, accept now. */
- vlr->ops.tx_lu_acc(lcvp->msc_conn_ref, GSM_RESERVED_TMSI);
- vlr_lu_compl_fsm_success(fi);
-}
-
-/* Waiting for TMSI confirmation */
-static void lu_compl_vlr_wait_tmsi(struct osmo_fsm_inst *fi, uint32_t event,
- void *data)
-{
- struct lu_compl_vlr_priv *lcvp = fi->priv;
- struct vlr_subscr *vsub = lcvp->vsub;
-
- OSMO_ASSERT(event == LU_COMPL_VLR_E_NEW_TMSI_ACK);
-
- if (!vsub || vsub->tmsi_new == GSM_RESERVED_TMSI) {
- LOGPFSML(fi, LOGL_ERROR, "TMSI Realloc Compl implies that"
- " the subscriber has a new TMSI allocated, but"
- " the new TMSI is unset.\n");
- vlr_lu_compl_fsm_failure(fi, GSM48_REJECT_NETWORK_FAILURE);
- return;
- }
-
- vsub->tmsi = vsub->tmsi_new;
- vsub->tmsi_new = GSM_RESERVED_TMSI;
-
- vlr_lu_compl_fsm_success(fi);
-}
-
-static const struct osmo_fsm_state lu_compl_vlr_states[] = {
- [LU_COMPL_VLR_S_INIT] = {
- .in_event_mask = S(LU_COMPL_VLR_E_START),
- .out_state_mask = S(LU_COMPL_VLR_S_DONE) |
- S(LU_COMPL_VLR_S_WAIT_SUB_PRES) |
- S(LU_COMPL_VLR_S_WAIT_IMEI),
- .name = OSMO_STRINGIFY(LU_COMPL_VLR_S_INIT),
- .action = lu_compl_vlr_init,
- },
- [LU_COMPL_VLR_S_WAIT_SUB_PRES] = {
- .in_event_mask = S(LU_COMPL_VLR_E_SUB_PRES_COMPL),
- .out_state_mask = S(LU_COMPL_VLR_S_WAIT_IMEI) |
- S(LU_COMPL_VLR_S_WAIT_IMEI_TMSI) |
- S(LU_COMPL_VLR_S_WAIT_TMSI_CNF) |
- S(LU_COMPL_VLR_S_DONE),
- .name = OSMO_STRINGIFY(LU_COMPL_VLR_S_WAIT_SUB_PRES),
- .action = lu_compl_vlr_wait_subscr_pres,
- },
- [LU_COMPL_VLR_S_WAIT_IMEI] = {
- .in_event_mask = S(LU_COMPL_VLR_E_IMEI_CHECK_ACK) |
- S(LU_COMPL_VLR_E_IMEI_CHECK_NACK),
- .out_state_mask = S(LU_COMPL_VLR_S_DONE),
- .name = OSMO_STRINGIFY(LU_COMPL_VLR_S_WAIT_IMEI),
- .action = lu_compl_vlr_wait_imei,
- },
- [LU_COMPL_VLR_S_WAIT_IMEI_TMSI] = {
- .in_event_mask = S(LU_COMPL_VLR_E_IMEI_CHECK_ACK) |
- S(LU_COMPL_VLR_E_IMEI_CHECK_NACK),
- .out_state_mask = S(LU_COMPL_VLR_S_DONE) |
- S(LU_COMPL_VLR_S_WAIT_TMSI_CNF),
- .name = OSMO_STRINGIFY(LU_COMPL_VLR_S_WAIT_IMEI_TMSI),
- .action = lu_compl_vlr_wait_imei,
- },
- [LU_COMPL_VLR_S_WAIT_TMSI_CNF] = {
- .in_event_mask = S(LU_COMPL_VLR_E_NEW_TMSI_ACK),
- .out_state_mask = S(LU_COMPL_VLR_S_DONE),
- .name = OSMO_STRINGIFY(LU_COMPL_VLR_S_WAIT_TMSI_CNF),
- .action = lu_compl_vlr_wait_tmsi,
- },
- [LU_COMPL_VLR_S_DONE] = {
- .name = OSMO_STRINGIFY(LU_COMPL_VLR_S_DONE),
- .onenter = vlr_lu_compl_fsm_dispatch_result,
- },
-};
-
-static struct osmo_fsm lu_compl_vlr_fsm = {
- .name = "lu_compl_vlr_fsm",
- .states = lu_compl_vlr_states,
- .num_states = ARRAY_SIZE(lu_compl_vlr_states),
- .allstate_event_mask = 0,
- .allstate_action = NULL,
- .log_subsys = DVLR,
- .event_names = lu_compl_vlr_event_names,
-};
-
-struct osmo_fsm_inst *
-lu_compl_vlr_proc_alloc(struct osmo_fsm_inst *parent,
- struct vlr_subscr *vsub,
- void *msc_conn_ref,
- uint32_t parent_event_success,
- uint32_t parent_event_failure,
- bool assign_tmsi)
-{
- struct osmo_fsm_inst *fi;
- struct lu_compl_vlr_priv *lcvp;
-
- fi = osmo_fsm_inst_alloc_child(&lu_compl_vlr_fsm, parent,
- parent_event_failure);
- if (!fi)
- return NULL;
-
- lcvp = talloc_zero(fi, struct lu_compl_vlr_priv);
- lcvp->vsub = vsub;
- lcvp->msc_conn_ref = msc_conn_ref;
- lcvp->parent_event_success = parent_event_success;
- lcvp->parent_event_failure = parent_event_failure;
- lcvp->assign_tmsi = assign_tmsi;
- fi->priv = lcvp;
-
- return fi;
-}
-
-
-/***********************************************************************
- * Update_Location_Area_VLR, TS 23.012 Chapter 4.1.2.1
- ***********************************************************************/
-
-static const struct value_string fsm_lu_event_names[] = {
- OSMO_VALUE_STRING(VLR_ULA_E_UPDATE_LA),
- OSMO_VALUE_STRING(VLR_ULA_E_SEND_ID_ACK),
- OSMO_VALUE_STRING(VLR_ULA_E_SEND_ID_NACK),
- OSMO_VALUE_STRING(VLR_ULA_E_AUTH_RES),
- OSMO_VALUE_STRING(VLR_ULA_E_CIPH_RES),
- OSMO_VALUE_STRING(VLR_ULA_E_ID_IMSI),
- OSMO_VALUE_STRING(VLR_ULA_E_ID_IMEI),
- OSMO_VALUE_STRING(VLR_ULA_E_ID_IMEISV),
- OSMO_VALUE_STRING(VLR_ULA_E_HLR_LU_RES),
- OSMO_VALUE_STRING(VLR_ULA_E_UPD_HLR_COMPL),
- OSMO_VALUE_STRING(VLR_ULA_E_LU_COMPL_SUCCESS),
- OSMO_VALUE_STRING(VLR_ULA_E_LU_COMPL_FAILURE),
- OSMO_VALUE_STRING(VLR_ULA_E_NEW_TMSI_ACK),
- { 0, NULL }
-};
-
-struct lu_fsm_priv {
- struct vlr_instance *vlr;
- struct vlr_subscr *vsub;
- void *msc_conn_ref;
- struct osmo_fsm_inst *upd_hlr_vlr_fsm;
- struct osmo_fsm_inst *lu_compl_vlr_fsm;
- uint32_t parent_event_success;
- uint32_t parent_event_failure;
- void *parent_event_data;
- enum vlr_fsm_result result;
- uint8_t rej_cause;
-
- enum vlr_lu_type type;
- bool lu_by_tmsi;
- char imsi[16];
- uint32_t tmsi;
- struct osmo_location_area_id old_lai;
- struct osmo_location_area_id new_lai;
- bool authentication_required;
- enum vlr_ciph ciphering_required;
- bool is_r99;
- bool is_utran;
- bool assign_tmsi;
-};
-
-
-/* Determine if given location area is served by this VLR */
-static bool lai_in_this_vlr(struct vlr_instance *vlr,
- const struct osmo_location_area_id *lai)
-{
- /* TODO: VLR needs to keep a locally configued list of LAIs */
- return true;
-}
-
-/* Determine if authentication is required */
-static bool is_auth_required(struct lu_fsm_priv *lfp)
-{
- /* The cases where the authentication procedure should be used
- * are defined in 3GPP TS 33.102 */
- /* For now we use a default value passed in to vlr_lu_fsm(). */
- return lfp->authentication_required
- || (lfp->ciphering_required != VLR_CIPH_NONE);
-}
-
-/* Determine if ciphering is required */
-static bool is_ciph_required(struct lu_fsm_priv *lfp)
-{
- return lfp->ciphering_required != VLR_CIPH_NONE;
-}
-
-/* Determine if a HLR Update is required */
-static bool hlr_update_needed(struct vlr_subscr *vsub)
-{
- /* TODO: properly decide this, rather than always assuming we
- * need to update the HLR. */
- return true;
-}
-
-static void lu_fsm_dispatch_result(struct osmo_fsm_inst *fi,
- uint32_t prev_state)
-{
- struct lu_fsm_priv *lfp = fi->priv;
- if (!fi->proc.parent) {
- LOGPFSML(fi, LOGL_ERROR, "No parent FSM\n");
- return;
- }
- osmo_fsm_inst_dispatch(fi->proc.parent,
- (lfp->result == VLR_FSM_RESULT_SUCCESS)
- ? lfp->parent_event_success
- : lfp->parent_event_failure,
- lfp->parent_event_data);
-}
-
-static void _lu_fsm_done(struct osmo_fsm_inst *fi,
- enum vlr_fsm_result result)
-{
- struct lu_fsm_priv *lfp = fi->priv;
- lfp->result = result;
- osmo_fsm_inst_state_chg(fi, VLR_ULA_S_DONE, 0, 0);
-}
-
-static void lu_fsm_success(struct osmo_fsm_inst *fi)
-{
- _lu_fsm_done(fi, VLR_FSM_RESULT_SUCCESS);
-}
-
-static void lu_fsm_failure(struct osmo_fsm_inst *fi, uint8_t rej_cause)
-{
- struct lu_fsm_priv *lfp = fi->priv;
- if (rej_cause)
- lfp->vlr->ops.tx_lu_rej(lfp->msc_conn_ref, rej_cause);
- _lu_fsm_done(fi, VLR_FSM_RESULT_FAILURE);
-}
-
-static void vlr_loc_upd_start_lu_compl_fsm(struct osmo_fsm_inst *fi)
-{
- struct lu_fsm_priv *lfp = fi->priv;
- lfp->lu_compl_vlr_fsm =
- lu_compl_vlr_proc_alloc(fi, lfp->vsub, lfp->msc_conn_ref,
- VLR_ULA_E_LU_COMPL_SUCCESS,
- VLR_ULA_E_LU_COMPL_FAILURE,
- lfp->assign_tmsi);
-
- osmo_fsm_inst_dispatch(lfp->lu_compl_vlr_fsm, LU_COMPL_VLR_E_START, NULL);
-}
-
-static void lu_fsm_discard_lu_compl_fsm(struct osmo_fsm_inst *fi)
-{
- struct lu_fsm_priv *lfp = fi->priv;
- if (!lfp->lu_compl_vlr_fsm)
- return;
- osmo_fsm_inst_term(lfp->lu_compl_vlr_fsm, OSMO_FSM_TERM_PARENT, NULL);
-}
-
-/* 4.1.2.1 Node 4 */
-static void vlr_loc_upd_node_4(struct osmo_fsm_inst *fi)
-{
- struct lu_fsm_priv *lfp = fi->priv;
- struct vlr_subscr *vsub = lfp->vsub;
- bool hlr_unknown = false;
-
- LOGPFSM(fi, "%s()\n", __func__);
-
- if (hlr_unknown) {
- /* FIXME: Delete subscriber record */
- /* LU REJ: Roaming not allowed */
- lu_fsm_failure(fi, GSM48_REJECT_ROAMING_NOT_ALLOWED);
- } else {
- /* Update_HLR_VLR */
- osmo_fsm_inst_state_chg(fi, VLR_ULA_S_WAIT_HLR_UPD,
- LU_TIMEOUT_LONG, 0);
- lfp->upd_hlr_vlr_fsm =
- upd_hlr_vlr_proc_start(fi, vsub, VLR_ULA_E_UPD_HLR_COMPL);
- }
-}
-
-/* 4.1.2.1 Node B */
-static void vlr_loc_upd_node_b(struct osmo_fsm_inst *fi)
-{
- LOGPFSM(fi, "%s()\n", __func__);
-
- /* OsmoHLR does not support PgA, neither stores the IMEISV, so we have no need to update the HLR
- * with either. TODO: depend on actual HLR configuration. See 3GPP TS 23.012 Release 14, process
- * Update_Location_Area_VLR (ULA_VLR2). */
- if (0) { /* IMEISV or PgA to send */
- vlr_loc_upd_node_4(fi);
- } else {
- /* Location_Update_Completion */
- osmo_fsm_inst_state_chg(fi, VLR_ULA_S_WAIT_LU_COMPL,
- LU_TIMEOUT_LONG, 0);
- vlr_loc_upd_start_lu_compl_fsm(fi);
- }
-}
-
-/* Non-standard: after Ciphering Mode Complete (or no ciph required) */
-static void vlr_loc_upd_post_ciph(struct osmo_fsm_inst *fi)
-{
- struct lu_fsm_priv *lfp = fi->priv;
- struct vlr_subscr *vsub = lfp->vsub;
-
- LOGPFSM(fi, "%s()\n", __func__);
-
- OSMO_ASSERT(vsub);
-
- if (lfp->is_utran) {
- int rc;
- rc = lfp->vlr->ops.tx_common_id(lfp->msc_conn_ref);
- if (rc)
- LOGPFSML(fi, LOGL_ERROR,
- "Error while sending Common ID (%d)\n", rc);
- }
-
- vsub->conf_by_radio_contact_ind = true;
- /* Update LAI */
- vsub->cgi.lai = lfp->new_lai;
- vsub->dormant_ind = false;
- vsub->cancel_loc_rx = false;
- if (hlr_update_needed(vsub)) {
- vlr_loc_upd_node_4(fi);
- } else {
- /* TODO: ADD Support */
- /* TODO: Node A: PgA Support */
- vlr_loc_upd_node_b(fi);
- }
-}
-
-/* 4.1.2.1 after Authentication successful (or no auth rqd) */
-static void vlr_loc_upd_post_auth(struct osmo_fsm_inst *fi)
-{
- struct lu_fsm_priv *lfp = fi->priv;
- struct vlr_subscr *vsub = lfp->vsub;
-
- LOGPFSM(fi, "%s()\n", __func__);
-
- OSMO_ASSERT(vsub);
-
- if (!is_ciph_required(lfp)) {
- vlr_loc_upd_post_ciph(fi);
- return;
- }
-
- if (vlr_set_ciph_mode(vsub->vlr, fi, lfp->msc_conn_ref,
- lfp->ciphering_required,
- vsub->vlr->cfg.retrieve_imeisv_ciphered)) {
- LOGPFSML(fi, LOGL_ERROR,
- "Failed to send Ciphering Mode Command\n");
- vlr_lu_compl_fsm_failure(fi, GSM48_REJECT_NETWORK_FAILURE);
- return;
- }
-
- osmo_fsm_inst_state_chg(fi, VLR_ULA_S_WAIT_CIPH, LU_TIMEOUT_LONG, 0);
-}
-
-static void vlr_loc_upd_node1(struct osmo_fsm_inst *fi)
-{
- struct lu_fsm_priv *lfp = fi->priv;
- struct vlr_subscr *vsub = lfp->vsub;
-
- LOGPFSM(fi, "%s()\n", __func__);
-
- OSMO_ASSERT(vsub);
-
- if (is_auth_required(lfp)) {
- /* Authenticate_VLR */
- osmo_fsm_inst_state_chg(fi, VLR_ULA_S_WAIT_AUTH,
- LU_TIMEOUT_LONG, 0);
- vsub->auth_fsm = auth_fsm_start(lfp->vsub, fi->log_level,
- fi, VLR_ULA_E_AUTH_RES,
- lfp->is_r99,
- lfp->is_utran);
- } else {
- /* no need for authentication */
- vlr_loc_upd_post_auth(fi);
- }
-}
-
-static void vlr_loc_upd_want_imsi(struct osmo_fsm_inst *fi)
-{
- struct lu_fsm_priv *lfp = fi->priv;
- struct vlr_instance *vlr = lfp->vlr;
-
- LOGPFSM(fi, "%s()\n", __func__);
-
- OSMO_ASSERT(lfp->vsub);
-
- /* Obtain_IMSI_VLR */
- osmo_fsm_inst_state_chg(fi, VLR_ULA_S_WAIT_IMSI,
- vlr_timer(vlr, 3270), 3270);
- vlr->ops.tx_id_req(lfp->msc_conn_ref, GSM_MI_TYPE_IMSI);
- /* will continue at vlr_loc_upd_node1() once IMSI arrives */
-}
-
-static int assoc_lfp_with_sub(struct osmo_fsm_inst *fi, struct vlr_subscr *vsub)
-{
- struct lu_fsm_priv *lfp = fi->priv;
- struct vlr_instance *vlr = lfp->vlr;
-
- if (vsub->lu_fsm) {
- LOGPFSML(fi, LOGL_ERROR,
- "A Location Updating process is already pending for"
- " this subscriber. Aborting.\n");
- /* Also get rid of the other pending LU attempt? */
- /*lu_fsm_failure(vsub->lu_fsm, GSM48_REJECT_CONGESTION);*/
- lu_fsm_failure(fi, GSM48_REJECT_CONGESTION);
- return -EINVAL;
- }
- vsub->lu_fsm = fi;
- vsub->msc_conn_ref = lfp->msc_conn_ref;
- /* FIXME: send new LAC to HLR? */
- vsub->lac = lfp->new_lai.lac;
- lfp->vsub = vsub;
- /* Tell MSC to associate this subscriber with the given
- * connection */
- vlr->ops.subscr_assoc(lfp->msc_conn_ref, lfp->vsub);
- return 0;
-}
-
-static const char *lai_name(struct osmo_location_area_id *lai)
-{
- static char buf[64];
- snprintf(buf, sizeof(buf),"MCC:%u, MNC:%u, LAC:%u",
- lai->plmn.mcc, lai->plmn.mnc, lai->lac);
- return buf;
-}
-
-static int _lu_fsm_associate_vsub(struct osmo_fsm_inst *fi)
-{
- struct lu_fsm_priv *lfp = fi->priv;
- struct vlr_instance *vlr = lfp->vlr;
- struct vlr_subscr *vsub = NULL;
-
- if (!lfp->imsi[0]) {
- /* TMSI was used */
- lfp->lu_by_tmsi = true;
- /* TMSI clash: if a different subscriber already has this TMSI,
- * we will find that other subscriber in the VLR. So the IMSIs
- * would mismatch, but we don't know about it. Theoretically,
- * an authentication process would thwart any attempt to use
- * someone else's TMSI.
- * TODO: Otherwise we can ask for the IMSI and verify that it
- * matches the IMSI on record. */
- vsub = vlr_subscr_find_or_create_by_tmsi(vlr, lfp->tmsi, NULL);
-
- if (!vsub) {
- LOGPFSML(fi, LOGL_ERROR, "VLR subscriber allocation failed\n");
- lu_fsm_failure(fi, GSM48_REJECT_SRV_OPT_TMP_OUT_OF_ORDER);
- return -1;
- }
-
- vsub->sub_dataconf_by_hlr_ind = false;
- if (assoc_lfp_with_sub(fi, vsub)) {
- vlr_subscr_put(vsub);
- return -1; /* error, fsm failure invoked in assoc_lfp_with_sub() */
- }
- vlr_subscr_put(vsub);
- } else {
- /* IMSI was used */
- vsub = vlr_subscr_find_or_create_by_imsi(vlr, lfp->imsi, NULL);
-
- if (!vsub) {
- LOGPFSML(fi, LOGL_ERROR, "VLR subscriber allocation failed\n");
- lu_fsm_failure(fi, GSM48_REJECT_SRV_OPT_TMP_OUT_OF_ORDER);
- vlr_subscr_put(vsub);
- return -1;
- }
-
- vsub->sub_dataconf_by_hlr_ind = false;
- if (assoc_lfp_with_sub(fi, vsub)) {
- vlr_subscr_put(vsub);
- return -1; /* error, fsm failure invoked in assoc_lfp_with_sub() */
- }
- vlr_subscr_put(vsub);
- }
- return 0;
-}
-
-/* 4.1.2.1: Subscriber (via MSC/SGSN) requests location update */
-static void _start_lu_main(struct osmo_fsm_inst *fi)
-{
- struct lu_fsm_priv *lfp = fi->priv;
- struct vlr_instance *vlr = lfp->vlr;
-
- /* TODO: PUESBINE related handling */
-
- /* Is previous LAI in this VLR? */
- if (!lai_in_this_vlr(vlr, &lfp->old_lai)) {
-#if 0
- /* FIXME: check previous VLR, (3) */
- osmo_fsm_inst_state_chg(fi, VLR_ULA_S_WAIT_PVLR,
- LU_TIMEOUT_LONG, 0);
- return;
-#endif
- LOGPFSML(fi, LOGL_NOTICE, "LAI change from %s,"
- " but checking previous VLR not implemented\n",
- lai_name(&lfp->old_lai));
- }
-
- /* If this is a TMSI based LU, we may not have the IMSI. Make sure that
- * we know the IMSI, either on record, or request it. */
- if (!lfp->vsub->imsi[0])
- vlr_loc_upd_want_imsi(fi);
- else
- vlr_loc_upd_node1(fi);
-}
-
-static void lu_fsm_idle(struct osmo_fsm_inst *fi, uint32_t event,
- void *data)
-{
- struct lu_fsm_priv *lfp = fi->priv;
- struct vlr_instance *vlr = lfp->vlr;
-
- OSMO_ASSERT(event == VLR_ULA_E_UPDATE_LA);
-
- if (_lu_fsm_associate_vsub(fi))
- return; /* error. FSM already terminated. */
-
- OSMO_ASSERT(lfp->vsub);
-
- /* See 3GPP TS 23.012, procedure Retrieve_IMEISV_If_Required */
- if ((!vlr->cfg.retrieve_imeisv_early)
- || (lfp->type == VLR_LU_TYPE_PERIODIC && lfp->vsub->imeisv[0])) {
- /* R_IMEISV_IR1 passed */
- _start_lu_main(fi);
- } else {
- vlr->ops.tx_id_req(lfp->msc_conn_ref, GSM_MI_TYPE_IMEISV);
- osmo_fsm_inst_state_chg(fi, VLR_ULA_S_WAIT_IMEISV,
- vlr_timer(vlr, 3270), 3270);
- }
-}
-
-static void lu_fsm_wait_imeisv(struct osmo_fsm_inst *fi, uint32_t event,
- void *data)
-{
- switch (event) {
- case VLR_ULA_E_ID_IMEISV:
- /* IMEISV was copied in vlr_subscr_rx_id_resp(), and that's
- * where we received this event from. */
- _start_lu_main(fi);
- break;
- default:
- LOGPFSML(fi, LOGL_ERROR, "event without effect: %s\n",
- osmo_fsm_event_name(fi->fsm, event));
- break;
- }
-}
-
-/* Wait for response from Send_Identification to PVLR */
-static void lu_fsm_wait_pvlr(struct osmo_fsm_inst *fi, uint32_t event,
- void *data)
-{
- switch (event) {
- case VLR_ULA_E_SEND_ID_ACK:
- vlr_loc_upd_node1(fi);
- break;
- case VLR_ULA_E_SEND_ID_NACK:
- vlr_loc_upd_want_imsi(fi);
- break;
- default:
- LOGPFSML(fi, LOGL_ERROR, "event without effect: %s\n",
- osmo_fsm_event_name(fi->fsm, event));
- break;
- }
-}
-
-/* Wait for result of Authenticate_VLR procedure */
-static void lu_fsm_wait_auth(struct osmo_fsm_inst *fi, uint32_t event,
- void *data)
-{
- struct lu_fsm_priv *lfp = fi->priv;
- enum vlr_auth_fsm_result *res = data;
- uint8_t rej_cause = 0;
-
- OSMO_ASSERT(event == VLR_ULA_E_AUTH_RES);
-
- lfp->upd_hlr_vlr_fsm = NULL;
-
- if (res) {
- switch (*res) {
- case VLR_AUTH_RES_PASSED:
- /* Result == Pass */
- vlr_loc_upd_post_auth(fi);
- return;
- case VLR_AUTH_RES_ABORTED:
- /* go to Idle with no response */
- rej_cause = 0;
- break;
- case VLR_AUTH_RES_UNKNOWN_SUBSCR:
- /* FIXME: delete subscribe record */
- rej_cause = GSM48_REJECT_IMSI_UNKNOWN_IN_HLR;
- break;
- case VLR_AUTH_RES_AUTH_FAILED:
- /* cause = illegal subscriber */
- rej_cause = GSM48_REJECT_ILLEGAL_MS;
- break;
- case VLR_AUTH_RES_PROC_ERR:
- /* cause = system failure */
- rej_cause = GSM48_REJECT_NETWORK_FAILURE;
- break;
- default:
- LOGPFSML(fi, LOGL_ERROR, "event without effect: %s\n",
- osmo_fsm_event_name(fi->fsm, event));
- break;
- }
- } else
- rej_cause = GSM48_REJECT_NETWORK_FAILURE;
-
- lu_fsm_failure(fi, rej_cause);
-}
-
-static void lu_fsm_wait_ciph(struct osmo_fsm_inst *fi, uint32_t event,
- void *data)
-{
- struct lu_fsm_priv *lfp = fi->priv;
- struct vlr_subscr *vsub = lfp->vsub;
- struct vlr_ciph_result res = { .cause = VLR_CIPH_REJECT };
-
- OSMO_ASSERT(event == VLR_ULA_E_CIPH_RES);
-
- if (!data)
- LOGPFSML(fi, LOGL_ERROR, "invalid ciphering result: NULL\n");
- else
- res = *(struct vlr_ciph_result*)data;
-
- switch (res.cause) {
- case VLR_CIPH_COMPL:
- break;
- case VLR_CIPH_REJECT:
- LOGPFSM(fi, "ciphering rejected\n");
- lu_fsm_failure(fi, GSM48_REJECT_INVALID_MANDANTORY_INF);
- return;
- default:
- LOGPFSML(fi, LOGL_ERROR, "invalid ciphering result: %d\n",
- res.cause);
- lu_fsm_failure(fi, GSM48_REJECT_INVALID_MANDANTORY_INF);
- return;
- }
-
- if (res.imeisv) {
- LOGPFSM(fi, "got IMEISV: %s\n", res.imeisv);
- vlr_subscr_set_imeisv(vsub, res.imeisv);
- }
- vlr_loc_upd_post_ciph(fi);
-}
-
-static void lu_fsm_wait_imsi(struct osmo_fsm_inst *fi, uint32_t event,
- void *data)
-{
- struct lu_fsm_priv *lfp = fi->priv;
- struct vlr_subscr *vsub = lfp->vsub;
- char *mi_string = data;
-
- switch (event) {
- case VLR_ULA_E_ID_IMSI:
- vlr_subscr_set_imsi(vsub, mi_string);
- vlr_loc_upd_node1(fi);
- break;
- default:
- LOGPFSML(fi, LOGL_ERROR, "event without effect: %s\n",
- osmo_fsm_event_name(fi->fsm, event));
- break;
- }
-}
-
-/* At the end of Update_HLR_VLR */
-static void lu_fsm_wait_hlr_ul_res(struct osmo_fsm_inst *fi, uint32_t event,
- void *data)
-{
- struct lu_fsm_priv *lfp = fi->priv;
-
- switch (event) {
- case VLR_ULA_E_HLR_LU_RES:
- /* pass-through this event to Update_HLR_VLR */
- if (data == NULL)
- osmo_fsm_inst_dispatch(lfp->upd_hlr_vlr_fsm, UPD_HLR_VLR_E_UPD_LOC_ACK, NULL);
- else
- osmo_fsm_inst_dispatch(lfp->upd_hlr_vlr_fsm, UPD_HLR_VLR_E_UPD_LOC_NACK, data);
- break;
- case VLR_ULA_E_UPD_HLR_COMPL:
- if (data == NULL) {
- /* successful case */
- osmo_fsm_inst_state_chg(fi, VLR_ULA_S_WAIT_LU_COMPL,
- LU_TIMEOUT_LONG, 0);
- vlr_loc_upd_start_lu_compl_fsm(fi);
- /* continue in MSC ?!? */
- } else {
- /* unsuccessful case */
- enum gsm48_gmm_cause cause =
- *(enum gsm48_gmm_cause *)data;
- /* Ignoring standalone mode for now. */
- if (0 /* procedure_error && vlr->cfg.standalone_mode */) {
- osmo_fsm_inst_state_chg(fi,
- VLR_ULA_S_WAIT_LU_COMPL_STANDALONE,
- LU_TIMEOUT_LONG, 0);
- vlr_loc_upd_start_lu_compl_fsm(fi);
- } else {
- lu_fsm_failure(fi, cause);
- }
- }
- break;
- default:
- LOGPFSML(fi, LOGL_ERROR, "event without effect: %s\n",
- osmo_fsm_event_name(fi->fsm, event));
- break;
- }
-}
-
-/* Wait for end of Location_Update_Completion_VLR */
-static void lu_fsm_wait_lu_compl(struct osmo_fsm_inst *fi, uint32_t event,
- void *data)
-{
- struct lu_fsm_priv *lfp = fi->priv;
- uint8_t cause;
-
- switch (event) {
- case VLR_ULA_E_NEW_TMSI_ACK:
- osmo_fsm_inst_dispatch(lfp->lu_compl_vlr_fsm,
- LU_COMPL_VLR_E_NEW_TMSI_ACK, NULL);
- break;
- case VLR_ULA_E_ID_IMEI:
- osmo_fsm_inst_dispatch(lfp->lu_compl_vlr_fsm,
- LU_COMPL_VLR_E_IMEI_CHECK_ACK, NULL);
- break;
- case VLR_ULA_E_LU_COMPL_SUCCESS:
- lu_fsm_discard_lu_compl_fsm(fi);
-
- /* Update Register */
- /* TODO: Set_Notification_Type 23.078 */
- /* TODO: Notify_gsmSCF 23.078 */
- /* TODO: Authenticated Radio Contact Established -> ARC */
- lu_fsm_success(fi);
- break;
- case VLR_ULA_E_LU_COMPL_FAILURE:
- cause = GSM48_REJECT_NETWORK_FAILURE;
- if (data)
- cause = *(uint8_t*)data;
- lu_fsm_discard_lu_compl_fsm(fi);
- lu_fsm_failure(fi, cause);
- break;
- default:
- LOGPFSML(fi, LOGL_ERROR, "event without effect: %s\n",
- osmo_fsm_event_name(fi->fsm, event));
- break;
- }
-}
-
-/* Wait for end of Location_Update_Completion_VLR (standalone case) */
-static void lu_fsm_wait_lu_compl_standalone(struct osmo_fsm_inst *fi,
- uint32_t event, void *data)
-{
- struct lu_fsm_priv *lfp = fi->priv;
- struct vlr_subscr *vsub = lfp->vsub;
- uint8_t cause;
-
- switch (event) {
- case VLR_ULA_E_NEW_TMSI_ACK:
- osmo_fsm_inst_dispatch(lfp->lu_compl_vlr_fsm,
- LU_COMPL_VLR_E_NEW_TMSI_ACK, NULL);
- break;
- case VLR_ULA_E_LU_COMPL_SUCCESS:
- lu_fsm_discard_lu_compl_fsm(fi);
- vsub->sub_dataconf_by_hlr_ind = false;
- lu_fsm_success(fi);
- break;
- case VLR_ULA_E_LU_COMPL_FAILURE:
- vsub->sub_dataconf_by_hlr_ind = false;
- cause = GSM48_REJECT_NETWORK_FAILURE;
- if (data)
- cause = *(uint8_t*)data;
- lu_fsm_discard_lu_compl_fsm(fi);
- lu_fsm_failure(fi, cause);
- break;
- default:
- LOGPFSML(fi, LOGL_ERROR, "event without effect: %s\n",
- osmo_fsm_event_name(fi->fsm, event));
- break;
- }
-}
-
-static const struct osmo_fsm_state vlr_lu_fsm_states[] = {
- [VLR_ULA_S_IDLE] = {
- .in_event_mask = S(VLR_ULA_E_UPDATE_LA),
- .out_state_mask = S(VLR_ULA_S_WAIT_IMEISV) |
- S(VLR_ULA_S_WAIT_PVLR) |
- S(VLR_ULA_S_WAIT_IMSI) |
- S(VLR_ULA_S_WAIT_AUTH) |
- S(VLR_ULA_S_WAIT_HLR_UPD) |
- S(VLR_ULA_S_DONE),
- .name = OSMO_STRINGIFY(VLR_ULA_S_IDLE),
- .action = lu_fsm_idle,
- },
- [VLR_ULA_S_WAIT_IMEISV] = {
- .in_event_mask = S(VLR_ULA_E_ID_IMEISV),
- .out_state_mask = S(VLR_ULA_S_WAIT_PVLR) |
- S(VLR_ULA_S_WAIT_IMSI) |
- S(VLR_ULA_S_WAIT_AUTH) |
- S(VLR_ULA_S_WAIT_HLR_UPD) |
- S(VLR_ULA_S_DONE),
- .name = OSMO_STRINGIFY(VLR_ULA_S_WAIT_IMEISV),
- .action = lu_fsm_wait_imeisv,
- },
- [VLR_ULA_S_WAIT_PVLR] = {
- .in_event_mask = S(VLR_ULA_E_SEND_ID_ACK) |
- S(VLR_ULA_E_SEND_ID_NACK),
- .out_state_mask = S(VLR_ULA_S_WAIT_IMSI) |
- S(VLR_ULA_S_WAIT_AUTH) |
- S(VLR_ULA_S_DONE),
- .name = OSMO_STRINGIFY(VLR_ULA_S_WAIT_PVLR),
- .action = lu_fsm_wait_pvlr,
- },
- [VLR_ULA_S_WAIT_AUTH] = {
- .in_event_mask = S(VLR_ULA_E_AUTH_RES),
- .out_state_mask = S(VLR_ULA_S_WAIT_CIPH) |
- S(VLR_ULA_S_WAIT_LU_COMPL) |
- S(VLR_ULA_S_WAIT_HLR_UPD) |
- S(VLR_ULA_S_DONE),
- .name = OSMO_STRINGIFY(VLR_ULA_S_WAIT_AUTH),
- .action = lu_fsm_wait_auth,
- },
- [VLR_ULA_S_WAIT_CIPH] = {
- .name = OSMO_STRINGIFY(VLR_ULA_S_WAIT_CIPH),
- .in_event_mask = S(VLR_ULA_E_CIPH_RES),
- .out_state_mask = S(VLR_ULA_S_WAIT_LU_COMPL) |
- S(VLR_ULA_S_WAIT_HLR_UPD) |
- S(VLR_ULA_S_DONE),
- .action = lu_fsm_wait_ciph,
- },
- [VLR_ULA_S_WAIT_IMSI] = {
- .in_event_mask = S(VLR_ULA_E_ID_IMSI),
- .out_state_mask = S(VLR_ULA_S_WAIT_AUTH) |
- S(VLR_ULA_S_WAIT_HLR_UPD) |
- S(VLR_ULA_S_DONE),
- .name = OSMO_STRINGIFY(VLR_ULA_S_WAIT_IMSI),
- .action = lu_fsm_wait_imsi,
- },
- [VLR_ULA_S_WAIT_HLR_UPD] = {
- .in_event_mask = S(VLR_ULA_E_HLR_LU_RES) |
- S(VLR_ULA_E_UPD_HLR_COMPL),
- .out_state_mask = S(VLR_ULA_S_WAIT_LU_COMPL) |
- S(VLR_ULA_S_WAIT_LU_COMPL_STANDALONE) |
- S(VLR_ULA_S_DONE),
- .name = OSMO_STRINGIFY(VLR_ULA_S_WAIT_HLR_UPD),
- .action = lu_fsm_wait_hlr_ul_res,
- },
- [VLR_ULA_S_WAIT_LU_COMPL] = {
- .in_event_mask = S(VLR_ULA_E_LU_COMPL_SUCCESS) |
- S(VLR_ULA_E_LU_COMPL_FAILURE) |
- S(VLR_ULA_E_NEW_TMSI_ACK) |
- S(VLR_ULA_E_ID_IMEI) |
- S(VLR_ULA_E_ID_IMEISV),
- .out_state_mask = S(VLR_ULA_S_DONE),
- .name = OSMO_STRINGIFY(VLR_ULA_S_WAIT_LU_COMPL),
- .action = lu_fsm_wait_lu_compl,
- },
- [VLR_ULA_S_WAIT_LU_COMPL_STANDALONE] = {
- .in_event_mask = S(VLR_ULA_E_LU_COMPL_SUCCESS) |
- S(VLR_ULA_E_LU_COMPL_FAILURE) |
- S(VLR_ULA_E_NEW_TMSI_ACK),
- .out_state_mask = S(VLR_ULA_S_DONE),
- .name = OSMO_STRINGIFY(VLR_ULA_S_WAIT_LU_COMPL_STANDALONE),
- .action = lu_fsm_wait_lu_compl_standalone,
- },
- [VLR_ULA_S_DONE] = {
- .name = OSMO_STRINGIFY(VLR_ULA_S_DONE),
- .onenter = lu_fsm_dispatch_result,
- },
-};
-
-static void fsm_lu_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
-{
- struct lu_fsm_priv *lfp = fi->priv;
- struct vlr_subscr *vsub = lfp->vsub;
-
- LOGPFSM(fi, "fsm_lu_cleanup called with cause %s\n",
- osmo_fsm_term_cause_name(cause));
- if (vsub && vsub->lu_fsm == fi)
- vsub->lu_fsm = NULL;
-}
-
-static struct osmo_fsm vlr_lu_fsm = {
- .name = "vlr_lu_fsm",
- .states = vlr_lu_fsm_states,
- .num_states = ARRAY_SIZE(vlr_lu_fsm_states),
- .allstate_event_mask = 0,
- .allstate_action = NULL,
- .log_subsys = DVLR,
- .event_names = fsm_lu_event_names,
- .cleanup = fsm_lu_cleanup,
-};
-
-struct osmo_fsm_inst *
-vlr_loc_update(struct osmo_fsm_inst *parent,
- uint32_t parent_event_success,
- uint32_t parent_event_failure,
- void *parent_event_data,
- struct vlr_instance *vlr, void *msc_conn_ref,
- enum vlr_lu_type type, uint32_t tmsi, const char *imsi,
- const struct osmo_location_area_id *old_lai,
- const struct osmo_location_area_id *new_lai,
- bool authentication_required,
- enum vlr_ciph ciphering_required,
- bool is_r99, bool is_utran,
- bool assign_tmsi)
-{
- struct osmo_fsm_inst *fi;
- struct lu_fsm_priv *lfp;
-
- fi = osmo_fsm_inst_alloc_child(&vlr_lu_fsm, parent, parent_event_failure);
- if (!fi)
- return NULL;
-
- lfp = talloc_zero(fi, struct lu_fsm_priv);
- lfp->vlr = vlr;
- lfp->msc_conn_ref = msc_conn_ref;
- lfp->tmsi = tmsi;
- lfp->type = type;
- lfp->old_lai = *old_lai;
- lfp->new_lai = *new_lai;
- lfp->lu_by_tmsi = true;
- lfp->parent_event_success = parent_event_success;
- lfp->parent_event_failure = parent_event_failure;
- lfp->parent_event_data = parent_event_data;
- lfp->authentication_required = authentication_required;
- lfp->ciphering_required = ciphering_required;
- lfp->is_r99 = is_r99;
- lfp->is_utran = is_utran;
- lfp->assign_tmsi = assign_tmsi;
- if (imsi) {
- strncpy(lfp->imsi, imsi, sizeof(lfp->imsi)-1);
- lfp->imsi[sizeof(lfp->imsi)-1] = '\0';
- lfp->lu_by_tmsi = false;
- }
- fi->priv = lfp;
-
- LOGPFSM(fi, "rev=%s net=%s%s%s\n",
- is_r99 ? "R99" : "GSM",
- is_utran ? "UTRAN" : "GERAN",
- (authentication_required || ciphering_required)?
- " Auth" : " (no Auth)",
- (authentication_required || ciphering_required)?
- (ciphering_required? "+Ciph" : " (no Ciph)")
- : "");
-
- if (is_utran && !authentication_required)
- LOGPFSML(fi, LOGL_ERROR,
- "Authentication off on UTRAN network. Good luck.\n");
-
- osmo_fsm_inst_dispatch(fi, VLR_ULA_E_UPDATE_LA, NULL);
-
- return fi;
-}
-
-/* Gracefully terminate an FSM created by vlr_loc_update() in case of external
- * timeout (i.e. from MSC). */
-void vlr_loc_update_conn_timeout(struct osmo_fsm_inst *fi)
-{
- if (!fi || fi->state == VLR_ULA_S_DONE)
- return;
- LOGPFSM(fi, "Connection timed out\n");
- lu_fsm_failure(fi, GSM48_REJECT_CONGESTION);
-}
-
-void vlr_lu_fsm_init(void)
-{
- osmo_fsm_register(&vlr_lu_fsm);
- osmo_fsm_register(&upd_hlr_vlr_fsm);
- osmo_fsm_register(&sub_pres_vlr_fsm);
- osmo_fsm_register(&lu_compl_vlr_fsm);
-}
diff --git a/src/libvlr/vlr_lu_fsm.h b/src/libvlr/vlr_lu_fsm.h
deleted file mode 100644
index 5cf13c77e..000000000
--- a/src/libvlr/vlr_lu_fsm.h
+++ /dev/null
@@ -1,18 +0,0 @@
-#pragma once
-
-#include <osmocom/core/fsm.h>
-
-enum vlr_lu_state {
- VLR_ULA_S_IDLE,
- VLR_ULA_S_WAIT_IMEISV,
- VLR_ULA_S_WAIT_PVLR, /* Waiting for ID from PVLR */
- VLR_ULA_S_WAIT_AUTH, /* Waiting for Authentication */
- VLR_ULA_S_WAIT_CIPH, /* Waiting for Ciphering Complete */
- VLR_ULA_S_WAIT_IMSI, /* Waiting for IMSI from MS */
- VLR_ULA_S_WAIT_HLR_UPD, /* Waiting for end of HLR update */
- VLR_ULA_S_WAIT_LU_COMPL,/* Waiting for LU complete */
- VLR_ULA_S_WAIT_LU_COMPL_STANDALONE, /* Standalone VLR */
- VLR_ULA_S_DONE
-};
-
-void vlr_lu_fsm_init(void);
diff --git a/src/osmo-bsc/Makefile.am b/src/osmo-bsc/Makefile.am
index 5642fb2ed..dfc4defcb 100644
--- a/src/osmo-bsc/Makefile.am
+++ b/src/osmo-bsc/Makefile.am
@@ -43,7 +43,6 @@ osmo_bsc_LDADD = \
$(top_builddir)/src/libfilter/libfilter.a \
$(top_builddir)/src/libbsc/libbsc.a \
$(top_builddir)/src/libcommon-cs/libcommon-cs.a \
- $(top_builddir)/src/libmsc/libmsc.a \
$(top_builddir)/src/libtrau/libtrau.a \
$(top_builddir)/src/libcommon/libcommon.a \
$(LIBOSMOSCCP_LIBS) \
diff --git a/src/osmo-bsc/osmo_bsc_api.c b/src/osmo-bsc/osmo_bsc_api.c
index f7343f743..8e47069c1 100644
--- a/src/osmo-bsc/osmo_bsc_api.c
+++ b/src/osmo-bsc/osmo_bsc_api.c
@@ -22,9 +22,12 @@
#include <openbsc/debug.h>
#include <openbsc/gsm_04_80.h>
+#include <openbsc/gsm_04_08_utils.h>
#include <osmocom/gsm/protocol/gsm_08_08.h>
#include <osmocom/gsm/gsm0808.h>
+#include <osmocom/gsm/mncc.h>
+#include <osmocom/gsm/gsm48.h>
#include <osmocom/sccp/sccp.h>
#include <openbsc/osmo_bsc_sigtran.h>
diff --git a/src/osmo-bsc/osmo_bsc_bssap.c b/src/osmo-bsc/osmo_bsc_bssap.c
index b803433ba..6af883505 100644
--- a/src/osmo-bsc/osmo_bsc_bssap.c
+++ b/src/osmo-bsc/osmo_bsc_bssap.c
@@ -24,12 +24,14 @@
#include <openbsc/bsc_msc_data.h>
#include <openbsc/debug.h>
#include <openbsc/bsc_subscriber.h>
-#include <openbsc/mgcp.h>
+#include <osmocom/legacy_mgcp/mgcp.h>
#include <openbsc/paging.h>
+#include <openbsc/gsm_04_08_utils.h>
#include <osmocom/gsm/protocol/gsm_08_08.h>
#include <osmocom/gsm/gsm0808.h>
#include <osmocom/gsm/gsm0808_utils.h>
+#include <osmocom/gsm/gsm48.h>
#include <openbsc/osmo_bsc_sigtran.h>
#include <openbsc/a_reset.h>
#include <osmocom/core/byteswap.h>
@@ -42,8 +44,7 @@
/* Helper function for match_codec_pref(), looks up a matching permitted speech
* value for a given msc audio codec pref */
-enum gsm0808_permitted_speech audio_support_to_gsm88(struct gsm_audio_support
- *audio)
+enum gsm0808_permitted_speech audio_support_to_gsm88(struct gsm_audio_support *audio)
{
if (audio->hr) {
switch (audio->ver) {
@@ -516,9 +517,18 @@ static int bssmap_handle_assignm_req(struct osmo_bsc_sccp_con *conn,
* local preferences of the BSC */
rc = match_codec_pref(&full_rate, &chan_mode, &ct, scl_ptr, msc);
if (rc < 0) {
- LOGP(DMSC, LOGL_ERROR, "No supported audio type found.\n");
+ LOGP(DMSC, LOGL_ERROR, "No supported audio type found for channel_type ="
+ " { ch_indctr=0x%x, ch_rate_type=0x%x, perm_spch=[ %s] }\n",
+ ct.ch_indctr, ct.ch_rate_type, osmo_hexdump(ct.perm_spch, ct.perm_spch_len));
+ /* TODO: actually output codec names, e.g. implement gsm0808_permitted_speech_names[] and
+ * iterate perm_spch. */
goto reject;
}
+ DEBUGP(DMSC, "Found matching audio type: %s %s for channel_type ="
+ " { ch_indctr=0x%x, ch_rate_type=0x%x, perm_spch=[ %s] }\n",
+ full_rate? "full rate" : "half rate",
+ get_value_string(gsm48_chan_mode_names, chan_mode),
+ ct.ch_indctr, ct.ch_rate_type, osmo_hexdump(ct.perm_spch, ct.perm_spch_len));
if (aoip == false) {
/* map it to a MGCP Endpoint and a RTP port */
diff --git a/src/osmo-bsc/osmo_bsc_filter.c b/src/osmo-bsc/osmo_bsc_filter.c
index 2c84b169f..2a9820d40 100644
--- a/src/osmo-bsc/osmo_bsc_filter.c
+++ b/src/osmo-bsc/osmo_bsc_filter.c
@@ -17,6 +17,8 @@
*
*/
+#include <osmocom/gsm/gsm48.h>
+
#include <openbsc/osmo_bsc.h>
#include <openbsc/bsc_msc_data.h>
#include <openbsc/gsm_04_80.h>
diff --git a/src/osmo-bsc/osmo_bsc_main.c b/src/osmo-bsc/osmo_bsc_main.c
index 68f8b492d..e3eb17a80 100644
--- a/src/osmo-bsc/osmo_bsc_main.c
+++ b/src/osmo-bsc/osmo_bsc_main.c
@@ -58,7 +58,7 @@
#include "../../bscconfig.h"
struct gsm_network *bsc_gsmnet = 0;
-static const char *config_file = "openbsc.cfg";
+static const char *config_file = "osmo-bsc.cfg";
static const char *rf_ctrl = NULL;
extern const char *openbsc_copyright;
static int daemonize = 0;
diff --git a/src/osmo-bsc_nat/Makefile.am b/src/osmo-bsc_nat/Makefile.am
index b7c13ad3e..1eec7aa1c 100644
--- a/src/osmo-bsc_nat/Makefile.am
+++ b/src/osmo-bsc_nat/Makefile.am
@@ -13,6 +13,7 @@ AM_CFLAGS = \
$(LIBOSMOSCCP_CFLAGS) \
$(LIBOSMOABIS_CFLAGS) \
$(LIBOSMONETIF_CFLAGS) \
+ $(LIBOSMOLEGACYMGCP_CFLAGS) \
$(LIBCRYPTO_CFLAGS) \
$(COVERAGE_CFLAGS) \
$(NULL)
@@ -40,7 +41,6 @@ osmo_bsc_nat_SOURCES = \
$(NULL)
osmo_bsc_nat_LDADD = \
- $(top_builddir)/src/libmgcp/libmgcp.a \
$(top_builddir)/src/libfilter/libfilter.a \
$(top_builddir)/src/libbsc/libbsc.a \
$(top_builddir)/src/libcommon-cs/libcommon-cs.a \
@@ -54,6 +54,8 @@ osmo_bsc_nat_LDADD = \
$(LIBOSMOABIS_LIBS) \
$(LIBOSMONETIF_LIBS) \
$(LIBOSMOSIGTRAN_LIBS) \
+ $(LIBOSMOLEGACYMGCP_LIBS) \
$(LIBCRYPTO_LIBS) \
+ $(LIBRARY_GSM) \
-lrt \
$(NULL)
diff --git a/src/osmo-bsc_nat/bsc_mgcp_utils.c b/src/osmo-bsc_nat/bsc_mgcp_utils.c
index 7df362f89..49050fc89 100644
--- a/src/osmo-bsc_nat/bsc_mgcp_utils.c
+++ b/src/osmo-bsc_nat/bsc_mgcp_utils.c
@@ -48,8 +48,8 @@
#include <openbsc/gsm_data.h>
#include <openbsc/debug.h>
#include <openbsc/ipaccess.h>
-#include <openbsc/mgcp.h>
-#include <openbsc/mgcp_internal.h>
+#include <osmocom/legacy_mgcp/mgcp.h>
+#include <osmocom/legacy_mgcp/mgcp_internal.h>
#include <openbsc/osmux.h>
#include <osmocom/ctrl/control_cmd.h>
diff --git a/src/osmo-bsc_nat/bsc_nat_ctrl.c b/src/osmo-bsc_nat/bsc_nat_ctrl.c
index 128ea6518..ff6a73978 100644
--- a/src/osmo-bsc_nat/bsc_nat_ctrl.c
+++ b/src/osmo-bsc_nat/bsc_nat_ctrl.c
@@ -32,6 +32,7 @@
#include <openbsc/bsc_msg_filter.h>
#include <openbsc/vty.h>
#include <openbsc/gsm_data.h>
+#include <openbsc/debug.h>
#include <unistd.h>
#include <string.h>
diff --git a/src/osmo-bsc_nat/bsc_nat_rewrite.c b/src/osmo-bsc_nat/bsc_nat_rewrite.c
index e7c387c22..943e1fa7a 100644
--- a/src/osmo-bsc_nat/bsc_nat_rewrite.c
+++ b/src/osmo-bsc_nat/bsc_nat_rewrite.c
@@ -34,6 +34,8 @@
#include <osmocom/core/utils.h>
#include <osmocom/gsm/gsm0808.h>
#include <osmocom/gsm/ipa.h>
+#include <osmocom/gsm/mncc.h>
+#include <osmocom/gsm/gsm48.h>
#include <osmocom/gsm/protocol/gsm_08_08.h>
#include <osmocom/gsm/protocol/gsm_04_11.h>
diff --git a/src/osmo-bsc_nat/bsc_nat_vty.c b/src/osmo-bsc_nat/bsc_nat_vty.c
index a11ae151e..6de29b516 100644
--- a/src/osmo-bsc_nat/bsc_nat_vty.c
+++ b/src/osmo-bsc_nat/bsc_nat_vty.c
@@ -24,10 +24,10 @@
#include <openbsc/bsc_nat_sccp.h>
#include <openbsc/bsc_msg_filter.h>
#include <openbsc/bsc_msc.h>
-#include <openbsc/gsm_04_08.h>
-#include <openbsc/mgcp.h>
+#include <osmocom/legacy_mgcp/mgcp.h>
#include <openbsc/vty.h>
#include <openbsc/nat_rewrite_trie.h>
+#include <openbsc/debug.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/rate_ctr.h>
diff --git a/src/osmo-bsc_nat/bsc_ussd.c b/src/osmo-bsc_nat/bsc_ussd.c
index 0ba63270d..29b4feef9 100644
--- a/src/osmo-bsc_nat/bsc_ussd.c
+++ b/src/osmo-bsc_nat/bsc_ussd.c
@@ -25,12 +25,14 @@
#include <openbsc/bsc_msg_filter.h>
#include <openbsc/ipaccess.h>
#include <openbsc/socket.h>
+#include <openbsc/debug.h>
#include <osmocom/gsm/protocol/gsm_08_08.h>
#include <osmocom/gsm/gsm0480.h>
#include <osmocom/core/talloc.h>
#include <osmocom/gsm/tlv.h>
#include <osmocom/gsm/ipa.h>
+#include <osmocom/gsm/gsm0480.h>
#include <osmocom/sccp/sccp.h>
@@ -39,6 +41,7 @@
#include <sys/socket.h>
#include <string.h>
#include <unistd.h>
+#include <errno.h>
#define USSD_LAC_IE 0
#define USSD_CI_IE 1
diff --git a/src/osmo-msc/Makefile.am b/src/osmo-msc/Makefile.am
deleted file mode 100644
index 0d4876fa9..000000000
--- a/src/osmo-msc/Makefile.am
+++ /dev/null
@@ -1,58 +0,0 @@
-AM_CPPFLAGS = \
- $(all_includes) \
- -I$(top_srcdir)/include \
- -I$(top_builddir) \
- $(NULL)
-
-AM_CFLAGS = \
- -Wall \
- $(COVERAGE_CFLAGS) \
- $(LIBOSMOCORE_CFLAGS) \
- $(LIBOSMOGSM_CFLAGS) \
- $(LIBOSMOVTY_CFLAGS) \
- $(LIBOSMOCTRL_CFLAGS) \
- $(LIBOSMOABIS_CFLAGS) \
- $(LIBSMPP34_CFLAGS) \
- $(LIBCRYPTO_CFLAGS) \
- $(LIBOSMORANAP_CFLAGS) \
- $(LIBASN1C_CFLAGS) \
- $(LIBOSMOSIGTRAN_CFLAGS) \
- $(LIBOSMOLEGACYMGCP_CFLAGS) \
- $(NULL)
-
-AM_LDFLAGS = \
- $(COVERAGE_LDFLAGS) \
- $(NULL)
-
-bin_PROGRAMS = \
- osmo-msc \
- $(NULL)
-
-osmo_msc_SOURCES = \
- msc_main.c \
- $(NULL)
-
-osmo_msc_LDADD = \
- $(top_builddir)/src/libmsc/libmsc.a \
- $(top_builddir)/src/libvlr/libvlr.a \
- $(top_builddir)/src/libcommon-cs/libcommon-cs.a \
- $(top_builddir)/src/libtrau/libtrau.a \
- $(top_builddir)/src/libcommon/libcommon.a \
- $(LIBOSMOGSM_LIBS) \
- $(LIBOSMOVTY_LIBS) \
- $(LIBOSMOCORE_LIBS) \
- $(LIBOSMOCTRL_LIBS) \
- $(LIBOSMOABIS_LIBS) \
- $(LIBSMPP34_LIBS) \
- $(LIBCRYPTO_LIBS) \
- $(LIBOSMOSIGTRAN_LIBS) \
- $(LIBOSMOLEGACYMGCP_LIBS) \
- $(LIBRARY_GSM) \
- -ldbi \
- $(NULL)
-if BUILD_IU
-osmo_msc_LDADD += \
- $(LIBOSMORANAP_LIBS) \
- $(LIBASN1C_LIBS) \
- $(NULL)
-endif
diff --git a/src/osmo-msc/msc_main.c b/src/osmo-msc/msc_main.c
deleted file mode 100644
index 0db542013..000000000
--- a/src/osmo-msc/msc_main.c
+++ /dev/null
@@ -1,584 +0,0 @@
-/* OsmoMSC - Circuit-Switched Core Network (MSC+VLR+HLR+SMSC) implementation
- */
-
-/* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
- * All Rights Reserved
- *
- * Based on OsmoNITB:
- * (C) 2008-2010 by Harald Welte <laforge@gnumonks.org>
- * (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <unistd.h>
-#include <time.h>
-#include <errno.h>
-#include <signal.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-
-#define _GNU_SOURCE
-#include <getopt.h>
-
-/* build switches from the configure script */
-#include "../../bscconfig.h"
-
-#include <openbsc/db.h>
-#include <osmocom/core/application.h>
-#include <osmocom/core/select.h>
-#include <osmocom/core/stats.h>
-#include <openbsc/debug.h>
-#include <osmocom/abis/abis.h>
-#include <osmocom/abis/e1_input.h>
-#include <osmocom/core/talloc.h>
-#include <openbsc/signal.h>
-#include <openbsc/osmo_msc.h>
-#include <openbsc/bsc_msc_data.h>
-#include <openbsc/sms_queue.h>
-#include <osmocom/vty/telnet_interface.h>
-#include <osmocom/vty/ports.h>
-#include <osmocom/vty/logging.h>
-#include <openbsc/vty.h>
-#include <openbsc/bss.h>
-#include <openbsc/mncc.h>
-#include <openbsc/handover_decision.h>
-#include <openbsc/rrlp.h>
-#include <osmocom/ctrl/control_if.h>
-#include <osmocom/ctrl/control_vty.h>
-#include <osmocom/ctrl/ports.h>
-#include <openbsc/ctrl.h>
-#include <openbsc/osmo_bsc_rf.h>
-#include <openbsc/smpp.h>
-#include <osmocom/sigtran/osmo_ss7.h>
-#include <osmocom/legacy_mgcp/mgcpgw_client.h>
-
-#ifdef BUILD_IU
-#include <osmocom/ranap/iu_client.h>
-#endif
-
-#include <openbsc/msc_ifaces.h>
-#include <openbsc/iucs.h>
-#include <openbsc/iucs_ranap.h>
-#include <openbsc/a_iface.h>
-
-static const char * const osmomsc_copyright =
- "OsmoMSC - Osmocom Circuit-Switched Core Network implementation\r\n"
- "Copyright (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>\r\n"
- "Based on OsmoNITB:\r\n"
- " (C) 2008-2010 by Harald Welte <laforge@gnumonks.org>\r\n"
- " (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>\r\n"
- "Contributions by Daniel Willmann, Jan Lübbe, Stefan Schmidt\r\n"
- "Dieter Spaar, Andreas Eversberg, Sylvain Munaut, Neels Hofmeyr\r\n\r\n"
- "License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
- "This is free software: you are free to change and redistribute it.\r\n"
- "There is NO WARRANTY, to the extent permitted by law.\r\n";
-
-void *tall_msc_ctx = NULL;
-
-/* satisfy deps from libbsc legacy.
- TODO double check these */
-void *tall_fle_ctx = NULL;
-void *tall_paging_ctx = NULL;
-void *tall_map_ctx = NULL;
-void *tall_upq_ctx = NULL;
-/* end deps from libbsc legacy. */
-
-static struct {
- const char *database_name;
- const char *config_file;
- int daemonize;
- const char *mncc_sock_path;
- int use_db_counter;
-} msc_cmdline_config = {
- "sms.db",
- "osmo-msc.cfg",
- 0,
- 0,
- 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-path PATH Disable built-in MNCC handler and offer socket.\n");
- printf(" -m --mncc-sock Same as `-M /tmp/bsc_mncc' (deprecated).\n");
- printf(" -C --no-dbcounter Disable regular syncing of counters to database.\n");
- printf(" -r --rf-ctl PATH A unix domain socket to listen for cmds.\n");
- printf(" -p --pcap PATH Write abis communication to pcap trace file.\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'},
- {"mncc-sock-path", 1, 0, 'M'},
- {"no-dbcounter", 0, 0, 'C'},
- {0, 0, 0, 0}
- };
-
- c = getopt_long(argc, argv, "hd:Dsl:ap:TPVc:e:mCM:",
- 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':
- msc_cmdline_config.daemonize = 1;
- break;
- case 'l':
- msc_cmdline_config.database_name = optarg;
- break;
- case 'c':
- msc_cmdline_config.config_file = optarg;
- break;
- case 'p':
- create_pcap_file(optarg);
- break;
- case 'T':
- log_set_print_timestamp(osmo_stderr_target, 1);
- break;
-#if BEFORE_MSCSPLIT
- case 'P':
- ipacc_rtp_direct = 0;
- break;
-#endif
- case 'e':
- log_set_log_level(osmo_stderr_target, atoi(optarg));
- break;
- case 'M':
- msc_cmdline_config.mncc_sock_path = optarg;
- break;
- case 'm':
- msc_cmdline_config.mncc_sock_path = "/tmp/bsc_mncc";
- break;
- case 'C':
- msc_cmdline_config.use_db_counter = 0;
- break;
- case 'V':
- print_version(1);
- exit(0);
- break;
- default:
- /* catch unknown options *as well as* missing arguments. */
- fprintf(stderr, "Error in command line options. Exiting.\n");
- exit(-1);
- }
- }
-}
-
-struct gsm_network *msc_network_alloc(void *ctx,
- mncc_recv_cb_t mncc_recv)
-{
- struct gsm_network *net = gsm_network_init(ctx, 1, 1, mncc_recv);
- if (!net)
- return NULL;
-
- net->name_long = talloc_strdup(net, "OsmoMSC");
- net->name_short = talloc_strdup(net, "OsmoMSC");
-
- net->gsup_server_addr_str = talloc_strdup(net,
- MSC_HLR_REMOTE_IP_DEFAULT);
- net->gsup_server_port = MSC_HLR_REMOTE_PORT_DEFAULT;
-
- mgcpgw_client_conf_init(&net->mgcpgw.conf);
-
- return net;
-}
-
-void msc_network_shutdown(struct gsm_network *net)
-{
- /* nothing here yet */
-}
-
-static struct gsm_network *msc_network = NULL;
-
-extern void *tall_vty_ctx;
-static void signal_handler(int signal)
-{
- fprintf(stdout, "signal %u received\n", signal);
-
- switch (signal) {
- case SIGINT:
- msc_network_shutdown(msc_network);
- osmo_signal_dispatch(SS_L_GLOBAL, S_L_GLOBAL_SHUTDOWN, NULL);
- sleep(3);
- exit(0);
- break;
- case SIGABRT:
- osmo_generate_backtrace();
- /* 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_msc_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);
-}
-
-extern int bsc_vty_go_parent(struct vty *vty);
-
-static struct vty_app_info msc_vty_info = {
- .name = "OsmoMSC",
- .version = PACKAGE_VERSION,
- .go_parent_cb = bsc_vty_go_parent,
- .is_config_node = bsc_vty_is_config_node,
-};
-
-#ifdef BUILD_IU
-static int rcvmsg_iu_cs(struct msgb *msg, struct gprs_ra_id *ra_id, uint16_t *sai)
-{
- DEBUGP(DIUCS, "got IuCS message"
- " %d bytes: %s\n",
- msg->len, osmo_hexdump(msg->data, msg->len));
- if (ra_id) {
- DEBUGP(DIUCS, "got IuCS message on"
- " MNC %d MCC %d LAC %d RAC %d\n",
- ra_id->mnc, ra_id->mcc, ra_id->lac, ra_id->rac);
- }
-
- return gsm0408_rcvmsg_iucs(msc_network, msg, ra_id? &ra_id->lac : NULL);
-}
-
-static int rx_iu_event(struct ranap_ue_conn_ctx *ctx, enum ranap_iu_event_type type,
- void *data)
-{
- DEBUGP(DIUCS, "got IuCS event %u: %s\n", type,
- ranap_iu_event_type_str(type));
-
- return iucs_rx_ranap_event(msc_network, ctx, type, data);
-}
-#endif
-
-#define DEFAULT_M3UA_REMOTE_IP "127.0.0.1"
-#define DEFAULT_LOCAL_PC (1 << 11) /* pc 1.0.0 in 3-8-3 format */
-
-/* Setup sigtran connection */
-int ss7_setup(void *ctx)
-{
- char *sccp_inst_name_a = "OsmoMSC-A";
- uint32_t cs7_instance_a = msc_network->a.cs7_instance;
-#if BUILD_IU
- char *sccp_inst_name_iu = "OsmoMSC-Iu";
- uint32_t cs7_instance_iu = msc_network->iu.cs7_instance;
-#endif
-
- LOGP(DMSC, LOGL_NOTICE, "CS7 Instance identifier, A-Interface: %u\n",
- cs7_instance_a);
-#if BUILD_IU
- LOGP(DMSC, LOGL_NOTICE, "CS7 Instance identifier, Iu-Interface: %u\n",
- cs7_instance_iu);
-
- /* Setup instance names */
- if (cs7_instance_a == cs7_instance_iu)
- sccp_inst_name_a = sccp_inst_name_iu = "OsmoMSC";
-
- /* Create first SCCP instance (Iu and possibly also for A) */
- msc_network->iu.sccp =
- osmo_sccp_simple_client_on_ss7_id(ctx, cs7_instance_iu,
- sccp_inst_name_iu,
- DEFAULT_LOCAL_PC,
- OSMO_SS7_ASP_PROT_M3UA,
- 0, NULL,
- 0, DEFAULT_M3UA_REMOTE_IP);
- if (!msc_network->iu.sccp)
- return -EINVAL;
-
- /* If the VTY settings indicate, that the user wants to use only a
- single cs7 instance (A and Iu are running on the same instance,
- we just copy the pointer and exit early */
- if (cs7_instance_a == cs7_instance_iu) {
- msc_network->a.sccp = msc_network->iu.sccp;
- return 0;
- }
-#endif
-
- /* Create second SCCP instance (A) */
- msc_network->a.sccp =
- osmo_sccp_simple_client_on_ss7_id(ctx, cs7_instance_a,
- sccp_inst_name_a,
- DEFAULT_LOCAL_PC,
- OSMO_SS7_ASP_PROT_M3UA,
- 0, NULL,
- 0, DEFAULT_M3UA_REMOTE_IP);
- if (!msc_network->a.sccp)
- return -EINVAL;
-
- return 0;
-}
-
-int main(int argc, char **argv)
-{
- int rc;
-
- msc_vty_info.copyright = osmomsc_copyright;
-
- tall_msc_ctx = talloc_named_const(NULL, 1, "osmo_msc");
- talloc_ctx_init(tall_msc_ctx);
-
- osmo_init_logging(&log_info);
- osmo_stats_init(tall_msc_ctx);
-
- /* For --version, vty_init() must be called before handling options */
- vty_init(&msc_vty_info);
-
- osmo_ss7_init();
- osmo_ss7_vty_init_asp(tall_msc_ctx);
-
- /* Parse options */
- handle_options(argc, argv);
-
- /* Allocate global gsm_network struct; choose socket/internal MNCC */
- msc_network = msc_network_alloc(tall_msc_ctx,
- msc_cmdline_config.mncc_sock_path?
- mncc_sock_from_cc
- : int_mncc_recv);
- if (!msc_network)
- return -ENOMEM;
-
- if (msc_vlr_alloc(msc_network)) {
- fprintf(stderr, "Failed to allocate VLR\n");
- exit(1);
- }
-
- ctrl_vty_init(tall_msc_ctx);
- logging_vty_add_cmds(&log_info);
- msc_vty_init(msc_network);
- bsc_vty_init_extra();
-
-#ifdef BUILD_SMPP
- if (smpp_openbsc_alloc_init(tall_msc_ctx) < 0)
- return -1;
-#endif
-
- /*
- * For osmo-nitb, skip TCH/F for now, because otherwise dyn TS
- * always imply the possibility to have a mix of TCH/F and
- * TCH/H channels; if two phones request a TCH/F and a TCH/H,
- * respectively, they cannot call each other. If we deny TCH/F,
- * they will both fall back to TCH/H, and dynamic channels are
- * usable. See OS#1778.
- *
- * A third-party MSC may well be able to handle a TCH/H TCH/F
- * mismatch. Moreover, this option may be overwritten in the
- * config file or in VTY.
- */
- msc_network->dyn_ts_allow_tch_f = false;
-
- rc = vty_read_config_file(msc_cmdline_config.config_file, NULL);
- if (rc < 0) {
- LOGP(DNM, LOGL_FATAL, "Failed to parse the config file: '%s'\n",
- msc_cmdline_config.config_file);
- return 1;
- }
-
- /* Initialize MNCC socket if appropriate */
- if (msc_cmdline_config.mncc_sock_path) {
- rc = mncc_sock_init(msc_network,
- msc_cmdline_config.mncc_sock_path);
- if (rc) {
- fprintf(stderr, "MNCC socket initialization failed. exiting.\n");
- exit(1);
- }
- } else
- DEBUGP(DMNCC, "Using internal MNCC handler.\n");
-
- /* start telnet after reading config for vty_get_bind_addr() */
- rc = telnet_init_dynif(tall_msc_ctx, &msc_network,
- vty_get_bind_addr(), OSMO_VTY_PORT_MSC);
- if (rc < 0)
- return 2;
-
- /* BSC stuff is to be split behind an A-interface to be used with
- * OsmoBSC, but there is no need to remove it yet. Most of the
- * following code until iu_init() is legacy. */
-
-#ifdef BUILD_SMPP
- smpp_openbsc_start(msc_network);
-#endif
-
- /* start control interface after reading config for
- * ctrl_vty_get_bind_addr() */
- msc_network->ctrl = ctrl_interface_setup_dynip(msc_network, ctrl_vty_get_bind_addr(),
- OSMO_CTRL_PORT_MSC, NULL);
- if (!msc_network->ctrl) {
- printf("Failed to initialize control interface. Exiting.\n");
- return -1;
- }
-
-#if 0
-TODO: we probably want some of the _net_ ctrl commands from bsc_base_ctrl_cmds_install().
- if (bsc_base_ctrl_cmds_install() != 0) {
- printf("Failed to initialize the BSC control commands.\n");
- return -1;
- }
-#endif
-
- if (msc_ctrl_cmds_install(msc_network) != 0) {
- printf("Failed to initialize the MSC control commands.\n");
- return -1;
- }
-
- /* seed the PRNG */
- srand(time(NULL));
- /* TODO: is this used for crypto?? Improve randomness, at least we
- * should try to use the nanoseconds part of the current time. */
-
- if (db_init(msc_cmdline_config.database_name)) {
- printf("DB: Failed to init database: %s\n",
- msc_cmdline_config.database_name);
- return 4;
- }
-
- osmo_fsm_log_addr(true);
- if (msc_vlr_start(msc_network)) {
- fprintf(stderr, "Failed to start VLR\n");
- exit(1);
- }
-
- msc_subscr_conn_init();
-
- if (db_prepare()) {
- printf("DB: Failed to prepare database.\n");
- return 5;
- }
-
- /* setup the timer */
- osmo_timer_setup(&db_sync_timer, db_sync_timer_cb, NULL);
- if (msc_cmdline_config.use_db_counter)
- osmo_timer_schedule(&db_sync_timer, DB_SYNC_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(msc_network, 20) != 0)
- return -1;
-
- msc_network->mgcpgw.client = mgcpgw_client_init(
- msc_network, &msc_network->mgcpgw.conf);
-
- if (mgcpgw_client_connect(msc_network->mgcpgw.client)) {
- printf("MGCPGW connect failed\n");
- return 7;
- }
-
- if (ss7_setup(tall_msc_ctx)) {
- printf("Setting up SCCP client failed.\n");
- return 8;
- }
-
-#ifdef BUILD_IU
- /* Set up IuCS */
- ranap_iu_init(tall_msc_ctx, DRANAP, "OsmoMSC-IuCS", msc_network->iu.sccp, rcvmsg_iu_cs, rx_iu_event);
-#endif
-
- /* Set up A interface */
- a_init(msc_network->a.sccp, msc_network);
-
- if (msc_cmdline_config.daemonize) {
- rc = osmo_daemonize();
- if (rc < 0) {
- perror("Error during daemonize");
- return 6;
- }
- }
-
- while (1) {
- log_reset_context();
- osmo_select_main(0);
- }
-}