aboutsummaryrefslogtreecommitdiffstats
path: root/openbsc/src
diff options
context:
space:
mode:
Diffstat (limited to 'openbsc/src')
-rw-r--r--openbsc/src/Makefile.am60
-rw-r--r--openbsc/src/gprs/.gitignore2
-rw-r--r--openbsc/src/gprs/Makefile.am132
-rw-r--r--openbsc/src/gprs/crc24.c67
-rw-r--r--openbsc/src/gprs/gb_proxy.c1437
-rw-r--r--openbsc/src/gprs/gb_proxy_main.c315
-rw-r--r--openbsc/src/gprs/gb_proxy_patch.c458
-rw-r--r--openbsc/src/gprs/gb_proxy_peer.c218
-rw-r--r--openbsc/src/gprs/gb_proxy_tlli.c723
-rw-r--r--openbsc/src/gprs/gb_proxy_vty.c852
-rw-r--r--openbsc/src/gprs/gprs_gb_parse.c636
-rw-r--r--openbsc/src/gprs/gprs_gmm.c2939
-rw-r--r--openbsc/src/gprs/gprs_llc.c1132
-rw-r--r--openbsc/src/gprs/gprs_llc_parse.c251
-rw-r--r--openbsc/src/gprs/gprs_llc_vty.c116
-rw-r--r--openbsc/src/gprs/gprs_llc_xid.c281
-rw-r--r--openbsc/src/gprs/gprs_sgsn.c895
-rw-r--r--openbsc/src/gprs/gprs_sndcp.c1258
-rw-r--r--openbsc/src/gprs/gprs_sndcp_comp.c323
-rw-r--r--openbsc/src/gprs/gprs_sndcp_dcomp.c358
-rw-r--r--openbsc/src/gprs/gprs_sndcp_pcomp.c282
-rw-r--r--openbsc/src/gprs/gprs_sndcp_vty.c71
-rw-r--r--openbsc/src/gprs/gprs_sndcp_xid.c1822
-rw-r--r--openbsc/src/gprs/gprs_subscriber.c921
-rw-r--r--openbsc/src/gprs/gprs_utils.c274
-rw-r--r--openbsc/src/gprs/gtphub.c2931
-rw-r--r--openbsc/src/gprs/gtphub_ares.c220
-rw-r--r--openbsc/src/gprs/gtphub_main.c357
-rw-r--r--openbsc/src/gprs/gtphub_sock.c60
-rw-r--r--openbsc/src/gprs/gtphub_vty.c613
-rw-r--r--openbsc/src/gprs/osmo_sgsn.cfg23
-rw-r--r--openbsc/src/gprs/sgsn_ares.c173
-rw-r--r--openbsc/src/gprs/sgsn_auth.c312
-rw-r--r--openbsc/src/gprs/sgsn_cdr.c258
-rw-r--r--openbsc/src/gprs/sgsn_ctrl.c69
-rw-r--r--openbsc/src/gprs/sgsn_libgtp.c860
-rw-r--r--openbsc/src/gprs/sgsn_main.c462
-rw-r--r--openbsc/src/gprs/sgsn_vty.c1323
-rw-r--r--openbsc/src/gprs/slhc.c813
-rw-r--r--openbsc/src/gprs/v42bis.c767
-rw-r--r--openbsc/src/ipaccess/Makefile.am66
-rw-r--r--openbsc/src/ipaccess/abisip-find.c216
-rw-r--r--openbsc/src/ipaccess/ipaccess-config.c1019
-rw-r--r--openbsc/src/ipaccess/ipaccess-firmware.c135
-rw-r--r--openbsc/src/ipaccess/ipaccess-proxy.c1226
-rw-r--r--openbsc/src/ipaccess/network_listen.c257
-rw-r--r--openbsc/src/libbsc/Makefile.am57
-rw-r--r--openbsc/src/libbsc/abis_nm.c2917
-rw-r--r--openbsc/src/libbsc/abis_nm_ipaccess.c87
-rw-r--r--openbsc/src/libbsc/abis_nm_vty.c191
-rw-r--r--openbsc/src/libbsc/abis_om2000.c2776
-rw-r--r--openbsc/src/libbsc/abis_om2000_vty.c609
-rw-r--r--openbsc/src/libbsc/abis_rsl.c2927
-rw-r--r--openbsc/src/libbsc/arfcn_range_encode.c330
-rw-r--r--openbsc/src/libbsc/bsc_api.c894
-rw-r--r--openbsc/src/libbsc/bsc_ctrl_commands.c459
-rw-r--r--openbsc/src/libbsc/bsc_ctrl_lookup.c107
-rw-r--r--openbsc/src/libbsc/bsc_dyn_ts.c77
-rw-r--r--openbsc/src/libbsc/bsc_init.c552
-rw-r--r--openbsc/src/libbsc/bsc_msc.c320
-rw-r--r--openbsc/src/libbsc/bsc_rf_ctrl.c534
-rw-r--r--openbsc/src/libbsc/bsc_rll.c139
-rw-r--r--openbsc/src/libbsc/bsc_subscriber.c168
-rw-r--r--openbsc/src/libbsc/bsc_vty.c4304
-rw-r--r--openbsc/src/libbsc/bts_ericsson_rbs2000.c204
-rw-r--r--openbsc/src/libbsc/bts_init.c30
-rw-r--r--openbsc/src/libbsc/bts_ipaccess_nanobts.c520
-rw-r--r--openbsc/src/libbsc/bts_ipaccess_nanobts_omlattr.c240
-rw-r--r--openbsc/src/libbsc/bts_nokia_site.c1739
-rw-r--r--openbsc/src/libbsc/bts_siemens_bs11.c602
-rw-r--r--openbsc/src/libbsc/bts_sysmobts.c60
-rw-r--r--openbsc/src/libbsc/bts_unknown.c40
-rw-r--r--openbsc/src/libbsc/chan_alloc.c543
-rw-r--r--openbsc/src/libbsc/e1_config.c297
-rw-r--r--openbsc/src/libbsc/gsm_04_08_utils.c687
-rw-r--r--openbsc/src/libbsc/gsm_04_80_utils.c40
-rw-r--r--openbsc/src/libbsc/handover_decision.c304
-rw-r--r--openbsc/src/libbsc/handover_logic.c378
-rw-r--r--openbsc/src/libbsc/meas_proc.c84
-rw-r--r--openbsc/src/libbsc/meas_rep.c115
-rw-r--r--openbsc/src/libbsc/net_init.c69
-rw-r--r--openbsc/src/libbsc/paging.c449
-rw-r--r--openbsc/src/libbsc/pcu_sock.c742
-rw-r--r--openbsc/src/libbsc/rest_octets.c860
-rw-r--r--openbsc/src/libbsc/system_information.c1169
-rw-r--r--openbsc/src/libcommon-cs/Makefile.am20
-rw-r--r--openbsc/src/libcommon-cs/common_cs.c133
-rw-r--r--openbsc/src/libcommon-cs/common_cs_vty.c325
-rw-r--r--openbsc/src/libcommon/Makefile.am47
-rw-r--r--openbsc/src/libcommon/bsc_version.c30
-rw-r--r--openbsc/src/libcommon/common_vty.c145
-rw-r--r--openbsc/src/libcommon/debug.c235
-rw-r--r--openbsc/src/libcommon/gsm_data.c473
-rw-r--r--openbsc/src/libcommon/gsm_data_shared.c849
-rw-r--r--openbsc/src/libcommon/gsm_subscriber_base.c163
-rw-r--r--openbsc/src/libcommon/gsup_client.c341
-rw-r--r--openbsc/src/libcommon/gsup_test_client.c298
-rw-r--r--openbsc/src/libcommon/oap_client.c280
-rw-r--r--openbsc/src/libcommon/socket.c111
-rw-r--r--openbsc/src/libcommon/talloc_ctx.c56
-rw-r--r--openbsc/src/libfilter/Makefile.am26
-rw-r--r--openbsc/src/libfilter/bsc_msg_acc.c118
-rw-r--r--openbsc/src/libfilter/bsc_msg_filter.c398
-rw-r--r--openbsc/src/libfilter/bsc_msg_vty.c140
-rw-r--r--openbsc/src/libiu/Makefile.am28
-rw-r--r--openbsc/src/libiu/iu.c759
-rw-r--r--openbsc/src/libiu/iu_vty.c50
-rw-r--r--openbsc/src/libmgcp/Makefile.am43
-rw-r--r--openbsc/src/libmgcp/g711common.h187
-rw-r--r--openbsc/src/libmgcp/mgcp_network.c1064
-rw-r--r--openbsc/src/libmgcp/mgcp_osmux.c586
-rw-r--r--openbsc/src/libmgcp/mgcp_protocol.c1598
-rw-r--r--openbsc/src/libmgcp/mgcp_sdp.c305
-rw-r--r--openbsc/src/libmgcp/mgcp_transcode.c612
-rw-r--r--openbsc/src/libmgcp/mgcp_vty.c1543
-rw-r--r--openbsc/src/libmsc/Makefile.am58
-rw-r--r--openbsc/src/libmsc/auth.c157
-rw-r--r--openbsc/src/libmsc/ctrl_commands.c212
-rw-r--r--openbsc/src/libmsc/db.c1752
-rw-r--r--openbsc/src/libmsc/gsm_04_08.c4047
-rw-r--r--openbsc/src/libmsc/gsm_04_11.c1070
-rw-r--r--openbsc/src/libmsc/gsm_04_80.c155
-rw-r--r--openbsc/src/libmsc/gsm_subscriber.c422
-rw-r--r--openbsc/src/libmsc/meas_feed.c167
-rw-r--r--openbsc/src/libmsc/meas_feed.h12
-rw-r--r--openbsc/src/libmsc/mncc.c107
-rw-r--r--openbsc/src/libmsc/mncc_builtin.c426
-rw-r--r--openbsc/src/libmsc/mncc_sock.c318
-rw-r--r--openbsc/src/libmsc/osmo_msc.c177
-rw-r--r--openbsc/src/libmsc/rrlp.c104
-rw-r--r--openbsc/src/libmsc/silent_call.c152
-rw-r--r--openbsc/src/libmsc/smpp_openbsc.c758
-rw-r--r--openbsc/src/libmsc/smpp_smsc.c1030
-rw-r--r--openbsc/src/libmsc/smpp_smsc.h166
-rw-r--r--openbsc/src/libmsc/smpp_utils.c62
-rw-r--r--openbsc/src/libmsc/smpp_vty.c612
-rw-r--r--openbsc/src/libmsc/sms_queue.c544
-rw-r--r--openbsc/src/libmsc/token_auth.c160
-rw-r--r--openbsc/src/libmsc/transaction.c163
-rw-r--r--openbsc/src/libmsc/ussd.c95
-rw-r--r--openbsc/src/libmsc/vty_interface_layer3.c1210
-rw-r--r--openbsc/src/libtrau/Makefile.am31
-rw-r--r--openbsc/src/libtrau/rtp_proxy.c764
-rw-r--r--openbsc/src/libtrau/trau_mux.c547
-rw-r--r--openbsc/src/libtrau/trau_upqueue.c27
-rw-r--r--openbsc/src/osmo-bsc/Makefile.am55
-rw-r--r--openbsc/src/osmo-bsc/osmo_bsc_api.c550
-rw-r--r--openbsc/src/osmo-bsc/osmo_bsc_audio.c73
-rw-r--r--openbsc/src/osmo-bsc/osmo_bsc_bssap.c548
-rw-r--r--openbsc/src/osmo-bsc/osmo_bsc_ctrl.c680
-rw-r--r--openbsc/src/osmo-bsc/osmo_bsc_filter.c381
-rw-r--r--openbsc/src/osmo-bsc/osmo_bsc_grace.c169
-rw-r--r--openbsc/src/osmo-bsc/osmo_bsc_main.c301
-rw-r--r--openbsc/src/osmo-bsc/osmo_bsc_msc.c586
-rw-r--r--openbsc/src/osmo-bsc/osmo_bsc_sccp.c328
-rw-r--r--openbsc/src/osmo-bsc/osmo_bsc_vty.c945
-rw-r--r--openbsc/src/osmo-bsc_mgcp/Makefile.am35
-rw-r--r--openbsc/src/osmo-bsc_mgcp/mgcp_main.c311
-rw-r--r--openbsc/src/osmo-bsc_nat/Makefile.am58
-rw-r--r--openbsc/src/osmo-bsc_nat/bsc_filter.c218
-rw-r--r--openbsc/src/osmo-bsc_nat/bsc_mgcp_utils.c1152
-rw-r--r--openbsc/src/osmo-bsc_nat/bsc_nat.c1736
-rw-r--r--openbsc/src/osmo-bsc_nat/bsc_nat_ctrl.c524
-rw-r--r--openbsc/src/osmo-bsc_nat/bsc_nat_filter.c119
-rw-r--r--openbsc/src/osmo-bsc_nat/bsc_nat_rewrite.c714
-rw-r--r--openbsc/src/osmo-bsc_nat/bsc_nat_rewrite_trie.c259
-rw-r--r--openbsc/src/osmo-bsc_nat/bsc_nat_utils.c535
-rw-r--r--openbsc/src/osmo-bsc_nat/bsc_nat_vty.c1336
-rw-r--r--openbsc/src/osmo-bsc_nat/bsc_sccp.c247
-rw-r--r--openbsc/src/osmo-bsc_nat/bsc_ussd.c453
-rw-r--r--openbsc/src/osmo-nitb/Makefile.am45
-rw-r--r--openbsc/src/osmo-nitb/bsc_hack.c402
-rw-r--r--openbsc/src/utils/Makefile.am147
-rw-r--r--openbsc/src/utils/bs11_config.c953
-rw-r--r--openbsc/src/utils/isdnsync.c189
-rw-r--r--openbsc/src/utils/meas_db.c330
-rw-r--r--openbsc/src/utils/meas_db.h17
-rw-r--r--openbsc/src/utils/meas_json.c190
-rw-r--r--openbsc/src/utils/meas_pcap2db.c138
-rw-r--r--openbsc/src/utils/meas_udp2db.c126
-rw-r--r--openbsc/src/utils/meas_vis.c310
-rw-r--r--openbsc/src/utils/smpp_mirror.c329
182 files changed, 0 insertions, 96560 deletions
diff --git a/openbsc/src/Makefile.am b/openbsc/src/Makefile.am
deleted file mode 100644
index cfad7dfea..000000000
--- a/openbsc/src/Makefile.am
+++ /dev/null
@@ -1,60 +0,0 @@
-AM_CPPFLAGS = \
- $(all_includes) \
- -I$(top_srcdir)/include \
- -I$(top_builddir) \
- $(NULL)
-
-AM_CFLAGS = \
- -Wall \
- $(LIBOSMOCORE_CFLAGS) \
- $(LIBOSMOGSM_CFLAGS) \
- $(LIBOSMOGSM_CFLAGS) \
- $(LIBOSMOVTY_CFLAGS) \
- $(COVERAGE_CFLAGS) \
- $(NULL)
-
-AM_LDFLAGS = \
- $(LIBOSMOCORE_LIBS) \
- $(LIBOSMOGSM_LIBS) \
- $(COVERAGE_LDFLAGS) \
- $(NULL)
-
-# Libraries
-SUBDIRS = \
- libcommon \
- libmgcp \
- libbsc \
- libmsc \
- libtrau \
- libfilter \
- libcommon-cs \
- $(NULL)
-
-# Conditional Libraries
-if BUILD_IU
-SUBDIRS += \
- libiu \
- $(NULL)
-endif
-
-# Programs
-SUBDIRS += \
- osmo-nitb \
- osmo-bsc_mgcp \
- utils \
- ipaccess \
- gprs \
- $(NULL)
-
-# Conditional Programs
-if BUILD_NAT
-SUBDIRS += \
- osmo-bsc_nat \
- $(NULL)
-endif
-
-if BUILD_BSC
-SUBDIRS += \
- osmo-bsc \
- $(NULL)
-endif
diff --git a/openbsc/src/gprs/.gitignore b/openbsc/src/gprs/.gitignore
deleted file mode 100644
index 7cfefbac2..000000000
--- a/openbsc/src/gprs/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-gsn_restart
-osmo_*.cfg*
diff --git a/openbsc/src/gprs/Makefile.am b/openbsc/src/gprs/Makefile.am
deleted file mode 100644
index cb0997902..000000000
--- a/openbsc/src/gprs/Makefile.am
+++ /dev/null
@@ -1,132 +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) \
- $(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 += \
- $(top_builddir)/src/libiu/libiu.a \
- $(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) \
- -lrt \
- $(NULL)
diff --git a/openbsc/src/gprs/crc24.c b/openbsc/src/gprs/crc24.c
deleted file mode 100644
index 1a420ed66..000000000
--- a/openbsc/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/openbsc/src/gprs/gb_proxy.c b/openbsc/src/gprs/gb_proxy.c
deleted file mode 100644
index d95139f8d..000000000
--- a/openbsc/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/openbsc/src/gprs/gb_proxy_main.c b/openbsc/src/gprs/gb_proxy_main.c
deleted file mode 100644
index 69a93b6f7..000000000
--- a/openbsc/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/openbsc/src/gprs/gb_proxy_patch.c b/openbsc/src/gprs/gb_proxy_patch.c
deleted file mode 100644
index 7bddc4494..000000000
--- a/openbsc/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/openbsc/src/gprs/gb_proxy_peer.c b/openbsc/src/gprs/gb_proxy_peer.c
deleted file mode 100644
index 5365ff0fa..000000000
--- a/openbsc/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/openbsc/src/gprs/gb_proxy_tlli.c b/openbsc/src/gprs/gb_proxy_tlli.c
deleted file mode 100644
index 3b3b976a5..000000000
--- a/openbsc/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/openbsc/src/gprs/gb_proxy_vty.c b/openbsc/src/gprs/gb_proxy_vty.c
deleted file mode 100644
index 933b6b010..000000000
--- a/openbsc/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/openbsc/src/gprs/gprs_gb_parse.c b/openbsc/src/gprs/gprs_gb_parse.c
deleted file mode 100644
index d5a122bda..000000000
--- a/openbsc/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/openbsc/src/gprs/gprs_gmm.c b/openbsc/src/gprs/gprs_gmm.c
deleted file mode 100644
index e6751db7c..000000000
--- a/openbsc/src/gprs/gprs_gmm.c
+++ /dev/null
@@ -1,2939 +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>
-#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/iu.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 ue_conn_ctx *ctx, enum iu_event_type type, void *data)
-{
- struct sgsn_mm_ctx *mm;
- int rc = -1;
-
- mm = sgsn_mm_ctx_by_ue_ctx(ctx);
- if (!mm) {
- LOGP(DRANAP, LOGL_NOTICE, "Cannot find mm ctx for IU event %i!\n", type);
- return rc;
- }
-
- switch (type) {
- case IU_EVENT_RAB_ASSIGN:
- rc = sgsn_ranap_rab_ass_resp(mm, (RANAP_RAB_SetupOrModifiedItemIEs_t *)data);
- break;
- case IU_EVENT_IU_RELEASE:
- /* fall thru */
- case IU_EVENT_LINK_INVALIDATED:
- /* Clean up ue_conn_ctx here */
- LOGMMCTXP(LOGL_INFO, mm, "IU release for imsi %s\n", mm->imsi);
- if (mm->pmm_state == PMM_CONNECTED)
- mmctx_set_pmm_state(mm, PMM_IDLE);
- rc = 0;
- break;
- case IU_EVENT_SECURITY_MODE_COMPLETE:
- /* 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 iu_tx(msg, GPRS_SAPI_GMM);
-#endif
- }
-
-#ifdef BUILD_IU
- /* In Iu mode, msg->dst contains the ue_conn_ctx pointer, in Gb mode
- * dst is empty. */
- /* FIXME: have a more explicit indicator for Iu messages */
- if (msg->dst)
- return 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;
-}
-
-/* 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
-void activate_pdp_rabs(struct sgsn_mm_ctx *ctx)
-{
- /* Send RAB activation requests for all PDP contexts */
- struct sgsn_pdp_ctx *pdp;
- llist_for_each_entry(pdp, &ctx->pdp_list, list) {
- iu_rab_act_ps(pdp->nsapi, pdp, 1);
- }
-}
-#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 = iu_tx_sec_mode_cmd(ctx->iu.ue_ctx, &ctx->auth_triplet, 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 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
- ra_id = ((struct ue_conn_ctx*)msg->dst)->ra_id;
-
- /* 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;
- else if (ctx->ran_type == MM_CTX_T_UTRAN_Iu) {
- /* DEVELOPMENT HACK: Our current HLR does not support 3G
- * authentication tokens. A new HLR/VLR implementation is being
- * developed. Until it is ready and actual milenage
- * authentication is properly supported, we are hardcoding a
- * fixed Ki and use 2G auth. */
- unsigned char tmp_rand[16];
- /* Ki 000102030405060708090a0b0c0d0e0f */
- struct osmo_sub_auth_data auth = {
- .type = OSMO_AUTH_TYPE_GSM,
- .algo = OSMO_AUTH_ALG_COMP128v1,
- .u.gsm.ki = {
- 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
- 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
- 0x0e, 0x0f
- },
- };
- /* XXX: Hack to make 3G auth work with special SIM card */
- ctx->auth_state = SGSN_AUTH_AUTHENTICATE;
-
- RAND_bytes(tmp_rand, 16);
-
- memset(&ctx->auth_triplet.vec, 0, sizeof(ctx->auth_triplet.vec));
- osmo_auth_gen_vec(&ctx->auth_triplet.vec, &auth, tmp_rand);
-
- ctx->auth_triplet.key_seq = 0;
- }
-
- /* 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 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, bool use_x213_nsap)
-{
- struct msgb *msg;
- struct sgsn_mm_ctx *mm = pdp->mm;
- struct ue_conn_ctx *uectx;
- uint32_t ggsn_ip;
-
- uectx = mm->iu.ue_ctx;
-
- /* 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 iu_rab_act(uectx, msg);
-}
-#endif
diff --git a/openbsc/src/gprs/gprs_llc.c b/openbsc/src/gprs/gprs_llc.c
deleted file mode 100644
index 2be663f98..000000000
--- a/openbsc/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/openbsc/src/gprs/gprs_llc_parse.c b/openbsc/src/gprs/gprs_llc_parse.c
deleted file mode 100644
index a5a7a7122..000000000
--- a/openbsc/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/openbsc/src/gprs/gprs_llc_vty.c b/openbsc/src/gprs/gprs_llc_vty.c
deleted file mode 100644
index bf34e9782..000000000
--- a/openbsc/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/openbsc/src/gprs/gprs_llc_xid.c b/openbsc/src/gprs/gprs_llc_xid.c
deleted file mode 100644
index fe631715a..000000000
--- a/openbsc/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/openbsc/src/gprs/gprs_sgsn.c b/openbsc/src/gprs/gprs_sgsn.c
deleted file mode 100644
index 071dd97c8..000000000
--- a/openbsc/src/gprs/gprs_sgsn.c
+++ /dev/null
@@ -1,895 +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 <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 <openbsc/iu.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.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/openbsc/src/gprs/gprs_sndcp.c b/openbsc/src/gprs/gprs_sndcp.c
deleted file mode 100644
index a18998f9e..000000000
--- a/openbsc/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/openbsc/src/gprs/gprs_sndcp_comp.c b/openbsc/src/gprs/gprs_sndcp_comp.c
deleted file mode 100644
index a12c39aa6..000000000
--- a/openbsc/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/openbsc/src/gprs/gprs_sndcp_dcomp.c b/openbsc/src/gprs/gprs_sndcp_dcomp.c
deleted file mode 100644
index b0f95b486..000000000
--- a/openbsc/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/openbsc/src/gprs/gprs_sndcp_pcomp.c b/openbsc/src/gprs/gprs_sndcp_pcomp.c
deleted file mode 100644
index a2236c3b1..000000000
--- a/openbsc/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/openbsc/src/gprs/gprs_sndcp_vty.c b/openbsc/src/gprs/gprs_sndcp_vty.c
deleted file mode 100644
index 430881fc8..000000000
--- a/openbsc/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/openbsc/src/gprs/gprs_sndcp_xid.c b/openbsc/src/gprs/gprs_sndcp_xid.c
deleted file mode 100644
index dfea5febc..000000000
--- a/openbsc/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/openbsc/src/gprs/gprs_subscriber.c b/openbsc/src/gprs/gprs_subscriber.c
deleted file mode 100644
index 1bb51418a..000000000
--- a/openbsc/src/gprs/gprs_subscriber.c
+++ /dev/null
@@ -1,921 +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(
- 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/openbsc/src/gprs/gprs_utils.c b/openbsc/src/gprs/gprs_utils.c
deleted file mode 100644
index 64ed9788d..000000000
--- a/openbsc/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/openbsc/src/gprs/gtphub.c b/openbsc/src/gprs/gtphub.c
deleted file mode 100644
index 211018b53..000000000
--- a/openbsc/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/openbsc/src/gprs/gtphub_ares.c b/openbsc/src/gprs/gtphub_ares.c
deleted file mode 100644
index afeeda657..000000000
--- a/openbsc/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/openbsc/src/gprs/gtphub_main.c b/openbsc/src/gprs/gtphub_main.c
deleted file mode 100644
index 73a122c31..000000000
--- a/openbsc/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/openbsc/src/gprs/gtphub_sock.c b/openbsc/src/gprs/gtphub_sock.c
deleted file mode 100644
index 60bebaaeb..000000000
--- a/openbsc/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/openbsc/src/gprs/gtphub_vty.c b/openbsc/src/gprs/gtphub_vty.c
deleted file mode 100644
index a30ad2a54..000000000
--- a/openbsc/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/openbsc/src/gprs/osmo_sgsn.cfg b/openbsc/src/gprs/osmo_sgsn.cfg
deleted file mode 100644
index c4c9ec1cf..000000000
--- a/openbsc/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/openbsc/src/gprs/sgsn_ares.c b/openbsc/src/gprs/sgsn_ares.c
deleted file mode 100644
index d94d184a3..000000000
--- a/openbsc/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/openbsc/src/gprs/sgsn_auth.c b/openbsc/src/gprs/sgsn_auth.c
deleted file mode 100644
index a64339c3e..000000000
--- a/openbsc/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/openbsc/src/gprs/sgsn_cdr.c b/openbsc/src/gprs/sgsn_cdr.c
deleted file mode 100644
index 091089610..000000000
--- a/openbsc/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/openbsc/src/gprs/sgsn_ctrl.c b/openbsc/src/gprs/sgsn_ctrl.c
deleted file mode 100644
index 31ac74f1f..000000000
--- a/openbsc/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/openbsc/src/gprs/sgsn_libgtp.c b/openbsc/src/gprs/sgsn_libgtp.c
deleted file mode 100644
index 001e61146..000000000
--- a/openbsc/src/gprs/sgsn_libgtp.c
+++ /dev/null
@@ -1,860 +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 <openbsc/iu.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));
-
- /* Assume we are a GERAN system */
- pdp->rattype.l = 1;
- pdp->rattype.v[0] = 2;
- pdp->rattype_given = 1;
-
- /* Include RAI and ULI all the time */
- pdp->rai_given = 1;
- pdp->rai.l = 6;
- raid = mmctx->ra;
- raid.lac = 0xFFFE;
- raid.rac = 0xFF;
- gsm48_construct_ra(pdp->rai.v, &raid);
-
- pdp->userloc_given = 1;
- pdp->userloc.l = 8;
- pdp->userloc.v[0] = 0; /* CGI for GERAN */
- bssgp_create_cell_id(&pdp->userloc.v[1], &mmctx->ra, mmctx->gb.cell_id);
-
- /* include the IMEI(SV) */
- pdp->imeisv_given = 1;
- gsm48_encode_bcd_number(&pdp->imeisv.v[0], 8, 0, mmctx->imei);
- pdp->imeisv.l = pdp->imeisv.v[0];
- memmove(&pdp->imeisv.v[0], &pdp->imeisv.v[1], 8);
-
- /* change pdp state to 'requested' */
- pctx->state = PDP_STATE_CR_REQ;
-
- rc = gtp_create_context_req(ggsn->gsn, pdp, pctx);
- /* FIXME */
-
- return pctx;
-}
-
-/* SGSN wants to delete a PDP context */
-int sgsn_delete_pdp_ctx(struct sgsn_pdp_ctx *pctx)
-{
- LOGPDPCTXP(LOGL_ERROR, pctx, "Delete PDP Context\n");
-
- /* FIXME: decide if we need teardown or not ! */
- return gtp_delete_context_req(pctx->ggsn->gsn, pctx->lib, pctx, 1);
-}
-
-struct cause_map {
- uint8_t cause_in;
- uint8_t cause_out;
-};
-
-static uint8_t cause_map(const struct cause_map *map, uint8_t in, uint8_t deflt)
-{
- const struct cause_map *m;
-
- for (m = map; m->cause_in && m->cause_out; m++) {
- if (m->cause_in == in)
- return m->cause_out;
- }
- return deflt;
-}
-
-/* how do we map from gtp cause to SM cause */
-static const struct cause_map gtp2sm_cause_map[] = {
- { GTPCAUSE_NO_RESOURCES, GSM_CAUSE_INSUFF_RSRC },
- { GTPCAUSE_NOT_SUPPORTED, GSM_CAUSE_SERV_OPT_NOTSUPP },
- { GTPCAUSE_MAN_IE_INCORRECT, GSM_CAUSE_INV_MAND_INFO },
- { GTPCAUSE_MAN_IE_MISSING, GSM_CAUSE_INV_MAND_INFO },
- { GTPCAUSE_OPT_IE_INCORRECT, GSM_CAUSE_PROTO_ERR_UNSPEC },
- { GTPCAUSE_SYS_FAIL, GSM_CAUSE_NET_FAIL },
- { GTPCAUSE_ROAMING_REST, GSM_CAUSE_REQ_SERV_OPT_NOTSUB },
- { GTPCAUSE_PTIMSI_MISMATCH, GSM_CAUSE_PROTO_ERR_UNSPEC },
- { GTPCAUSE_CONN_SUSP, GSM_CAUSE_PROTO_ERR_UNSPEC },
- { GTPCAUSE_AUTH_FAIL, GSM_CAUSE_AUTH_FAILED },
- { GTPCAUSE_USER_AUTH_FAIL, GSM_CAUSE_ACT_REJ_GGSN },
- { GTPCAUSE_CONTEXT_NOT_FOUND, GSM_CAUSE_PROTO_ERR_UNSPEC },
- { GTPCAUSE_ADDR_OCCUPIED, GSM_CAUSE_INSUFF_RSRC },
- { GTPCAUSE_NO_MEMORY, GSM_CAUSE_INSUFF_RSRC },
- { GTPCAUSE_RELOC_FAIL, GSM_CAUSE_PROTO_ERR_UNSPEC },
- { GTPCAUSE_UNKNOWN_MAN_EXTHEADER, GSM_CAUSE_PROTO_ERR_UNSPEC },
- { GTPCAUSE_MISSING_APN, GSM_CAUSE_MISSING_APN },
- { GTPCAUSE_UNKNOWN_PDP, GSM_CAUSE_UNKNOWN_PDP },
- { 0, 0 }
-};
-
-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, 1);
- 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 */
- 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 */
- 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/openbsc/src/gprs/sgsn_main.c b/openbsc/src/gprs/sgsn_main.c
deleted file mode 100644
index 04f2825f7..000000000
--- a/openbsc/src/gprs/sgsn_main.c
+++ /dev/null
@@ -1,462 +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/gprs/gprs_ns.h>
-#include <osmocom/gprs/gprs_bssgp.h>
-
-#include <osmocom/vty/telnet_interface.h>
-#include <osmocom/vty/logging.h>
-#include <osmocom/vty/stats.h>
-#include <osmocom/vty/ports.h>
-
-#include <osmocom/ctrl/control_vty.h>
-
-#include <openbsc/signal.h>
-#include <openbsc/debug.h>
-#include <openbsc/vty.h>
-#include <openbsc/sgsn.h>
-#include <openbsc/gprs_llc.h>
-#include <openbsc/gprs_gmm.h>
-#include <openbsc/iu.h>
-
-#include <osmocom/ctrl/control_if.h>
-#include <osmocom/ctrl/ports.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,
- },
-};
-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),
-};
-
-/* Implement the extern asn_debug from libasn1c to indicate whether the ASN.1
- * binary code decoded and encoded during Iu communication should be logged to
- * stderr. See osmocom's libasn1c, asn_internal.h, at "if (asn_debug)":
- * http://git.osmocom.org/libasn1c/tree/include/asn1c/asn_internal.h */
-int asn_debug = 0;
-
-int sgsn_ranap_iu_event(struct ue_conn_ctx *ctx, enum iu_event_type type, void *data);
-
-int main(int argc, char **argv)
-{
- struct ctrl_handle *ctrl;
- struct gsm_network dummy_network;
- 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();
- ctrl_vty_init(tall_bsc_ctx);
-#ifdef BUILD_IU
- iu_vty_init(&asn_debug);
-#endif
-
- 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, &sgsn_inst.cfg);
- 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
- iu_init(tall_bsc_ctx, "127.0.0.2", 14001, 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/openbsc/src/gprs/sgsn_vty.c b/openbsc/src/gprs/sgsn_vty.c
deleted file mode 100644
index e09a0296b..000000000
--- a/openbsc/src/gprs/sgsn_vty.c
+++ /dev/null
@@ -1,1323 +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>
-
-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);
-
- 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(void)
-{
- install_element_ve(&show_sgsn_cmd);
- //install_element_ve(&show_mmctx_tlli_cmd);
- install_element_ve(&show_mmctx_imsi_cmd);
- install_element_ve(&show_mmctx_all_cmd);
- install_element_ve(&show_pdpctx_all_cmd);
- install_element_ve(&show_subscr_cache_cmd);
-
- install_element(ENABLE_NODE, &update_subscr_insert_auth_triplet_cmd);
- install_element(ENABLE_NODE, &update_subscr_create_cmd);
- install_element(ENABLE_NODE, &update_subscr_destroy_cmd);
- install_element(ENABLE_NODE, &update_subscr_cancel_cmd);
- install_element(ENABLE_NODE, &update_subscr_update_location_result_cmd);
- install_element(ENABLE_NODE, &update_subscr_update_auth_info_cmd);
-
- install_element(CONFIG_NODE, &cfg_sgsn_cmd);
- install_node(&sgsn_node, config_write_sgsn);
- vty_install_default(SGSN_NODE);
- install_element(SGSN_NODE, &cfg_sgsn_bind_addr_cmd);
- install_element(SGSN_NODE, &cfg_ggsn_remote_ip_cmd);
- //install_element(SGSN_NODE, &cfg_ggsn_remote_port_cmd);
- install_element(SGSN_NODE, &cfg_ggsn_gtp_version_cmd);
- install_element(SGSN_NODE, &cfg_imsi_acl_cmd);
- install_element(SGSN_NODE, &cfg_auth_policy_cmd);
- install_element(SGSN_NODE, &cfg_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);
- return 0;
-}
-
-int sgsn_parse_config(const char *config_file, struct sgsn_config *cfg)
-{
- int rc;
-
- g_cfg = cfg;
-
- g_cfg->timers.T3312 = GSM0408_T3312_SECS;
- g_cfg->timers.T3322 = GSM0408_T3322_SECS;
- g_cfg->timers.T3350 = GSM0408_T3350_SECS;
- g_cfg->timers.T3360 = GSM0408_T3360_SECS;
- g_cfg->timers.T3370 = GSM0408_T3370_SECS;
- g_cfg->timers.T3313 = GSM0408_T3313_SECS;
- g_cfg->timers.T3314 = GSM0408_T3314_SECS;
- g_cfg->timers.T3316 = GSM0408_T3316_SECS;
- g_cfg->timers.T3385 = GSM0408_T3385_SECS;
- g_cfg->timers.T3386 = GSM0408_T3386_SECS;
- g_cfg->timers.T3395 = GSM0408_T3395_SECS;
- g_cfg->timers.T3397 = GSM0408_T3397_SECS;
-
- rc = vty_read_config_file(config_file, NULL);
- if (rc < 0) {
- fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
- return rc;
- }
-
- 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/openbsc/src/gprs/slhc.c b/openbsc/src/gprs/slhc.c
deleted file mode 100644
index cbdf8dbd8..000000000
--- a/openbsc/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/openbsc/src/gprs/v42bis.c b/openbsc/src/gprs/v42bis.c
deleted file mode 100644
index a04b0af5c..000000000
--- a/openbsc/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/openbsc/src/ipaccess/Makefile.am b/openbsc/src/ipaccess/Makefile.am
deleted file mode 100644
index 4dfe24738..000000000
--- a/openbsc/src/ipaccess/Makefile.am
+++ /dev/null
@@ -1,66 +0,0 @@
-AM_CPPFLAGS = \
- $(all_includes) \
- -I$(top_srcdir)/include \
- -I$(top_builddir) \
- $(NULL)
-
-AM_CFLAGS = \
- -Wall \
- $(LIBOSMOCORE_CFLAGS) \
- $(LIBOSMOGSM_CFLAGS) \
- $(LIBOSMOABIS_CFLAGS) \
- $(COVERAGE_CFLAGS) \
- $(NULL)
-
-AM_LDFLAGS = \
- $(COVERAGE_LDFLAGS) \
- $(NULL)
-
-OSMO_LIBS = \
- $(LIBOSMOCORE_LIBS) \
- $(LIBOSMOGSM_LIBS) \
- $(LIBOSMOABIS_LIBS) \
- $(NULL)
-
-bin_PROGRAMS = \
- abisip-find \
- ipaccess-config \
- ipaccess-proxy \
- $(NULL)
-
-abisip_find_LDADD = \
- $(top_builddir)/src/libbsc/libbsc.a \
- $(top_builddir)/src/libtrau/libtrau.a \
- $(top_builddir)/src/libcommon/libcommon.a \
- $(OSMO_LIBS) \
- $(NULL)
-
-abisip_find_SOURCES = \
- abisip-find.c \
- $(NULL)
-
-ipaccess_config_SOURCES = \
- ipaccess-config.c \
- ipaccess-firmware.c \
- network_listen.c \
- $(NULL)
-
-# FIXME: resolve the bogus dependencies patched around here:
-ipaccess_config_LDADD = \
- $(top_builddir)/src/libbsc/libbsc.a \
- $(top_builddir)/src/libcommon-cs/libcommon-cs.a \
- $(top_builddir)/src/libtrau/libtrau.a \
- $(top_builddir)/src/libcommon/libcommon.a \
- $(OSMO_LIBS) \
- $(NULL)
-
-ipaccess_proxy_SOURCES = \
- ipaccess-proxy.c \
- $(NULL)
-
-ipaccess_proxy_LDADD = \
- $(top_builddir)/src/libbsc/libbsc.a \
- $(top_builddir)/src/libtrau/libtrau.a \
- $(top_builddir)/src/libcommon/libcommon.a \
- $(OSMO_LIBS) \
- $(NULL)
diff --git a/openbsc/src/ipaccess/abisip-find.c b/openbsc/src/ipaccess/abisip-find.c
deleted file mode 100644
index 21d9f2290..000000000
--- a/openbsc/src/ipaccess/abisip-find.c
+++ /dev/null
@@ -1,216 +0,0 @@
-/* ip.access nanoBTS configuration tool */
-
-/* (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 <unistd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-
-
-#include <osmocom/core/select.h>
-#include <osmocom/core/timer.h>
-#include <osmocom/gsm/protocol/ipaccess.h>
-#include <osmocom/gsm/ipa.h>
-#include <openbsc/gsm_data.h>
-
-static int udp_sock(const char *ifname)
-{
- int fd, rc, bc = 1;
- struct sockaddr_in sa;
-
- fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
- if (fd < 0)
- return fd;
-
- if (ifname) {
-#ifdef __FreeBSD__
- rc = setsockopt(fd, SOL_SOCKET, IP_RECVIF, ifname,
- strlen(ifname));
-#else
- rc = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname,
- strlen(ifname));
-#endif
- if (rc < 0)
- goto err;
- }
-
- memset(&sa, 0, sizeof(sa));
- sa.sin_family = AF_INET;
- sa.sin_port = htons(3006);
- sa.sin_addr.s_addr = INADDR_ANY;
-
- rc = bind(fd, (struct sockaddr *)&sa, sizeof(sa));
- if (rc < 0)
- goto err;
-
- rc = setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &bc, sizeof(bc));
- if (rc < 0)
- goto err;
-
-#if 0
- /* we cannot bind, since the response packets don't come from
- * the broadcast address */
- sa.sin_family = AF_INET;
- sa.sin_port = htons(3006);
- inet_aton("255.255.255.255", &sa.sin_addr);
-
- rc = connect(fd, (struct sockaddr *)&sa, sizeof(sa));
- if (rc < 0)
- goto err;
-#endif
- return fd;
-
-err:
- close(fd);
- return rc;
-}
-
-const unsigned char find_pkt[] = { 0x00, 0x0b+8, IPAC_PROTO_IPACCESS, 0x00,
- IPAC_MSGT_ID_GET,
- 0x01, IPAC_IDTAG_MACADDR,
- 0x01, IPAC_IDTAG_IPADDR,
- 0x01, IPAC_IDTAG_UNIT,
- 0x01, IPAC_IDTAG_LOCATION1,
- 0x01, IPAC_IDTAG_LOCATION2,
- 0x01, IPAC_IDTAG_EQUIPVERS,
- 0x01, IPAC_IDTAG_SWVERSION,
- 0x01, IPAC_IDTAG_UNITNAME,
- 0x01, IPAC_IDTAG_SERNR,
- };
-
-
-static int bcast_find(int fd)
-{
- struct sockaddr_in sa;
-
- sa.sin_family = AF_INET;
- sa.sin_port = htons(3006);
- inet_aton("255.255.255.255", &sa.sin_addr);
-
- return sendto(fd, find_pkt, sizeof(find_pkt), 0, (struct sockaddr *) &sa, sizeof(sa));
-}
-
-static int parse_response(unsigned char *buf, int len)
-{
- uint8_t t_len;
- uint8_t t_tag;
- uint8_t *cur = buf;
-
- while (cur < buf + len) {
- t_len = *cur++;
- t_tag = *cur++;
-
- printf("%s='%s' ", ipa_ccm_idtag_name(t_tag), cur);
-
- cur += t_len;
- }
- printf("\n");
- return 0;
-}
-
-static int read_response(int fd)
-{
- unsigned char buf[255];
- struct sockaddr_in sa;
- int len;
- socklen_t sa_len = sizeof(sa);
-
- len = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&sa, &sa_len);
- if (len < 0)
- return len;
-
- /* 2 bytes length, 1 byte protocol */
- if (buf[2] != IPAC_PROTO_IPACCESS)
- return 0;
-
- if (buf[4] != IPAC_MSGT_ID_RESP)
- return 0;
-
- return parse_response(buf+6, len-6);
-}
-
-static int bfd_cb(struct osmo_fd *bfd, unsigned int flags)
-{
- if (flags & BSC_FD_READ)
- return read_response(bfd->fd);
- if (flags & BSC_FD_WRITE) {
- bfd->when &= ~BSC_FD_WRITE;
- return bcast_find(bfd->fd);
- }
- return 0;
-}
-
-static struct osmo_timer_list timer;
-
-static void timer_cb(void *_data)
-{
- struct osmo_fd *bfd = _data;
-
- bfd->when |= BSC_FD_WRITE;
-
- osmo_timer_schedule(&timer, 5, 0);
-}
-
-int main(int argc, char **argv)
-{
- struct osmo_fd bfd;
- char *ifname = NULL;
- int rc;
-
- printf("abisip-find (C) 2009 by Harald Welte\n");
- printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n");
-
- if (argc < 2) {
- fprintf(stdout, "you might need to specify the outgoing\n"
- " network interface, e.g. ``%s eth0''\n", argv[0]);
- } else {
- ifname = argv[1];
- }
-
- bfd.cb = bfd_cb;
- bfd.when = BSC_FD_READ | BSC_FD_WRITE;
- bfd.fd = udp_sock(ifname);
- if (bfd.fd < 0) {
- perror("Cannot create local socket for broadcast udp");
- exit(1);
- }
-
- rc = osmo_fd_register(&bfd);
- if (rc < 0) {
- fprintf(stderr, "Cannot register FD\n");
- exit(1);
- }
-
- osmo_timer_setup(&timer, timer_cb, &bfd);
- osmo_timer_schedule(&timer, 5, 0);
-
- printf("Trying to find ip.access BTS by broadcast UDP...\n");
-
- while (1) {
- rc = osmo_select_main(0);
- if (rc < 0)
- exit(3);
- }
-
- exit(0);
-}
-
diff --git a/openbsc/src/ipaccess/ipaccess-config.c b/openbsc/src/ipaccess/ipaccess-config.c
deleted file mode 100644
index 6822c06a6..000000000
--- a/openbsc/src/ipaccess/ipaccess-config.c
+++ /dev/null
@@ -1,1019 +0,0 @@
-/* ip.access nanoBTS configuration tool */
-
-/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
- * (C) 2009-2011 by Holger Hans Peter Freyther
- * (C) 2009-2010 by On-Waves
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <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 <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-
-
-#include <osmocom/core/application.h>
-#include <osmocom/core/select.h>
-#include <osmocom/core/timer.h>
-#include <openbsc/ipaccess.h>
-#include <openbsc/common_bsc.h>
-#include <osmocom/abis/e1_input.h>
-#include <openbsc/abis_nm.h>
-#include <openbsc/signal.h>
-#include <openbsc/debug.h>
-#include <openbsc/network_listen.h>
-#include <osmocom/abis/ipaccess.h>
-#include <openbsc/gsm_data.h>
-#include <openbsc/abis_nm.h>
-#include <openbsc/signal.h>
-#include <openbsc/debug.h>
-#include <openbsc/network_listen.h>
-#include <osmocom/core/talloc.h>
-#include <osmocom/abis/abis.h>
-#include <osmocom/gsm/protocol/gsm_12_21.h>
-
-struct gsm_network *bsc_gsmnet;
-
-static int net_listen_testnr;
-static int restart;
-static char *prim_oml_ip;
-static char *bts_ip_addr, *bts_ip_mask, *bts_ip_gw;
-static char *unit_id;
-static uint16_t nv_flags;
-static uint16_t nv_mask;
-static char *software = NULL;
-static int sw_load_state = 0;
-static int oml_state = 0;
-static int dump_files = 0;
-static char *firmware_analysis = NULL;
-static int found_trx = 0;
-static int loop_tests = 0;
-
-static void *tall_ctx_config = NULL;
-static struct abis_nm_sw_desc *sw_load1 = NULL;
-static struct abis_nm_sw_desc *sw_load2 = NULL;
-
-/*
-static uint8_t prim_oml_attr[] = { 0x95, 0x00, 7, 0x88, 192, 168, 100, 11, 0x00, 0x00 };
-static uint8_t unit_id_attr[] = { 0x91, 0x00, 9, '2', '3', '4', '2', '/' , '0', '/', '0', 0x00 };
-*/
-
-extern int ipaccess_fd_cb(struct osmo_fd *bfd, unsigned int what);
-extern struct e1inp_line_ops ipaccess_e1inp_line_ops;
-
-/* Actively connect to a BTS. Currently used by ipaccess-config.c */
-static int ipaccess_connect(struct e1inp_line *line, struct sockaddr_in *sa)
-{
- struct e1inp_ts *e1i_ts = &line->ts[0];
- struct osmo_fd *bfd = &e1i_ts->driver.ipaccess.fd;
- int ret, on = 1;
-
- bfd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- bfd->cb = ipaccess_fd_cb;
- bfd->when = BSC_FD_READ | BSC_FD_WRITE;
- bfd->data = line;
- bfd->priv_nr = E1INP_SIGN_OML;
-
- if (bfd->fd < 0) {
- LOGP(DLINP, LOGL_ERROR, "could not create TCP socket.\n");
- return -EIO;
- }
-
- ret = setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
- if (ret < 0) {
- LOGP(DLINP, LOGL_ERROR, "could not set socket option\n");
- close(bfd->fd);
- return -EIO;
- }
-
- ret = connect(bfd->fd, (struct sockaddr *) sa, sizeof(*sa));
- if (ret < 0) {
- LOGP(DLINP, LOGL_ERROR, "could not connect socket\n");
- close(bfd->fd);
- return ret;
- }
-
- ret = osmo_fd_register(bfd);
- if (ret < 0) {
- close(bfd->fd);
- return ret;
- }
- return ret;
- //return e1inp_line_register(line);
-}
-
-/* configure pseudo E1 line in ip.access style and connect to BTS */
-static int ia_config_connect(struct gsm_bts *bts, struct sockaddr_in *sin)
-{
- struct e1inp_line *line;
- struct e1inp_ts *sign_ts, *rsl_ts;
- struct e1inp_sign_link *oml_link, *rsl_link;
-
- line = talloc_zero(tall_bsc_ctx, struct e1inp_line);
- if (!line)
- return -ENOMEM;
-
- line->driver = e1inp_driver_find("ipa");
- if (!line->driver) {
- fprintf(stderr, "cannot `ipa' driver, giving up.\n");
- return -EINVAL;
- }
- line->ops = &ipaccess_e1inp_line_ops;
-
- /* create E1 timeslots for signalling and TRAU frames */
- e1inp_ts_config_sign(&line->ts[1-1], line);
- e1inp_ts_config_sign(&line->ts[2-1], line);
-
- /* create signalling links for TS1 */
- sign_ts = &line->ts[1-1];
- rsl_ts = &line->ts[2-1];
- oml_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OML,
- bts->c0, 0xff, 0);
- rsl_link = e1inp_sign_link_create(rsl_ts, E1INP_SIGN_RSL,
- bts->c0, 0, 0);
-
- /* create back-links from bts/trx */
- bts->oml_link = oml_link;
- bts->c0->rsl_link = rsl_link;
-
- /* default port at BTS for incoming connections is 3006 */
- if (sin->sin_port == 0)
- sin->sin_port = htons(3006);
-
- return ipaccess_connect(line, sin);
-}
-
-/*
- * Callback function for NACK on the OML NM
- *
- * Currently we send the config requests but don't check the
- * result. The nanoBTS will send us a NACK when we did something the
- * BTS didn't like.
- */
-static int ipacc_msg_nack(uint8_t mt)
-{
- fprintf(stderr, "Failure to set attribute. This seems fatal\n");
- exit(-1);
- return 0;
-}
-
-static void check_restart_or_exit(struct gsm_bts_trx *trx)
-{
- if (restart) {
- abis_nm_ipaccess_restart(trx);
- } else {
- exit(0);
- }
-}
-
-static int ipacc_msg_ack(uint8_t mt, struct gsm_bts_trx *trx)
-{
- if (sw_load_state == 1) {
- fprintf(stderr, "The new software is activaed.\n");
- check_restart_or_exit(trx);
- } else if (oml_state == 1) {
- fprintf(stderr, "Set the NV Attributes.\n");
- check_restart_or_exit(trx);
- }
-
- return 0;
-}
-
-static const uint8_t phys_conf_min[] = { 0x02 };
-
-static uint16_t build_physconf(uint8_t *physconf_buf, const struct rxlev_stats *st)
-{
- uint16_t *whitelist = (uint16_t *) (physconf_buf + 4);
- int num_arfcn;
- unsigned int arfcnlist_size;
-
- /* Create whitelist from rxlevels */
- physconf_buf[0] = phys_conf_min[0];
- physconf_buf[1] = NM_IPAC_EIE_ARFCN_WHITE;
- num_arfcn = ipac_rxlevstat2whitelist(whitelist, st, 0, 100);
- arfcnlist_size = num_arfcn * 2;
- *((uint16_t *) (physconf_buf+2)) = htons(arfcnlist_size);
- DEBUGP(DNM, "physconf_buf (%s)\n", osmo_hexdump(physconf_buf, arfcnlist_size+4));
- return arfcnlist_size+4;
-}
-
-static int nwl_sig_cb(unsigned int subsys, unsigned int signal,
- void *handler_data, void *signal_data)
-{
- struct gsm_bts_trx *trx;
- uint8_t physconf_buf[2*NUM_ARFCNS+16];
- uint16_t physconf_len;
-
- switch (signal) {
- case S_IPAC_NWL_COMPLETE:
- trx = signal_data;
- DEBUGP(DNM, "received S_IPAC_NWL_COMPLETE signal\n");
- switch (trx->ipaccess.test_nr) {
- case NM_IPACC_TESTNO_CHAN_USAGE:
- /* Dump RxLev results */
- //rxlev_stat_dump(&trx->ipaccess.rxlev_stat);
- /* Create whitelist from results */
- physconf_len = build_physconf(physconf_buf,
- &trx->ipaccess.rxlev_stat);
- /* Start next test abbout BCCH channel usage */
- ipac_nwl_test_start(trx, NM_IPACC_TESTNO_BCCH_CHAN_USAGE,
- physconf_buf, physconf_len);
- break;
- case NM_IPACC_TESTNO_BCCH_CHAN_USAGE:
- /* Dump BCCH RxLev results */
- //rxlev_stat_dump(&trx->ipaccess.rxlev_stat);
- /* Create whitelist from results */
- physconf_len = build_physconf(physconf_buf,
- &trx->ipaccess.rxlev_stat);
- /* Start next test about BCCH info */
- ipac_nwl_test_start(trx, NM_IPACC_TESTNO_BCCH_INFO,
- physconf_buf, physconf_len);
- break;
- case NM_IPACC_TESTNO_BCCH_INFO:
- /* re-start full process with CHAN_USAGE */
- if (loop_tests) {
- DEBUGP(DNM, "starting next test cycle\n");
- ipac_nwl_test_start(trx, net_listen_testnr, phys_conf_min,
- sizeof(phys_conf_min));
- } else {
- exit(0);
- }
- break;
- }
- break;
- }
- return 0;
-}
-
-static int nm_state_event(int evt, uint8_t obj_class, void *obj,
- struct gsm_nm_state *old_state, struct gsm_nm_state *new_state,
- struct abis_om_obj_inst *obj_inst);
-
-static int nm_sig_cb(unsigned int subsys, unsigned int signal,
- void *handler_data, void *signal_data)
-{
- struct ipacc_ack_signal_data *ipacc_data;
- struct nm_statechg_signal_data *nsd;
-
- switch (signal) {
- case S_NM_IPACC_NACK:
- ipacc_data = signal_data;
- return ipacc_msg_nack(ipacc_data->msg_type);
- case S_NM_IPACC_ACK:
- ipacc_data = signal_data;
- return ipacc_msg_ack(ipacc_data->msg_type, ipacc_data->trx);
- case S_NM_IPACC_RESTART_ACK:
- printf("The BTS has acked the restart. Exiting.\n");
- exit(0);
- break;
- case S_NM_IPACC_RESTART_NACK:
- printf("The BTS has nacked the restart. Exiting.\n");
- exit(0);
- break;
- case S_NM_STATECHG_OPER:
- case S_NM_STATECHG_ADM:
- nsd = signal_data;
- nm_state_event(signal, nsd->obj_class, nsd->obj, nsd->old_state,
- nsd->new_state, nsd->obj_inst);
- break;
- default:
- break;
- }
-
- return 0;
-}
-
-/* callback function passed to the ABIS OML code */
-static int percent;
-static int percent_old;
-static int swload_cbfn(unsigned int hook, unsigned int event, struct msgb *_msg,
- void *data, void *param)
-{
- struct msgb *msg;
- struct gsm_bts_trx *trx;
-
- if (hook != GSM_HOOK_NM_SWLOAD)
- return 0;
-
- trx = (struct gsm_bts_trx *) data;
-
- switch (event) {
- case NM_MT_LOAD_INIT_ACK:
- fprintf(stdout, "Software Load Initiate ACK\n");
- break;
- case NM_MT_LOAD_INIT_NACK:
- fprintf(stderr, "ERROR: Software Load Initiate NACK\n");
- exit(5);
- break;
- case NM_MT_LOAD_END_ACK:
- fprintf(stderr, "LOAD END ACK...");
- /* now make it the default */
- sw_load_state = 1;
-
- msg = msgb_alloc(1024, "sw: nvattr");
- msg->l2h = msgb_put(msg, 3);
- msg->l3h = &msg->l2h[3];
-
- /* activate software */
- if (sw_load1)
- abis_nm_put_sw_desc(msg, sw_load1, true);
-
- if (sw_load2)
- abis_nm_put_sw_desc(msg, sw_load2, true);
-
- /* fill in the data */
- msg->l2h[0] = NM_ATT_IPACC_CUR_SW_CFG;
- msg->l2h[1] = msgb_l3len(msg) >> 8;
- msg->l2h[2] = msgb_l3len(msg) & 0xff;
- printf("Foo l2h: %p l3h: %p... length l2: %u l3: %u\n", msg->l2h, msg->l3h, msgb_l2len(msg), msgb_l3len(msg));
- abis_nm_ipaccess_set_nvattr(trx, msg->l2h, msgb_l2len(msg));
- msgb_free(msg);
- break;
- case NM_MT_LOAD_END_NACK:
- fprintf(stderr, "ERROR: Software Load End NACK\n");
- exit(3);
- break;
- case NM_MT_ACTIVATE_SW_NACK:
- fprintf(stderr, "ERROR: Activate Software NACK\n");
- exit(4);
- break;
- case NM_MT_ACTIVATE_SW_ACK:
- break;
- case NM_MT_LOAD_SEG_ACK:
- percent = abis_nm_software_load_status(trx->bts);
- if (percent > percent_old)
- printf("Software Download Progress: %d%%\n", percent);
- percent_old = percent;
- break;
- case NM_MT_LOAD_ABORT:
- fprintf(stderr, "ERROR: Load aborted by the BTS.\n");
- exit(6);
- break;
- }
- return 0;
-}
-
-static void nv_put_ip_if_cfg(struct msgb *nmsg, uint32_t ip, uint32_t mask)
-{
- msgb_put_u8(nmsg, NM_ATT_IPACC_IP_IF_CFG);
-
- msgb_put_u32(nmsg, ip);
- msgb_put_u32(nmsg, mask);
-}
-
-static void nv_put_gw_cfg(struct msgb *nmsg, uint32_t addr, uint32_t mask, uint32_t gw)
-{
- msgb_put_u8(nmsg, NM_ATT_IPACC_IP_GW_CFG);
- msgb_put_u32(nmsg, addr);
- msgb_put_u32(nmsg, mask);
- msgb_put_u32(nmsg, gw);
-}
-
-static void nv_put_unit_id(struct msgb *nmsg, const char *unit_id)
-{
- msgb_tl16v_put(nmsg, NM_ATT_IPACC_UNIT_ID, strlen(unit_id)+1,
- (const uint8_t *)unit_id);
-}
-
-static void nv_put_prim_oml(struct msgb *nmsg, uint32_t ip, uint16_t port)
-{
- int len;
-
- /* 0x88 + IP + port */
- len = 1 + sizeof(ip) + sizeof(port);
-
- msgb_put_u8(nmsg, NM_ATT_IPACC_PRIM_OML_CFG_LIST);
- msgb_put_u16(nmsg, len);
-
- msgb_put_u8(nmsg, 0x88);
-
- /* IP address */
- msgb_put_u32(nmsg, ip);
-
- /* port number */
- msgb_put_u16(nmsg, port);
-}
-
-static void nv_put_flags(struct msgb *nmsg, uint16_t nv_flags, uint16_t nv_mask)
-{
- msgb_put_u8(nmsg, NM_ATT_IPACC_NV_FLAGS);
- msgb_put_u16(nmsg, sizeof(nv_flags) + sizeof(nv_mask));
- msgb_put_u8(nmsg, nv_flags & 0xff);
- msgb_put_u8(nmsg, nv_mask & 0xff);
- msgb_put_u8(nmsg, nv_flags >> 8);
- msgb_put_u8(nmsg, nv_mask >> 8);
-}
-
-/* human-readable test names for the ip.access tests */
-static const struct value_string ipa_test_strs[] = {
- { 64, "ccch-usage" },
- { 65, "bcch-usage" },
- { 66, "freq-sync" },
- { 67, "rtp-usage" },
- { 68, "rtp-perf" },
- { 69, "gprs-ccch" },
- { 70, "pccch-usage" },
- { 71, "gprs-usage" },
- { 72, "esta-mf" },
- { 73, "uplink-mf" },
- { 74, "dolink-mf" },
- { 75, "tbf-details" },
- { 76, "tbf-usage" },
- { 77, "llc-data" },
- { 78, "pdch-usage" },
- { 79, "power-control" },
- { 80, "link-adaption" },
- { 81, "tch-usage" },
- { 82, "amr-mf" },
- { 83, "rtp-multiplex-perf" },
- { 84, "rtp-multiplex-usage" },
- { 85, "srtp-multiplex-usage" },
- { 86, "abis-traffic" },
- { 89, "gprs-multiplex-perf" },
- { 90, "gprs-multiplex-usage" },
- { 0, NULL },
-};
-
-/* human-readable names for the ip.access nanoBTS NVRAM Flags */
-static const struct value_string ipa_nvflag_strs[] = {
- { 0x0001, "static-ip" },
- { 0x0002, "static-gw" },
- { 0x0004, "no-dhcp-vsi" },
- { 0x0008, "dhcp-enabled" },
- { 0x0040, "led-disabled" },
- { 0x0100, "secondary-oml-enabled" },
- { 0x0200, "diag-enabled" },
- { 0x0400, "cli-enabled" },
- { 0x0800, "http-enabled" },
- { 0x1000, "post-enabled" },
- { 0x2000, "snmp-enabled" },
- { 0, NULL }
-};
-
-/* set the flags in flags/mask according to a string-identified flag and 'enable' */
-static int ipa_nvflag_set(uint16_t *flags, uint16_t *mask, const char *name, int en)
-{
- int rc;
- rc = get_string_value(ipa_nvflag_strs, name);
- if (rc < 0)
- return rc;
-
- *mask |= rc;
- if (en)
- *flags |= rc;
- else
- *flags &= ~rc;
-
- return 0;
-}
-
-static void bootstrap_om(struct gsm_bts_trx *trx)
-{
- struct msgb *nmsg = msgb_alloc(1024, "nested msgb");
- int need_to_set_attr = 0;
- int len;
-
- printf("OML link established using TRX %d\n", trx->nr);
-
- if (unit_id) {
- len = strlen(unit_id);
- if (len > nmsg->data_len-10)
- goto out_err;
- printf("setting Unit ID to '%s'\n", unit_id);
- nv_put_unit_id(nmsg, unit_id);
- need_to_set_attr = 1;
- }
- if (prim_oml_ip) {
- struct in_addr ia;
-
- if (!inet_aton(prim_oml_ip, &ia)) {
- fprintf(stderr, "invalid IP address: %s\n",
- prim_oml_ip);
- goto out_err;
- }
-
- printf("setting primary OML link IP to '%s'\n", inet_ntoa(ia));
- nv_put_prim_oml(nmsg, ntohl(ia.s_addr), 0);
- need_to_set_attr = 1;
- }
- if (nv_mask) {
- printf("setting NV Flags/Mask to 0x%04x/0x%04x\n",
- nv_flags, nv_mask);
- nv_put_flags(nmsg, nv_flags, nv_mask);
- need_to_set_attr = 1;
- }
- if (bts_ip_addr && bts_ip_mask) {
- struct in_addr ia_addr, ia_mask;
-
- if (!inet_aton(bts_ip_addr, &ia_addr)) {
- fprintf(stderr, "invalid IP address: %s\n",
- bts_ip_addr);
- goto out_err;
- }
-
- if (!inet_aton(bts_ip_mask, &ia_mask)) {
- fprintf(stderr, "invalid IP address: %s\n",
- bts_ip_mask);
- goto out_err;
- }
-
- printf("setting static IP Address/Mask\n");
- nv_put_ip_if_cfg(nmsg, ntohl(ia_addr.s_addr), ntohl(ia_mask.s_addr));
- need_to_set_attr = 1;
- }
- if (bts_ip_gw) {
- struct in_addr ia_gw;
-
- if (!inet_aton(bts_ip_gw, &ia_gw)) {
- fprintf(stderr, "invalid IP address: %s\n",
- bts_ip_gw);
- goto out_err;
- }
-
- printf("setting static IP Gateway\n");
- /* we only set the default gateway with zero addr/mask */
- nv_put_gw_cfg(nmsg, 0, 0, ntohl(ia_gw.s_addr));
- need_to_set_attr = 1;
- }
-
- if (need_to_set_attr) {
- abis_nm_ipaccess_set_nvattr(trx, nmsg->head, nmsg->len);
- oml_state = 1;
- }
-
- if (restart && !prim_oml_ip && !software) {
- printf("restarting BTS\n");
- abis_nm_ipaccess_restart(trx);
- }
-
-out_err:
- msgb_free(nmsg);
-}
-
-static int nm_state_event(int evt, uint8_t obj_class, void *obj,
- struct gsm_nm_state *old_state, struct gsm_nm_state *new_state,
- struct abis_om_obj_inst *obj_inst)
-{
- if (obj_class == NM_OC_BASEB_TRANSC) {
- if (!found_trx && obj_inst->trx_nr != 0xff) {
- struct gsm_bts_trx *trx = container_of(obj, struct gsm_bts_trx, bb_transc);
- bootstrap_om(trx);
- found_trx = 1;
- }
- } else if (evt == S_NM_STATECHG_OPER &&
- obj_class == NM_OC_RADIO_CARRIER &&
- new_state->availability == 3) {
- struct gsm_bts_trx *trx = obj;
-
- if (net_listen_testnr)
- ipac_nwl_test_start(trx, net_listen_testnr,
- phys_conf_min, sizeof(phys_conf_min));
- else if (software) {
- int rc;
- printf("Attempting software upload with '%s'\n", software);
- rc = abis_nm_software_load(trx->bts, trx->nr, software, 19, 0, swload_cbfn, trx);
- if (rc < 0) {
- fprintf(stderr, "Failed to start software load\n");
- exit(-3);
- }
- }
- }
- return 0;
-}
-
-static struct abis_nm_sw_desc *create_swload(struct sdp_header *header)
-{
- struct abis_nm_sw_desc *load;
-
- load = talloc_zero(tall_ctx_config, struct abis_nm_sw_desc);
-
- osmo_strlcpy((char *)load->file_id, header->firmware_info.sw_part,
- sizeof(load->file_id));
- load->file_id_len = strlen((char*)load->file_id) + 1;
-
- osmo_strlcpy((char *)load->file_version, header->firmware_info.version,
- sizeof(load->file_version));
- load->file_version_len = strlen((char*)load->file_version) + 1;
-
- return load;
-}
-
-static int find_sw_load_params(const char *filename)
-{
- struct stat stat;
- struct sdp_header *header;
- struct llist_head *entry;
- int fd;
- void *tall_firm_ctx = 0;
-
- entry = talloc_zero(tall_firm_ctx, struct llist_head);
- INIT_LLIST_HEAD(entry);
-
- fd = open(filename, O_RDONLY);
- if (!fd) {
- perror("nada");
- return -1;
- }
-
- /* verify the file */
- if (fstat(fd, &stat) == -1) {
- perror("Can not stat the file");
- close(fd);
- return -1;
- }
-
- ipaccess_analyze_file(fd, stat.st_size, 0, entry);
- if (close(fd) != 0) {
- perror("Close failed.\n");
- return -1;
- }
-
- /* try to find what we are looking for */
- llist_for_each_entry(header, entry, entry) {
- if (ntohs(header->firmware_info.more_more_magic) == 0x1000) {
- sw_load1 = create_swload(header);
- } else if (ntohs(header->firmware_info.more_more_magic) == 0x2001) {
- sw_load2 = create_swload(header);
- }
- }
-
- if (!sw_load1 || !sw_load2) {
- fprintf(stderr, "Did not find data.\n");
- talloc_free(tall_firm_ctx);
- return -1;
- }
-
- talloc_free(tall_firm_ctx);
- return 0;
-}
-
-static void dump_entry(struct sdp_header_item *sub_entry, int part, int fd)
-{
- int out_fd;
- int copied;
- char filename[4096];
- off_t target;
-
- if (!dump_files)
- return;
-
- if (sub_entry->header_entry.something1 == 0)
- return;
-
- snprintf(filename, sizeof(filename), "part.%d", part++);
- out_fd = open(filename, O_WRONLY | O_CREAT, 0660);
- if (out_fd < 0) {
- perror("Can not dump firmware");
- return;
- }
-
- target = sub_entry->absolute_offset + ntohl(sub_entry->header_entry.start) + 4;
- if (lseek(fd, target, SEEK_SET) != target) {
- perror("seek failed");
- close(out_fd);
- return;
- }
-
- for (copied = 0; copied < ntohl(sub_entry->header_entry.length); ++copied) {
- char c;
- if (read(fd, &c, sizeof(c)) != sizeof(c)) {
- perror("copy failed");
- break;
- }
-
- if (write(out_fd, &c, sizeof(c)) != sizeof(c)) {
- perror("write failed");
- break;
- }
- }
-
- close(out_fd);
-}
-
-static void analyze_firmware(const char *filename)
-{
- struct stat stat;
- struct sdp_header *header;
- struct sdp_header_item *sub_entry;
- struct llist_head *entry;
- int fd;
- void *tall_firm_ctx = 0;
- int part = 0;
-
- entry = talloc_zero(tall_firm_ctx, struct llist_head);
- INIT_LLIST_HEAD(entry);
-
- printf("Opening possible firmware '%s'\n", filename);
- fd = open(filename, O_RDONLY);
- if (!fd) {
- perror("nada");
- return;
- }
-
- /* verify the file */
- if (fstat(fd, &stat) == -1) {
- perror("Can not stat the file");
- close(fd);
- return;
- }
-
- ipaccess_analyze_file(fd, stat.st_size, 0, entry);
-
- llist_for_each_entry(header, entry, entry) {
- printf("Printing header information:\n");
- printf("more_more_magic: 0x%x\n", ntohs(header->firmware_info.more_more_magic));
- printf("header_length: %u\n", ntohl(header->firmware_info.header_length));
- printf("file_length: %u\n", ntohl(header->firmware_info.file_length));
- printf("sw_part: %.20s\n", header->firmware_info.sw_part);
- printf("text1: %.64s\n", header->firmware_info.text1);
- printf("time: %.12s\n", header->firmware_info.time);
- printf("date: %.14s\n", header->firmware_info.date);
- printf("text2: %.10s\n", header->firmware_info.text2);
- printf("version: %.20s\n", header->firmware_info.version);
- printf("subitems...\n");
-
- llist_for_each_entry(sub_entry, &header->header_list, entry) {
- printf("\tsomething1: %u\n", sub_entry->header_entry.something1);
- printf("\ttext1: %.64s\n", sub_entry->header_entry.text1);
- printf("\ttime: %.12s\n", sub_entry->header_entry.time);
- printf("\tdate: %.14s\n", sub_entry->header_entry.date);
- printf("\ttext2: %.10s\n", sub_entry->header_entry.text2);
- printf("\tversion: %.20s\n", sub_entry->header_entry.version);
- printf("\tlength: %u\n", ntohl(sub_entry->header_entry.length));
- printf("\taddr1: 0x%x\n", ntohl(sub_entry->header_entry.addr1));
- printf("\taddr2: 0x%x\n", ntohl(sub_entry->header_entry.addr2));
- printf("\tstart: 0x%x\n", ntohl(sub_entry->header_entry.start));
- printf("\tabs. offset: 0x%lx\n", sub_entry->absolute_offset);
- printf("\n\n");
-
- dump_entry(sub_entry, part++, fd);
- }
- printf("\n\n");
- }
-
- if (close(fd) != 0) {
- perror("Close failed.\n");
- return;
- }
-
- talloc_free(tall_firm_ctx);
-}
-
-static void print_usage(void)
-{
- printf("Usage: ipaccess-config IP_OF_BTS\n");
-}
-
-static void print_help(void)
-{
-#if 0
- printf("Commands for reading from the BTS:\n");
- printf(" -D --dump\t\t\tDump the BTS configuration\n");
- printf("\n");
-#endif
- printf("Commands for writing to the BTS:\n");
- printf(" -u --unit-id UNIT_ID\t\tSet the Unit ID of the BTS\n");
- printf(" -o --oml-ip IP\t\tSet primary OML IP (IP of your BSC)\n");
- printf(" -i --ip-address IP/MASK\tSet static IP address + netmask of BTS\n");
- printf(" -g --ip-gateway IP\t\tSet static IP gateway of BTS\n");
- printf(" -r --restart\t\t\tRestart the BTS (after other operations)\n");
- printf(" -n --nvram-flags FLAGS/MASK\tSet NVRAM attributes\n");
- printf(" -S --nvattr-set FLAG\tSet one additional NVRAM attribute\n");
- printf(" -U --nvattr-unset FLAG\tSet one additional NVRAM attribute\n");
- printf(" -l --listen TESTNR\t\tPerform specified test number\n");
- printf(" -L --Listen TEST_NAME\t\tPerform specified test\n");
- printf(" -s --stream-id ID\t\tSet the IPA Stream Identifier for OML\n");
- printf(" -d --software FIRMWARE\tDownload firmware into BTS\n");
- printf("\n");
- printf("Miscellaneous commands:\n");
- printf(" -h --help\t\t\tthis text\n");
- printf(" -H --HELP\t\t\tPrint parameter details.\n");
- printf(" -f --firmware FIRMWARE\tProvide firmware information\n");
- printf(" -w --write-firmware\t\tThis will dump the firmware parts to the filesystem. Use with -f.\n");
- printf(" -p --loop\t\t\tLoop the tests executed with the --listen command.\n");
-}
-
-static void print_value_string(const struct value_string *val, int size)
-{
- int i;
-
- for (i = 0; i < size - 1; ++i) {
- char sep = val[i + 1].str == NULL ? '.' : ',';
- printf("%s%c ", val[i].str, sep);
- }
- printf("\n");
-}
-
-static void print_options(void)
-{
-
- printf("Options for NVRAM (-S,-U):\n ");
- print_value_string(&ipa_nvflag_strs[0], ARRAY_SIZE(ipa_nvflag_strs));
-
- printf("Options for Tests (-L):\n ");
- print_value_string(&ipa_test_strs[0], ARRAY_SIZE(ipa_test_strs));
-}
-
-extern void bts_model_nanobts_init();
-
-int main(int argc, char **argv)
-{
- struct gsm_bts *bts;
- struct sockaddr_in sin;
- int rc, option_index = 0, stream_id = 0xff;
-
- tall_ctx_config = talloc_named_const(NULL, 0, "ipaccess-config");
- msgb_talloc_ctx_init(tall_ctx_config, 0);
-
- osmo_init_logging(&log_info);
- log_parse_category_mask(osmo_stderr_target, "DNM,0");
- bts_model_nanobts_init();
-
- printf("ipaccess-config (C) 2009-2010 by Harald Welte and others\n");
- printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n");
-
- while (1) {
- int c;
- unsigned long ul;
- char *slash;
- static struct option long_options[] = {
- { "unit-id", 1, 0, 'u' },
- { "oml-ip", 1, 0, 'o' },
- { "ip-address", 1, 0, 'i' },
- { "ip-gateway", 1, 0, 'g' },
- { "restart", 0, 0, 'r' },
- { "nvram-flags", 1, 0, 'n' },
- { "nvattr-set", 1, 0, 'S' },
- { "nvattr-unset", 1, 0, 'U' },
- { "help", 0, 0, 'h' },
- { "HELP", 0, 0, 'H' },
- { "listen", 1, 0, 'l' },
- { "Listen", 1, 0, 'L' },
- { "stream-id", 1, 0, 's' },
- { "software", 1, 0, 'd' },
- { "firmware", 1, 0, 'f' },
- { "write-firmware", 0, 0, 'w' },
- { "disable-color", 0, 0, 'c'},
- { "loop", 0, 0, 'p' },
- { 0, 0, 0, 0 },
- };
-
- c = getopt_long(argc, argv, "u:o:i:g:rn:S:U:l:L:hs:d:f:wcpH", long_options,
- &option_index);
-
- if (c == -1)
- break;
-
- switch (c) {
- case 'u':
- unit_id = optarg;
- break;
- case 'o':
- prim_oml_ip = optarg;
- break;
- case 'i':
- slash = strchr(optarg, '/');
- if (!slash)
- exit(2);
- bts_ip_addr = optarg;
- *slash = 0;
- bts_ip_mask = slash+1;
- break;
- case 'g':
- bts_ip_gw = optarg;
- break;
- case 'r':
- restart = 1;
- break;
- case 'n':
- slash = strchr(optarg, '/');
- if (!slash)
- exit(2);
- ul = strtoul(optarg, NULL, 16);
- nv_flags = ul & 0xffff;
- ul = strtoul(slash+1, NULL, 16);
- nv_mask = ul & 0xffff;
- break;
- case 'S':
- if (ipa_nvflag_set(&nv_flags, &nv_mask, optarg, 1) < 0)
- exit(2);
- break;
- case 'U':
- if (ipa_nvflag_set(&nv_flags, &nv_mask, optarg, 0) < 0)
- exit(2);
- break;
- case 'l':
- net_listen_testnr = atoi(optarg);
- break;
- case 'L':
- net_listen_testnr = get_string_value(ipa_test_strs,
- optarg);
- if (net_listen_testnr < 0) {
- fprintf(stderr,
- "The test '%s' is not known. Use -H to"
- " see available tests.\n", optarg);
- exit(2);
- }
- break;
- case 's':
- stream_id = atoi(optarg);
- break;
- case 'd':
- software = strdup(optarg);
- if (find_sw_load_params(optarg) != 0)
- exit(0);
- break;
- case 'f':
- firmware_analysis = optarg;
- break;
- case 'w':
- dump_files = 1;
- break;
- case 'c':
- log_set_use_color(osmo_stderr_target, 0);
- break;
- case 'p':
- loop_tests = 1;
- break;
- case 'h':
- print_usage();
- print_help();
- exit(0);
- case 'H':
- print_options();
- exit(0);
- }
- };
-
- if (firmware_analysis)
- analyze_firmware(firmware_analysis);
-
- if (optind >= argc) {
- /* only warn if we have not done anything else */
- if (!firmware_analysis)
- fprintf(stderr, "you have to specify the IP address of the BTS. Use --help for more information\n");
- exit(2);
- }
- libosmo_abis_init(tall_ctx_config);
-
- bsc_gsmnet = bsc_network_init(tall_bsc_ctx, 1, 1, NULL);
- if (!bsc_gsmnet)
- exit(1);
-
- bts = gsm_bts_alloc_register(bsc_gsmnet, GSM_BTS_TYPE_NANOBTS,
- HARDCODED_BSIC);
- /* ip.access supports up to 4 chained TRX */
- gsm_bts_trx_alloc(bts);
- gsm_bts_trx_alloc(bts);
- gsm_bts_trx_alloc(bts);
- bts->oml_tei = stream_id;
-
- osmo_signal_register_handler(SS_NM, nm_sig_cb, NULL);
- osmo_signal_register_handler(SS_IPAC_NWL, nwl_sig_cb, NULL);
-
- ipac_nwl_init();
-
- printf("Trying to connect to ip.access BTS ...\n");
-
- memset(&sin, 0, sizeof(sin));
- sin.sin_family = AF_INET;
- inet_aton(argv[optind], &sin.sin_addr);
- rc = ia_config_connect(bts, &sin);
- if (rc < 0) {
- perror("Error connecting to the BTS");
- exit(1);
- }
-
- bts->oml_link->ts->sign.delay = 10;
- bts->c0->rsl_link->ts->sign.delay = 10;
- while (1) {
- rc = osmo_select_main(0);
- if (rc < 0)
- exit(3);
- }
-
- exit(0);
-}
-
diff --git a/openbsc/src/ipaccess/ipaccess-firmware.c b/openbsc/src/ipaccess/ipaccess-firmware.c
deleted file mode 100644
index 5f55bb526..000000000
--- a/openbsc/src/ipaccess/ipaccess-firmware.c
+++ /dev/null
@@ -1,135 +0,0 @@
-/* Routines for parsing an ipacces SDP firmware file */
-
-/* (C) 2009 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 <openbsc/debug.h>
-#include <openbsc/ipaccess.h>
-#include <osmocom/core/talloc.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#define PART_LENGTH 138
-
-osmo_static_assert(sizeof(struct sdp_header_entry) == 138, right_entry);
-osmo_static_assert(sizeof(struct sdp_firmware) == 158, _right_header_length);
-
-/* more magic, the second "int" in the header */
-static char more_magic[] = { 0x10, 0x02 };
-
-int ipaccess_analyze_file(int fd, const unsigned int st_size, const unsigned int base_offset, struct llist_head *list)
-{
- struct sdp_firmware *firmware_header = 0;
- struct sdp_header *header;
- char buf[4096];
- int rc, i;
- uint16_t table_size;
- uint16_t table_offset;
- off_t table_start;
-
-
- rc = read(fd, buf, sizeof(*firmware_header));
- if (rc < 0) {
- perror("Can not read header start.");
- return -1;
- }
-
- firmware_header = (struct sdp_firmware *) &buf[0];
- if (strncmp(firmware_header->magic, " SDP", 4) != 0) {
- fprintf(stderr, "Wrong magic.\n");
- return -1;
- }
-
- if (memcmp(firmware_header->more_magic, more_magic, 2) != 0) {
- fprintf(stderr, "Wrong more magic. Got: 0x%x 0x%x vs. 0x%x 0x%x\n",
- firmware_header->more_magic[0] & 0xff, firmware_header->more_magic[1] & 0xff,
- more_magic[0], more_magic[1]);
- return -1;
- }
-
-
- if (ntohl(firmware_header->file_length) != st_size) {
- fprintf(stderr, "The filesize and the header do not match.\n");
- return -1;
- }
-
- /* add the firmware */
- header = talloc_zero(list, struct sdp_header);
- header->firmware_info = *firmware_header;
- INIT_LLIST_HEAD(&header->header_list);
- llist_add(&header->entry, list);
-
- table_offset = ntohs(firmware_header->table_offset);
- table_start = lseek(fd, table_offset, SEEK_CUR);
- if (table_start == -1) {
- fprintf(stderr, "Failed to seek to the rel position: 0x%x\n", table_offset);
- return -1;
- }
-
- if (read(fd, &table_size, sizeof(table_size)) != sizeof(table_size)) {
- fprintf(stderr, "The table size could not be read.\n");
- return -1;
- }
-
- table_size = ntohs(table_size);
-
- if (table_size % PART_LENGTH != 0) {
- fprintf(stderr, "The part length seems to be wrong: 0x%x\n", table_size);
- return -1;
- }
-
- /* look into each firmware now */
- for (i = 0; i < table_size / PART_LENGTH; ++i) {
- struct sdp_header_entry entry;
- struct sdp_header_item *header_entry;
- unsigned int offset = table_start + 2;
- offset += i * 138;
-
- if (lseek(fd, offset, SEEK_SET) != offset) {
- fprintf(stderr, "Can not seek to the offset: %u.\n", offset);
- return -1;
- }
-
- rc = read(fd, &entry, sizeof(entry));
- if (rc != sizeof(entry)) {
- fprintf(stderr, "Can not read the header entry.\n");
- return -1;
- }
-
- header_entry = talloc_zero(header, struct sdp_header_item);
- header_entry->header_entry = entry;
- header_entry->absolute_offset = base_offset;
- llist_add(&header_entry->entry, &header->header_list);
-
- /* now we need to find the SDP file... */
- offset = ntohl(entry.start) + 4 + base_offset;
- if (lseek(fd, offset, SEEK_SET) != offset) {
- perror("can't seek to sdp");
- return -1;
- }
-
-
- ipaccess_analyze_file(fd, ntohl(entry.length), offset, list);
- }
-
- return 0;
-}
-
diff --git a/openbsc/src/ipaccess/ipaccess-proxy.c b/openbsc/src/ipaccess/ipaccess-proxy.c
deleted file mode 100644
index d3674426c..000000000
--- a/openbsc/src/ipaccess/ipaccess-proxy.c
+++ /dev/null
@@ -1,1226 +0,0 @@
-/* OpenBSC Abis/IP proxy ip.access nanoBTS */
-
-/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
- * (C) 2010 by On-Waves
- * (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/>.
- *
- */
-
-#include <stdio.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <string.h>
-#include <signal.h>
-#include <time.h>
-#include <sys/fcntl.h>
-#include <sys/socket.h>
-#include <sys/ioctl.h>
-#include <arpa/inet.h>
-#include <netinet/in.h>
-
-#define _GNU_SOURCE
-#include <getopt.h>
-
-#include <openbsc/gsm_data.h>
-#include <osmocom/core/application.h>
-#include <osmocom/core/select.h>
-#include <osmocom/gsm/tlv.h>
-#include <osmocom/core/msgb.h>
-#include <osmocom/gsm/ipa.h>
-#include <osmocom/abis/ipa.h>
-#include <osmocom/abis/ipaccess.h>
-#include <openbsc/debug.h>
-#include <openbsc/ipaccess.h>
-#include <openbsc/socket.h>
-#include <osmocom/core/talloc.h>
-
-/* one instance of an ip.access protocol proxy */
-struct ipa_proxy {
- /* socket where we listen for incoming OML from BTS */
- struct osmo_fd oml_listen_fd;
- /* socket where we listen for incoming RSL from BTS */
- struct osmo_fd rsl_listen_fd;
- /* list of BTS's (struct ipa_bts_conn */
- struct llist_head bts_list;
- /* the BSC reconnect timer */
- struct osmo_timer_list reconn_timer;
- /* global GPRS NS data */
- struct in_addr gprs_addr;
- struct in_addr listen_addr;
-};
-
-/* global pointer to the proxy structure */
-static struct ipa_proxy *ipp;
-
-struct ipa_proxy_conn {
- struct osmo_fd fd;
- struct llist_head tx_queue;
- struct ipa_bts_conn *bts_conn;
-};
-#define MAX_TRX 4
-
-/* represents a particular BTS in our proxy */
-struct ipa_bts_conn {
- /* list of BTS's (ipa_proxy->bts_list) */
- struct llist_head list;
- /* back pointer to the proxy which we belong to */
- struct ipa_proxy *ipp;
- /* the unit ID as determined by CCM */
- struct {
- uint16_t site_id;
- uint16_t bts_id;
- } unit_id;
-
- /* incoming connections from BTS */
- struct ipa_proxy_conn *oml_conn;
- struct ipa_proxy_conn *rsl_conn[MAX_TRX];
-
- /* outgoing connections to BSC */
- struct ipa_proxy_conn *bsc_oml_conn;
- struct ipa_proxy_conn *bsc_rsl_conn[MAX_TRX];
-
- /* UDP sockets for BTS and BSC injection */
- struct osmo_fd udp_bts_fd;
- struct osmo_fd udp_bsc_fd;
-
- /* NS data */
- struct in_addr bts_addr;
- struct osmo_fd gprs_ns_fd;
- int gprs_local_port;
- uint16_t gprs_orig_port;
- uint32_t gprs_orig_ip;
-
- char *id_tags[256];
- uint8_t *id_resp;
- unsigned int id_resp_len;
-};
-
-enum ipp_fd_type {
- OML_FROM_BTS = 1,
- RSL_FROM_BTS = 2,
- OML_TO_BSC = 3,
- RSL_TO_BSC = 4,
- UDP_TO_BTS = 5,
- UDP_TO_BSC = 6,
-};
-
-/* some of the code against we link from OpenBSC needs this */
-void *tall_bsc_ctx;
-
-static char *listen_ipaddr;
-static char *bsc_ipaddr;
-static char *gprs_ns_ipaddr;
-
-static int gprs_ns_cb(struct osmo_fd *bfd, unsigned int what);
-
-#define PROXY_ALLOC_SIZE 1200
-
-static struct ipa_bts_conn *find_bts_by_unitid(struct ipa_proxy *ipp,
- uint16_t site_id,
- uint16_t bts_id)
-{
- struct ipa_bts_conn *ipbc;
-
- llist_for_each_entry(ipbc, &ipp->bts_list, list) {
- if (ipbc->unit_id.site_id == site_id &&
- ipbc->unit_id.bts_id == bts_id)
- return ipbc;
- }
-
- return NULL;
-}
-
-struct ipa_proxy_conn *alloc_conn(void)
-{
- struct ipa_proxy_conn *ipc;
-
- ipc = talloc_zero(tall_bsc_ctx, struct ipa_proxy_conn);
- if (!ipc)
- return NULL;
-
- INIT_LLIST_HEAD(&ipc->tx_queue);
-
- return ipc;
-}
-
-static int store_idtags(struct ipa_bts_conn *ipbc, struct tlv_parsed *tlvp)
-{
- unsigned int i, len;
-
- for (i = 0; i <= 0xff; i++) {
- if (!TLVP_PRESENT(tlvp, i))
- continue;
-
- len = TLVP_LEN(tlvp, i);
-#if 0
- if (!ipbc->id_tags[i])
- ipbc->id_tags[i] = talloc_size(tall_bsc_ctx, len);
- else
-#endif
- ipbc->id_tags[i] = talloc_realloc_size(ipbc,
- ipbc->id_tags[i], len);
- if (!ipbc->id_tags[i])
- return -ENOMEM;
-
- memset(ipbc->id_tags[i], 0, len);
- //memcpy(ipbc->id_tags[i], TLVP_VAL(tlvp, i), len);
- }
- return 0;
-}
-
-
-static struct ipa_proxy_conn *connect_bsc(struct sockaddr_in *sa, int priv_nr, void *data);
-
-#define logp_ipbc_uid(ss, lvl, ipbc, trx_id) _logp_ipbc_uid(ss, lvl, __FILE__, __LINE__, ipbc, trx_id)
-
-static void _logp_ipbc_uid(unsigned int ss, unsigned int lvl, char *file, int line,
- struct ipa_bts_conn *ipbc, uint8_t trx_id)
-{
- if (ipbc)
- logp2(ss, lvl, file, line, 0, "(%u/%u/%u) ", ipbc->unit_id.site_id,
- ipbc->unit_id.bts_id, trx_id);
- else
- logp2(ss, lvl, file, line, 0, "unknown ");
-}
-
-static int handle_udp_read(struct osmo_fd *bfd)
-{
- struct ipa_bts_conn *ipbc = bfd->data;
- struct ipa_proxy_conn *other_conn = NULL;
- struct msgb *msg = msgb_alloc(PROXY_ALLOC_SIZE, "Abis/IP UDP");
- struct ipaccess_head *hh;
- int ret;
-
- /* with UDP sockets, we cannot read partial packets but have to read
- * all of it in one go */
- hh = (struct ipaccess_head *) msg->data;
- ret = recv(bfd->fd, msg->data, msg->data_len, 0);
- if (ret < 0) {
- if (errno != EAGAIN)
- LOGP(DLINP, LOGL_ERROR, "recv error %s\n", strerror(errno));
- msgb_free(msg);
- return ret;
- }
- if (ret == 0) {
- DEBUGP(DLINP, "UDP peer disappeared, dead socket\n");
- osmo_fd_unregister(bfd);
- close(bfd->fd);
- bfd->fd = -1;
- msgb_free(msg);
- return -EIO;
- }
- if (ret < sizeof(*hh)) {
- DEBUGP(DLINP, "could not even read header!?!\n");
- msgb_free(msg);
- return -EIO;
- }
- msgb_put(msg, ret);
- msg->l2h = msg->data + sizeof(*hh);
- DEBUGP(DLMI, "UDP RX: %s\n", osmo_hexdump(msg->data, msg->len));
-
- if (hh->len != msg->len - sizeof(*hh)) {
- DEBUGP(DLINP, "length (%u/%u) disagrees with header(%u)\n",
- msg->len, msg->len - 3, hh->len);
- msgb_free(msg);
- return -EIO;
- }
-
- switch (bfd->priv_nr & 0xff) {
- case UDP_TO_BTS:
- /* injection towards BTS */
- switch (hh->proto) {
- case IPAC_PROTO_RSL:
- /* FIXME: what to do about TRX > 0 */
- other_conn = ipbc->rsl_conn[0];
- break;
- default:
- DEBUGP(DLINP, "Unknown protocol 0x%02x, sending to "
- "OML FD\n", hh->proto);
- /* fall through */
- case IPAC_PROTO_IPACCESS:
- case IPAC_PROTO_OML:
- other_conn = ipbc->oml_conn;
- break;
- }
- break;
- case UDP_TO_BSC:
- /* injection towards BSC */
- switch (hh->proto) {
- case IPAC_PROTO_RSL:
- /* FIXME: what to do about TRX > 0 */
- other_conn = ipbc->bsc_rsl_conn[0];
- break;
- default:
- DEBUGP(DLINP, "Unknown protocol 0x%02x, sending to "
- "OML FD\n", hh->proto);
- /* fall through */
- case IPAC_PROTO_IPACCESS:
- case IPAC_PROTO_OML:
- other_conn = ipbc->bsc_oml_conn;
- break;
- }
- break;
- default:
- DEBUGP(DLINP, "Unknown filedescriptor priv_nr=%04x\n", bfd->priv_nr);
- break;
- }
-
- if (other_conn) {
- /* enqueue the message for TX on the respective FD */
- msgb_enqueue(&other_conn->tx_queue, msg);
- other_conn->fd.when |= BSC_FD_WRITE;
- } else
- msgb_free(msg);
-
- return 0;
-}
-
-static int handle_udp_write(struct osmo_fd *bfd)
-{
- /* not implemented yet */
- bfd->when &= ~BSC_FD_WRITE;
-
- return -EIO;
-}
-
-/* callback from select.c in case one of the fd's can be read/written */
-static int udp_fd_cb(struct osmo_fd *bfd, unsigned int what)
-{
- int rc = 0;
-
- if (what & BSC_FD_READ)
- rc = handle_udp_read(bfd);
- if (what & BSC_FD_WRITE)
- rc = handle_udp_write(bfd);
-
- return rc;
-}
-
-
-static int ipbc_alloc_connect(struct ipa_proxy_conn *ipc, struct osmo_fd *bfd,
- uint16_t site_id, uint16_t bts_id,
- uint16_t trx_id, struct tlv_parsed *tlvp,
- struct msgb *msg)
-{
- struct ipa_bts_conn *ipbc;
- uint16_t udp_port;
- int ret = 0;
- struct sockaddr_in sin;
-
- memset(&sin, 0, sizeof(sin));
- sin.sin_family = AF_INET;
- inet_aton(bsc_ipaddr, &sin.sin_addr);
-
- DEBUGP(DLINP, "(%u/%u/%u) New BTS connection: ",
- site_id, bts_id, trx_id);
-
- /* OML needs to be established before RSL */
- if ((bfd->priv_nr & 0xff) != OML_FROM_BTS) {
- DEBUGPC(DLINP, "Not a OML connection ?!?\n");
- return -EIO;
- }
-
- /* allocate new BTS connection data structure */
- ipbc = talloc_zero(tall_bsc_ctx, struct ipa_bts_conn);
- if (!ipbc) {
- ret = -ENOMEM;
- goto err_out;
- }
-
- DEBUGPC(DLINP, "Created BTS Conn data structure\n");
- ipbc->ipp = ipp;
- ipbc->unit_id.site_id = site_id;
- ipbc->unit_id.bts_id = bts_id;
- ipbc->oml_conn = ipc;
- ipc->bts_conn = ipbc;
-
- /* store the content of the ID TAGS for later reference */
- store_idtags(ipbc, tlvp);
- ipbc->id_resp_len = msg->len;
- ipbc->id_resp = talloc_size(tall_bsc_ctx, ipbc->id_resp_len);
- memcpy(ipbc->id_resp, msg->data, ipbc->id_resp_len);
-
- /* Create OML TCP connection towards BSC */
- sin.sin_port = htons(IPA_TCP_PORT_OML);
- ipbc->bsc_oml_conn = connect_bsc(&sin, OML_TO_BSC, ipbc);
- if (!ipbc->bsc_oml_conn) {
- ret = -EIO;
- goto err_bsc_conn;
- }
-
- DEBUGP(DLINP, "(%u/%u/%u) OML Connected to BSC\n",
- site_id, bts_id, trx_id);
-
- /* Create UDP socket for BTS packet injection */
- udp_port = 10000 + (site_id % 1000)*100 + (bts_id % 100);
- ret = make_sock(&ipbc->udp_bts_fd, IPPROTO_UDP, INADDR_ANY, udp_port,
- UDP_TO_BTS, udp_fd_cb, ipbc);
- if (ret < 0)
- goto err_udp_bts;
- DEBUGP(DLINP, "(%u/%u/%u) Created UDP socket for injection "
- "towards BTS at port %u\n", site_id, bts_id, trx_id, udp_port);
-
- /* Create UDP socket for BSC packet injection */
- udp_port = 20000 + (site_id % 1000)*100 + (bts_id % 100);
- ret = make_sock(&ipbc->udp_bsc_fd, IPPROTO_UDP, INADDR_ANY, udp_port,
- UDP_TO_BSC, udp_fd_cb, ipbc);
- if (ret < 0)
- goto err_udp_bsc;
- DEBUGP(DLINP, "(%u/%u/%u) Created UDP socket for injection "
- "towards BSC at port %u\n", site_id, bts_id, trx_id, udp_port);
-
-
- /* GPRS NS related code */
- if (gprs_ns_ipaddr) {
- struct sockaddr_in sock;
- socklen_t len = sizeof(sock);
- struct in_addr addr;
- uint32_t ip;
-
- inet_aton(listen_ipaddr, &addr);
- ip = ntohl(addr.s_addr); /* make_sock() needs host byte order */
- ret = make_sock(&ipbc->gprs_ns_fd, IPPROTO_UDP, ip, 0, 0,
- gprs_ns_cb, ipbc);
- if (ret < 0) {
- LOGP(DLINP, LOGL_ERROR, "Creating the GPRS socket failed.\n");
- goto err_udp_bsc;
- }
-
- ret = getsockname(ipbc->gprs_ns_fd.fd, (struct sockaddr* ) &sock, &len);
- ipbc->gprs_local_port = ntohs(sock.sin_port);
- LOGP(DLINP, LOGL_NOTICE,
- "Created GPRS NS Socket. Listening on: %s:%d\n",
- inet_ntoa(sock.sin_addr), ipbc->gprs_local_port);
-
- ret = getpeername(bfd->fd, (struct sockaddr* ) &sock, &len);
- ipbc->bts_addr = sock.sin_addr;
- }
-
- llist_add(&ipbc->list, &ipp->bts_list);
-
- return 0;
-
-err_udp_bsc:
- osmo_fd_unregister(&ipbc->udp_bts_fd);
-err_udp_bts:
- osmo_fd_unregister(&ipbc->bsc_oml_conn->fd);
- close(ipbc->bsc_oml_conn->fd.fd);
- talloc_free(ipbc->bsc_oml_conn);
- ipbc->bsc_oml_conn = NULL;
-err_bsc_conn:
- talloc_free(ipbc->id_resp);
- talloc_free(ipbc);
-#if 0
- osmo_fd_unregister(bfd);
- close(bfd->fd);
- talloc_free(bfd);
-#endif
-err_out:
- return ret;
-}
-
-static int ipaccess_rcvmsg(struct ipa_proxy_conn *ipc, struct msgb *msg,
- struct osmo_fd *bfd)
-{
- struct tlv_parsed tlvp;
- uint8_t msg_type = *(msg->l2h);
- struct ipaccess_unit unit_data;
- struct ipa_bts_conn *ipbc;
- int ret = 0;
-
- switch (msg_type) {
- case IPAC_MSGT_PING:
- ret = ipa_ccm_send_pong(bfd->fd);
- break;
- case IPAC_MSGT_PONG:
- DEBUGP(DLMI, "PONG!\n");
- break;
- case IPAC_MSGT_ID_RESP:
- DEBUGP(DLMI, "ID_RESP ");
- /* parse tags, search for Unit ID */
- ipa_ccm_idtag_parse(&tlvp, (uint8_t *)msg->l2h + 2,
- msgb_l2len(msg)-2);
- DEBUGP(DLMI, "\n");
-
- if (!TLVP_PRESENT(&tlvp, IPAC_IDTAG_UNIT)) {
- LOGP(DLINP, LOGL_ERROR, "No Unit ID in ID RESPONSE !?!\n");
- return -EIO;
- }
-
- /* lookup BTS, create sign_link, ... */
- memset(&unit_data, 0, sizeof(unit_data));
- ipa_parse_unitid((char *)TLVP_VAL(&tlvp, IPAC_IDTAG_UNIT),
- &unit_data);
- ipbc = find_bts_by_unitid(ipp, unit_data.site_id, unit_data.bts_id);
- if (!ipbc) {
- /* We have not found an ipbc (per-bts proxy instance)
- * for this BTS yet. The first connection of a new BTS must
- * be a OML connection. We allocate the associated data structures,
- * and try to connect to the remote end */
-
- return ipbc_alloc_connect(ipc, bfd, unit_data.site_id,
- unit_data.bts_id,
- unit_data.trx_id, &tlvp, msg);
- /* if this fails, the caller will clean up bfd */
- } else {
- struct sockaddr_in sin;
- memset(&sin, 0, sizeof(sin));
- sin.sin_family = AF_INET;
- inet_aton(bsc_ipaddr, &sin.sin_addr);
-
- DEBUGP(DLINP, "Identified BTS %u/%u/%u\n",
- unit_data.site_id, unit_data.bts_id, unit_data.trx_id);
-
- if ((bfd->priv_nr & 0xff) != RSL_FROM_BTS) {
- LOGP(DLINP, LOGL_ERROR, "Second OML connection from "
- "same BTS ?!?\n");
- return 0;
- }
-
- if (unit_data.trx_id >= MAX_TRX) {
- LOGP(DLINP, LOGL_ERROR, "We don't support more "
- "than %u TRX\n", MAX_TRX);
- return -EINVAL;
- }
-
- ipc->bts_conn = ipbc;
- /* store TRX number in higher 8 bit of the bfd private number */
- bfd->priv_nr |= unit_data.trx_id << 8;
- ipbc->rsl_conn[unit_data.trx_id] = ipc;
-
- /* Create RSL TCP connection towards BSC */
- sin.sin_port = htons(IPA_TCP_PORT_RSL);
- ipbc->bsc_rsl_conn[unit_data.trx_id] =
- connect_bsc(&sin, RSL_TO_BSC | (unit_data.trx_id << 8), ipbc);
- if (!ipbc->bsc_oml_conn)
- return -EIO;
- DEBUGP(DLINP, "(%u/%u/%u) Connected RSL to BSC\n",
- unit_data.site_id, unit_data.bts_id, unit_data.trx_id);
- }
- break;
- case IPAC_MSGT_ID_GET:
- DEBUGP(DLMI, "ID_GET\n");
- if ((bfd->priv_nr & 0xff) != OML_TO_BSC &&
- (bfd->priv_nr & 0xff) != RSL_TO_BSC) {
- DEBUGP(DLINP, "IDentity REQuest from BTS ?!?\n");
- return -EIO;
- }
- ipbc = ipc->bts_conn;
- if (!ipbc) {
- DEBUGP(DLINP, "ID_GET from BSC before we have ID_RESP from BTS\n");
- return -EIO;
- }
- ret = write(bfd->fd, ipbc->id_resp, ipbc->id_resp_len);
- if (ret != ipbc->id_resp_len) {
- LOGP(DLINP, LOGL_ERROR, "Partial write: %d of %d\n",
- ret, ipbc->id_resp_len);
- return -EIO;
- }
- ret = 0;
- break;
- case IPAC_MSGT_ID_ACK:
- DEBUGP(DLMI, "ID_ACK? -> ACK!\n");
- ret = ipa_ccm_send_id_ack(bfd->fd);
- break;
- default:
- LOGP(DLMI, LOGL_ERROR, "Unhandled IPA type; %d\n", msg_type);
- return 1;
- break;
- }
- return ret;
-}
-
-struct msgb *ipaccess_proxy_read_msg(struct osmo_fd *bfd, int *error)
-{
- struct msgb *msg = msgb_alloc(PROXY_ALLOC_SIZE, "Abis/IP");
- struct ipaccess_head *hh;
- int len, ret = 0;
-
- if (!msg) {
- *error = -ENOMEM;
- return NULL;
- }
-
- /* first read our 3-byte header */
- hh = (struct ipaccess_head *) msg->data;
- ret = recv(bfd->fd, msg->data, 3, 0);
- if (ret < 0) {
- if (errno != EAGAIN)
- LOGP(DLINP, LOGL_ERROR, "recv error: %s\n", strerror(errno));
- msgb_free(msg);
- *error = ret;
- return NULL;
- } else if (ret == 0) {
- msgb_free(msg);
- *error = ret;
- return NULL;
- }
-
- msgb_put(msg, ret);
-
- /* then read te length as specified in header */
- msg->l2h = msg->data + sizeof(*hh);
- len = ntohs(hh->len);
- ret = recv(bfd->fd, msg->l2h, len, 0);
- if (ret < len) {
- LOGP(DLINP, LOGL_ERROR, "short read!\n");
- msgb_free(msg);
- *error = -EIO;
- return NULL;
- }
- msgb_put(msg, ret);
-
- return msg;
-}
-
-static struct ipa_proxy_conn *ipc_by_priv_nr(struct ipa_bts_conn *ipbc,
- unsigned int priv_nr)
-{
- struct ipa_proxy_conn *bsc_conn;
- unsigned int trx_id = priv_nr >> 8;
-
- switch (priv_nr & 0xff) {
- case OML_FROM_BTS: /* incoming OML data from BTS, forward to BSC OML */
- bsc_conn = ipbc->bsc_oml_conn;
- break;
- case RSL_FROM_BTS: /* incoming RSL data from BTS, forward to BSC RSL */
- bsc_conn = ipbc->bsc_rsl_conn[trx_id];
- break;
- case OML_TO_BSC: /* incoming OML data from BSC, forward to BTS OML */
- bsc_conn = ipbc->oml_conn;
- break;
- case RSL_TO_BSC: /* incoming RSL data from BSC, forward to BTS RSL */
- bsc_conn = ipbc->rsl_conn[trx_id];
- break;
- default:
- bsc_conn = NULL;
- break;
- }
- return bsc_conn;
-}
-
-static void reconn_tmr_cb(void *data)
-{
- struct ipa_proxy *ipp = data;
- struct ipa_bts_conn *ipbc;
- struct sockaddr_in sin;
- int i;
-
- DEBUGP(DLINP, "Running reconnect timer\n");
-
- memset(&sin, 0, sizeof(sin));
- sin.sin_family = AF_INET;
- inet_aton(bsc_ipaddr, &sin.sin_addr);
-
- llist_for_each_entry(ipbc, &ipp->bts_list, list) {
- /* if OML to BSC is dead, try to restore it */
- if (ipbc->oml_conn && !ipbc->bsc_oml_conn) {
- sin.sin_port = htons(IPA_TCP_PORT_OML);
- logp_ipbc_uid(DLINP, LOGL_NOTICE, ipbc, 0);
- LOGPC(DLINP, LOGL_NOTICE, "OML Trying to reconnect\n");
- ipbc->bsc_oml_conn = connect_bsc(&sin, OML_TO_BSC, ipbc);
- if (!ipbc->bsc_oml_conn)
- goto reschedule;
- logp_ipbc_uid(DLINP, LOGL_NOTICE, ipbc, 0);
- LOGPC(DLINP, LOGL_NOTICE, "OML Reconnected\n");
- }
- /* if we (still) don't have a OML connection, skip RSL */
- if (!ipbc->oml_conn || !ipbc->bsc_oml_conn)
- continue;
-
- for (i = 0; i < ARRAY_SIZE(ipbc->rsl_conn); i++) {
- unsigned int priv_nr;
- /* don't establish RSL links which we don't have */
- if (!ipbc->rsl_conn[i])
- continue;
- if (ipbc->bsc_rsl_conn[i])
- continue;
- priv_nr = ipbc->rsl_conn[i]->fd.priv_nr;
- priv_nr &= ~0xff;
- priv_nr |= RSL_TO_BSC;
- sin.sin_port = htons(IPA_TCP_PORT_RSL);
- logp_ipbc_uid(DLINP, LOGL_NOTICE, ipbc, priv_nr >> 8);
- LOGPC(DLINP, LOGL_NOTICE, "RSL Trying to reconnect\n");
- ipbc->bsc_rsl_conn[i] = connect_bsc(&sin, priv_nr, ipbc);
- if (!ipbc->bsc_rsl_conn[i])
- goto reschedule;
- logp_ipbc_uid(DLINP, LOGL_NOTICE, ipbc, priv_nr >> 8);
- LOGPC(DLINP, LOGL_NOTICE, "RSL Reconnected\n");
- }
- }
- return;
-
-reschedule:
- osmo_timer_schedule(&ipp->reconn_timer, 5, 0);
-}
-
-static void handle_dead_socket(struct osmo_fd *bfd)
-{
- struct ipa_proxy_conn *ipc = bfd->data; /* local conn */
- struct ipa_proxy_conn *bsc_conn; /* remote conn */
- struct ipa_bts_conn *ipbc = ipc->bts_conn;
- unsigned int trx_id = bfd->priv_nr >> 8;
- struct msgb *msg, *msg2;
-
- osmo_fd_unregister(bfd);
- close(bfd->fd);
- bfd->fd = -1;
-
- /* FIXME: clear tx_queue, remove all references, etc. */
- llist_for_each_entry_safe(msg, msg2, &ipc->tx_queue, list)
- msgb_free(msg);
-
- switch (bfd->priv_nr & 0xff) {
- case OML_FROM_BTS: /* incoming OML data from BTS, forward to BSC OML */
- /* The BTS started a connection with us but we got no
- * IPAC_MSGT_ID_RESP message yet, in that scenario we did not
- * allocate the ipa_bts_conn structure. */
- if (ipbc == NULL)
- break;
- ipbc->oml_conn = NULL;
- bsc_conn = ipbc->bsc_oml_conn;
- /* close the connection to the BSC */
- osmo_fd_unregister(&bsc_conn->fd);
- close(bsc_conn->fd.fd);
- llist_for_each_entry_safe(msg, msg2, &bsc_conn->tx_queue, list)
- msgb_free(msg);
- talloc_free(bsc_conn);
- ipbc->bsc_oml_conn = NULL;
- /* FIXME: do we need to delete the entire ipbc ? */
- break;
- case RSL_FROM_BTS: /* incoming RSL data from BTS, forward to BSC RSL */
- ipbc->rsl_conn[trx_id] = NULL;
- bsc_conn = ipbc->bsc_rsl_conn[trx_id];
- /* close the connection to the BSC */
- osmo_fd_unregister(&bsc_conn->fd);
- close(bsc_conn->fd.fd);
- llist_for_each_entry_safe(msg, msg2, &bsc_conn->tx_queue, list)
- msgb_free(msg);
- talloc_free(bsc_conn);
- ipbc->bsc_rsl_conn[trx_id] = NULL;
- break;
- case OML_TO_BSC: /* incoming OML data from BSC, forward to BTS OML */
- ipbc->bsc_oml_conn = NULL;
- bsc_conn = ipbc->oml_conn;
- /* start reconnect timer */
- osmo_timer_schedule(&ipp->reconn_timer, 5, 0);
- break;
- case RSL_TO_BSC: /* incoming RSL data from BSC, forward to BTS RSL */
- ipbc->bsc_rsl_conn[trx_id] = NULL;
- bsc_conn = ipbc->rsl_conn[trx_id];
- /* start reconnect timer */
- osmo_timer_schedule(&ipp->reconn_timer, 5, 0);
- break;
- default:
- bsc_conn = NULL;
- break;
- }
-
- talloc_free(ipc);
-}
-
-static void patch_gprs_msg(struct ipa_bts_conn *ipbc, int priv_nr, struct msgb *msg)
-{
- uint8_t *nsvci;
-
- if ((priv_nr & 0xff) != OML_FROM_BTS && (priv_nr & 0xff) != OML_TO_BSC)
- return;
-
- if (msgb_l2len(msg) != 39)
- return;
-
- /*
- * Check if this is a IPA Set Attribute or IPA Set Attribute ACK
- * and if the FOM Class is GPRS NSVC0 and then we will patch it.
- *
- * The patch assumes the message looks like the one from the trace
- * but we only match messages with a specific size anyway... So
- * this hack should work just fine.
- */
-
- if (msg->l2h[0] == 0x10 && msg->l2h[1] == 0x80 &&
- msg->l2h[2] == 0x00 && msg->l2h[3] == 0x15 &&
- msg->l2h[18] == 0xf5 && msg->l2h[19] == 0xf2) {
- nsvci = &msg->l2h[23];
- ipbc->gprs_orig_port = *(uint16_t *)(nsvci+8);
- ipbc->gprs_orig_ip = *(uint32_t *)(nsvci+10);
- *(uint16_t *)(nsvci+8) = htons(ipbc->gprs_local_port);
- *(uint32_t *)(nsvci+10) = ipbc->ipp->listen_addr.s_addr;
- } else if (msg->l2h[0] == 0x10 && msg->l2h[1] == 0x80 &&
- msg->l2h[2] == 0x00 && msg->l2h[3] == 0x15 &&
- msg->l2h[18] == 0xf6 && msg->l2h[19] == 0xf2) {
- nsvci = &msg->l2h[23];
- *(uint16_t *)(nsvci+8) = ipbc->gprs_orig_port;
- *(uint32_t *)(nsvci+10) = ipbc->gprs_orig_ip;
- }
-}
-
-static int handle_tcp_read(struct osmo_fd *bfd)
-{
- struct ipa_proxy_conn *ipc = bfd->data;
- struct ipa_bts_conn *ipbc = ipc->bts_conn;
- struct ipa_proxy_conn *bsc_conn;
- struct msgb *msg;
- struct ipaccess_head *hh;
- int ret = 0;
- char *btsbsc;
-
- if ((bfd->priv_nr & 0xff) <= 2)
- btsbsc = "BTS";
- else
- btsbsc = "BSC";
-
- msg = ipaccess_proxy_read_msg(bfd, &ret);
- if (!msg) {
- if (ret == 0) {
- logp_ipbc_uid(DLINP, LOGL_NOTICE, ipbc, bfd->priv_nr >> 8);
- LOGPC(DLINP, LOGL_NOTICE, "%s disappeared, "
- "dead socket\n", btsbsc);
- handle_dead_socket(bfd);
- }
- return ret;
- }
-
- msgb_put(msg, ret);
- logp_ipbc_uid(DLMI, LOGL_DEBUG, ipbc, bfd->priv_nr >> 8);
- DEBUGPC(DLMI, "RX<-%s: %s\n", btsbsc, osmo_hexdump(msg->data, msg->len));
-
- hh = (struct ipaccess_head *) msg->data;
- if (hh->proto == IPAC_PROTO_IPACCESS) {
- ret = ipaccess_rcvmsg(ipc, msg, bfd);
- if (ret < 0) {
- osmo_fd_unregister(bfd);
- close(bfd->fd);
- bfd->fd = -1;
- talloc_free(bfd);
- msgb_free(msg);
- return ret;
- } else if (ret == 0) {
- /* we do not forward parts of the CCM protocol
- * through the proxy but rather terminate it ourselves. */
- msgb_free(msg);
- return ret;
- }
- }
-
- if (!ipbc) {
- LOGP(DLINP, LOGL_ERROR,
- "received %s packet but no ipc->bts_conn?!?\n", btsbsc);
- msgb_free(msg);
- return -EIO;
- }
-
- bsc_conn = ipc_by_priv_nr(ipbc, bfd->priv_nr);
- if (bsc_conn) {
- if (gprs_ns_ipaddr)
- patch_gprs_msg(ipbc, bfd->priv_nr, msg);
- /* enqueue packet towards BSC */
- msgb_enqueue(&bsc_conn->tx_queue, msg);
- /* mark respective filedescriptor as 'we want to write' */
- bsc_conn->fd.when |= BSC_FD_WRITE;
- } else {
- logp_ipbc_uid(DLINP, LOGL_INFO, ipbc, bfd->priv_nr >> 8);
- LOGPC(DLINP, LOGL_INFO, "Dropping packet from %s, "
- "since remote connection is dead\n", btsbsc);
- msgb_free(msg);
- }
-
- return ret;
-}
-
-/* a TCP socket is ready to be written to */
-static int handle_tcp_write(struct osmo_fd *bfd)
-{
- struct ipa_proxy_conn *ipc = bfd->data;
- struct ipa_bts_conn *ipbc = ipc->bts_conn;
- struct llist_head *lh;
- struct msgb *msg;
- char *btsbsc;
- int ret;
-
- if ((bfd->priv_nr & 0xff) <= 2)
- btsbsc = "BTS";
- else
- btsbsc = "BSC";
-
-
- /* get the next msg for this timeslot */
- if (llist_empty(&ipc->tx_queue)) {
- bfd->when &= ~BSC_FD_WRITE;
- return 0;
- }
- lh = ipc->tx_queue.next;
- llist_del(lh);
- msg = llist_entry(lh, struct msgb, list);
-
- logp_ipbc_uid(DLMI, LOGL_DEBUG, ipbc, bfd->priv_nr >> 8);
- DEBUGPC(DLMI, "TX %04x: %s\n", bfd->priv_nr,
- osmo_hexdump(msg->data, msg->len));
-
- ret = send(bfd->fd, msg->data, msg->len, 0);
- msgb_free(msg);
-
- if (ret == 0) {
- logp_ipbc_uid(DLINP, LOGL_NOTICE, ipbc, bfd->priv_nr >> 8);
- LOGP(DLINP, LOGL_NOTICE, "%s disappeared, dead socket\n", btsbsc);
- handle_dead_socket(bfd);
- }
-
- return ret;
-}
-
-/* callback from select.c in case one of the fd's can be read/written */
-static int proxy_ipaccess_fd_cb(struct osmo_fd *bfd, unsigned int what)
-{
- int rc = 0;
-
- if (what & BSC_FD_READ) {
- rc = handle_tcp_read(bfd);
- if (rc < 0)
- return rc;
- }
- if (what & BSC_FD_WRITE)
- rc = handle_tcp_write(bfd);
-
- return rc;
-}
-
-/* callback of the listening filedescriptor */
-static int listen_fd_cb(struct osmo_fd *listen_bfd, unsigned int what)
-{
- int ret;
- struct ipa_proxy_conn *ipc;
- struct osmo_fd *bfd;
- struct sockaddr_in sa;
- socklen_t sa_len = sizeof(sa);
-
- if (!(what & BSC_FD_READ))
- return 0;
-
- ret = accept(listen_bfd->fd, (struct sockaddr *) &sa, &sa_len);
- if (ret < 0) {
- perror("accept");
- return ret;
- }
- DEBUGP(DLINP, "accept()ed new %s link from %s\n",
- (listen_bfd->priv_nr & 0xff) == OML_FROM_BTS ? "OML" : "RSL",
- inet_ntoa(sa.sin_addr));
-
- ipc = alloc_conn();
- if (!ipc) {
- close(ret);
- return -ENOMEM;
- }
-
- bfd = &ipc->fd;
- bfd->fd = ret;
- bfd->data = ipc;
- bfd->priv_nr = listen_bfd->priv_nr;
- bfd->cb = proxy_ipaccess_fd_cb;
- bfd->when = BSC_FD_READ;
- ret = osmo_fd_register(bfd);
- if (ret < 0) {
- LOGP(DLINP, LOGL_ERROR, "could not register FD\n");
- close(bfd->fd);
- talloc_free(ipc);
- return ret;
- }
-
- /* Request ID. FIXME: request LOCATION, HW/SW VErsion, Unit Name, Serno */
- ret = ipa_ccm_send_id_req(bfd->fd);
-
- return 0;
-}
-
-static void send_ns(int fd, const char *buf, int size, struct in_addr ip, int port)
-{
- int ret;
- struct sockaddr_in addr;
- socklen_t len = sizeof(addr);
- memset(&addr, 0, sizeof(addr));
-
- addr.sin_family = AF_INET;
- addr.sin_port = htons(port);
- addr.sin_addr = ip;
-
- ret = sendto(fd, buf, size, 0, (struct sockaddr *) &addr, len);
- if (ret < 0) {
- LOGP(DLINP, LOGL_ERROR, "Failed to forward GPRS message.\n");
- }
-}
-
-static int gprs_ns_cb(struct osmo_fd *bfd, unsigned int what)
-{
- struct ipa_bts_conn *bts;
- char buf[4096];
- int ret;
- struct sockaddr_in sock;
- socklen_t len = sizeof(sock);
-
- /* 1. get the data... */
- ret = recvfrom(bfd->fd, buf, sizeof(buf), 0, (struct sockaddr *) &sock, &len);
- if (ret < 0) {
- LOGP(DLINP, LOGL_ERROR, "Failed to recv GPRS NS msg: %s.\n", strerror(errno));
- return -1;
- }
-
- bts = bfd->data;
-
- /* 2. figure out where to send it to */
- if (memcmp(&sock.sin_addr, &ipp->gprs_addr, sizeof(sock.sin_addr)) == 0) {
- LOGP(DLINP, LOGL_DEBUG, "GPRS NS msg from network.\n");
- send_ns(bfd->fd, buf, ret, bts->bts_addr, 23000);
- } else if (memcmp(&sock.sin_addr, &bts->bts_addr, sizeof(sock.sin_addr)) == 0) {
- LOGP(DLINP, LOGL_DEBUG, "GPRS NS msg from BTS.\n");
- send_ns(bfd->fd, buf, ret, ipp->gprs_addr, 23000);
- } else {
- LOGP(DLINP, LOGL_ERROR, "Unknown GPRS source: %s\n", inet_ntoa(sock.sin_addr));
- }
-
- return 0;
-}
-
-/* Actively connect to a BSC. */
-static struct ipa_proxy_conn *connect_bsc(struct sockaddr_in *sa, int priv_nr, void *data)
-{
- struct ipa_proxy_conn *ipc;
- struct osmo_fd *bfd;
- int ret, on = 1;
-
- ipc = alloc_conn();
- if (!ipc)
- return NULL;
-
- ipc->bts_conn = data;
-
- bfd = &ipc->fd;
- bfd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- bfd->cb = ipaccess_fd_cb;
- bfd->when = BSC_FD_READ | BSC_FD_WRITE;
- bfd->data = ipc;
- bfd->priv_nr = priv_nr;
-
- if (bfd->fd < 0) {
- LOGP(DLINP, LOGL_ERROR, "Could not create socket: %s\n",
- strerror(errno));
- talloc_free(ipc);
- return NULL;
- }
-
- ret = setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
- if (ret < 0) {
- LOGP(DLINP, LOGL_ERROR, "Could not set socket option\n");
- close(bfd->fd);
- talloc_free(ipc);
- return NULL;
- }
-
- ret = connect(bfd->fd, (struct sockaddr *) sa, sizeof(*sa));
- if (ret < 0) {
- LOGP(DLINP, LOGL_ERROR, "Could not connect socket: %s\n",
- inet_ntoa(sa->sin_addr));
- close(bfd->fd);
- talloc_free(ipc);
- return NULL;
- }
-
- /* pre-fill tx_queue with identity request */
- ret = osmo_fd_register(bfd);
- if (ret < 0) {
- close(bfd->fd);
- talloc_free(ipc);
- return NULL;
- }
-
- return ipc;
-}
-
-static int ipaccess_proxy_setup(void)
-{
- int ret;
-
- ipp = talloc_zero(tall_bsc_ctx, struct ipa_proxy);
- if (!ipp)
- return -ENOMEM;
- INIT_LLIST_HEAD(&ipp->bts_list);
- osmo_timer_setup(&ipp->reconn_timer, reconn_tmr_cb, ipp);
-
- /* Listen for OML connections */
- ret = make_sock(&ipp->oml_listen_fd, IPPROTO_TCP, INADDR_ANY,
- IPA_TCP_PORT_OML, OML_FROM_BTS, listen_fd_cb, NULL);
- if (ret < 0)
- return ret;
-
- /* Listen for RSL connections */
- ret = make_sock(&ipp->rsl_listen_fd, IPPROTO_TCP, INADDR_ANY,
- IPA_TCP_PORT_RSL, RSL_FROM_BTS, listen_fd_cb, NULL);
-
- if (ret < 0)
- return ret;
-
- /* Connect the GPRS NS Socket */
- if (gprs_ns_ipaddr) {
- inet_aton(gprs_ns_ipaddr, &ipp->gprs_addr);
- inet_aton(listen_ipaddr, &ipp->listen_addr);
- }
-
- return ret;
-}
-
-static void signal_handler(int signal)
-{
- fprintf(stdout, "signal %u received\n", signal);
-
- switch (signal) {
- case SIGABRT:
- /* in case of abort, we want to obtain a talloc report
- * and then return to the caller, who will abort the process */
- case SIGUSR1:
- talloc_report_full(tall_bsc_ctx, stderr);
- break;
- default:
- break;
- }
-}
-
-static void print_help(void)
-{
- printf(" ipaccess-proxy is a proxy BTS.\n");
- printf(" -h --help. This help text.\n");
- printf(" -l --listen IP. The ip to listen to.\n");
- printf(" -b --bsc IP. The BSC IP address.\n");
- printf(" -g --gprs IP. Take GPRS NS from that IP.\n");
- printf("\n");
- printf(" -s --disable-color. Disable the color inside the logging message.\n");
- printf(" -e --log-level number. Set the global loglevel.\n");
- printf(" -T --timestamp. Prefix every log message with a timestamp.\n");
- printf(" -V --version. Print the version of OpenBSC.\n");
-}
-
-static void print_usage(void)
-{
- printf("Usage: ipaccess-proxy [options]\n");
-}
-
-enum {
- IPA_PROXY_OPT_LISTEN_NONE = 0,
- IPA_PROXY_OPT_LISTEN_IP = (1 << 0),
- IPA_PROXY_OPT_BSC_IP = (1 << 1),
-};
-
-static void handle_options(int argc, char** argv)
-{
- int options_mask = 0;
-
- /* disable explicit missing arguments error output from getopt_long */
- opterr = 0;
-
- while (1) {
- int option_index = 0, c;
- static struct option long_options[] = {
- {"help", 0, 0, 'h'},
- {"disable-color", 0, 0, 's'},
- {"timestamp", 0, 0, 'T'},
- {"log-level", 1, 0, 'e'},
- {"listen", 1, 0, 'l'},
- {"bsc", 1, 0, 'b'},
- {0, 0, 0, 0}
- };
-
- c = getopt_long(argc, argv, "hsTe:l:b:g:",
- long_options, &option_index);
- if (c == -1)
- break;
-
- switch (c) {
- case 'h':
- print_usage();
- print_help();
- exit(0);
- case 'l':
- listen_ipaddr = optarg;
- options_mask |= IPA_PROXY_OPT_LISTEN_IP;
- break;
- case 'b':
- bsc_ipaddr = optarg;
- options_mask |= IPA_PROXY_OPT_BSC_IP;
- break;
- case 'g':
- gprs_ns_ipaddr = optarg;
- break;
- case 's':
- log_set_use_color(osmo_stderr_target, 0);
- break;
- case 'T':
- log_set_print_timestamp(osmo_stderr_target, 1);
- break;
- case 'e':
- log_set_log_level(osmo_stderr_target, atoi(optarg));
- break;
- case '?':
- if (optopt) {
- printf("ERROR: missing mandatory argument "
- "for `%s' option\n", argv[optind-1]);
- } else {
- printf("ERROR: unknown option `%s'\n",
- argv[optind-1]);
- }
- print_usage();
- print_help();
- exit(EXIT_FAILURE);
- break;
- default:
- /* ignore */
- break;
- }
- }
- if ((options_mask & (IPA_PROXY_OPT_LISTEN_IP | IPA_PROXY_OPT_BSC_IP))
- != (IPA_PROXY_OPT_LISTEN_IP | IPA_PROXY_OPT_BSC_IP)) {
- printf("ERROR: You have to specify `--listen' and `--bsc' "
- "options at least.\n");
- print_usage();
- print_help();
- exit(EXIT_FAILURE);
- }
-}
-
-int main(int argc, char **argv)
-{
- int rc;
-
- tall_bsc_ctx = talloc_named_const(NULL, 1, "ipaccess-proxy");
- msgb_talloc_ctx_init(tall_bsc_ctx, 0);
-
- osmo_init_logging(&log_info);
- log_parse_category_mask(osmo_stderr_target, "DLINP:DLMI");
-
- handle_options(argc, argv);
-
- rc = ipaccess_proxy_setup();
- if (rc < 0)
- exit(1);
-
- signal(SIGUSR1, &signal_handler);
- signal(SIGABRT, &signal_handler);
- osmo_init_ignore_signals();
-
- while (1) {
- osmo_select_main(0);
- }
-}
diff --git a/openbsc/src/ipaccess/network_listen.c b/openbsc/src/ipaccess/network_listen.c
deleted file mode 100644
index 3b44ceb74..000000000
--- a/openbsc/src/ipaccess/network_listen.c
+++ /dev/null
@@ -1,257 +0,0 @@
-/* ip.access nanoBTS network listen mode */
-
-/* (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 <unistd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <stdint.h>
-
-#include <arpa/inet.h>
-
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/timer.h>
-#include <osmocom/gsm/rxlev_stat.h>
-#include <osmocom/gsm/gsm48_ie.h>
-
-#include <openbsc/gsm_data.h>
-#include <openbsc/abis_nm.h>
-#include <openbsc/signal.h>
-#include <openbsc/debug.h>
-#include <osmocom/abis/e1_input.h>
-
-#define WHITELIST_MAX_SIZE ((NUM_ARFCNS*2)+2+1)
-
-int ipac_rxlevstat2whitelist(uint16_t *buf, const struct rxlev_stats *st, uint8_t min_rxlev,
- uint16_t max_num_arfcns)
-{
- int i;
- unsigned int num_arfcn = 0;
-
- for (i = NUM_RXLEVS-1; i >= min_rxlev; i--) {
- int16_t arfcn = -1;
-
- while ((arfcn = rxlev_stat_get_next(st, i, arfcn)) >= 0) {
- *buf++ = htons(arfcn);
- num_arfcn++;
-
- }
-
- if (num_arfcn > max_num_arfcns)
- break;
- }
-
- return num_arfcn;
-}
-
-enum ipac_test_state {
- IPAC_TEST_S_IDLE,
- IPAC_TEST_S_RQD,
- IPAC_TEST_S_EXEC,
- IPAC_TEST_S_PARTIAL,
-};
-
-int ipac_nwl_test_start(struct gsm_bts_trx *trx, uint8_t testnr,
- const uint8_t *phys_conf, unsigned int phys_conf_len)
-{
- struct msgb *msg;
-
- if (trx->ipaccess.test_state != IPAC_TEST_S_IDLE) {
- fprintf(stderr, "Cannot start test in state %u\n", trx->ipaccess.test_state);
- return -EINVAL;
- }
-
- switch (testnr) {
- case NM_IPACC_TESTNO_CHAN_USAGE:
- case NM_IPACC_TESTNO_BCCH_CHAN_USAGE:
- rxlev_stat_reset(&trx->ipaccess.rxlev_stat);
- break;
- }
-
- msg = msgb_alloc_headroom(phys_conf_len+256, 128, "OML");
-
- if (phys_conf && phys_conf_len) {
- uint8_t *payload;
- /* first put the phys conf header */
- msgb_tv16_put(msg, NM_ATT_PHYS_CONF, phys_conf_len);
- payload = msgb_put(msg, phys_conf_len);
- memcpy(payload, phys_conf, phys_conf_len);
- }
-
- abis_nm_perform_test(trx->bts, NM_OC_RADIO_CARRIER, 0, trx->nr, 0xff,
- testnr, 1, msg);
- trx->ipaccess.test_nr = testnr;
-
- /* FIXME: start safety timer until when test is supposed to complete */
-
- return 0;
-}
-
-static uint16_t last_arfcn;
-static struct gsm_sysinfo_freq nwl_si_freq[1024];
-#define FREQ_TYPE_NCELL_2 0x04 /* sub channel of SI 2 */
-#define FREQ_TYPE_NCELL_2bis 0x08 /* sub channel of SI 2bis */
-#define FREQ_TYPE_NCELL_2ter 0x10 /* sub channel of SI 2ter */
-
-struct ipacc_ferr_elem {
- int16_t freq_err;
- uint8_t freq_qual;
- uint8_t arfcn;
-} __attribute__((packed));
-
-struct ipacc_cusage_elem {
- uint16_t arfcn:10,
- rxlev:6;
-} __attribute__ ((packed));
-
-static int test_rep(void *_msg)
-{
- struct msgb *msg = _msg;
- struct abis_om_fom_hdr *foh = msgb_l3(msg);
- uint16_t test_rep_len, ferr_list_len;
- struct ipacc_ferr_elem *ife;
- struct ipac_bcch_info binfo;
- struct e1inp_sign_link *sign_link = (struct e1inp_sign_link *)msg->dst;
- int i, rc;
-
- DEBUGP(DNM, "TEST REPORT: ");
-
- if (foh->data[0] != NM_ATT_TEST_NO ||
- foh->data[2] != NM_ATT_TEST_REPORT)
- return -EINVAL;
-
- DEBUGPC(DNM, "test_no=0x%02x ", foh->data[1]);
- /* data[2] == NM_ATT_TEST_REPORT */
- /* data[3..4]: test_rep_len */
- memcpy(&test_rep_len, &foh->data[3], sizeof(uint16_t));
- test_rep_len = ntohs(test_rep_len);
- /* data[5]: ip.access test result */
- DEBUGPC(DNM, "tst_res=%s\n", ipacc_testres_name(foh->data[5]));
-
- /* data[6]: ip.access nested IE. 3 == freq_err_list */
- switch (foh->data[6]) {
- case NM_IPAC_EIE_FREQ_ERR_LIST:
- /* data[7..8]: length of ferr_list */
- memcpy(&ferr_list_len, &foh->data[7], sizeof(uint16_t));
- ferr_list_len = ntohs(ferr_list_len);
-
- /* data[9...]: frequency error list elements */
- for (i = 0; i < ferr_list_len; i+= sizeof(*ife)) {
- ife = (struct ipacc_ferr_elem *) (foh->data + 9 + i);
- DEBUGP(DNM, "==> ARFCN %4u, Frequency Error %6hd\n",
- ife->arfcn, ntohs(ife->freq_err));
- }
- break;
- case NM_IPAC_EIE_CHAN_USE_LIST:
- /* data[7..8]: length of ferr_list */
- memcpy(&ferr_list_len, &foh->data[7], sizeof(uint16_t));
- ferr_list_len = ntohs(ferr_list_len);
-
- /* data[9...]: channel usage list elements */
- for (i = 0; i < ferr_list_len; i+= 2) {
- uint16_t *cu_ptr = (uint16_t *)(foh->data + 9 + i);
- uint16_t cu = ntohs(*cu_ptr);
- uint16_t arfcn = cu & 0x3ff;
- uint8_t rxlev = cu >> 10;
- DEBUGP(DNM, "==> ARFCN %4u, RxLev %2u\n", arfcn, rxlev);
- rxlev_stat_input(&sign_link->trx->ipaccess.rxlev_stat,
- arfcn, rxlev);
- }
- break;
- case NM_IPAC_EIE_BCCH_INFO_TYPE:
- break;
- case NM_IPAC_EIE_BCCH_INFO:
- rc = ipac_parse_bcch_info(&binfo, foh->data+6);
- if (rc < 0) {
- DEBUGP(DNM, "BCCH Info parsing failed\n");
- break;
- }
- DEBUGP(DNM, "==> ARFCN %u, RxLev %2u, RxQual %2u: %3d-%d, LAC %d CI %d BSIC %u\n",
- binfo.arfcn, binfo.rx_lev, binfo.rx_qual,
- binfo.cgi.mcc, binfo.cgi.mnc,
- binfo.cgi.lac, binfo.cgi.ci, binfo.bsic);
-
- if (binfo.arfcn != last_arfcn) {
- /* report is on a new arfcn, need to clear channel list */
- memset(nwl_si_freq, 0, sizeof(nwl_si_freq));
- last_arfcn = binfo.arfcn;
- }
- if (binfo.info_type & IPAC_BINF_NEIGH_BA_SI2) {
- DEBUGP(DNM, "BA SI2: %s\n", osmo_hexdump(binfo.ba_list_si2, sizeof(binfo.ba_list_si2)));
- gsm48_decode_freq_list(nwl_si_freq, binfo.ba_list_si2, sizeof(binfo.ba_list_si2),
- 0x8c, FREQ_TYPE_NCELL_2);
- }
- if (binfo.info_type & IPAC_BINF_NEIGH_BA_SI2bis) {
- DEBUGP(DNM, "BA SI2bis: %s\n", osmo_hexdump(binfo.ba_list_si2bis, sizeof(binfo.ba_list_si2bis)));
- gsm48_decode_freq_list(nwl_si_freq, binfo.ba_list_si2bis, sizeof(binfo.ba_list_si2bis),
- 0x8e, FREQ_TYPE_NCELL_2bis);
- }
- if (binfo.info_type & IPAC_BINF_NEIGH_BA_SI2ter) {
- DEBUGP(DNM, "BA SI2ter: %s\n", osmo_hexdump(binfo.ba_list_si2ter, sizeof(binfo.ba_list_si2ter)));
- gsm48_decode_freq_list(nwl_si_freq, binfo.ba_list_si2ter, sizeof(binfo.ba_list_si2ter),
- 0x8e, FREQ_TYPE_NCELL_2ter);
- }
- for (i = 0; i < ARRAY_SIZE(nwl_si_freq); i++) {
- if (nwl_si_freq[i].mask)
- DEBUGP(DNM, "Neighbor Cell on ARFCN %u\n", i);
- }
- break;
- default:
- break;
- }
-
- switch (foh->data[5]) {
- case NM_IPACC_TESTRES_SUCCESS:
- case NM_IPACC_TESTRES_STOPPED:
- case NM_IPACC_TESTRES_TIMEOUT:
- case NM_IPACC_TESTRES_NO_CHANS:
- sign_link->trx->ipaccess.test_state = IPAC_TEST_S_IDLE;
- /* Send signal to notify higher layers of test completion */
- DEBUGP(DNM, "dispatching S_IPAC_NWL_COMPLETE signal\n");
- osmo_signal_dispatch(SS_IPAC_NWL, S_IPAC_NWL_COMPLETE,
- sign_link->trx);
- break;
- case NM_IPACC_TESTRES_PARTIAL:
- sign_link->trx->ipaccess.test_state = IPAC_TEST_S_PARTIAL;
- break;
- }
-
- return 0;
-}
-
-static int nwl_sig_cb(unsigned int subsys, unsigned int signal,
- void *handler_data, void *signal_data)
-{
- switch (signal) {
- case S_NM_TEST_REP:
- return test_rep(signal_data);
- default:
- break;
- }
-
- return 0;
-}
-
-void ipac_nwl_init(void)
-{
- osmo_signal_register_handler(SS_NM, nwl_sig_cb, NULL);
-}
diff --git a/openbsc/src/libbsc/Makefile.am b/openbsc/src/libbsc/Makefile.am
deleted file mode 100644
index e78bde624..000000000
--- a/openbsc/src/libbsc/Makefile.am
+++ /dev/null
@@ -1,57 +0,0 @@
-AM_CPPFLAGS = \
- $(all_includes) \
- -I$(top_srcdir)/include \
- -I$(top_builddir) \
- $(NULL)
-
-AM_CFLAGS = \
- -Wall \
- $(LIBOSMOCORE_CFLAGS) \
- $(LIBOSMOGSM_CFLAGS) \
- $(LIBOSMOVTY_CFLAGS) \
- $(LIBOSMOABIS_CFLAGS) \
- $(COVERAGE_CFLAGS) \
- $(NULL)
-
-noinst_LIBRARIES = \
- libbsc.a \
- $(NULL)
-
-libbsc_a_SOURCES = \
- abis_nm.c \
- abis_nm_vty.c \
- abis_om2000.c \
- abis_om2000_vty.c \
- abis_rsl.c \
- bsc_rll.c \
- bsc_subscriber.c \
- paging.c \
- bts_ericsson_rbs2000.c \
- bts_ipaccess_nanobts.c \
- bts_siemens_bs11.c \
- bts_nokia_site.c \
- bts_unknown.c \
- bts_sysmobts.c \
- chan_alloc.c \
- handover_decision.c \
- handover_logic.c \
- meas_rep.c \
- pcu_sock.c \
- rest_octets.c \
- system_information.c \
- e1_config.c \
- bsc_api.c \
- bsc_msc.c bsc_vty.c \
- gsm_04_08_utils.c \
- gsm_04_80_utils.c \
- bsc_init.c \
- bts_init.c \
- bsc_rf_ctrl.c \
- arfcn_range_encode.c \
- bsc_ctrl_commands.c \
- bsc_ctrl_lookup.c \
- net_init.c \
- bsc_dyn_ts.c \
- bts_ipaccess_nanobts_omlattr.c \
- $(NULL)
-
diff --git a/openbsc/src/libbsc/abis_nm.c b/openbsc/src/libbsc/abis_nm.c
deleted file mode 100644
index 019d03952..000000000
--- a/openbsc/src/libbsc/abis_nm.c
+++ /dev/null
@@ -1,2917 +0,0 @@
-/* GSM Network Management (OML) messages on the A-bis interface
- * 3GPP TS 12.21 version 8.0.0 Release 1999 / ETSI TS 100 623 V8.0.0 */
-
-/* (C) 2008-2009 by Harald Welte <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 <unistd.h>
-#include <stdio.h>
-#include <fcntl.h>
-#include <stdlib.h>
-#include <libgen.h>
-#include <time.h>
-#include <limits.h>
-
-#include <sys/stat.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-
-#include <openbsc/gsm_data.h>
-#include <openbsc/debug.h>
-#include <osmocom/core/msgb.h>
-#include <osmocom/gsm/protocol/gsm_12_21.h>
-#include <osmocom/gsm/tlv.h>
-#include <osmocom/gsm/abis_nm.h>
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/utils.h>
-#include <openbsc/abis_nm.h>
-#include <openbsc/misdn.h>
-#include <openbsc/signal.h>
-#include <osmocom/abis/e1_input.h>
-
-#define OM_ALLOC_SIZE 1024
-#define OM_HEADROOM_SIZE 128
-#define IPACC_SEGMENT_SIZE 245
-
-int abis_nm_tlv_parse(struct tlv_parsed *tp, struct gsm_bts *bts, const uint8_t *buf, int len)
-{
- if (!bts->model)
- return -EIO;
- return tlv_parse(tp, &bts->model->nm_att_tlvdef, buf, len, 0, 0);
-}
-
-static int is_in_arr(enum abis_nm_msgtype mt, const enum abis_nm_msgtype *arr, int size)
-{
- int i;
-
- for (i = 0; i < size; i++) {
- if (arr[i] == mt)
- return 1;
- }
-
- return 0;
-}
-
-#if 0
-/* is this msgtype the usual ACK/NACK type ? */
-static int is_ack_nack(enum abis_nm_msgtype mt)
-{
- return !is_in_arr(mt, no_ack_nack, ARRAY_SIZE(no_ack_nack));
-}
-#endif
-
-/* is this msgtype a report ? */
-static int is_report(enum abis_nm_msgtype mt)
-{
- return is_in_arr(mt, abis_nm_reports, ARRAY_SIZE(abis_nm_reports));
-}
-
-#define MT_ACK(x) (x+1)
-#define MT_NACK(x) (x+2)
-
-static void fill_om_hdr(struct abis_om_hdr *oh, uint8_t len)
-{
- oh->mdisc = ABIS_OM_MDISC_FOM;
- oh->placement = ABIS_OM_PLACEMENT_ONLY;
- oh->sequence = 0;
- oh->length = len;
-}
-
-static struct abis_om_fom_hdr *fill_om_fom_hdr(struct abis_om_hdr *oh, uint8_t len,
- uint8_t msg_type, uint8_t obj_class,
- uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr)
-{
- struct abis_om_fom_hdr *foh =
- (struct abis_om_fom_hdr *) oh->data;
-
- fill_om_hdr(oh, len+sizeof(*foh));
- foh->msg_type = msg_type;
- foh->obj_class = obj_class;
- foh->obj_inst.bts_nr = bts_nr;
- foh->obj_inst.trx_nr = trx_nr;
- foh->obj_inst.ts_nr = ts_nr;
- return foh;
-}
-
-static struct msgb *nm_msgb_alloc(void)
-{
- return msgb_alloc_headroom(OM_ALLOC_SIZE, OM_HEADROOM_SIZE,
- "OML");
-}
-
-int _abis_nm_sendmsg(struct msgb *msg)
-{
- msg->l2h = msg->data;
-
- if (!msg->dst) {
- LOGP(DNM, LOGL_ERROR, "%s: msg->dst == NULL\n", __func__);
- return -EINVAL;
- }
-
- return abis_sendmsg(msg);
-}
-
-/* Send a OML NM Message from BSC to BTS */
-static int abis_nm_queue_msg(struct gsm_bts *bts, struct msgb *msg)
-{
- msg->dst = bts->oml_link;
-
- /* queue OML messages */
- if (llist_empty(&bts->abis_queue) && !bts->abis_nm_pend) {
- bts->abis_nm_pend = OBSC_NM_W_ACK_CB(msg);
- return _abis_nm_sendmsg(msg);
- } else {
- msgb_enqueue(&bts->abis_queue, msg);
- return 0;
- }
-
-}
-
-int abis_nm_sendmsg(struct gsm_bts *bts, struct msgb *msg)
-{
- OBSC_NM_W_ACK_CB(msg) = 1;
- return abis_nm_queue_msg(bts, msg);
-}
-
-static int abis_nm_sendmsg_direct(struct gsm_bts *bts, struct msgb *msg)
-{
- OBSC_NM_W_ACK_CB(msg) = 0;
- return abis_nm_queue_msg(bts, msg);
-}
-
-static int abis_nm_rcvmsg_sw(struct msgb *mb);
-
-int nm_is_running(struct gsm_nm_state *s) {
- return (s->operational == NM_OPSTATE_ENABLED) && (
- (s->availability == NM_AVSTATE_OK) ||
- (s->availability == 0xff)
- );
-}
-
-/* Update the administrative state of a given object in our in-memory data
- * structures and send an event to the higher layer */
-static int update_admstate(struct gsm_bts *bts, uint8_t obj_class,
- struct abis_om_obj_inst *obj_inst, uint8_t adm_state)
-{
- struct gsm_nm_state *nm_state, new_state;
- struct nm_statechg_signal_data nsd;
-
- memset(&nsd, 0, sizeof(nsd));
-
- nsd.obj = gsm_objclass2obj(bts, obj_class, obj_inst);
- if (!nsd.obj)
- return -EINVAL;
- nm_state = gsm_objclass2nmstate(bts, obj_class, obj_inst);
- if (!nm_state)
- return -1;
-
- new_state = *nm_state;
- new_state.administrative = adm_state;
-
- nsd.bts = bts;
- nsd.obj_class = obj_class;
- nsd.old_state = nm_state;
- nsd.new_state = &new_state;
- nsd.obj_inst = obj_inst;
- osmo_signal_dispatch(SS_NM, S_NM_STATECHG_ADM, &nsd);
-
- nm_state->administrative = adm_state;
-
- return 0;
-}
-
-static int abis_nm_rx_statechg_rep(struct msgb *mb)
-{
- struct abis_om_hdr *oh = msgb_l2(mb);
- struct abis_om_fom_hdr *foh = msgb_l3(mb);
- struct e1inp_sign_link *sign_link = mb->dst;
- struct gsm_bts *bts = sign_link->trx->bts;
- struct tlv_parsed tp;
- struct gsm_nm_state *nm_state, new_state;
-
- DEBUGPC(DNM, "STATE CHG: ");
-
- memset(&new_state, 0, sizeof(new_state));
-
- nm_state = gsm_objclass2nmstate(bts, foh->obj_class, &foh->obj_inst);
- if (!nm_state) {
- DEBUGPC(DNM, "unknown object class\n");
- return -EINVAL;
- }
-
- new_state = *nm_state;
-
- abis_nm_tlv_parse(&tp, bts, foh->data, oh->length-sizeof(*foh));
- if (TLVP_PRESENT(&tp, NM_ATT_OPER_STATE)) {
- new_state.operational = *TLVP_VAL(&tp, NM_ATT_OPER_STATE);
- DEBUGPC(DNM, "OP_STATE=%s ",
- abis_nm_opstate_name(new_state.operational));
- }
- if (TLVP_PRESENT(&tp, NM_ATT_AVAIL_STATUS)) {
- if (TLVP_LEN(&tp, NM_ATT_AVAIL_STATUS) == 0)
- new_state.availability = 0xff;
- else
- new_state.availability = *TLVP_VAL(&tp, NM_ATT_AVAIL_STATUS);
- DEBUGPC(DNM, "AVAIL=%s(%02x) ",
- abis_nm_avail_name(new_state.availability),
- new_state.availability);
- } else
- new_state.availability = 0xff;
- if (TLVP_PRESENT(&tp, NM_ATT_ADM_STATE)) {
- new_state.administrative = *TLVP_VAL(&tp, NM_ATT_ADM_STATE);
- DEBUGPC(DNM, "ADM=%2s ",
- get_value_string(abis_nm_adm_state_names,
- new_state.administrative));
- }
- DEBUGPC(DNM, "\n");
-
- if ((new_state.administrative != 0 && nm_state->administrative == 0) ||
- new_state.operational != nm_state->operational ||
- new_state.availability != nm_state->availability) {
- /* Update the operational state of a given object in our in-memory data
- * structures and send an event to the higher layer */
- struct nm_statechg_signal_data nsd;
- nsd.obj = gsm_objclass2obj(bts, foh->obj_class, &foh->obj_inst);
- nsd.obj_class = foh->obj_class;
- nsd.old_state = nm_state;
- nsd.new_state = &new_state;
- nsd.obj_inst = &foh->obj_inst;
- nsd.bts = bts;
- osmo_signal_dispatch(SS_NM, S_NM_STATECHG_OPER, &nsd);
- nm_state->operational = new_state.operational;
- nm_state->availability = new_state.availability;
- if (nm_state->administrative == 0)
- nm_state->administrative = new_state.administrative;
- }
-#if 0
- if (op_state == 1) {
- /* try to enable objects that are disabled */
- abis_nm_opstart(bts, foh->obj_class,
- foh->obj_inst.bts_nr,
- foh->obj_inst.trx_nr,
- foh->obj_inst.ts_nr);
- }
-#endif
- return 0;
-}
-
-static inline void log_oml_fail_rep(const struct gsm_bts *bts, const char *type,
- const char *severity, const uint8_t *p_val,
- const char *text)
-{
- enum abis_nm_pcause_type pcause = p_val[0];
- enum abis_mm_event_causes cause = osmo_load16be(p_val + 1);
-
- LOGPC(DNM, LOGL_ERROR, "BTS %u: Failure Event Report: ", bts->nr);
- if (type)
- LOGPC(DNM, LOGL_ERROR, "Type=%s, ", type);
- if (severity)
- LOGPC(DNM, LOGL_ERROR, "Severity=%s, ", severity);
-
- LOGPC(DNM, LOGL_ERROR, "Probable cause=%s: ",
- get_value_string(abis_nm_pcause_type_names, pcause));
-
- if (pcause == NM_PCAUSE_T_MANUF)
- LOGPC(DNM, LOGL_ERROR, "%s, ",
- get_value_string(abis_mm_event_cause_names, cause));
- else
- LOGPC(DNM, LOGL_ERROR, "%02X %02X ", p_val[1], p_val[2]);
-
- if (text) {
- LOGPC(DNM, LOGL_ERROR, "Additional Text=%s. ", text);
- }
-
- LOGPC(DNM, LOGL_ERROR, "\n");
-}
-
-static inline void handle_manufact_report(struct gsm_bts *bts, const uint8_t *p_val, const char *type,
- const char *severity, const char *text)
-{
- enum abis_mm_event_causes cause = osmo_load16be(p_val + 1);
-
- switch (cause) {
- case OSMO_EVT_PCU_VERS:
- if (text) {
- LOGPC(DNM, LOGL_NOTICE, "BTS %u reported connected PCU version %s\n", bts->nr, text);
- osmo_strlcpy(bts->pcu_version, text, sizeof(bts->pcu_version));
- } else {
- LOGPC(DNM, LOGL_ERROR, "BTS %u reported PCU disconnection.\n", bts->nr);
- bts->pcu_version[0] = '\0';
- }
- break;
- default:
- log_oml_fail_rep(bts, type, severity, p_val, text);
- };
-}
-
-static int rx_fail_evt_rep(struct msgb *mb, struct gsm_bts *bts)
-{
- struct abis_om_hdr *oh = msgb_l2(mb);
- struct abis_om_fom_hdr *foh = msgb_l3(mb);
- struct e1inp_sign_link *sign_link = mb->dst;
- struct tlv_parsed tp;
- int rc = 0;
- const uint8_t *p_val = NULL;
- char *p_text = NULL;
- const char *e_type = NULL, *severity = NULL;
-
- abis_nm_tlv_parse(&tp, sign_link->trx->bts, foh->data,
- oh->length-sizeof(*foh));
-
- if (TLVP_PRESENT(&tp, NM_ATT_ADD_TEXT)) {
- p_val = TLVP_VAL(&tp, NM_ATT_ADD_TEXT);
- p_text = talloc_strndup(tall_bsc_ctx, (const char *) p_val,
- TLVP_LEN(&tp, NM_ATT_ADD_TEXT));
- }
-
- if (TLVP_PRESENT(&tp, NM_ATT_EVENT_TYPE))
- e_type = abis_nm_event_type_name(*TLVP_VAL(&tp,
- NM_ATT_EVENT_TYPE));
-
- if (TLVP_PRESENT(&tp, NM_ATT_SEVERITY))
- severity = abis_nm_severity_name(*TLVP_VAL(&tp,
- NM_ATT_SEVERITY));
-
- if (TLVP_PRESENT(&tp, NM_ATT_PROB_CAUSE)) {
- p_val = TLVP_VAL(&tp, NM_ATT_PROB_CAUSE);
-
- switch (p_val[0]) {
- case NM_PCAUSE_T_MANUF:
- handle_manufact_report(bts, p_val, e_type, severity,
- p_text);
- break;
- default:
- log_oml_fail_rep(bts, e_type, severity, p_val, p_text);
- };
- } else {
- LOGPC(DNM, LOGL_ERROR, "BTS%u: Failure Event Report without "
- "Probable Cause?!\n", bts->nr);
- rc = -EINVAL;
- }
-
- if (p_text)
- talloc_free(p_text);
-
- return rc;
-}
-
-static int abis_nm_rcvmsg_report(struct msgb *mb, struct gsm_bts *bts)
-{
- struct abis_om_fom_hdr *foh = msgb_l3(mb);
- uint8_t mt = foh->msg_type;
-
- abis_nm_debugp_foh(DNM, foh);
-
- //nmh->cfg->report_cb(mb, foh);
-
- switch (mt) {
- case NM_MT_STATECHG_EVENT_REP:
- return abis_nm_rx_statechg_rep(mb);
- break;
- case NM_MT_SW_ACTIVATED_REP:
- DEBUGPC(DNM, "Software Activated Report\n");
- osmo_signal_dispatch(SS_NM, S_NM_SW_ACTIV_REP, mb);
- break;
- case NM_MT_FAILURE_EVENT_REP:
- rx_fail_evt_rep(mb, bts);
- osmo_signal_dispatch(SS_NM, S_NM_FAIL_REP, mb);
- break;
- case NM_MT_TEST_REP:
- DEBUGPC(DNM, "Test Report\n");
- osmo_signal_dispatch(SS_NM, S_NM_TEST_REP, mb);
- break;
- default:
- DEBUGPC(DNM, "reporting NM MT 0x%02x\n", mt);
- break;
-
- };
-
- return 0;
-}
-
-/* Activate the specified software into the BTS */
-static int ipacc_sw_activate(struct gsm_bts *bts, uint8_t obj_class, uint8_t i0, uint8_t i1,
- uint8_t i2, const struct abis_nm_sw_desc *sw_desc)
-{
- struct abis_om_hdr *oh;
- struct msgb *msg = nm_msgb_alloc();
- uint16_t len = abis_nm_sw_desc_len(sw_desc, true);
-
- oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
- fill_om_fom_hdr(oh, len, NM_MT_ACTIVATE_SW, obj_class, i0, i1, i2);
- abis_nm_put_sw_desc(msg, sw_desc, true);
-
- return abis_nm_sendmsg(bts, msg);
-}
-
-int abis_nm_select_newest_sw(const struct abis_nm_sw_desc *sw_descr,
- const size_t size)
-{
- int res = 0;
- int i;
-
- for (i = 1; i < size; ++i) {
- if (memcmp(sw_descr[res].file_version, sw_descr[i].file_version,
- OSMO_MIN(sw_descr[i].file_version_len,
- sw_descr[res].file_version_len)) < 0) {
- res = i;
- }
- }
-
- return res;
-}
-
-static inline bool handle_attr(const struct gsm_bts *bts, enum bts_attribute id, uint8_t *val, uint8_t len)
-{
- switch (id) {
- case BTS_TYPE_VARIANT:
- LOGP(DNM, LOGL_NOTICE, "BTS%u reported variant: %s\n", bts->nr, val);
- break;
- case BTS_SUB_MODEL:
- LOGP(DNM, LOGL_NOTICE, "BTS%u reported submodel: %s\n", bts->nr, val);
- break;
- default:
- return false;
- }
- return true;
-}
-
-/* Parse Attribute Response Info - return pointer to the actual content */
-static inline uint8_t *parse_attr_resp_info_unreported(uint8_t bts_nr, uint8_t *ari, uint16_t ari_len, uint16_t *out_len)
-{
- uint8_t num_unreported = ari[0], i;
-
- DEBUGP(DNM, "BTS%u Get Attributes Response Info: %u bytes total with %u unreported attributes\n",
- bts_nr, ari_len, num_unreported);
-
- /* +1 because we have to account for number of unreported attributes, prefixing the list: */
- for (i = 0; i < num_unreported; i++)
- LOGP(DNM, LOGL_ERROR, "BTS%u Attribute %s is unreported\n",
- bts_nr, get_value_string(abis_nm_att_names, ari[i + 1]));
-
- /* the data starts right after the list of unreported attributes + space for length of that list */
- *out_len = ari_len - (num_unreported + 2);
-
- return ari + num_unreported + 1; /* we have to account for 1st byte with number of unreported attributes */
-}
-
-/* Parse Attribute Response Info content for 3GPP TS 52.021 §9.4.30 Manufacturer Id */
-static inline uint8_t *parse_attr_resp_info_manuf_id(struct gsm_bts *bts, uint8_t *data, uint16_t *data_len)
-{
- struct tlv_parsed tp;
- uint16_t m_id_len = 0;
- uint8_t adjust = 0, i;
-
- abis_nm_tlv_parse(&tp, bts, data, *data_len);
- if (TLVP_PRES_LEN(&tp, NM_ATT_MANUF_ID, 2)) {
- m_id_len = TLVP_LEN(&tp, NM_ATT_MANUF_ID);
-
- /* log potential BTS feature vector overflow */
- if (m_id_len > sizeof(bts->_features_data))
- LOGP(DNM, LOGL_NOTICE, "BTS%u Get Attributes Response: feature vector is truncated to %u bytes\n",
- bts->nr, MAX_BTS_FEATURES/8);
-
- /* check that max. expected BTS attribute is above given feature vector length */
- if (m_id_len > OSMO_BYTES_FOR_BITS(_NUM_BTS_FEAT))
- LOGP(DNM, LOGL_NOTICE, "BTS%u Get Attributes Response: reported unexpectedly long (%u bytes) "
- "feature vector - most likely it was compiled against newer BSC headers. "
- "Consider upgrading your BSC to later version.\n",
- bts->nr, m_id_len);
-
- memcpy(bts->_features_data, TLVP_VAL(&tp, NM_ATT_MANUF_ID), sizeof(bts->_features_data));
- adjust = m_id_len + 3; /* adjust for parsed TL16V struct */
-
- for (i = 0; i < _NUM_BTS_FEAT; i++)
- if (gsm_bts_has_feature(bts, i) != gsm_btsmodel_has_feature(bts->model, i))
- LOGP(DNM, LOGL_NOTICE, "BTS%u feature '%s' reported via OML does not match statically "
- "set feature: %u != %u. Please fix.\n", bts->nr,
- get_value_string(gsm_bts_features_descs, i),
- gsm_bts_has_feature(bts, i), gsm_btsmodel_has_feature(bts->model, i));
- }
-
- *data_len -= adjust;
-
- return data + adjust;
-}
-
-/* Parse Attribute Response Info content for 3GPP TS 52.021 §9.4.28 Manufacturer Dependent State */
-static inline uint8_t *parse_attr_resp_info_manuf_state(const struct gsm_bts_trx *trx, uint8_t *data, uint16_t *data_len)
-{
- struct tlv_parsed tp;
- const uint8_t *power;
- uint8_t adjust = 0;
-
- if (!trx) /* this attribute does not make sense on BTS level, only on TRX level */
- return data;
-
- abis_nm_tlv_parse(&tp, trx->bts, data, *data_len);
- if (TLVP_PRES_LEN(&tp, NM_ATT_MANUF_STATE, 1)) {
- power = TLVP_VAL(&tp, NM_ATT_MANUF_STATE);
- LOGP(DNM, LOGL_NOTICE, "%s Get Attributes Response: nominal power is %u\n", gsm_trx_name(trx), *power);
- adjust = 2; /* adjust for parsed TV struct */
- }
-
- *data_len -= adjust;
-
- return data + adjust;
-}
-
-/* Handle 3GPP TS 52.021 §9.4.64 Get Attribute Response Info */
-static int abis_nm_rx_get_attr_resp(struct msgb *mb, const struct gsm_bts_trx *trx)
-{
- struct abis_om_hdr *oh = msgb_l2(mb);
- struct abis_om_fom_hdr *foh = msgb_l3(mb);
- struct e1inp_sign_link *sign_link = mb->dst;
- struct gsm_bts *bts = trx ? trx->bts : sign_link->trx->bts;
- struct tlv_parsed tp;
- uint8_t *data, i;
- uint16_t data_len;
- int rc;
- struct abis_nm_sw_desc sw_descr[MAX_BTS_ATTR];
-
- abis_nm_debugp_foh(DNM, foh);
-
- DEBUGPC(DNM, "Get Attributes Response for BTS%u\n", bts->nr);
-
- abis_nm_tlv_parse(&tp, bts, foh->data, oh->length-sizeof(*foh));
- if (!TLVP_PRES_LEN(&tp, NM_ATT_GET_ARI, 1)) {
- LOGP(DNM, LOGL_ERROR, "BTS%u: Get Attributes Response without Response Info?!\n", bts->nr);
- return -EINVAL;
- }
-
- data = parse_attr_resp_info_unreported(bts->nr, TLVP_VAL(&tp, NM_ATT_GET_ARI), TLVP_LEN(&tp, NM_ATT_GET_ARI),
- &data_len);
-
- data = parse_attr_resp_info_manuf_state(trx, data, &data_len);
- data = parse_attr_resp_info_manuf_id(bts, data, &data_len);
-
- /* after parsing manufacturer-specific attributes there's list of replies in form of sw-conf structure: */
- rc = abis_nm_get_sw_conf(data, data_len, &sw_descr[0], ARRAY_SIZE(sw_descr));
- if (rc > 0) {
- for (i = 0; i < rc; i++) {
- if (!handle_attr(bts, str2btsattr((const char *)sw_descr[i].file_id),
- sw_descr[i].file_version, sw_descr[i].file_version_len))
- LOGP(DNM, LOGL_NOTICE, "BTS%u: ARI reported sw[%d/%d]: %s is %s\n",
- bts->nr, i, rc, sw_descr[i].file_id, sw_descr[i].file_version);
- }
- } else
- LOGP(DNM, LOGL_ERROR, "BTS%u: failed to parse SW-Config part of Get Attribute Response Info: %s\n",
- bts->nr, strerror(-rc));
-
- return 0;
-}
-
-/* 3GPP TS 52.021 §6.2.5 */
-static int abis_nm_rx_sw_act_req(struct msgb *mb)
-{
- struct abis_om_hdr *oh = msgb_l2(mb);
- struct abis_om_fom_hdr *foh = msgb_l3(mb);
- struct e1inp_sign_link *sign_link = mb->dst;
- struct tlv_parsed tp;
- const uint8_t *sw_config;
- int ret, sw_config_len, len;
- struct abis_nm_sw_desc sw_descr[MAX_BTS_ATTR];
-
- abis_nm_debugp_foh(DNM, foh);
-
- DEBUGPC(DNM, "SW Activate Request: ");
-
- DEBUGP(DNM, "Software Activate Request, ACKing and Activating\n");
-
- ret = abis_nm_sw_act_req_ack(sign_link->trx->bts, foh->obj_class,
- foh->obj_inst.bts_nr,
- foh->obj_inst.trx_nr,
- foh->obj_inst.ts_nr, 0,
- foh->data, oh->length-sizeof(*foh));
- if (ret != 0) {
- LOGP(DNM, LOGL_ERROR,
- "Sending SW ActReq ACK failed: %d\n", ret);
- return ret;
- }
-
- abis_nm_tlv_parse(&tp, sign_link->trx->bts, foh->data, oh->length-sizeof(*foh));
- sw_config = TLVP_VAL(&tp, NM_ATT_SW_CONFIG);
- sw_config_len = TLVP_LEN(&tp, NM_ATT_SW_CONFIG);
- if (!TLVP_PRESENT(&tp, NM_ATT_SW_CONFIG)) {
- LOGP(DNM, LOGL_ERROR,
- "SW config not found! Can't continue.\n");
- return -EINVAL;
- } else {
- DEBUGP(DNM, "Found SW config: %s\n", osmo_hexdump(sw_config, sw_config_len));
- }
-
- /* Parse up to two sw descriptions from the data */
- len = abis_nm_get_sw_conf(sw_config, sw_config_len, &sw_descr[0],
- ARRAY_SIZE(sw_descr));
- if (len <= 0) {
- LOGP(DNM, LOGL_ERROR, "Failed to parse SW Config.\n");
- return -EINVAL;
- }
-
- ret = abis_nm_select_newest_sw(&sw_descr[0], len);
- DEBUGP(DNM, "Selected sw description %d of %d\n", ret, len);
-
- return ipacc_sw_activate(sign_link->trx->bts, foh->obj_class,
- foh->obj_inst.bts_nr,
- foh->obj_inst.trx_nr,
- foh->obj_inst.ts_nr,
- &sw_descr[ret]);
-}
-
-/* Receive a CHANGE_ADM_STATE_ACK, parse the TLV and update local state */
-static int abis_nm_rx_chg_adm_state_ack(struct msgb *mb)
-{
- struct abis_om_hdr *oh = msgb_l2(mb);
- struct abis_om_fom_hdr *foh = msgb_l3(mb);
- struct e1inp_sign_link *sign_link = mb->dst;
- struct tlv_parsed tp;
- uint8_t adm_state;
-
- abis_nm_tlv_parse(&tp, sign_link->trx->bts, foh->data, oh->length-sizeof(*foh));
- if (!TLVP_PRESENT(&tp, NM_ATT_ADM_STATE))
- return -EINVAL;
-
- adm_state = *TLVP_VAL(&tp, NM_ATT_ADM_STATE);
-
- return update_admstate(sign_link->trx->bts, foh->obj_class, &foh->obj_inst, adm_state);
-}
-
-static int abis_nm_rx_lmt_event(struct msgb *mb)
-{
- struct abis_om_hdr *oh = msgb_l2(mb);
- struct abis_om_fom_hdr *foh = msgb_l3(mb);
- struct e1inp_sign_link *sign_link = mb->dst;
- struct tlv_parsed tp;
-
- DEBUGP(DNM, "LMT Event ");
- abis_nm_tlv_parse(&tp, sign_link->trx->bts, foh->data, oh->length-sizeof(*foh));
- if (TLVP_PRESENT(&tp, NM_ATT_BS11_LMT_LOGON_SESSION) &&
- TLVP_LEN(&tp, NM_ATT_BS11_LMT_LOGON_SESSION) >= 1) {
- uint8_t onoff = *TLVP_VAL(&tp, NM_ATT_BS11_LMT_LOGON_SESSION);
- DEBUGPC(DNM, "LOG%s ", onoff ? "ON" : "OFF");
- }
- if (TLVP_PRESENT(&tp, NM_ATT_BS11_LMT_USER_ACC_LEV) &&
- TLVP_LEN(&tp, NM_ATT_BS11_LMT_USER_ACC_LEV) >= 1) {
- uint8_t level = *TLVP_VAL(&tp, NM_ATT_BS11_LMT_USER_ACC_LEV);
- DEBUGPC(DNM, "Level=%u ", level);
- }
- if (TLVP_PRESENT(&tp, NM_ATT_BS11_LMT_USER_NAME) &&
- TLVP_LEN(&tp, NM_ATT_BS11_LMT_USER_NAME) >= 1) {
- char *name = (char *) TLVP_VAL(&tp, NM_ATT_BS11_LMT_USER_NAME);
- DEBUGPC(DNM, "Username=%s ", name);
- }
- DEBUGPC(DNM, "\n");
- /* FIXME: parse LMT LOGON TIME */
- return 0;
-}
-
-void abis_nm_queue_send_next(struct gsm_bts *bts)
-{
- int wait = 0;
- struct msgb *msg;
- /* the queue is empty */
- while (!llist_empty(&bts->abis_queue)) {
- msg = msgb_dequeue(&bts->abis_queue);
- wait = OBSC_NM_W_ACK_CB(msg);
- _abis_nm_sendmsg(msg);
-
- if (wait)
- break;
- }
-
- bts->abis_nm_pend = wait;
-}
-
-/* Receive a OML NM Message from BTS */
-static int abis_nm_rcvmsg_fom(struct msgb *mb)
-{
- struct abis_om_hdr *oh = msgb_l2(mb);
- struct abis_om_fom_hdr *foh = msgb_l3(mb);
- struct e1inp_sign_link *sign_link = mb->dst;
- uint8_t mt = foh->msg_type;
- /* sign_link might get deleted via osmo_signal_dispatch -> save bts */
- struct gsm_bts *bts = sign_link->trx->bts;
- int ret = 0;
-
- /* check for unsolicited message */
- if (is_report(mt))
- return abis_nm_rcvmsg_report(mb, bts);
-
- if (is_in_arr(mt, abis_nm_sw_load_msgs, ARRAY_SIZE(abis_nm_sw_load_msgs)))
- return abis_nm_rcvmsg_sw(mb);
-
- if (is_in_arr(mt, abis_nm_nacks, ARRAY_SIZE(abis_nm_nacks))) {
- struct nm_nack_signal_data nack_data;
- struct tlv_parsed tp;
-
- abis_nm_debugp_foh(DNM, foh);
-
- DEBUGPC(DNM, "%s NACK ", abis_nm_nack_name(mt));
-
- abis_nm_tlv_parse(&tp, bts, foh->data, oh->length-sizeof(*foh));
- if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES))
- DEBUGPC(DNM, "CAUSE=%s\n",
- abis_nm_nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES)));
- else
- DEBUGPC(DNM, "\n");
-
- nack_data.msg = mb;
- nack_data.mt = mt;
- nack_data.bts = bts;
- osmo_signal_dispatch(SS_NM, S_NM_NACK, &nack_data);
- abis_nm_queue_send_next(bts);
- return 0;
- }
-#if 0
- /* check if last message is to be acked */
- if (is_ack_nack(nmh->last_msgtype)) {
- if (mt == MT_ACK(nmh->last_msgtype)) {
- DEBUGP(DNM, "received ACK (0x%x)\n", foh->msg_type);
- /* we got our ACK, continue sending the next msg */
- } else if (mt == MT_NACK(nmh->last_msgtype)) {
- /* we got a NACK, signal this to the caller */
- DEBUGP(DNM, "received NACK (0x%x)\n", foh->msg_type);
- /* FIXME: somehow signal this to the caller */
- } else {
- /* really strange things happen */
- return -EINVAL;
- }
- }
-#endif
-
- switch (mt) {
- case NM_MT_CHG_ADM_STATE_ACK:
- ret = abis_nm_rx_chg_adm_state_ack(mb);
- break;
- case NM_MT_SW_ACT_REQ:
- ret = abis_nm_rx_sw_act_req(mb);
- break;
- case NM_MT_BS11_LMT_SESSION:
- ret = abis_nm_rx_lmt_event(mb);
- break;
- case NM_MT_OPSTART_ACK:
- abis_nm_debugp_foh(DNM, foh);
- DEBUGPC(DNM, "Opstart ACK\n");
- break;
- case NM_MT_SET_CHAN_ATTR_ACK:
- abis_nm_debugp_foh(DNM, foh);
- DEBUGPC(DNM, "Set Channel Attributes ACK\n");
- break;
- case NM_MT_SET_RADIO_ATTR_ACK:
- abis_nm_debugp_foh(DNM, foh);
- DEBUGPC(DNM, "Set Radio Carrier Attributes ACK\n");
- break;
- case NM_MT_CONN_MDROP_LINK_ACK:
- abis_nm_debugp_foh(DNM, foh);
- DEBUGPC(DNM, "CONN MDROP LINK ACK\n");
- break;
- case NM_MT_IPACC_RESTART_ACK:
- osmo_signal_dispatch(SS_NM, S_NM_IPACC_RESTART_ACK, NULL);
- break;
- case NM_MT_IPACC_RESTART_NACK:
- osmo_signal_dispatch(SS_NM, S_NM_IPACC_RESTART_NACK, NULL);
- break;
- case NM_MT_SET_BTS_ATTR_ACK:
- break;
- case NM_MT_GET_ATTR_RESP:
- ret = abis_nm_rx_get_attr_resp(mb, gsm_bts_trx_num(bts, (foh)->obj_inst.trx_nr));
- break;
- default:
- abis_nm_debugp_foh(DNM, foh);
- LOGPC(DNM, LOGL_ERROR, "Unhandled message %s\n",
- get_value_string(abis_nm_msgtype_names, mt));
- }
-
- abis_nm_queue_send_next(bts);
- return ret;
-}
-
-static int abis_nm_rx_ipacc(struct msgb *mb);
-
-static int abis_nm_rcvmsg_manuf(struct msgb *mb)
-{
- int rc;
- struct e1inp_sign_link *sign_link = mb->dst;
- int bts_type = sign_link->trx->bts->type;
-
- switch (bts_type) {
- case GSM_BTS_TYPE_NANOBTS:
- case GSM_BTS_TYPE_OSMOBTS:
- rc = abis_nm_rx_ipacc(mb);
- abis_nm_queue_send_next(sign_link->trx->bts);
- break;
- default:
- LOGP(DNM, LOGL_ERROR, "don't know how to parse OML for this "
- "BTS type (%u)\n", bts_type);
- rc = 0;
- break;
- }
-
- return rc;
-}
-
-/* High-Level API */
-/* Entry-point where L2 OML from BTS enters the NM code */
-int abis_nm_rcvmsg(struct msgb *msg)
-{
- struct abis_om_hdr *oh = msgb_l2(msg);
- int rc = 0;
-
- /* Various consistency checks */
- if (oh->placement != ABIS_OM_PLACEMENT_ONLY) {
- LOGP(DNM, LOGL_ERROR, "ABIS OML placement 0x%x not supported\n",
- oh->placement);
- if (oh->placement != ABIS_OM_PLACEMENT_FIRST) {
- rc = -EINVAL;
- goto err;
- }
- }
- if (oh->sequence != 0) {
- LOGP(DNM, LOGL_ERROR, "ABIS OML sequence 0x%x != 0x00\n",
- oh->sequence);
- rc = -EINVAL;
- goto err;
- }
-#if 0
- unsigned int l2_len = msg->tail - (uint8_t *)msgb_l2(msg);
- unsigned int hlen = sizeof(*oh) + sizeof(struct abis_om_fom_hdr);
- if (oh->length + hlen > l2_len) {
- LOGP(DNM, LOGL_ERROR, "ABIS OML truncated message (%u > %u)\n",
- oh->length + sizeof(*oh), l2_len);
- return -EINVAL;
- }
- if (oh->length + hlen < l2_len)
- LOGP(DNM, LOGL_ERROR, "ABIS OML message with extra trailer?!? (oh->len=%d, sizeof_oh=%d l2_len=%d\n", oh->length, sizeof(*oh), l2_len);
-#endif
- msg->l3h = (unsigned char *)oh + sizeof(*oh);
-
- switch (oh->mdisc) {
- case ABIS_OM_MDISC_FOM:
- rc = abis_nm_rcvmsg_fom(msg);
- break;
- case ABIS_OM_MDISC_MANUF:
- rc = abis_nm_rcvmsg_manuf(msg);
- break;
- case ABIS_OM_MDISC_MMI:
- case ABIS_OM_MDISC_TRAU:
- LOGP(DNM, LOGL_ERROR, "unimplemented ABIS OML message discriminator 0x%x\n",
- oh->mdisc);
- break;
- default:
- LOGP(DNM, LOGL_ERROR, "unknown ABIS OML message discriminator 0x%x\n",
- oh->mdisc);
- rc = -EINVAL;
- break;
- }
-err:
- msgb_free(msg);
- return rc;
-}
-
-#if 0
-/* initialized all resources */
-struct abis_nm_h *abis_nm_init(struct abis_nm_cfg *cfg)
-{
- struct abis_nm_h *nmh;
-
- nmh = malloc(sizeof(*nmh));
- if (!nmh)
- return NULL;
-
- nmh->cfg = cfg;
-
- return nmh;
-}
-
-/* free all resources */
-void abis_nm_fini(struct abis_nm_h *nmh)
-{
- free(nmh);
-}
-#endif
-
-/* Here we are trying to define a high-level API that can be used by
- * the actual BSC implementation. However, the architecture is currently
- * still under design. Ideally the calls to this API would be synchronous,
- * while the underlying stack behind the APi runs in a traditional select
- * based state machine.
- */
-
-/* 6.2 Software Load: */
-enum sw_state {
- SW_STATE_NONE,
- SW_STATE_WAIT_INITACK,
- SW_STATE_WAIT_SEGACK,
- SW_STATE_WAIT_ENDACK,
- SW_STATE_WAIT_ACTACK,
- SW_STATE_ERROR,
-};
-
-struct abis_nm_sw {
- struct gsm_bts *bts;
- int trx_nr;
- gsm_cbfn *cbfn;
- void *cb_data;
- int forced;
-
- /* this will become part of the SW LOAD INITIATE */
- uint8_t obj_class;
- uint8_t obj_instance[3];
-
- uint8_t file_id[255];
- uint8_t file_id_len;
-
- uint8_t file_version[255];
- uint8_t file_version_len;
-
- uint8_t window_size;
- uint8_t seg_in_window;
-
- int fd;
- FILE *stream;
- enum sw_state state;
- int last_seg;
-};
-
-static struct abis_nm_sw g_sw;
-
-static void sw_add_file_id_and_ver(struct abis_nm_sw *sw, struct msgb *msg)
-{
- if (sw->bts->type == GSM_BTS_TYPE_NANOBTS) {
- msgb_v_put(msg, NM_ATT_SW_DESCR);
- msgb_tl16v_put(msg, NM_ATT_FILE_ID, sw->file_id_len, sw->file_id);
- msgb_tl16v_put(msg, NM_ATT_FILE_VERSION, sw->file_version_len,
- sw->file_version);
- } else if (sw->bts->type == GSM_BTS_TYPE_BS11) {
- msgb_tlv_put(msg, NM_ATT_FILE_ID, sw->file_id_len, sw->file_id);
- msgb_tlv_put(msg, NM_ATT_FILE_VERSION, sw->file_version_len,
- sw->file_version);
- } else {
- LOGP(DNM, LOGL_ERROR, "Please implement this for the BTS.\n");
- }
-}
-
-/* 6.2.1 / 8.3.1: Load Data Initiate */
-static int sw_load_init(struct abis_nm_sw *sw)
-{
- struct abis_om_hdr *oh;
- struct msgb *msg = nm_msgb_alloc();
- uint8_t len = 3*2 + sw->file_id_len + sw->file_version_len;
-
- oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
- fill_om_fom_hdr(oh, len, NM_MT_LOAD_INIT, sw->obj_class,
- sw->obj_instance[0], sw->obj_instance[1],
- sw->obj_instance[2]);
-
- sw_add_file_id_and_ver(sw, msg);
- msgb_tv_put(msg, NM_ATT_WINDOW_SIZE, sw->window_size);
-
- return abis_nm_sendmsg(sw->bts, msg);
-}
-
-static int is_last_line(FILE *stream)
-{
- char next_seg_buf[256];
- long pos;
-
- /* check if we're sending the last line */
- pos = ftell(stream);
-
- /* Did ftell fail? Then we are at the end for sure */
- if (pos < 0)
- return 1;
-
- if (!fgets(next_seg_buf, sizeof(next_seg_buf)-2, stream)) {
- int rc = fseek(stream, pos, SEEK_SET);
- if (rc < 0)
- return rc;
- return 1;
- }
-
- fseek(stream, pos, SEEK_SET);
- return 0;
-}
-
-/* 6.2.2 / 8.3.2 Load Data Segment */
-static int sw_load_segment(struct abis_nm_sw *sw)
-{
- struct abis_om_hdr *oh;
- struct msgb *msg = nm_msgb_alloc();
- char seg_buf[256];
- char *line_buf = seg_buf+2;
- unsigned char *tlv;
- int len;
-
- oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
-
- switch (sw->bts->type) {
- case GSM_BTS_TYPE_BS11:
- if (fgets(line_buf, sizeof(seg_buf)-2, sw->stream) == NULL) {
- perror("fgets reading segment");
- return -EINVAL;
- }
- seg_buf[0] = 0x00;
-
- /* check if we're sending the last line */
- sw->last_seg = is_last_line(sw->stream);
- if (sw->last_seg)
- seg_buf[1] = 0;
- else
- seg_buf[1] = 1 + sw->seg_in_window++;
-
- len = strlen(line_buf) + 2;
- tlv = msgb_put(msg, TLV_GROSS_LEN(len));
- tlv_put(tlv, NM_ATT_BS11_FILE_DATA, len, (uint8_t *)seg_buf);
- /* BS11 wants CR + LF in excess of the TLV length !?! */
- tlv[1] -= 2;
-
- /* we only now know the exact length for the OM hdr */
- len = strlen(line_buf)+2;
- break;
- case GSM_BTS_TYPE_NANOBTS: {
- osmo_static_assert(sizeof(seg_buf) >= IPACC_SEGMENT_SIZE, buffer_big_enough);
- len = read(sw->fd, &seg_buf, IPACC_SEGMENT_SIZE);
- if (len < 0) {
- perror("read failed");
- return -EINVAL;
- }
-
- if (len != IPACC_SEGMENT_SIZE)
- sw->last_seg = 1;
-
- ++sw->seg_in_window;
- msgb_tl16v_put(msg, NM_ATT_IPACC_FILE_DATA, len, (const uint8_t *) seg_buf);
- len += 3;
- break;
- }
- default:
- LOGP(DNM, LOGL_ERROR, "sw_load_segment needs implementation for the BTS.\n");
- /* FIXME: Other BTS types */
- return -1;
- }
-
- fill_om_fom_hdr(oh, len, NM_MT_LOAD_SEG, sw->obj_class,
- sw->obj_instance[0], sw->obj_instance[1],
- sw->obj_instance[2]);
-
- return abis_nm_sendmsg_direct(sw->bts, msg);
-}
-
-/* 6.2.4 / 8.3.4 Load Data End */
-static int sw_load_end(struct abis_nm_sw *sw)
-{
- struct abis_om_hdr *oh;
- struct msgb *msg = nm_msgb_alloc();
- uint8_t len = 2*2 + sw->file_id_len + sw->file_version_len;
-
- oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
- fill_om_fom_hdr(oh, len, NM_MT_LOAD_END, sw->obj_class,
- sw->obj_instance[0], sw->obj_instance[1],
- sw->obj_instance[2]);
-
- sw_add_file_id_and_ver(sw, msg);
- return abis_nm_sendmsg(sw->bts, msg);
-}
-
-/* Activate the specified software into the BTS */
-static int sw_activate(struct abis_nm_sw *sw)
-{
- struct abis_om_hdr *oh;
- struct msgb *msg = nm_msgb_alloc();
- uint8_t len = 2*2 + sw->file_id_len + sw->file_version_len;
-
- oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
- fill_om_fom_hdr(oh, len, NM_MT_ACTIVATE_SW, sw->obj_class,
- sw->obj_instance[0], sw->obj_instance[1],
- sw->obj_instance[2]);
-
- /* FIXME: this is BS11 specific format */
- msgb_tlv_put(msg, NM_ATT_FILE_ID, sw->file_id_len, sw->file_id);
- msgb_tlv_put(msg, NM_ATT_FILE_VERSION, sw->file_version_len,
- sw->file_version);
-
- return abis_nm_sendmsg(sw->bts, msg);
-}
-
-struct sdp_firmware {
- char magic[4];
- char more_magic[4];
- unsigned int header_length;
- unsigned int file_length;
-} __attribute__ ((packed));
-
-static int parse_sdp_header(struct abis_nm_sw *sw)
-{
- struct sdp_firmware firmware_header;
- int rc;
- struct stat stat;
-
- rc = read(sw->fd, &firmware_header, sizeof(firmware_header));
- if (rc != sizeof(firmware_header)) {
- LOGP(DNM, LOGL_ERROR, "Could not read SDP file header.\n");
- return -1;
- }
-
- if (strncmp(firmware_header.magic, " SDP", 4) != 0) {
- LOGP(DNM, LOGL_ERROR, "The magic number1 is wrong.\n");
- return -1;
- }
-
- if (firmware_header.more_magic[0] != 0x10 ||
- firmware_header.more_magic[1] != 0x02 ||
- firmware_header.more_magic[2] != 0x00 ||
- firmware_header.more_magic[3] != 0x00) {
- LOGP(DNM, LOGL_ERROR, "The more magic number is wrong.\n");
- return -1;
- }
-
-
- if (fstat(sw->fd, &stat) == -1) {
- LOGP(DNM, LOGL_ERROR, "Could not stat the file.\n");
- return -1;
- }
-
- if (ntohl(firmware_header.file_length) != stat.st_size) {
- LOGP(DNM, LOGL_ERROR, "The filesizes do not match.\n");
- return -1;
- }
-
- /* go back to the start as we checked the whole filesize.. */
- lseek(sw->fd, 0l, SEEK_SET);
- LOGP(DNM, LOGL_NOTICE, "The ipaccess SDP header is not fully understood.\n"
- "There might be checksums in the file that are not\n"
- "verified and incomplete firmware might be flashed.\n"
- "There is absolutely no WARRANTY that flashing will\n"
- "work.\n");
- return 0;
-}
-
-static int sw_open_file(struct abis_nm_sw *sw, const char *fname)
-{
- char file_id[12+1];
- char file_version[80+1];
- int rc;
-
- sw->fd = open(fname, O_RDONLY);
- if (sw->fd < 0)
- return sw->fd;
-
- switch (sw->bts->type) {
- case GSM_BTS_TYPE_BS11:
- sw->stream = fdopen(sw->fd, "r");
- if (!sw->stream) {
- perror("fdopen");
- return -1;
- }
- /* read first line and parse file ID and VERSION */
- rc = fscanf(sw->stream, "@(#)%12s:%80s\r\n",
- file_id, file_version);
- if (rc != 2) {
- perror("parsing header line of software file");
- return -1;
- }
- strcpy((char *)sw->file_id, file_id);
- sw->file_id_len = strlen(file_id);
- strcpy((char *)sw->file_version, file_version);
- sw->file_version_len = strlen(file_version);
- /* rewind to start of file */
- rewind(sw->stream);
- break;
- case GSM_BTS_TYPE_NANOBTS:
- /* TODO: extract that from the filename or content */
- rc = parse_sdp_header(sw);
- if (rc < 0) {
- fprintf(stderr, "Could not parse the ipaccess SDP header\n");
- return -1;
- }
-
- strcpy((char *)sw->file_id, "id");
- sw->file_id_len = 3;
- strcpy((char *)sw->file_version, "version");
- sw->file_version_len = 8;
- break;
- default:
- /* We don't know how to treat them yet */
- close(sw->fd);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static void sw_close_file(struct abis_nm_sw *sw)
-{
- switch (sw->bts->type) {
- case GSM_BTS_TYPE_BS11:
- fclose(sw->stream);
- break;
- default:
- close(sw->fd);
- break;
- }
-}
-
-/* Fill the window */
-static int sw_fill_window(struct abis_nm_sw *sw)
-{
- int rc;
-
- while (sw->seg_in_window < sw->window_size) {
- rc = sw_load_segment(sw);
- if (rc < 0)
- return rc;
- if (sw->last_seg)
- break;
- }
- return 0;
-}
-
-/* callback function from abis_nm_rcvmsg() handler */
-static int abis_nm_rcvmsg_sw(struct msgb *mb)
-{
- struct abis_om_fom_hdr *foh = msgb_l3(mb);
- struct e1inp_sign_link *sign_link = mb->dst;
- int rc = -1;
- struct abis_nm_sw *sw = &g_sw;
- enum sw_state old_state = sw->state;
-
- //DEBUGP(DNM, "state %u, NM MT 0x%02x\n", sw->state, foh->msg_type);
-
- switch (sw->state) {
- case SW_STATE_WAIT_INITACK:
- switch (foh->msg_type) {
- case NM_MT_LOAD_INIT_ACK:
- /* fill window with segments */
- if (sw->cbfn)
- sw->cbfn(GSM_HOOK_NM_SWLOAD,
- NM_MT_LOAD_INIT_ACK, mb,
- sw->cb_data, NULL);
- rc = sw_fill_window(sw);
- sw->state = SW_STATE_WAIT_SEGACK;
- abis_nm_queue_send_next(sign_link->trx->bts);
- break;
- case NM_MT_LOAD_INIT_NACK:
- if (sw->forced) {
- DEBUGP(DNM, "FORCED: Ignoring Software Load "
- "Init NACK\n");
- if (sw->cbfn)
- sw->cbfn(GSM_HOOK_NM_SWLOAD,
- NM_MT_LOAD_INIT_ACK, mb,
- sw->cb_data, NULL);
- rc = sw_fill_window(sw);
- sw->state = SW_STATE_WAIT_SEGACK;
- } else {
- DEBUGP(DNM, "Software Load Init NACK\n");
- /* FIXME: cause */
- if (sw->cbfn)
- sw->cbfn(GSM_HOOK_NM_SWLOAD,
- NM_MT_LOAD_INIT_NACK, mb,
- sw->cb_data, NULL);
- sw->state = SW_STATE_ERROR;
- }
- abis_nm_queue_send_next(sign_link->trx->bts);
- break;
- }
- break;
- case SW_STATE_WAIT_SEGACK:
- switch (foh->msg_type) {
- case NM_MT_LOAD_SEG_ACK:
- if (sw->cbfn)
- sw->cbfn(GSM_HOOK_NM_SWLOAD,
- NM_MT_LOAD_SEG_ACK, mb,
- sw->cb_data, NULL);
- sw->seg_in_window = 0;
- if (!sw->last_seg) {
- /* fill window with more segments */
- rc = sw_fill_window(sw);
- sw->state = SW_STATE_WAIT_SEGACK;
- } else {
- /* end the transfer */
- sw->state = SW_STATE_WAIT_ENDACK;
- rc = sw_load_end(sw);
- }
- abis_nm_queue_send_next(sign_link->trx->bts);
- break;
- case NM_MT_LOAD_ABORT:
- if (sw->cbfn)
- sw->cbfn(GSM_HOOK_NM_SWLOAD,
- NM_MT_LOAD_ABORT, mb,
- sw->cb_data, NULL);
- break;
- }
- break;
- case SW_STATE_WAIT_ENDACK:
- switch (foh->msg_type) {
- case NM_MT_LOAD_END_ACK:
- sw_close_file(sw);
- DEBUGP(DNM, "Software Load End (BTS %u)\n",
- sw->bts->nr);
- sw->state = SW_STATE_NONE;
- if (sw->cbfn)
- sw->cbfn(GSM_HOOK_NM_SWLOAD,
- NM_MT_LOAD_END_ACK, mb,
- sw->cb_data, NULL);
- rc = 0;
- abis_nm_queue_send_next(sign_link->trx->bts);
- break;
- case NM_MT_LOAD_END_NACK:
- if (sw->forced) {
- DEBUGP(DNM, "FORCED: Ignoring Software Load"
- "End NACK\n");
- sw->state = SW_STATE_NONE;
- if (sw->cbfn)
- sw->cbfn(GSM_HOOK_NM_SWLOAD,
- NM_MT_LOAD_END_ACK, mb,
- sw->cb_data, NULL);
- } else {
- DEBUGP(DNM, "Software Load End NACK\n");
- /* FIXME: cause */
- sw->state = SW_STATE_ERROR;
- if (sw->cbfn)
- sw->cbfn(GSM_HOOK_NM_SWLOAD,
- NM_MT_LOAD_END_NACK, mb,
- sw->cb_data, NULL);
- }
- abis_nm_queue_send_next(sign_link->trx->bts);
- break;
- }
- case SW_STATE_WAIT_ACTACK:
- switch (foh->msg_type) {
- case NM_MT_ACTIVATE_SW_ACK:
- /* we're done */
- DEBUGP(DNM, "Activate Software DONE!\n");
- sw->state = SW_STATE_NONE;
- rc = 0;
- if (sw->cbfn)
- sw->cbfn(GSM_HOOK_NM_SWLOAD,
- NM_MT_ACTIVATE_SW_ACK, mb,
- sw->cb_data, NULL);
- abis_nm_queue_send_next(sign_link->trx->bts);
- break;
- case NM_MT_ACTIVATE_SW_NACK:
- DEBUGP(DNM, "Activate Software NACK\n");
- /* FIXME: cause */
- sw->state = SW_STATE_ERROR;
- if (sw->cbfn)
- sw->cbfn(GSM_HOOK_NM_SWLOAD,
- NM_MT_ACTIVATE_SW_NACK, mb,
- sw->cb_data, NULL);
- abis_nm_queue_send_next(sign_link->trx->bts);
- break;
- }
- case SW_STATE_NONE:
- switch (foh->msg_type) {
- case NM_MT_ACTIVATE_SW_ACK:
- rc = 0;
- break;
- }
- break;
- case SW_STATE_ERROR:
- break;
- }
-
- if (rc)
- DEBUGP(DNM, "unexpected NM MT 0x%02x in state %u -> %u\n",
- foh->msg_type, old_state, sw->state);
-
- return rc;
-}
-
-/* Load the specified software into the BTS */
-int abis_nm_software_load(struct gsm_bts *bts, int trx_nr, const char *fname,
- uint8_t win_size, int forced,
- gsm_cbfn *cbfn, void *cb_data)
-{
- struct abis_nm_sw *sw = &g_sw;
- int rc;
-
- DEBUGP(DNM, "Software Load (BTS %u, File \"%s\")\n",
- bts->nr, fname);
-
- if (sw->state != SW_STATE_NONE)
- return -EBUSY;
-
- sw->bts = bts;
- sw->trx_nr = trx_nr;
-
- switch (bts->type) {
- case GSM_BTS_TYPE_BS11:
- sw->obj_class = NM_OC_SITE_MANAGER;
- sw->obj_instance[0] = 0xff;
- sw->obj_instance[1] = 0xff;
- sw->obj_instance[2] = 0xff;
- break;
- case GSM_BTS_TYPE_NANOBTS:
- sw->obj_class = NM_OC_BASEB_TRANSC;
- sw->obj_instance[0] = sw->bts->nr;
- sw->obj_instance[1] = sw->trx_nr;
- sw->obj_instance[2] = 0xff;
- break;
- case GSM_BTS_TYPE_UNKNOWN:
- default:
- LOGPC(DNM, LOGL_ERROR, "Software Load not properly implemented.\n");
- return -1;
- break;
- }
- sw->window_size = win_size;
- sw->state = SW_STATE_WAIT_INITACK;
- sw->cbfn = cbfn;
- sw->cb_data = cb_data;
- sw->forced = forced;
-
- rc = sw_open_file(sw, fname);
- if (rc < 0) {
- sw->state = SW_STATE_NONE;
- return rc;
- }
-
- return sw_load_init(sw);
-}
-
-int abis_nm_software_load_status(struct gsm_bts *bts)
-{
- struct abis_nm_sw *sw = &g_sw;
- struct stat st;
- int rc, percent;
-
- rc = fstat(sw->fd, &st);
- if (rc < 0) {
- perror("ERROR during stat");
- return rc;
- }
-
- if (sw->stream)
- percent = (ftell(sw->stream) * 100) / st.st_size;
- else
- percent = (lseek(sw->fd, 0, SEEK_CUR) * 100) / st.st_size;
- return percent;
-}
-
-/* Activate the specified software into the BTS */
-int abis_nm_software_activate(struct gsm_bts *bts, const char *fname,
- gsm_cbfn *cbfn, void *cb_data)
-{
- struct abis_nm_sw *sw = &g_sw;
- int rc;
-
- DEBUGP(DNM, "Activating Software (BTS %u, File \"%s\")\n",
- bts->nr, fname);
-
- if (sw->state != SW_STATE_NONE)
- return -EBUSY;
-
- sw->bts = bts;
- sw->obj_class = NM_OC_SITE_MANAGER;
- sw->obj_instance[0] = 0xff;
- sw->obj_instance[1] = 0xff;
- sw->obj_instance[2] = 0xff;
- sw->state = SW_STATE_WAIT_ACTACK;
- sw->cbfn = cbfn;
- sw->cb_data = cb_data;
-
- /* Open the file in order to fill some sw struct members */
- rc = sw_open_file(sw, fname);
- if (rc < 0) {
- sw->state = SW_STATE_NONE;
- return rc;
- }
- sw_close_file(sw);
-
- return sw_activate(sw);
-}
-
-static void fill_nm_channel(struct abis_nm_channel *ch, uint8_t bts_port,
- uint8_t ts_nr, uint8_t subslot_nr)
-{
- ch->attrib = NM_ATT_ABIS_CHANNEL;
- ch->bts_port = bts_port;
- ch->timeslot = ts_nr;
- ch->subslot = subslot_nr;
-}
-
-int abis_nm_establish_tei(struct gsm_bts *bts, uint8_t trx_nr,
- uint8_t e1_port, uint8_t e1_timeslot, uint8_t e1_subslot,
- uint8_t tei)
-{
- struct abis_om_hdr *oh;
- struct abis_nm_channel *ch;
- uint8_t len = sizeof(*ch) + 2;
- struct msgb *msg = nm_msgb_alloc();
-
- oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
- fill_om_fom_hdr(oh, len, NM_MT_ESTABLISH_TEI, NM_OC_RADIO_CARRIER,
- bts->bts_nr, trx_nr, 0xff);
-
- msgb_tv_put(msg, NM_ATT_TEI, tei);
-
- ch = (struct abis_nm_channel *) msgb_put(msg, sizeof(*ch));
- fill_nm_channel(ch, e1_port, e1_timeslot, e1_subslot);
-
- return abis_nm_sendmsg(bts, msg);
-}
-
-/* connect signalling of one (BTS,TRX) to a particular timeslot on the E1 */
-int abis_nm_conn_terr_sign(struct gsm_bts_trx *trx,
- uint8_t e1_port, uint8_t e1_timeslot, uint8_t e1_subslot)
-{
- struct gsm_bts *bts = trx->bts;
- struct abis_om_hdr *oh;
- struct abis_nm_channel *ch;
- struct msgb *msg = nm_msgb_alloc();
-
- oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
- fill_om_fom_hdr(oh, sizeof(*ch), NM_MT_CONN_TERR_SIGN,
- NM_OC_RADIO_CARRIER, bts->bts_nr, trx->nr, 0xff);
-
- ch = (struct abis_nm_channel *) msgb_put(msg, sizeof(*ch));
- fill_nm_channel(ch, e1_port, e1_timeslot, e1_subslot);
-
- return abis_nm_sendmsg(bts, msg);
-}
-
-#if 0
-int abis_nm_disc_terr_sign(struct abis_nm_h *h, struct abis_om_obj_inst *inst,
- struct abis_nm_abis_channel *chan)
-{
-}
-#endif
-
-int abis_nm_conn_terr_traf(struct gsm_bts_trx_ts *ts,
- uint8_t e1_port, uint8_t e1_timeslot,
- uint8_t e1_subslot)
-{
- struct gsm_bts *bts = ts->trx->bts;
- struct abis_om_hdr *oh;
- struct abis_nm_channel *ch;
- struct msgb *msg = nm_msgb_alloc();
-
- oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
- fill_om_fom_hdr(oh, sizeof(*ch), NM_MT_CONN_TERR_TRAF,
- NM_OC_CHANNEL, bts->bts_nr, ts->trx->nr, ts->nr);
-
- ch = (struct abis_nm_channel *) msgb_put(msg, sizeof(*ch));
- fill_nm_channel(ch, e1_port, e1_timeslot, e1_subslot);
-
- DEBUGP(DNM, "CONNECT TERR TRAF Um=%s E1=(%u,%u,%u)\n",
- gsm_ts_name(ts),
- e1_port, e1_timeslot, e1_subslot);
-
- return abis_nm_sendmsg(bts, msg);
-}
-
-#if 0
-int abis_nm_disc_terr_traf(struct abis_nm_h *h, struct abis_om_obj_inst *inst,
- struct abis_nm_abis_channel *chan,
- uint8_t subchan)
-{
-}
-#endif
-
-/* 3GPP TS 52.021 § 8.11.1 */
-int abis_nm_get_attr(struct gsm_bts *bts, uint8_t obj_class, uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr,
- const uint8_t *attr, uint8_t attr_len)
-{
- struct abis_om_hdr *oh;
- struct msgb *msg = nm_msgb_alloc();
-
- DEBUGP(DNM, "Get Attr (bts=%d)\n", bts->nr);
-
- oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
- fill_om_fom_hdr(oh, attr_len, NM_MT_GET_ATTR, obj_class,
- bts_nr, trx_nr, ts_nr);
- msgb_tl16v_put(msg, NM_ATT_LIST_REQ_ATTR, attr_len, attr);
-
- return abis_nm_sendmsg(bts, msg);
-}
-
-/* Chapter 8.6.1 */
-int abis_nm_set_bts_attr(struct gsm_bts *bts, uint8_t *attr, int attr_len)
-{
- struct abis_om_hdr *oh;
- struct msgb *msg = nm_msgb_alloc();
- uint8_t *cur;
-
- DEBUGP(DNM, "Set BTS Attr (bts=%d)\n", bts->nr);
-
- oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
- fill_om_fom_hdr(oh, attr_len, NM_MT_SET_BTS_ATTR, NM_OC_BTS, bts->bts_nr, 0xff, 0xff);
- cur = msgb_put(msg, attr_len);
- memcpy(cur, attr, attr_len);
-
- return abis_nm_sendmsg(bts, msg);
-}
-
-/* Chapter 8.6.2 */
-int abis_nm_set_radio_attr(struct gsm_bts_trx *trx, uint8_t *attr, int attr_len)
-{
- struct abis_om_hdr *oh;
- struct msgb *msg = nm_msgb_alloc();
- uint8_t *cur;
-
- DEBUGP(DNM, "Set TRX Attr (bts=%d,trx=%d)\n", trx->bts->nr, trx->nr);
-
- oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
- fill_om_fom_hdr(oh, attr_len, NM_MT_SET_RADIO_ATTR, NM_OC_RADIO_CARRIER,
- trx->bts->bts_nr, trx->nr, 0xff);
- cur = msgb_put(msg, attr_len);
- memcpy(cur, attr, attr_len);
-
- return abis_nm_sendmsg(trx->bts, msg);
-}
-
-int abis_nm_update_max_power_red(struct gsm_bts_trx *trx)
-{
- uint8_t attr[] = { NM_ATT_RF_MAXPOWR_R, trx->max_power_red / 2 };
- return abis_nm_set_radio_attr(trx, attr, ARRAY_SIZE(attr));
-}
-
-static int verify_chan_comb(struct gsm_bts_trx_ts *ts, uint8_t chan_comb,
- const char **reason)
-{
- int i;
-
- *reason = "Reason unknown";
-
- /* As it turns out, the BS-11 has some very peculiar restrictions
- * on the channel combinations it allows */
- switch (ts->trx->bts->type) {
- case GSM_BTS_TYPE_BS11:
- switch (chan_comb) {
- case NM_CHANC_TCHHalf:
- case NM_CHANC_TCHHalf2:
- case NM_CHANC_OSMO_TCHFull_TCHHalf_PDCH:
- /* not supported */
- *reason = "TCH/H is not supported.";
- return -EINVAL;
- case NM_CHANC_SDCCH:
- /* only one SDCCH/8 per TRX */
- for (i = 0; i < TRX_NR_TS; i++) {
- if (i == ts->nr)
- continue;
- if (ts->trx->ts[i].nm_chan_comb ==
- NM_CHANC_SDCCH) {
- *reason = "Only one SDCCH/8 per TRX allowed.";
- return -EINVAL;
- }
- }
- /* not allowed for TS0 of BCCH-TRX */
- if (ts->trx == ts->trx->bts->c0 &&
- ts->nr == 0) {
- *reason = "SDCCH/8 must be on TS0.";
- return -EINVAL;
- }
-
- /* not on the same TRX that has a BCCH+SDCCH4
- * combination */
- if (ts->trx != ts->trx->bts->c0 &&
- (ts->trx->ts[0].nm_chan_comb == 5 ||
- ts->trx->ts[0].nm_chan_comb == 8)) {
- *reason = "SDCCH/8 and BCCH must be on the same TRX.";
- return -EINVAL;
- }
- break;
- case NM_CHANC_mainBCCH:
- case NM_CHANC_BCCHComb:
- /* allowed only for TS0 of C0 */
- if (ts->trx != ts->trx->bts->c0 || ts->nr != 0) {
- *reason = "Main BCCH must be on TS0.";
- return -EINVAL;
- }
- break;
- case NM_CHANC_BCCH:
- /* allowed only for TS 2/4/6 of C0 */
- if (ts->trx != ts->trx->bts->c0) {
- *reason = "BCCH must be on C0.";
- return -EINVAL;
- }
- if (ts->nr != 2 && ts->nr != 4 && ts->nr != 6) {
- *reason = "BCCH must be on TS 2/4/6.";
- return -EINVAL;
- }
- break;
- case 8: /* this is not like 08.58, but in fact
- * FCCH+SCH+BCCH+CCCH+SDCCH/4+SACCH/C4+CBCH */
- /* FIXME: only one CBCH allowed per cell */
- break;
- }
- break;
- case GSM_BTS_TYPE_NANOBTS:
- switch (ts->nr) {
- case 0:
- if (ts->trx->nr == 0) {
- /* only on TRX0 */
- switch (chan_comb) {
- case NM_CHANC_BCCH:
- case NM_CHANC_mainBCCH:
- case NM_CHANC_BCCHComb:
- return 0;
- break;
- default:
- *reason = "TS0 of TRX0 must carry a BCCH.";
- return -EINVAL;
- }
- } else {
- switch (chan_comb) {
- case NM_CHANC_TCHFull:
- case NM_CHANC_TCHHalf:
- case NM_CHANC_IPAC_TCHFull_TCHHalf:
- return 0;
- default:
- *reason = "TS0 must carry a TCH/F or TCH/H.";
- return -EINVAL;
- }
- }
- break;
- case 1:
- if (ts->trx->nr == 0) {
- switch (chan_comb) {
- case NM_CHANC_SDCCH_CBCH:
- if (ts->trx->ts[0].nm_chan_comb ==
- NM_CHANC_mainBCCH)
- return 0;
- *reason = "TS0 must be the main BCCH for CBCH.";
- return -EINVAL;
- case NM_CHANC_SDCCH:
- case NM_CHANC_TCHFull:
- case NM_CHANC_TCHHalf:
- case NM_CHANC_IPAC_TCHFull_TCHHalf:
- case NM_CHANC_IPAC_TCHFull_PDCH:
- case NM_CHANC_OSMO_TCHFull_TCHHalf_PDCH:
- return 0;
- default:
- *reason = "TS1 must carry a CBCH, SDCCH or TCH.";
- return -EINVAL;
- }
- } else {
- switch (chan_comb) {
- case NM_CHANC_SDCCH:
- case NM_CHANC_TCHFull:
- case NM_CHANC_TCHHalf:
- case NM_CHANC_IPAC_TCHFull_TCHHalf:
- return 0;
- default:
- *reason = "TS1 must carry a SDCCH or TCH.";
- return -EINVAL;
- }
- }
- break;
- case 2:
- case 3:
- case 4:
- case 5:
- case 6:
- case 7:
- switch (chan_comb) {
- case NM_CHANC_TCHFull:
- case NM_CHANC_TCHHalf:
- case NM_CHANC_IPAC_TCHFull_TCHHalf:
- return 0;
- case NM_CHANC_IPAC_PDCH:
- case NM_CHANC_IPAC_TCHFull_PDCH:
- case NM_CHANC_OSMO_TCHFull_TCHHalf_PDCH:
- if (ts->trx->nr == 0)
- return 0;
- else {
- *reason = "PDCH must be on TRX0.";
- return -EINVAL;
- }
- }
- break;
- }
- *reason = "Unknown combination";
- return -EINVAL;
- case GSM_BTS_TYPE_OSMOBTS:
- /* no known restrictions */
- return 0;
- default:
- /* unknown BTS type */
- return 0;
- }
- return 0;
-}
-
-/* Chapter 8.6.3 */
-int abis_nm_set_channel_attr(struct gsm_bts_trx_ts *ts, uint8_t chan_comb)
-{
- struct gsm_bts *bts = ts->trx->bts;
- struct abis_om_hdr *oh;
- uint8_t zero = 0x00;
- struct msgb *msg = nm_msgb_alloc();
- uint8_t len = 2 + 2;
- const char *reason = NULL;
-
- if (bts->type == GSM_BTS_TYPE_BS11)
- len += 4 + 2 + 2 + 3;
-
- DEBUGP(DNM, "Set Chan Attr %s\n", gsm_ts_name(ts));
- if (verify_chan_comb(ts, chan_comb, &reason) < 0) {
- msgb_free(msg);
- LOGP(DNM, LOGL_ERROR,
- "Invalid Channel Combination %d on %s. Reason: %s\n",
- chan_comb, gsm_ts_name(ts), reason);
- return -EINVAL;
- }
- ts->nm_chan_comb = chan_comb;
-
- oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
- fill_om_fom_hdr(oh, len, NM_MT_SET_CHAN_ATTR,
- NM_OC_CHANNEL, bts->bts_nr,
- ts->trx->nr, ts->nr);
- msgb_tv_put(msg, NM_ATT_CHAN_COMB, chan_comb);
- if (ts->hopping.enabled) {
- unsigned int i;
- uint8_t *len;
-
- msgb_tv_put(msg, NM_ATT_HSN, ts->hopping.hsn);
- msgb_tv_put(msg, NM_ATT_MAIO, ts->hopping.maio);
-
- /* build the ARFCN list */
- msgb_put_u8(msg, NM_ATT_ARFCN_LIST);
- len = msgb_put(msg, 1);
- *len = 0;
- for (i = 0; i < ts->hopping.arfcns.data_len*8; i++) {
- if (bitvec_get_bit_pos(&ts->hopping.arfcns, i)) {
- msgb_put_u16(msg, i);
- /* At least BS-11 wants a TLV16 here */
- if (bts->type == GSM_BTS_TYPE_BS11)
- *len += 1;
- else
- *len += sizeof(uint16_t);
- }
- }
- }
- msgb_tv_put(msg, NM_ATT_TSC, gsm_ts_tsc(ts)); /* training sequence */
- if (bts->type == GSM_BTS_TYPE_BS11)
- msgb_tlv_put(msg, 0x59, 1, &zero);
-
- return abis_nm_sendmsg(bts, msg);
-}
-
-int abis_nm_sw_act_req_ack(struct gsm_bts *bts, uint8_t obj_class, uint8_t i1,
- uint8_t i2, uint8_t i3, int nack, uint8_t *attr, int att_len)
-{
- struct abis_om_hdr *oh;
- struct msgb *msg = nm_msgb_alloc();
- uint8_t msgtype = NM_MT_SW_ACT_REQ_ACK;
- uint8_t len = att_len;
-
- if (nack) {
- len += 2;
- msgtype = NM_MT_SW_ACT_REQ_NACK;
- }
-
- oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
- fill_om_fom_hdr(oh, att_len, msgtype, obj_class, i1, i2, i3);
-
- if (attr) {
- uint8_t *ptr = msgb_put(msg, att_len);
- memcpy(ptr, attr, att_len);
- }
- if (nack)
- msgb_tv_put(msg, NM_ATT_NACK_CAUSES, NM_NACK_OBJCLASS_NOTSUPP);
-
- return abis_nm_sendmsg_direct(bts, msg);
-}
-
-int abis_nm_raw_msg(struct gsm_bts *bts, int len, uint8_t *rawmsg)
-{
- struct msgb *msg = nm_msgb_alloc();
- struct abis_om_hdr *oh;
- uint8_t *data;
-
- oh = (struct abis_om_hdr *) msgb_put(msg, sizeof(*oh));
- fill_om_hdr(oh, len);
- data = msgb_put(msg, len);
- memcpy(data, rawmsg, len);
-
- return abis_nm_sendmsg(bts, msg);
-}
-
-/* Siemens specific commands */
-static int __simple_cmd(struct gsm_bts *bts, uint8_t msg_type)
-{
- struct abis_om_hdr *oh;
- struct msgb *msg = nm_msgb_alloc();
-
- oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
- fill_om_fom_hdr(oh, 0, msg_type, NM_OC_SITE_MANAGER,
- 0xff, 0xff, 0xff);
-
- return abis_nm_sendmsg(bts, msg);
-}
-
-/* Chapter 8.9.2 */
-int abis_nm_opstart(struct gsm_bts *bts, uint8_t obj_class, uint8_t i0, uint8_t i1, uint8_t i2)
-{
- struct abis_om_hdr *oh;
- struct abis_om_fom_hdr *foh;
- struct msgb *msg = nm_msgb_alloc();
-
- oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
- foh = fill_om_fom_hdr(oh, 0, NM_MT_OPSTART, obj_class, i0, i1, i2);
-
- abis_nm_debugp_foh(DNM, foh);
- DEBUGPC(DNM, "Sending OPSTART\n");
-
- return abis_nm_sendmsg(bts, msg);
-}
-
-/* Chapter 8.8.5 */
-int abis_nm_chg_adm_state(struct gsm_bts *bts, uint8_t obj_class, uint8_t i0,
- uint8_t i1, uint8_t i2, enum abis_nm_adm_state adm_state)
-{
- struct abis_om_hdr *oh;
- struct msgb *msg = nm_msgb_alloc();
-
- oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
- fill_om_fom_hdr(oh, 2, NM_MT_CHG_ADM_STATE, obj_class, i0, i1, i2);
- msgb_tv_put(msg, NM_ATT_ADM_STATE, adm_state);
-
- return abis_nm_sendmsg(bts, msg);
-}
-
-int abis_nm_conn_mdrop_link(struct gsm_bts *bts, uint8_t e1_port0, uint8_t ts0,
- uint8_t e1_port1, uint8_t ts1)
-{
- struct abis_om_hdr *oh;
- struct msgb *msg = nm_msgb_alloc();
- uint8_t *attr;
-
- DEBUGP(DNM, "CONNECT MDROP LINK E1=(%u,%u) -> E1=(%u, %u)\n",
- e1_port0, ts0, e1_port1, ts1);
-
- oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
- fill_om_fom_hdr(oh, 6, NM_MT_CONN_MDROP_LINK,
- NM_OC_SITE_MANAGER, 0x00, 0x00, 0x00);
-
- attr = msgb_put(msg, 3);
- attr[0] = NM_ATT_MDROP_LINK;
- attr[1] = e1_port0;
- attr[2] = ts0;
-
- attr = msgb_put(msg, 3);
- attr[0] = NM_ATT_MDROP_NEXT;
- attr[1] = e1_port1;
- attr[2] = ts1;
-
- return abis_nm_sendmsg(bts, msg);
-}
-
-/* Chapter 8.7.1 */
-int abis_nm_perform_test(struct gsm_bts *bts, uint8_t obj_class,
- uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr,
- uint8_t test_nr, uint8_t auton_report, struct msgb *msg)
-{
- struct abis_om_hdr *oh;
-
- DEBUGP(DNM, "PEFORM TEST %s\n", abis_nm_test_name(test_nr));
-
- if (!msg)
- msg = nm_msgb_alloc();
-
- msgb_tv_push(msg, NM_ATT_AUTON_REPORT, auton_report);
- msgb_tv_push(msg, NM_ATT_TEST_NO, test_nr);
- oh = (struct abis_om_hdr *) msgb_push(msg, ABIS_OM_FOM_HDR_SIZE);
- fill_om_fom_hdr(oh, msgb_l3len(msg), NM_MT_PERF_TEST,
- obj_class, bts_nr, trx_nr, ts_nr);
-
- return abis_nm_sendmsg(bts, msg);
-}
-
-int abis_nm_event_reports(struct gsm_bts *bts, int on)
-{
- if (on == 0)
- return __simple_cmd(bts, NM_MT_STOP_EVENT_REP);
- else
- return __simple_cmd(bts, NM_MT_REST_EVENT_REP);
-}
-
-/* Siemens (or BS-11) specific commands */
-
-int abis_nm_bs11_bsc_disconnect(struct gsm_bts *bts, int reconnect)
-{
- if (reconnect == 0)
- return __simple_cmd(bts, NM_MT_BS11_DISCONNECT);
- else
- return __simple_cmd(bts, NM_MT_BS11_RECONNECT);
-}
-
-int abis_nm_bs11_restart(struct gsm_bts *bts)
-{
- return __simple_cmd(bts, NM_MT_BS11_RESTART);
-}
-
-
-struct bs11_date_time {
- uint16_t year;
- uint8_t month;
- uint8_t day;
- uint8_t hour;
- uint8_t min;
- uint8_t sec;
-} __attribute__((packed));
-
-
-void get_bs11_date_time(struct bs11_date_time *aet)
-{
- time_t t;
- struct tm *tm;
-
- t = time(NULL);
- tm = localtime(&t);
- aet->sec = tm->tm_sec;
- aet->min = tm->tm_min;
- aet->hour = tm->tm_hour;
- aet->day = tm->tm_mday;
- aet->month = tm->tm_mon;
- aet->year = htons(1900 + tm->tm_year);
-}
-
-int abis_nm_bs11_reset_resource(struct gsm_bts *bts)
-{
- return __simple_cmd(bts, NM_MT_BS11_RESET_RESOURCE);
-}
-
-int abis_nm_bs11_db_transmission(struct gsm_bts *bts, int begin)
-{
- if (begin)
- return __simple_cmd(bts, NM_MT_BS11_BEGIN_DB_TX);
- else
- return __simple_cmd(bts, NM_MT_BS11_END_DB_TX);
-}
-
-int abis_nm_bs11_create_object(struct gsm_bts *bts,
- enum abis_bs11_objtype type, uint8_t idx,
- uint8_t attr_len, const uint8_t *attr)
-{
- struct abis_om_hdr *oh;
- struct msgb *msg = nm_msgb_alloc();
- uint8_t *cur;
-
- oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
- fill_om_fom_hdr(oh, attr_len, NM_MT_BS11_CREATE_OBJ,
- NM_OC_BS11, type, 0, idx);
- cur = msgb_put(msg, attr_len);
- memcpy(cur, attr, attr_len);
-
- return abis_nm_sendmsg(bts, msg);
-}
-
-int abis_nm_bs11_delete_object(struct gsm_bts *bts,
- enum abis_bs11_objtype type, uint8_t idx)
-{
- struct abis_om_hdr *oh;
- struct msgb *msg = nm_msgb_alloc();
-
- oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
- fill_om_fom_hdr(oh, 0, NM_MT_BS11_DELETE_OBJ,
- NM_OC_BS11, type, 0, idx);
-
- return abis_nm_sendmsg(bts, msg);
-}
-
-int abis_nm_bs11_create_envaBTSE(struct gsm_bts *bts, uint8_t idx)
-{
- struct abis_om_hdr *oh;
- struct msgb *msg = nm_msgb_alloc();
- uint8_t zero = 0x00;
-
- oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
- fill_om_fom_hdr(oh, 3, NM_MT_BS11_CREATE_OBJ,
- NM_OC_BS11_ENVABTSE, 0, idx, 0xff);
- msgb_tlv_put(msg, 0x99, 1, &zero);
-
- return abis_nm_sendmsg(bts, msg);
-}
-
-int abis_nm_bs11_create_bport(struct gsm_bts *bts, uint8_t idx)
-{
- struct abis_om_hdr *oh;
- struct msgb *msg = nm_msgb_alloc();
-
- oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
- fill_om_fom_hdr(oh, 0, NM_MT_BS11_CREATE_OBJ, NM_OC_BS11_BPORT,
- idx, 0xff, 0xff);
-
- return abis_nm_sendmsg(bts, msg);
-}
-
-int abis_nm_bs11_delete_bport(struct gsm_bts *bts, uint8_t idx)
-{
- struct abis_om_hdr *oh;
- struct msgb *msg = nm_msgb_alloc();
-
- oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
- fill_om_fom_hdr(oh, 0, NM_MT_BS11_DELETE_OBJ, NM_OC_BS11_BPORT,
- idx, 0xff, 0xff);
-
- return abis_nm_sendmsg(bts, msg);
-}
-
-static const uint8_t sm_attr[] = { NM_ATT_TEI, NM_ATT_ABIS_CHANNEL };
-int abis_nm_bs11_get_oml_tei_ts(struct gsm_bts *bts)
-{
- struct abis_om_hdr *oh;
- struct msgb *msg = nm_msgb_alloc();
-
- oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
- fill_om_fom_hdr(oh, 2+sizeof(sm_attr), NM_MT_GET_ATTR, NM_OC_SITE_MANAGER,
- 0xff, 0xff, 0xff);
- msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(sm_attr), sm_attr);
-
- return abis_nm_sendmsg(bts, msg);
-}
-
-/* like abis_nm_conn_terr_traf + set_tei */
-int abis_nm_bs11_conn_oml_tei(struct gsm_bts *bts, uint8_t e1_port,
- uint8_t e1_timeslot, uint8_t e1_subslot,
- uint8_t tei)
-{
- struct abis_om_hdr *oh;
- struct abis_nm_channel *ch;
- struct msgb *msg = nm_msgb_alloc();
-
- oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
- fill_om_fom_hdr(oh, sizeof(*ch)+2, NM_MT_BS11_SET_ATTR,
- NM_OC_SITE_MANAGER, 0xff, 0xff, 0xff);
-
- ch = (struct abis_nm_channel *) msgb_put(msg, sizeof(*ch));
- fill_nm_channel(ch, e1_port, e1_timeslot, e1_subslot);
- msgb_tv_put(msg, NM_ATT_TEI, tei);
-
- return abis_nm_sendmsg(bts, msg);
-}
-
-int abis_nm_bs11_set_trx_power(struct gsm_bts_trx *trx, uint8_t level)
-{
- struct abis_om_hdr *oh;
- struct msgb *msg = nm_msgb_alloc();
-
- oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
- fill_om_fom_hdr(oh, 3, NM_MT_BS11_SET_ATTR,
- NM_OC_BS11, BS11_OBJ_PA, 0x00, trx->nr);
- msgb_tlv_put(msg, NM_ATT_BS11_TXPWR, 1, &level);
-
- return abis_nm_sendmsg(trx->bts, msg);
-}
-
-int abis_nm_bs11_get_trx_power(struct gsm_bts_trx *trx)
-{
- struct abis_om_hdr *oh;
- struct msgb *msg = nm_msgb_alloc();
- uint8_t attr = NM_ATT_BS11_TXPWR;
-
- oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
- fill_om_fom_hdr(oh, 2+sizeof(attr), NM_MT_GET_ATTR,
- NM_OC_BS11, BS11_OBJ_PA, 0x00, trx->nr);
- msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(attr), &attr);
-
- return abis_nm_sendmsg(trx->bts, msg);
-}
-
-int abis_nm_bs11_get_pll_mode(struct gsm_bts *bts)
-{
- struct abis_om_hdr *oh;
- struct msgb *msg = nm_msgb_alloc();
- uint8_t attr[] = { NM_ATT_BS11_PLL_MODE };
-
- oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
- fill_om_fom_hdr(oh, 2+sizeof(attr), NM_MT_GET_ATTR,
- NM_OC_BS11, BS11_OBJ_LI, 0x00, 0x00);
- msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(attr), attr);
-
- return abis_nm_sendmsg(bts, msg);
-}
-
-int abis_nm_bs11_get_cclk(struct gsm_bts *bts)
-{
- struct abis_om_hdr *oh;
- struct msgb *msg = nm_msgb_alloc();
- uint8_t attr[] = { NM_ATT_BS11_CCLK_ACCURACY,
- NM_ATT_BS11_CCLK_TYPE };
-
- oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
- fill_om_fom_hdr(oh, 2+sizeof(attr), NM_MT_GET_ATTR,
- NM_OC_BS11, BS11_OBJ_CCLK, 0x00, 0x00);
- msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(attr), attr);
-
- return abis_nm_sendmsg(bts, msg);
-
-}
-
-//static const uint8_t bs11_logon_c7[] = { 0x07, 0xd9, 0x01, 0x11, 0x0d, 0x10, 0x20 };
-
-int abis_nm_bs11_factory_logon(struct gsm_bts *bts, int on)
-{
- return abis_nm_bs11_logon(bts, 0x02, "FACTORY", on);
-}
-
-int abis_nm_bs11_infield_logon(struct gsm_bts *bts, int on)
-{
- return abis_nm_bs11_logon(bts, 0x03, "FIELD ", on);
-}
-
-int abis_nm_bs11_logon(struct gsm_bts *bts, uint8_t level, const char *name, int on)
-{
- struct abis_om_hdr *oh;
- struct msgb *msg = nm_msgb_alloc();
- struct bs11_date_time bdt;
-
- get_bs11_date_time(&bdt);
-
- oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
- if (on) {
- uint8_t len = 3*2 + sizeof(bdt)
- + 1 + strlen(name);
- fill_om_fom_hdr(oh, len, NM_MT_BS11_LMT_LOGON,
- NM_OC_BS11_BTSE, 0xff, 0xff, 0xff);
- msgb_tlv_put(msg, NM_ATT_BS11_LMT_LOGIN_TIME,
- sizeof(bdt), (uint8_t *) &bdt);
- msgb_tlv_put(msg, NM_ATT_BS11_LMT_USER_ACC_LEV,
- 1, &level);
- msgb_tlv_put(msg, NM_ATT_BS11_LMT_USER_NAME,
- strlen(name), (uint8_t *)name);
- } else {
- fill_om_fom_hdr(oh, 0, NM_MT_BS11_LMT_LOGOFF,
- NM_OC_BS11_BTSE, 0xff, 0xff, 0xff);
- }
-
- return abis_nm_sendmsg(bts, msg);
-}
-
-int abis_nm_bs11_set_trx1_pw(struct gsm_bts *bts, const char *password)
-{
- struct abis_om_hdr *oh;
- struct msgb *msg;
-
- if (strlen(password) != 10)
- return -EINVAL;
-
- msg = nm_msgb_alloc();
- oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
- fill_om_fom_hdr(oh, 2+strlen(password), NM_MT_BS11_SET_ATTR,
- NM_OC_BS11, BS11_OBJ_TRX1, 0x00, 0x00);
- msgb_tlv_put(msg, NM_ATT_BS11_PASSWORD, 10, (const uint8_t *)password);
-
- return abis_nm_sendmsg(bts, msg);
-}
-
-/* change the BS-11 PLL Mode to either locked (E1 derived) or standalone */
-int abis_nm_bs11_set_pll_locked(struct gsm_bts *bts, int locked)
-{
- struct abis_om_hdr *oh;
- struct msgb *msg;
- uint8_t tlv_value;
-
- msg = nm_msgb_alloc();
- oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
- fill_om_fom_hdr(oh, 3, NM_MT_BS11_SET_ATTR, NM_OC_BS11,
- BS11_OBJ_LI, 0x00, 0x00);
-
- if (locked)
- tlv_value = BS11_LI_PLL_LOCKED;
- else
- tlv_value = BS11_LI_PLL_STANDALONE;
-
- msgb_tlv_put(msg, NM_ATT_BS11_PLL_MODE, 1, &tlv_value);
-
- return abis_nm_sendmsg(bts, msg);
-}
-
-/* Set the calibration value of the PLL (work value/set value)
- * It depends on the login which one is changed */
-int abis_nm_bs11_set_pll(struct gsm_bts *bts, int value)
-{
- struct abis_om_hdr *oh;
- struct msgb *msg;
- uint8_t tlv_value[2];
-
- msg = nm_msgb_alloc();
- oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
- fill_om_fom_hdr(oh, 3, NM_MT_BS11_SET_ATTR, NM_OC_BS11,
- BS11_OBJ_TRX1, 0x00, 0x00);
-
- tlv_value[0] = value>>8;
- tlv_value[1] = value&0xff;
-
- msgb_tlv_put(msg, NM_ATT_BS11_PLL, 2, tlv_value);
-
- return abis_nm_sendmsg(bts, msg);
-}
-
-int abis_nm_bs11_get_state(struct gsm_bts *bts)
-{
- return __simple_cmd(bts, NM_MT_BS11_GET_STATE);
-}
-
-/* BS11 SWL */
-
-void *tall_fle_ctx;
-
-struct abis_nm_bs11_sw {
- struct gsm_bts *bts;
- char swl_fname[PATH_MAX];
- uint8_t win_size;
- int forced;
- struct llist_head file_list;
- gsm_cbfn *user_cb; /* specified by the user */
-};
-static struct abis_nm_bs11_sw _g_bs11_sw, *g_bs11_sw = &_g_bs11_sw;
-
-struct file_list_entry {
- struct llist_head list;
- char fname[PATH_MAX];
-};
-
-struct file_list_entry *fl_dequeue(struct llist_head *queue)
-{
- struct llist_head *lh;
-
- if (llist_empty(queue))
- return NULL;
-
- lh = queue->next;
- llist_del(lh);
-
- return llist_entry(lh, struct file_list_entry, list);
-}
-
-static int bs11_read_swl_file(struct abis_nm_bs11_sw *bs11_sw)
-{
- char linebuf[255];
- struct llist_head *lh, *lh2;
- FILE *swl;
- int rc = 0;
-
- swl = fopen(bs11_sw->swl_fname, "r");
- if (!swl)
- return -ENODEV;
-
- /* zero the stale file list, if any */
- llist_for_each_safe(lh, lh2, &bs11_sw->file_list) {
- llist_del(lh);
- talloc_free(lh);
- }
-
- while (fgets(linebuf, sizeof(linebuf), swl)) {
- char file_id[12+1];
- char file_version[80+1];
- struct file_list_entry *fle;
- static char dir[PATH_MAX];
-
- if (strlen(linebuf) < 4)
- continue;
-
- rc = sscanf(linebuf+4, "%12s:%80s\r\n", file_id, file_version);
- if (rc < 0) {
- perror("ERR parsing SWL file");
- rc = -EINVAL;
- goto out;
- }
- if (rc < 2)
- continue;
-
- fle = talloc_zero(tall_fle_ctx, struct file_list_entry);
- if (!fle) {
- rc = -ENOMEM;
- goto out;
- }
-
- /* construct new filename */
- osmo_strlcpy(dir, bs11_sw->swl_fname, sizeof(dir));
- strncat(fle->fname, dirname(dir), sizeof(fle->fname) - 1);
- strcat(fle->fname, "/");
- strncat(fle->fname, file_id, sizeof(fle->fname) - 1 -strlen(fle->fname));
-
- llist_add_tail(&fle->list, &bs11_sw->file_list);
- }
-
-out:
- fclose(swl);
- return rc;
-}
-
-/* bs11 swload specific callback, passed to abis_nm core swload */
-static int bs11_swload_cbfn(unsigned int hook, unsigned int event,
- struct msgb *msg, void *data, void *param)
-{
- struct abis_nm_bs11_sw *bs11_sw = data;
- struct file_list_entry *fle;
- int rc = 0;
-
- switch (event) {
- case NM_MT_LOAD_END_ACK:
- fle = fl_dequeue(&bs11_sw->file_list);
- if (fle) {
- /* start download the next file of our file list */
- rc = abis_nm_software_load(bs11_sw->bts, 0xff, fle->fname,
- bs11_sw->win_size,
- bs11_sw->forced,
- &bs11_swload_cbfn, bs11_sw);
- talloc_free(fle);
- } else {
- /* activate the SWL */
- rc = abis_nm_software_activate(bs11_sw->bts,
- bs11_sw->swl_fname,
- bs11_swload_cbfn,
- bs11_sw);
- }
- break;
- case NM_MT_LOAD_SEG_ACK:
- case NM_MT_LOAD_END_NACK:
- case NM_MT_LOAD_INIT_ACK:
- case NM_MT_LOAD_INIT_NACK:
- case NM_MT_ACTIVATE_SW_NACK:
- case NM_MT_ACTIVATE_SW_ACK:
- default:
- /* fallthrough to the user callback */
- if (bs11_sw->user_cb)
- rc = bs11_sw->user_cb(hook, event, msg, NULL, NULL);
- break;
- }
-
- return rc;
-}
-
-/* Siemens provides a SWL file that is a mere listing of all the other
- * files that are part of a software release. We need to upload first
- * the list file, and then each file that is listed in the list file */
-int abis_nm_bs11_load_swl(struct gsm_bts *bts, const char *fname,
- uint8_t win_size, int forced, gsm_cbfn *cbfn)
-{
- struct abis_nm_bs11_sw *bs11_sw = g_bs11_sw;
- struct file_list_entry *fle;
- int rc = 0;
-
- INIT_LLIST_HEAD(&bs11_sw->file_list);
- bs11_sw->bts = bts;
- bs11_sw->win_size = win_size;
- bs11_sw->user_cb = cbfn;
- bs11_sw->forced = forced;
-
- osmo_strlcpy(bs11_sw->swl_fname, fname, sizeof(bs11_sw->swl_fname));
- rc = bs11_read_swl_file(bs11_sw);
- if (rc < 0)
- return rc;
-
- /* dequeue next item in file list */
- fle = fl_dequeue(&bs11_sw->file_list);
- if (!fle)
- return -EINVAL;
-
- /* start download the next file of our file list */
- rc = abis_nm_software_load(bts, 0xff, fle->fname, win_size, forced,
- bs11_swload_cbfn, bs11_sw);
- talloc_free(fle);
- return rc;
-}
-
-#if 0
-static uint8_t req_attr_btse[] = {
- NM_ATT_ADM_STATE, NM_ATT_BS11_LMT_LOGON_SESSION,
- NM_ATT_BS11_LMT_LOGIN_TIME, NM_ATT_BS11_LMT_USER_ACC_LEV,
- NM_ATT_BS11_LMT_USER_NAME,
-
- 0xaf, NM_ATT_BS11_RX_OFFSET, NM_ATT_BS11_VENDOR_NAME,
-
- NM_ATT_BS11_SW_LOAD_INTENDED, NM_ATT_BS11_SW_LOAD_SAFETY,
-
- NM_ATT_BS11_SW_LOAD_STORED };
-
-static uint8_t req_attr_btsm[] = {
- NM_ATT_ABIS_CHANNEL, NM_ATT_TEI, NM_ATT_BS11_ABIS_EXT_TIME,
- NM_ATT_ADM_STATE, NM_ATT_AVAIL_STATUS, 0xce, NM_ATT_FILE_ID,
- NM_ATT_FILE_VERSION, NM_ATT_OPER_STATE, 0xe8, NM_ATT_BS11_ALL_TEST_CATG,
- NM_ATT_SW_DESCR, NM_ATT_GET_ARI };
-#endif
-
-static uint8_t req_attr[] = {
- NM_ATT_ADM_STATE, NM_ATT_AVAIL_STATUS, 0xa8, NM_ATT_OPER_STATE,
- 0xd5, 0xa1, NM_ATT_BS11_ESN_FW_CODE_NO, NM_ATT_BS11_ESN_HW_CODE_NO,
- 0x42, NM_ATT_BS11_ESN_PCB_SERIAL, NM_ATT_BS11_PLL };
-
-int abis_nm_bs11_get_serno(struct gsm_bts *bts)
-{
- struct abis_om_hdr *oh;
- struct msgb *msg = nm_msgb_alloc();
-
- oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
- /* SiemensHW CCTRL object */
- fill_om_fom_hdr(oh, 2+sizeof(req_attr), NM_MT_GET_ATTR, NM_OC_BS11,
- 0x03, 0x00, 0x00);
- msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(req_attr), req_attr);
-
- return abis_nm_sendmsg(bts, msg);
-}
-
-int abis_nm_bs11_set_ext_time(struct gsm_bts *bts)
-{
- struct abis_om_hdr *oh;
- struct msgb *msg = nm_msgb_alloc();
- struct bs11_date_time aet;
-
- get_bs11_date_time(&aet);
- oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
- /* SiemensHW CCTRL object */
- fill_om_fom_hdr(oh, 2+sizeof(aet), NM_MT_BS11_SET_ATTR, NM_OC_SITE_MANAGER,
- 0xff, 0xff, 0xff);
- msgb_tlv_put(msg, NM_ATT_BS11_ABIS_EXT_TIME, sizeof(aet), (uint8_t *) &aet);
-
- return abis_nm_sendmsg(bts, msg);
-}
-
-int abis_nm_bs11_get_bport_line_cfg(struct gsm_bts *bts, uint8_t bport)
-{
- struct abis_om_hdr *oh;
- struct msgb *msg = nm_msgb_alloc();
- uint8_t attr = NM_ATT_BS11_LINE_CFG;
-
- oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
- fill_om_fom_hdr(oh, 2+sizeof(attr), NM_MT_GET_ATTR,
- NM_OC_BS11_BPORT, bport, 0xff, 0x02);
- msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(attr), &attr);
-
- return abis_nm_sendmsg(bts, msg);
-}
-
-int abis_nm_bs11_set_bport_line_cfg(struct gsm_bts *bts, uint8_t bport, enum abis_bs11_line_cfg line_cfg)
-{
- struct abis_om_hdr *oh;
- struct msgb *msg = nm_msgb_alloc();
- struct bs11_date_time aet;
-
- get_bs11_date_time(&aet);
- oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
- fill_om_fom_hdr(oh, 2, NM_MT_BS11_SET_ATTR, NM_OC_BS11_BPORT,
- bport, 0xff, 0x02);
- msgb_tv_put(msg, NM_ATT_BS11_LINE_CFG, line_cfg);
-
- return abis_nm_sendmsg(bts, msg);
-}
-
-/* ip.access nanoBTS specific commands */
-static const char ipaccess_magic[] = "com.ipaccess";
-
-
-static int abis_nm_rx_ipacc(struct msgb *msg)
-{
- struct in_addr addr;
- struct abis_om_hdr *oh = msgb_l2(msg);
- struct abis_om_fom_hdr *foh;
- uint8_t idstrlen = oh->data[0];
- struct tlv_parsed tp;
- struct ipacc_ack_signal_data signal;
- struct e1inp_sign_link *sign_link = msg->dst;
-
- if (strncmp((char *)&oh->data[1], ipaccess_magic, idstrlen)) {
- LOGP(DNM, LOGL_ERROR, "id string is not com.ipaccess !?!\n");
- return -EINVAL;
- }
-
- foh = (struct abis_om_fom_hdr *) (oh->data + 1 + idstrlen);
- abis_nm_tlv_parse(&tp, sign_link->trx->bts, foh->data, oh->length-sizeof(*foh));
-
- abis_nm_debugp_foh(DNM, foh);
-
- DEBUGPC(DNM, "IPACCESS(0x%02x): ", foh->msg_type);
-
- switch (foh->msg_type) {
- case NM_MT_IPACC_RSL_CONNECT_ACK:
- DEBUGPC(DNM, "RSL CONNECT ACK ");
- if (TLVP_PRESENT(&tp, NM_ATT_IPACC_DST_IP)) {
- memcpy(&addr,
- TLVP_VAL(&tp, NM_ATT_IPACC_DST_IP), sizeof(addr));
-
- DEBUGPC(DNM, "IP=%s ", inet_ntoa(addr));
- }
- if (TLVP_PRESENT(&tp, NM_ATT_IPACC_DST_IP_PORT))
- DEBUGPC(DNM, "PORT=%u ",
- ntohs(*((uint16_t *)
- TLVP_VAL(&tp, NM_ATT_IPACC_DST_IP_PORT))));
- if (TLVP_PRESENT(&tp, NM_ATT_IPACC_STREAM_ID))
- DEBUGPC(DNM, "STREAM=0x%02x ",
- *TLVP_VAL(&tp, NM_ATT_IPACC_STREAM_ID));
- DEBUGPC(DNM, "\n");
- break;
- case NM_MT_IPACC_RSL_CONNECT_NACK:
- LOGP(DNM, LOGL_ERROR, "RSL CONNECT NACK ");
- if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES))
- LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n",
- abis_nm_nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES)));
- else
- LOGPC(DNM, LOGL_ERROR, "\n");
- break;
- case NM_MT_IPACC_SET_NVATTR_ACK:
- DEBUGPC(DNM, "SET NVATTR ACK\n");
- /* FIXME: decode and show the actual attributes */
- break;
- case NM_MT_IPACC_SET_NVATTR_NACK:
- LOGP(DNM, LOGL_ERROR, "SET NVATTR NACK ");
- if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES))
- LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n",
- abis_nm_nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES)));
- else
- LOGPC(DNM, LOGL_ERROR, "\n");
- break;
- case NM_MT_IPACC_GET_NVATTR_ACK:
- DEBUGPC(DNM, "GET NVATTR ACK\n");
- /* FIXME: decode and show the actual attributes */
- break;
- case NM_MT_IPACC_GET_NVATTR_NACK:
- LOGPC(DNM, LOGL_ERROR, "GET NVATTR NACK ");
- if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES))
- LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n",
- abis_nm_nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES)));
- else
- LOGPC(DNM, LOGL_ERROR, "\n");
- break;
- case NM_MT_IPACC_SET_ATTR_ACK:
- DEBUGPC(DNM, "SET ATTR ACK\n");
- break;
- case NM_MT_IPACC_SET_ATTR_NACK:
- LOGPC(DNM, LOGL_ERROR, "SET ATTR NACK ");
- if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES))
- LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n",
- abis_nm_nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES)));
- else
- LOGPC(DNM, LOGL_ERROR, "\n");
- break;
- default:
- DEBUGPC(DNM, "unknown\n");
- break;
- }
-
- /* signal handling */
- switch (foh->msg_type) {
- case NM_MT_IPACC_RSL_CONNECT_NACK:
- case NM_MT_IPACC_SET_NVATTR_NACK:
- case NM_MT_IPACC_GET_NVATTR_NACK:
- signal.trx = gsm_bts_trx_by_nr(sign_link->trx->bts, foh->obj_inst.trx_nr);
- signal.msg_type = foh->msg_type;
- osmo_signal_dispatch(SS_NM, S_NM_IPACC_NACK, &signal);
- break;
- case NM_MT_IPACC_SET_NVATTR_ACK:
- signal.trx = gsm_bts_trx_by_nr(sign_link->trx->bts, foh->obj_inst.trx_nr);
- signal.msg_type = foh->msg_type;
- osmo_signal_dispatch(SS_NM, S_NM_IPACC_ACK, &signal);
- break;
- default:
- break;
- }
-
- return 0;
-}
-
-/* send an ip-access manufacturer specific message */
-int abis_nm_ipaccess_msg(struct gsm_bts *bts, uint8_t msg_type,
- uint8_t obj_class, uint8_t bts_nr,
- uint8_t trx_nr, uint8_t ts_nr,
- uint8_t *attr, int attr_len)
-{
- struct msgb *msg = nm_msgb_alloc();
- struct abis_om_hdr *oh;
- struct abis_om_fom_hdr *foh;
- uint8_t *data;
-
- /* construct the 12.21 OM header, observe the erroneous length */
- oh = (struct abis_om_hdr *) msgb_put(msg, sizeof(*oh));
- fill_om_hdr(oh, sizeof(*foh) + attr_len);
- oh->mdisc = ABIS_OM_MDISC_MANUF;
-
- /* add the ip.access magic */
- data = msgb_put(msg, sizeof(ipaccess_magic)+1);
- *data++ = sizeof(ipaccess_magic);
- memcpy(data, ipaccess_magic, sizeof(ipaccess_magic));
-
- /* fill the 12.21 FOM header */
- foh = (struct abis_om_fom_hdr *) msgb_put(msg, sizeof(*foh));
- foh->msg_type = msg_type;
- foh->obj_class = obj_class;
- foh->obj_inst.bts_nr = bts_nr;
- foh->obj_inst.trx_nr = trx_nr;
- foh->obj_inst.ts_nr = ts_nr;
-
- if (attr && attr_len) {
- data = msgb_put(msg, attr_len);
- memcpy(data, attr, attr_len);
- }
-
- return abis_nm_sendmsg(bts, msg);
-}
-
-/* set some attributes in NVRAM */
-int abis_nm_ipaccess_set_nvattr(struct gsm_bts_trx *trx, uint8_t *attr,
- int attr_len)
-{
- return abis_nm_ipaccess_msg(trx->bts, NM_MT_IPACC_SET_NVATTR,
- NM_OC_BASEB_TRANSC, 0, trx->nr, 0xff, attr,
- attr_len);
-}
-
-int abis_nm_ipaccess_rsl_connect(struct gsm_bts_trx *trx,
- uint32_t ip, uint16_t port, uint8_t stream)
-{
- struct in_addr ia;
- uint8_t attr[] = { NM_ATT_IPACC_STREAM_ID, 0,
- NM_ATT_IPACC_DST_IP_PORT, 0, 0,
- NM_ATT_IPACC_DST_IP, 0, 0, 0, 0 };
-
- int attr_len = sizeof(attr);
-
- ia.s_addr = htonl(ip);
- attr[1] = stream;
- attr[3] = port >> 8;
- attr[4] = port & 0xff;
- *(uint32_t *)(attr+6) = ia.s_addr;
-
- /* if ip == 0, we use the default IP */
- if (ip == 0)
- attr_len -= 5;
-
- DEBUGP(DNM, "ip.access RSL CONNECT IP=%s PORT=%u STREAM=0x%02x\n",
- inet_ntoa(ia), port, stream);
-
- return abis_nm_ipaccess_msg(trx->bts, NM_MT_IPACC_RSL_CONNECT,
- NM_OC_BASEB_TRANSC, trx->bts->bts_nr,
- trx->nr, 0xff, attr, attr_len);
-}
-
-/* restart / reboot an ip.access nanoBTS */
-int abis_nm_ipaccess_restart(struct gsm_bts_trx *trx)
-{
- struct abis_om_hdr *oh;
- struct msgb *msg = nm_msgb_alloc();
-
- oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
- fill_om_fom_hdr(oh, 0, NM_MT_IPACC_RESTART, NM_OC_BASEB_TRANSC,
- trx->bts->nr, trx->nr, 0xff);
-
- return abis_nm_sendmsg_direct(trx->bts, msg);
-}
-
-int abis_nm_ipaccess_set_attr(struct gsm_bts *bts, uint8_t obj_class,
- uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr,
- uint8_t *attr, uint8_t attr_len)
-{
- return abis_nm_ipaccess_msg(bts, NM_MT_IPACC_SET_ATTR,
- obj_class, bts_nr, trx_nr, ts_nr,
- attr, attr_len);
-}
-
-void abis_nm_ipaccess_cgi(uint8_t *buf, struct gsm_bts *bts)
-{
- /* we simply reuse the GSM48 function and overwrite the RAC
- * with the Cell ID */
- gsm48_ra_id_by_bts(buf, bts);
- *((uint16_t *)(buf + 5)) = htons(bts->cell_identity);
-}
-
-void gsm_trx_lock_rf(struct gsm_bts_trx *trx, int locked)
-{
- int new_state = locked ? NM_STATE_LOCKED : NM_STATE_UNLOCKED;
-
- trx->mo.nm_state.administrative = new_state;
- if (!trx->bts || !trx->bts->oml_link)
- return;
-
- abis_nm_chg_adm_state(trx->bts, NM_OC_RADIO_CARRIER,
- trx->bts->bts_nr, trx->nr, 0xff,
- new_state);
-}
-
-static const struct value_string ipacc_testres_names[] = {
- { NM_IPACC_TESTRES_SUCCESS, "SUCCESS" },
- { NM_IPACC_TESTRES_TIMEOUT, "TIMEOUT" },
- { NM_IPACC_TESTRES_NO_CHANS, "NO CHANNELS" },
- { NM_IPACC_TESTRES_PARTIAL, "PARTIAL" },
- { NM_IPACC_TESTRES_STOPPED, "STOPPED" },
- { 0, NULL }
-};
-
-const char *ipacc_testres_name(uint8_t res)
-{
- return get_value_string(ipacc_testres_names, res);
-}
-
-void ipac_parse_cgi(struct cell_global_id *cid, const uint8_t *buf)
-{
- cid->mcc = (buf[0] & 0xf) * 100;
- cid->mcc += (buf[0] >> 4) * 10;
- cid->mcc += (buf[1] & 0xf) * 1;
-
- if (buf[1] >> 4 == 0xf) {
- cid->mnc = (buf[2] & 0xf) * 10;
- cid->mnc += (buf[2] >> 4) * 1;
- } else {
- cid->mnc = (buf[2] & 0xf) * 100;
- cid->mnc += (buf[2] >> 4) * 10;
- cid->mnc += (buf[1] >> 4) * 1;
- }
-
- cid->lac = ntohs(*((uint16_t *)&buf[3]));
- cid->ci = ntohs(*((uint16_t *)&buf[5]));
-}
-
-/* parse BCCH information IEI from wire format to struct ipac_bcch_info */
-int ipac_parse_bcch_info(struct ipac_bcch_info *binf, uint8_t *buf)
-{
- uint8_t *cur = buf;
- uint16_t len __attribute__((unused));
-
- memset(binf, 0, sizeof(*binf));
-
- if (cur[0] != NM_IPAC_EIE_BCCH_INFO)
- return -EINVAL;
- cur++;
-
- len = ntohs(*(uint16_t *)cur);
- cur += 2;
-
- binf->info_type = ntohs(*(uint16_t *)cur);
- cur += 2;
-
- if (binf->info_type & IPAC_BINF_FREQ_ERR_QUAL)
- binf->freq_qual = *cur >> 2;
-
- binf->arfcn = (*cur++ & 3) << 8;
- binf->arfcn |= *cur++;
-
- if (binf->info_type & IPAC_BINF_RXLEV)
- binf->rx_lev = *cur & 0x3f;
- cur++;
-
- if (binf->info_type & IPAC_BINF_RXQUAL)
- binf->rx_qual = *cur & 0x7;
- cur++;
-
- if (binf->info_type & IPAC_BINF_FREQ_ERR_QUAL)
- binf->freq_err = ntohs(*(uint16_t *)cur);
- cur += 2;
-
- if (binf->info_type & IPAC_BINF_FRAME_OFFSET)
- binf->frame_offset = ntohs(*(uint16_t *)cur);
- cur += 2;
-
- if (binf->info_type & IPAC_BINF_FRAME_NR_OFFSET)
- binf->frame_nr_offset = ntohl(*(uint32_t *)cur);
- cur += 4;
-
-#if 0
- /* Somehow this is not set correctly */
- if (binf->info_type & IPAC_BINF_BSIC)
-#endif
- binf->bsic = *cur & 0x3f;
- cur++;
-
- ipac_parse_cgi(&binf->cgi, cur);
- cur += 7;
-
- if (binf->info_type & IPAC_BINF_NEIGH_BA_SI2) {
- memcpy(binf->ba_list_si2, cur, sizeof(binf->ba_list_si2));
- cur += sizeof(binf->ba_list_si2);
- }
-
- if (binf->info_type & IPAC_BINF_NEIGH_BA_SI2bis) {
- memcpy(binf->ba_list_si2bis, cur,
- sizeof(binf->ba_list_si2bis));
- cur += sizeof(binf->ba_list_si2bis);
- }
-
- if (binf->info_type & IPAC_BINF_NEIGH_BA_SI2ter) {
- memcpy(binf->ba_list_si2ter, cur,
- sizeof(binf->ba_list_si2ter));
- cur += sizeof(binf->ba_list_si2ter);
- }
-
- return 0;
-}
-
-void abis_nm_clear_queue(struct gsm_bts *bts)
-{
- struct msgb *msg;
-
- while (!llist_empty(&bts->abis_queue)) {
- msg = msgb_dequeue(&bts->abis_queue);
- msgb_free(msg);
- }
-
- bts->abis_nm_pend = 0;
-}
diff --git a/openbsc/src/libbsc/abis_nm_ipaccess.c b/openbsc/src/libbsc/abis_nm_ipaccess.c
deleted file mode 100644
index b8225383a..000000000
--- a/openbsc/src/libbsc/abis_nm_ipaccess.c
+++ /dev/null
@@ -1,87 +0,0 @@
-/* GSM Network Management (OML) messages on the A-bis interface
- * Extensions for the ip.access A-bis over IP protocol*/
-
-/* (C) 2008-2009 by Harald Welte <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/>.
- *
- */
-
-/* A list of all the 'embedded' attributes of ip.access */
-enum ipa_embedded_att {
- IPA_ATT_ARFCN_WHITELIST = 0x01,
- IPA_ATT_ARFCN_BLACKLIST = 0x02,
- IPA_ATT_FREQ_ERR_LIST = 0x03,
- IPA_ATT_CHAN_USAGE_LIST = 0x04,
- IPA_ATT_BCCH_INF_TYPE = 0x05,
- IPA_ATT_BCCH_INF = 0x06,
- IPA_ATT_CONFIG = 0x07,
- IPA_ATT_RESULT_DETAILS = 0x08,
- IPA_ATT_RXLEV_THRESH = 0x09,
- IPA_ATT_FREQ_SYNC_OPT = 0x0a,
- IPA_ATT_MAC_ADDR = 0x0b,
- IPA_ATT_HW_SW_COMPAT_NR = 0x0c,
- IPA_ATT_MANUF_SER_NR = 0x0d,
- IPA_ATT_OEM_ID = 0x0e,
- IPA_ATT_DATETIME_MANUF = 0x0f,
- IPA_ATT_DATETIME_CALIB = 0x10,
- IPA_ATT_BEACON_INF = 0x11,
- IPA_ATT_FREQ_ERR = 0x12,
- IPA_ATT_SNMP_COMM_STRING = 0x13,
- IPA_ATT_SNMP_TRAP_ADDR = 0x14,
- IPA_ATT_SNMP_TRAP_PORT = 0x15,
- IPA_ATT_SNMP_MAN_ADDR = 0x16,
- IPA_ATT_SNMP_SYS_CONTACT = 0x17,
- IPA_ATT_FACTORY_ID = 0x18,
- IPA_ATT_FACTORY_SERIAL = 0x19,
- IPA_ATT_LOGGED_EVT_IND = 0x1a,
- IPA_ATT_LOCAL_ADD_TEXT = 0x1b,
- IPA_ATT_FREQ_BANDS = 0x1c,
- IPA_ATT_MAX_TA = 0x1d,
- IPA_ATT_CIPH_ALG = 0x1e,
- IPA_ATT_CHAN_TYPES = 0x1f,
- IPA_ATT_CHAN_MODES = 0x20,
- IPA_ATT_GPRS_CODING_SCHEMES = 0x21,
- IPA_ATT_RTP_FEATURES = 0x22,
- IPA_ATT_RSL_FEATURES = 0x23,
- IPA_ATT_BTS_HW_CLASS = 0x24,
- IPA_ATT_BTS_ID = 0x25,
- IPA_ATT_BCAST_L2_MSG = 0x26,
-};
-
-/* append an ip.access channel list to the given msgb */
-static int ipa_chan_list_append(struct msgb *msg, uint8_t ie,
- uint16_t *arfcns, int arfcn_count)
-{
- int i;
- uint8_t *u8;
- uint16_t *u16;
-
- /* tag */
- u8 = msgb_push(msg, 1);
- *u8 = ie;
-
- /* length in octets */
- u16 = msgb_push(msg, 2);
- *u16 = htons(arfcn_count * 2);
-
- for (i = 0; i < arfcn_count; i++) {
- u16 = msgb_push(msg, 2);
- *u16 = htons(arfcns[i]);
- }
-
- return 0;
-}
diff --git a/openbsc/src/libbsc/abis_nm_vty.c b/openbsc/src/libbsc/abis_nm_vty.c
deleted file mode 100644
index 6ec0a4a21..000000000
--- a/openbsc/src/libbsc/abis_nm_vty.c
+++ /dev/null
@@ -1,191 +0,0 @@
-/* VTY interface for A-bis OML (Netowrk Management) */
-
-/* (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 <stdlib.h>
-#include <unistd.h>
-#include <errno.h>
-#include <stdint.h>
-
-#include <arpa/inet.h>
-
-#include <osmocom/gsm/abis_nm.h>
-
-#include <openbsc/gsm_data.h>
-#include <osmocom/core/msgb.h>
-#include <osmocom/gsm/tlv.h>
-#include <osmocom/core/talloc.h>
-#include <openbsc/debug.h>
-#include <openbsc/signal.h>
-#include <openbsc/abis_nm.h>
-#include <openbsc/vty.h>
-
-#include <osmocom/vty/vty.h>
-#include <osmocom/vty/command.h>
-#include <osmocom/vty/logging.h>
-#include <osmocom/vty/telnet_interface.h>
-
-extern struct gsm_network *bsc_gsmnet;
-
-static struct cmd_node oml_node = {
- OML_NODE,
- "%s(oml)# ",
- 1,
-};
-
-struct oml_node_state {
- struct gsm_bts *bts;
- uint8_t obj_class;
- uint8_t obj_inst[3];
-};
-
-static int dummy_config_write(struct vty *v)
-{
- return CMD_SUCCESS;
-}
-
-/* FIXME: auto-generate those strings from the value_string lists */
-#define NM_OBJCLASS_VTY "(site-manager|bts|radio-carrier|baseband-transceiver|channel|adjc|handover|power-contorl|btse|rack|test|envabtse|bport|gprs-nse|gprs-cell|gprs-nsvc|siemenshw)"
-#define NM_OBJCLASS_VTY_HELP "Site Manager Object\n" \
- "BTS Object\n" \
- "Radio Carrier Object\n" \
- "Baseband Transceiver Object\n" \
- "Channel (Timeslot) Object\n" \
- "Adjacent Object (Siemens)\n" \
- "Handover Object (Siemens)\n" \
- "Power Control Object (Siemens)\n" \
- "BTSE Object (Siemens)\n" \
- "Rack Object (Siemens)\n" \
- "Test Object (Siemens)\n" \
- "ENVABTSE Object (Siemens)\n" \
- "BPORT Object (Siemens)\n" \
- "GPRS NSE Object (ip.access/osmo-bts)\n" \
- "GPRS Cell Object (ip.acecss/osmo-bts)\n" \
- "GPRS NSVC Object (ip.acecss/osmo-bts)\n" \
- "SIEMENSHW Object (Siemens)\n"
-
-
-DEFUN(oml_class_inst, oml_class_inst_cmd,
- "bts <0-255> oml class " NM_OBJCLASS_VTY
- " instance <0-255> <0-255> <0-255>",
- "BTS related commands\n" "BTS Number\n"
- "Manipulate the OML managed objects\n"
- "Object Class\n" NM_OBJCLASS_VTY_HELP
- "Object Instance\n" "BTS Number\n" "TRX Number\n" "TS Number\n")
-{
- struct gsm_bts *bts;
- struct oml_node_state *oms;
- int bts_nr = atoi(argv[0]);
-
- bts = gsm_bts_num(gsmnet_from_vty(vty), bts_nr);
- if (!bts) {
- vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- oms = talloc_zero(tall_bsc_ctx, struct oml_node_state);
- if (!oms)
- return CMD_WARNING;
-
- oms->bts = bts;
- oms->obj_class = get_string_value(abis_nm_obj_class_names, argv[1]);
- oms->obj_inst[0] = atoi(argv[2]);
- oms->obj_inst[1] = atoi(argv[3]);
- oms->obj_inst[2] = atoi(argv[4]);
-
- vty->index = oms;
- vty->node = OML_NODE;
-
- return CMD_SUCCESS;
-
-}
-
-DEFUN(oml_classnum_inst, oml_classnum_inst_cmd,
- "bts <0-255> oml class <0-255> instance <0-255> <0-255> <0-255>",
- "BTS related commands\n" "BTS Number\n"
- "Manipulate the OML managed objects\n"
- "Object Class\n" "Object Class\n"
- "Object Instance\n" "BTS Number\n" "TRX Number\n" "TS Number\n")
-{
- struct gsm_bts *bts;
- struct oml_node_state *oms;
- int bts_nr = atoi(argv[0]);
-
- bts = gsm_bts_num(gsmnet_from_vty(vty), bts_nr);
- if (!bts) {
- vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- oms = talloc_zero(tall_bsc_ctx, struct oml_node_state);
- if (!oms)
- return CMD_WARNING;
-
- oms->bts = bts;
- oms->obj_class = atoi(argv[1]);
- oms->obj_inst[0] = atoi(argv[2]);
- oms->obj_inst[1] = atoi(argv[3]);
- oms->obj_inst[2] = atoi(argv[4]);
-
- vty->index = oms;
- vty->node = OML_NODE;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(oml_chg_adm_state, oml_chg_adm_state_cmd,
- "change-adm-state (locked|unlocked|shutdown|null)",
- "Change the Administrative State\n"
- "Locked\n" "Unlocked\n" "Shutdown\n" "NULL\n")
-{
- struct oml_node_state *oms = vty->index;
- enum abis_nm_adm_state state;
-
- state = get_string_value(abis_nm_adm_state_names, argv[0]);
-
- abis_nm_chg_adm_state(oms->bts, oms->obj_class, oms->obj_inst[0],
- oms->obj_inst[1], oms->obj_inst[2], state);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(oml_opstart, oml_opstart_cmd,
- "opstart", "Send an OPSTART message to the object")
-{
- struct oml_node_state *oms = vty->index;
-
- abis_nm_opstart(oms->bts, oms->obj_class, oms->obj_inst[0],
- oms->obj_inst[1], oms->obj_inst[2]);
-
- return CMD_SUCCESS;
-}
-
-int abis_nm_vty_init(void)
-{
- install_element(ENABLE_NODE, &oml_class_inst_cmd);
- install_element(ENABLE_NODE, &oml_classnum_inst_cmd);
- install_node(&oml_node, dummy_config_write);
-
- vty_install_default(OML_NODE);
- install_element(OML_NODE, &oml_chg_adm_state_cmd);
- install_element(OML_NODE, &oml_opstart_cmd);
-
- return 0;
-}
diff --git a/openbsc/src/libbsc/abis_om2000.c b/openbsc/src/libbsc/abis_om2000.c
deleted file mode 100644
index 82a14b269..000000000
--- a/openbsc/src/libbsc/abis_om2000.c
+++ /dev/null
@@ -1,2776 +0,0 @@
-/* Ericsson RBS 2xxx GSM O&M (OM2000) messages on the A-bis interface
- * implemented based on protocol trace analysis, no formal documentation */
-
-/* (C) 2010-2011,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 <errno.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <time.h>
-#include <stdint.h>
-
-#include <arpa/inet.h>
-
-#include <osmocom/core/msgb.h>
-#include <osmocom/gsm/tlv.h>
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/utils.h>
-#include <osmocom/core/fsm.h>
-
-#include <openbsc/gsm_data.h>
-#include <openbsc/debug.h>
-#include <openbsc/abis_nm.h>
-#include <openbsc/abis_rsl.h>
-#include <openbsc/abis_om2000.h>
-#include <openbsc/signal.h>
-#include <osmocom/abis/e1_input.h>
-
-/* FIXME: move to libosmocore */
-struct osmo_fsm_inst *osmo_fsm_inst_alloc_child_id(struct osmo_fsm *fsm,
- struct osmo_fsm_inst *parent,
- uint32_t parent_term_event,
- const char *id)
-{
- struct osmo_fsm_inst *fi;
-
- fi = osmo_fsm_inst_alloc(fsm, parent, NULL, parent->log_level,
- id ? id : parent->id);
- if (!fi) {
- /* indicate immediate termination to caller */
- osmo_fsm_inst_dispatch(parent, parent_term_event, NULL);
- return NULL;
- }
-
- LOGPFSM(fi, "is child of %s\n", osmo_fsm_inst_name(parent));
-
- fi->proc.parent = parent;
- fi->proc.parent_term_event = parent_term_event;
- llist_add(&fi->proc.child, &parent->proc.children);
-
- return fi;
-}
-
-
-#define OM_ALLOC_SIZE 1024
-#define OM_HEADROOM_SIZE 128
-
-#define OM2K_TIMEOUT 10
-#define TRX_FSM_TIMEOUT 60
-#define BTS_FSM_TIMEOUT 60
-
-/* use following functions from abis_nm.c:
- * om2k_msgb_alloc()
- * abis_om2k_sendmsg()
- */
-
-struct abis_om2k_hdr {
- struct abis_om_hdr om;
- uint16_t msg_type;
- struct abis_om2k_mo mo;
- uint8_t data[0];
-} __attribute__ ((packed));
-
-enum abis_om2k_msgtype {
- OM2K_MSGT_ABORT_SP_CMD = 0x0000,
- OM2K_MSGT_ABORT_SP_COMPL = 0x0002,
- OM2K_MSGT_ALARM_REP_ACK = 0x0004,
- OM2K_MSGT_ALARM_REP_NACK = 0x0005,
- OM2K_MSGT_ALARM_REP = 0x0006,
- OM2K_MSGT_ALARM_STATUS_REQ = 0x0008,
- OM2K_MSGT_ALARM_STATUS_REQ_ACK = 0x000a,
- OM2K_MSGT_ALARM_STATUS_REQ_REJ = 0x000b,
- OM2K_MSGT_ALARM_STATUS_RES_ACK = 0x000c,
- OM2K_MSGT_ALARM_STATUS_RES_NACK = 0x000d,
- OM2K_MSGT_ALARM_STATUS_RES = 0x000e,
- OM2K_MSGT_CAL_TIME_RESP = 0x0010,
- OM2K_MSGT_CAL_TIME_REJ = 0x0011,
- OM2K_MSGT_CAL_TIME_REQ = 0x0012,
-
- OM2K_MSGT_CON_CONF_REQ = 0x0014,
- OM2K_MSGT_CON_CONF_REQ_ACK = 0x0016,
- OM2K_MSGT_CON_CONF_REQ_REJ = 0x0017,
- OM2K_MSGT_CON_CONF_RES_ACK = 0x0018,
- OM2K_MSGT_CON_CONF_RES_NACK = 0x0019,
- OM2K_MSGT_CON_CONF_RES = 0x001a,
-
- OM2K_MSGT_CONNECT_CMD = 0x001c,
- OM2K_MSGT_CONNECT_COMPL = 0x001e,
- OM2K_MSGT_CONNECT_REJ = 0x001f,
-
- OM2K_MSGT_DISABLE_REQ = 0x0028,
- OM2K_MSGT_DISABLE_REQ_ACK = 0x002a,
- OM2K_MSGT_DISABLE_REQ_REJ = 0x002b,
- OM2K_MSGT_DISABLE_RES_ACK = 0x002c,
- OM2K_MSGT_DISABLE_RES_NACK = 0x002d,
- OM2K_MSGT_DISABLE_RES = 0x002e,
- OM2K_MSGT_DISCONNECT_CMD = 0x0030,
- OM2K_MSGT_DISCONNECT_COMPL = 0x0032,
- OM2K_MSGT_DISCONNECT_REJ = 0x0033,
- OM2K_MSGT_ENABLE_REQ = 0x0034,
- OM2K_MSGT_ENABLE_REQ_ACK = 0x0036,
- OM2K_MSGT_ENABLE_REQ_REJ = 0x0037,
- OM2K_MSGT_ENABLE_RES_ACK = 0x0038,
- OM2K_MSGT_ENABLE_RES_NACK = 0x0039,
- OM2K_MSGT_ENABLE_RES = 0x003a,
-
- OM2K_MSGT_FAULT_REP_ACK = 0x0040,
- OM2K_MSGT_FAULT_REP_NACK = 0x0041,
- OM2K_MSGT_FAULT_REP = 0x0042,
-
- OM2K_MSGT_IS_CONF_REQ = 0x0060,
- OM2K_MSGT_IS_CONF_REQ_ACK = 0x0062,
- OM2K_MSGT_IS_CONF_REQ_REJ = 0x0063,
- OM2K_MSGT_IS_CONF_RES_ACK = 0x0064,
- OM2K_MSGT_IS_CONF_RES_NACK = 0x0065,
- OM2K_MSGT_IS_CONF_RES = 0x0066,
-
- OM2K_MSGT_OP_INFO = 0x0074,
- OM2K_MSGT_OP_INFO_ACK = 0x0076,
- OM2K_MSGT_OP_INFO_REJ = 0x0077,
- OM2K_MSGT_RESET_CMD = 0x0078,
- OM2K_MSGT_RESET_COMPL = 0x007a,
- OM2K_MSGT_RESET_REJ = 0x007b,
- OM2K_MSGT_RX_CONF_REQ = 0x007c,
- OM2K_MSGT_RX_CONF_REQ_ACK = 0x007e,
- OM2K_MSGT_RX_CONF_REQ_REJ = 0x007f,
- OM2K_MSGT_RX_CONF_RES_ACK = 0x0080,
- OM2K_MSGT_RX_CONF_RES_NACK = 0x0081,
- OM2K_MSGT_RX_CONF_RES = 0x0082,
- OM2K_MSGT_START_REQ = 0x0084,
- OM2K_MSGT_START_REQ_ACK = 0x0086,
- OM2K_MSGT_START_REQ_REJ = 0x0087,
- OM2K_MSGT_START_RES_ACK = 0x0088,
- OM2K_MSGT_START_RES_NACK = 0x0089,
- OM2K_MSGT_START_RES = 0x008a,
- OM2K_MSGT_STATUS_REQ = 0x008c,
- OM2K_MSGT_STATUS_RESP = 0x008e,
- OM2K_MSGT_STATUS_REJ = 0x008f,
-
- OM2K_MSGT_TEST_REQ = 0x0094,
- OM2K_MSGT_TEST_REQ_ACK = 0x0096,
- OM2K_MSGT_TEST_REQ_REJ = 0x0097,
- OM2K_MSGT_TEST_RES_ACK = 0x0098,
- OM2K_MSGT_TEST_RES_NACK = 0x0099,
- OM2K_MSGT_TEST_RES = 0x009a,
-
- OM2K_MSGT_TF_CONF_REQ = 0x00a0,
- OM2K_MSGT_TF_CONF_REQ_ACK = 0x00a2,
- OM2K_MSGT_TF_CONF_REQ_REJ = 0x00a3,
- OM2K_MSGT_TF_CONF_RES_ACK = 0x00a4,
- OM2K_MSGT_TF_CONF_RES_NACK = 0x00a5,
- OM2K_MSGT_TF_CONF_RES = 0x00a6,
- OM2K_MSGT_TS_CONF_REQ = 0x00a8,
- OM2K_MSGT_TS_CONF_REQ_ACK = 0x00aa,
- OM2K_MSGT_TS_CONF_REQ_REJ = 0x00ab,
- OM2K_MSGT_TS_CONF_RES_ACK = 0x00ac,
- OM2K_MSGT_TS_CONF_RES_NACK = 0x00ad,
- OM2K_MSGT_TS_CONF_RES = 0x00ae,
- OM2K_MSGT_TX_CONF_REQ = 0x00b0,
- OM2K_MSGT_TX_CONF_REQ_ACK = 0x00b2,
- OM2K_MSGT_TX_CONF_REQ_REJ = 0x00b3,
- OM2K_MSGT_TX_CONF_RES_ACK = 0x00b4,
- OM2K_MSGT_TX_CONF_RES_NACK = 0x00b5,
- OM2K_MSGT_TX_CONF_RES = 0x00b6,
-
- OM2K_MSGT_CAPA_REQ = 0x00e8,
- OM2K_MSGT_CAPA_REQ_ACK = 0x00ea,
- OM2K_MSGT_CAPA_REQ_REJ = 0x00eb,
- OM2K_MSGT_CAPA_RES = 0x00ee,
- OM2K_MSGT_CAPA_RES_ACK = 0x00ec,
- OM2K_MSGT_CAPA_RES_NACK = 0x00ed,
-
- OM2K_MSGT_NEGOT_REQ_ACK = 0x0104,
- OM2K_MSGT_NEGOT_REQ_NACK = 0x0105,
- OM2K_MSGT_NEGOT_REQ = 0x0106,
-};
-
-enum abis_om2k_dei {
- OM2K_DEI_ACCORDANCE_IND = 0x00,
- OM2K_DEI_BCC = 0x06,
- OM2K_DEI_BS_AG_BKS_RES = 0x07,
- OM2K_DEI_BSIC = 0x09,
- OM2K_DEI_BA_PA_MFRMS = 0x0a,
- OM2K_DEI_CBCH_INDICATOR = 0x0b,
- OM2K_DEI_CCCH_OPTIONS = 0x0c,
- OM2K_DEI_CAL_TIME = 0x0d,
- OM2K_DEI_COMBINATION = 0x0f,
- OM2K_DEI_CON_CONN_LIST = 0x10,
- OM2K_DEI_DRX_DEV_MAX = 0x12,
- OM2K_DEI_END_LIST_NR = 0x13,
- OM2K_DEI_EXT_COND_MAP_1 = 0x14,
- OM2K_DEI_EXT_COND_MAP_2 = 0x15,
- OM2K_DEI_FILLING_MARKER = 0x1c,
- OM2K_DEI_FN_OFFSET = 0x1d,
- OM2K_DEI_FREQ_LIST = 0x1e,
- OM2K_DEI_FREQ_SPEC_RX = 0x1f,
- OM2K_DEI_FREQ_SPEC_TX = 0x20,
- OM2K_DEI_HSN = 0x21,
- OM2K_DEI_ICM_INDICATOR = 0x22,
- OM2K_DEI_INT_FAULT_MAP_1A = 0x23,
- OM2K_DEI_INT_FAULT_MAP_1B = 0x24,
- OM2K_DEI_INT_FAULT_MAP_2A = 0x25,
- OM2K_DEI_INT_FAULT_MAP_2A_EXT = 0x26,
- OM2K_DEI_IS_CONN_LIST = 0x27,
- OM2K_DEI_LIST_NR = 0x28,
- OM2K_DEI_LOCAL_ACCESS = 0x2a,
- OM2K_DEI_MAIO = 0x2b,
- OM2K_DEI_MO_STATE = 0x2c,
- OM2K_DEI_NY1 = 0x2d,
- OM2K_DEI_OP_INFO = 0x2e,
- OM2K_DEI_POWER = 0x2f,
- OM2K_DEI_REASON_CODE = 0x32,
- OM2K_DEI_RX_DIVERSITY = 0x33,
- OM2K_DEI_REPL_UNIT_MAP = 0x34,
- OM2K_DEI_RESULT_CODE = 0x35,
- OM2K_DEI_T3105 = 0x38,
- OM2K_DEI_TF_MODE = 0x3a,
- OM2K_DEI_TS_NR = 0x3c,
- OM2K_DEI_TSC = 0x3d,
- OM2K_DEI_BTS_VERSION = 0x40,
- OM2K_DEI_OML_IWD_VERSION = 0x41,
- OM2K_DEI_RSL_IWD_VERSION = 0x42,
- OM2K_DEI_OML_FUNC_MAP_1 = 0x43,
- OM2K_DEI_OML_FUNC_MAP_2 = 0x44,
- OM2K_DEI_RSL_FUNC_MAP_1 = 0x45,
- OM2K_DEI_RSL_FUNC_MAP_2 = 0x46,
- OM2K_DEI_EXT_RANGE = 0x47,
- OM2K_DEI_REQ_IND = 0x48,
- OM2K_DEI_REPL_UNIT_MAP_EXT = 0x50,
- OM2K_DEI_ICM_BOUND_PARAMS = 0x74,
- OM2K_DEI_LSC = 0x79,
- OM2K_DEI_LSC_FILT_TIME = 0x7a,
- OM2K_DEI_CALL_SUPV_TIME = 0x7b,
- OM2K_DEI_ICM_CHAN_RATE = 0x7e,
- OM2K_DEI_HW_INFO_SIG = 0x84,
- OM2K_DEI_TF_SYNC_SRC = 0x86,
- OM2K_DEI_TTA = 0x87,
- OM2K_DEI_CAPA_SIG = 0x8a,
- OM2K_DEI_NEGOT_REC1 = 0x90,
- OM2K_DEI_NEGOT_REC2 = 0x91,
- OM2K_DEI_ENCR_ALG = 0x92,
- OM2K_DEI_INTERF_REJ_COMB = 0x94,
- OM2K_DEI_FS_OFFSET = 0x98,
- OM2K_DEI_EXT_COND_MAP_2_EXT = 0x9c,
- OM2K_DEI_TSS_MO_STATE = 0x9d,
-};
-
-const struct tlv_definition om2k_att_tlvdef = {
- .def = {
- [OM2K_DEI_ACCORDANCE_IND] = { TLV_TYPE_TV },
- [OM2K_DEI_BCC] = { TLV_TYPE_TV },
- [OM2K_DEI_BS_AG_BKS_RES] = { TLV_TYPE_TV },
- [OM2K_DEI_BSIC] = { TLV_TYPE_TV },
- [OM2K_DEI_BA_PA_MFRMS] = { TLV_TYPE_TV },
- [OM2K_DEI_CBCH_INDICATOR] = { TLV_TYPE_TV },
- [OM2K_DEI_INT_FAULT_MAP_1A] = { TLV_TYPE_FIXED, 6 },
- [OM2K_DEI_INT_FAULT_MAP_1B] = { TLV_TYPE_FIXED, 6 },
- [OM2K_DEI_INT_FAULT_MAP_2A] = { TLV_TYPE_FIXED, 6 },
- [OM2K_DEI_INT_FAULT_MAP_2A_EXT]={ TLV_TYPE_FIXED, 6 },
- [OM2K_DEI_CCCH_OPTIONS] = { TLV_TYPE_TV },
- [OM2K_DEI_CAL_TIME] = { TLV_TYPE_FIXED, 6 },
- [OM2K_DEI_COMBINATION] = { TLV_TYPE_TV },
- [OM2K_DEI_CON_CONN_LIST] = { TLV_TYPE_TLV },
- [OM2K_DEI_DRX_DEV_MAX] = { TLV_TYPE_TV },
- [OM2K_DEI_END_LIST_NR] = { TLV_TYPE_TV },
- [OM2K_DEI_EXT_COND_MAP_1] = { TLV_TYPE_FIXED, 2 },
- [OM2K_DEI_EXT_COND_MAP_2] = { TLV_TYPE_FIXED, 2 },
- [OM2K_DEI_FILLING_MARKER] = { TLV_TYPE_TV },
- [OM2K_DEI_FN_OFFSET] = { TLV_TYPE_FIXED, 2 },
- [OM2K_DEI_FREQ_LIST] = { TLV_TYPE_TLV },
- [OM2K_DEI_FREQ_SPEC_RX] = { TLV_TYPE_FIXED, 2 },
- [OM2K_DEI_FREQ_SPEC_TX] = { TLV_TYPE_FIXED, 2 },
- [OM2K_DEI_HSN] = { TLV_TYPE_TV },
- [OM2K_DEI_ICM_INDICATOR] = { TLV_TYPE_TV },
- [OM2K_DEI_IS_CONN_LIST] = { TLV_TYPE_TLV },
- [OM2K_DEI_LIST_NR] = { TLV_TYPE_TV },
- [OM2K_DEI_LOCAL_ACCESS] = { TLV_TYPE_TV },
- [OM2K_DEI_MAIO] = { TLV_TYPE_TV },
- [OM2K_DEI_MO_STATE] = { TLV_TYPE_TV },
- [OM2K_DEI_NY1] = { TLV_TYPE_TV },
- [OM2K_DEI_OP_INFO] = { TLV_TYPE_TV },
- [OM2K_DEI_POWER] = { TLV_TYPE_TV },
- [OM2K_DEI_REASON_CODE] = { TLV_TYPE_TV },
- [OM2K_DEI_RX_DIVERSITY] = { TLV_TYPE_TV },
- [OM2K_DEI_RESULT_CODE] = { TLV_TYPE_TV },
- [OM2K_DEI_T3105] = { TLV_TYPE_TV },
- [OM2K_DEI_TF_MODE] = { TLV_TYPE_TV },
- [OM2K_DEI_TS_NR] = { TLV_TYPE_TV },
- [OM2K_DEI_TSC] = { TLV_TYPE_TV },
- [OM2K_DEI_BTS_VERSION] = { TLV_TYPE_FIXED, 12 },
- [OM2K_DEI_OML_IWD_VERSION] = { TLV_TYPE_FIXED, 6 },
- [OM2K_DEI_RSL_IWD_VERSION] = { TLV_TYPE_FIXED, 6 },
- [OM2K_DEI_OML_FUNC_MAP_1] = { TLV_TYPE_TLV },
- [OM2K_DEI_OML_FUNC_MAP_2] = { TLV_TYPE_TLV },
- [OM2K_DEI_RSL_FUNC_MAP_1] = { TLV_TYPE_TLV },
- [OM2K_DEI_RSL_FUNC_MAP_2] = { TLV_TYPE_TLV },
- [OM2K_DEI_EXT_RANGE] = { TLV_TYPE_TV },
- [OM2K_DEI_REQ_IND] = { TLV_TYPE_TV },
- [OM2K_DEI_REPL_UNIT_MAP] = { TLV_TYPE_FIXED, 6 },
- [OM2K_DEI_REPL_UNIT_MAP_EXT] = {TLV_TYPE_FIXED, 6},
- [OM2K_DEI_ICM_BOUND_PARAMS] = { TLV_TYPE_FIXED, 5 },
- [OM2K_DEI_LSC] = { TLV_TYPE_TV },
- [OM2K_DEI_LSC_FILT_TIME] = { TLV_TYPE_TV },
- [OM2K_DEI_CALL_SUPV_TIME] = { TLV_TYPE_TV },
- [OM2K_DEI_ICM_CHAN_RATE] = { TLV_TYPE_TV },
- [OM2K_DEI_HW_INFO_SIG] = { TLV_TYPE_FIXED, 2 },
- [OM2K_DEI_TF_SYNC_SRC] = { TLV_TYPE_TV },
- [OM2K_DEI_TTA] = { TLV_TYPE_TV },
- [OM2K_DEI_CAPA_SIG] = { TLV_TYPE_FIXED, 2 },
- [OM2K_DEI_NEGOT_REC1] = { TLV_TYPE_TLV },
- [OM2K_DEI_NEGOT_REC2] = { TLV_TYPE_TLV },
- [OM2K_DEI_ENCR_ALG] = { TLV_TYPE_TV },
- [OM2K_DEI_INTERF_REJ_COMB] = { TLV_TYPE_TV },
- [OM2K_DEI_FS_OFFSET] = { TLV_TYPE_FIXED, 5 },
- [OM2K_DEI_EXT_COND_MAP_2_EXT] = { TLV_TYPE_FIXED, 4 },
- [OM2K_DEI_TSS_MO_STATE] = { TLV_TYPE_FIXED, 4 },
- },
-};
-
-static const struct value_string om2k_msgcode_vals[] = {
- { 0x0000, "Abort SP Command" },
- { 0x0002, "Abort SP Complete" },
- { 0x0004, "Alarm Report ACK" },
- { 0x0005, "Alarm Report NACK" },
- { 0x0006, "Alarm Report" },
- { 0x0008, "Alarm Status Request" },
- { 0x000a, "Alarm Status Request Accept" },
- { 0x000b, "Alarm Status Request Reject" },
- { 0x000c, "Alarm Status Result ACK" },
- { 0x000d, "Alarm Status Result NACK" },
- { 0x000e, "Alarm Status Result" },
- { 0x0010, "Calendar Time Response" },
- { 0x0011, "Calendar Time Reject" },
- { 0x0012, "Calendar Time Request" },
- { 0x0014, "CON Configuration Request" },
- { 0x0016, "CON Configuration Request Accept" },
- { 0x0017, "CON Configuration Request Reject" },
- { 0x0018, "CON Configuration Result ACK" },
- { 0x0019, "CON Configuration Result NACK" },
- { 0x001a, "CON Configuration Result" },
- { 0x001c, "Connect Command" },
- { 0x001e, "Connect Complete" },
- { 0x001f, "Connect Reject" },
- { 0x0028, "Disable Request" },
- { 0x002a, "Disable Request Accept" },
- { 0x002b, "Disable Request Reject" },
- { 0x002c, "Disable Result ACK" },
- { 0x002d, "Disable Result NACK" },
- { 0x002e, "Disable Result" },
- { 0x0030, "Disconnect Command" },
- { 0x0032, "Disconnect Complete" },
- { 0x0033, "Disconnect Reject" },
- { 0x0034, "Enable Request" },
- { 0x0036, "Enable Request Accept" },
- { 0x0037, "Enable Request Reject" },
- { 0x0038, "Enable Result ACK" },
- { 0x0039, "Enable Result NACK" },
- { 0x003a, "Enable Result" },
- { 0x003c, "Escape Downlink Normal" },
- { 0x003d, "Escape Downlink NACK" },
- { 0x003e, "Escape Uplink Normal" },
- { 0x003f, "Escape Uplink NACK" },
- { 0x0040, "Fault Report ACK" },
- { 0x0041, "Fault Report NACK" },
- { 0x0042, "Fault Report" },
- { 0x0044, "File Package End Command" },
- { 0x0046, "File Package End Result" },
- { 0x0047, "File Package End Reject" },
- { 0x0048, "File Relation Request" },
- { 0x004a, "File Relation Response" },
- { 0x004b, "File Relation Request Reject" },
- { 0x004c, "File Segment Transfer" },
- { 0x004e, "File Segment Transfer Complete" },
- { 0x004f, "File Segment Transfer Reject" },
- { 0x0050, "HW Information Request" },
- { 0x0052, "HW Information Request Accept" },
- { 0x0053, "HW Information Request Reject" },
- { 0x0054, "HW Information Result ACK" },
- { 0x0055, "HW Information Result NACK" },
- { 0x0056, "HW Information Result" },
- { 0x0060, "IS Configuration Request" },
- { 0x0062, "IS Configuration Request Accept" },
- { 0x0063, "IS Configuration Request Reject" },
- { 0x0064, "IS Configuration Result ACK" },
- { 0x0065, "IS Configuration Result NACK" },
- { 0x0066, "IS Configuration Result" },
- { 0x0068, "Load Data End" },
- { 0x006a, "Load Data End Result" },
- { 0x006b, "Load Data End Reject" },
- { 0x006c, "Load Data Init" },
- { 0x006e, "Load Data Init Accept" },
- { 0x006f, "Load Data Init Reject" },
- { 0x0070, "Loop Control Command" },
- { 0x0072, "Loop Control Complete" },
- { 0x0073, "Loop Control Reject" },
- { 0x0074, "Operational Information" },
- { 0x0076, "Operational Information Accept" },
- { 0x0077, "Operational Information Reject" },
- { 0x0078, "Reset Command" },
- { 0x007a, "Reset Complete" },
- { 0x007b, "Reset Reject" },
- { 0x007c, "RX Configuration Request" },
- { 0x007e, "RX Configuration Request Accept" },
- { 0x007f, "RX Configuration Request Reject" },
- { 0x0080, "RX Configuration Result ACK" },
- { 0x0081, "RX Configuration Result NACK" },
- { 0x0082, "RX Configuration Result" },
- { 0x0084, "Start Request" },
- { 0x0086, "Start Request Accept" },
- { 0x0087, "Start Request Reject" },
- { 0x0088, "Start Result ACK" },
- { 0x0089, "Start Result NACK" },
- { 0x008a, "Start Result" },
- { 0x008c, "Status Request" },
- { 0x008e, "Status Response" },
- { 0x008f, "Status Reject" },
- { 0x0094, "Test Request" },
- { 0x0096, "Test Request Accept" },
- { 0x0097, "Test Request Reject" },
- { 0x0098, "Test Result ACK" },
- { 0x0099, "Test Result NACK" },
- { 0x009a, "Test Result" },
- { 0x00a0, "TF Configuration Request" },
- { 0x00a2, "TF Configuration Request Accept" },
- { 0x00a3, "TF Configuration Request Reject" },
- { 0x00a4, "TF Configuration Result ACK" },
- { 0x00a5, "TF Configuration Result NACK" },
- { 0x00a6, "TF Configuration Result" },
- { 0x00a8, "TS Configuration Request" },
- { 0x00aa, "TS Configuration Request Accept" },
- { 0x00ab, "TS Configuration Request Reject" },
- { 0x00ac, "TS Configuration Result ACK" },
- { 0x00ad, "TS Configuration Result NACK" },
- { 0x00ae, "TS Configuration Result" },
- { 0x00b0, "TX Configuration Request" },
- { 0x00b2, "TX Configuration Request Accept" },
- { 0x00b3, "TX Configuration Request Reject" },
- { 0x00b4, "TX Configuration Result ACK" },
- { 0x00b5, "TX Configuration Result NACK" },
- { 0x00b6, "TX Configuration Result" },
- { 0x00bc, "DIP Alarm Report ACK" },
- { 0x00bd, "DIP Alarm Report NACK" },
- { 0x00be, "DIP Alarm Report" },
- { 0x00c0, "DIP Alarm Status Request" },
- { 0x00c2, "DIP Alarm Status Response" },
- { 0x00c3, "DIP Alarm Status Reject" },
- { 0x00c4, "DIP Quality Report I ACK" },
- { 0x00c5, "DIP Quality Report I NACK" },
- { 0x00c6, "DIP Quality Report I" },
- { 0x00c8, "DIP Quality Report II ACK" },
- { 0x00c9, "DIP Quality Report II NACK" },
- { 0x00ca, "DIP Quality Report II" },
- { 0x00dc, "DP Configuration Request" },
- { 0x00de, "DP Configuration Request Accept" },
- { 0x00df, "DP Configuration Request Reject" },
- { 0x00e0, "DP Configuration Result ACK" },
- { 0x00e1, "DP Configuration Result NACK" },
- { 0x00e2, "DP Configuration Result" },
- { 0x00e4, "Capabilities HW Info Report ACK" },
- { 0x00e5, "Capabilities HW Info Report NACK" },
- { 0x00e6, "Capabilities HW Info Report" },
- { 0x00e8, "Capabilities Request" },
- { 0x00ea, "Capabilities Request Accept" },
- { 0x00eb, "Capabilities Request Reject" },
- { 0x00ec, "Capabilities Result ACK" },
- { 0x00ed, "Capabilities Result NACK" },
- { 0x00ee, "Capabilities Result" },
- { 0x00f0, "FM Configuration Request" },
- { 0x00f2, "FM Configuration Request Accept" },
- { 0x00f3, "FM Configuration Request Reject" },
- { 0x00f4, "FM Configuration Result ACK" },
- { 0x00f5, "FM Configuration Result NACK" },
- { 0x00f6, "FM Configuration Result" },
- { 0x00f8, "FM Report Request" },
- { 0x00fa, "FM Report Response" },
- { 0x00fb, "FM Report Reject" },
- { 0x00fc, "FM Start Command" },
- { 0x00fe, "FM Start Complete" },
- { 0x00ff, "FM Start Reject" },
- { 0x0100, "FM Stop Command" },
- { 0x0102, "FM Stop Complete" },
- { 0x0103, "FM Stop Reject" },
- { 0x0104, "Negotiation Request ACK" },
- { 0x0105, "Negotiation Request NACK" },
- { 0x0106, "Negotiation Request" },
- { 0x0108, "BTS Initiated Request ACK" },
- { 0x0109, "BTS Initiated Request NACK" },
- { 0x010a, "BTS Initiated Request" },
- { 0x010c, "Radio Channels Release Command" },
- { 0x010e, "Radio Channels Release Complete" },
- { 0x010f, "Radio Channels Release Reject" },
- { 0x0118, "Feature Control Command" },
- { 0x011a, "Feature Control Complete" },
- { 0x011b, "Feature Control Reject" },
-
- { 0, NULL }
-};
-
-/* TS 12.21 Section 9.4: Attributes */
-static const struct value_string om2k_attr_vals[] = {
- { 0x00, "Accordance indication" },
- { 0x01, "Alarm Id" },
- { 0x02, "Alarm Data" },
- { 0x03, "Alarm Severity" },
- { 0x04, "Alarm Status" },
- { 0x05, "Alarm Status Type" },
- { 0x06, "BCC" },
- { 0x07, "BS_AG_BKS_RES" },
- { 0x09, "BSIC" },
- { 0x0a, "BA_PA_MFRMS" },
- { 0x0b, "CBCH Indicator" },
- { 0x0c, "CCCH Options" },
- { 0x0d, "Calendar Time" },
- { 0x0f, "Channel Combination" },
- { 0x10, "CON Connection List" },
- { 0x11, "Data End Indication" },
- { 0x12, "DRX_DEV_MAX" },
- { 0x13, "End List Number" },
- { 0x14, "External Condition Map Class 1" },
- { 0x15, "External Condition Map Class 2" },
- { 0x16, "File Relation Indication" },
- { 0x17, "File Revision" },
- { 0x18, "File Segment Data" },
- { 0x19, "File Segment Length" },
- { 0x1a, "File Segment Sequence Number" },
- { 0x1b, "File Size" },
- { 0x1c, "Filling Marker" },
- { 0x1d, "FN Offset" },
- { 0x1e, "Frequency List" },
- { 0x1f, "Frequency Specifier RX" },
- { 0x20, "Frequency Specifier TX" },
- { 0x21, "HSN" },
- { 0x22, "ICM Indicator" },
- { 0x23, "Internal Fault Map Class 1A" },
- { 0x24, "Internal Fault Map Class 1B" },
- { 0x25, "Internal Fault Map Class 2A" },
- { 0x26, "Internal Fault Map Class 2A Extension" },
- { 0x27, "IS Connection List" },
- { 0x28, "List Number" },
- { 0x29, "File Package State Indication" },
- { 0x2a, "Local Access State" },
- { 0x2b, "MAIO" },
- { 0x2c, "MO State" },
- { 0x2d, "Ny1" },
- { 0x2e, "Operational Information" },
- { 0x2f, "Power" },
- { 0x30, "RU Position Data" },
- { 0x31, "Protocol Error" },
- { 0x32, "Reason Code" },
- { 0x33, "Receiver Diversity" },
- { 0x34, "Replacement Unit Map" },
- { 0x35, "Result Code" },
- { 0x36, "RU Revision Data" },
- { 0x38, "T3105" },
- { 0x39, "Test Loop Setting" },
- { 0x3a, "TF Mode" },
- { 0x3b, "TF Compensation Value" },
- { 0x3c, "Time Slot Number" },
- { 0x3d, "TSC" },
- { 0x3e, "RU Logical Id" },
- { 0x3f, "RU Serial Number Data" },
- { 0x40, "BTS Version" },
- { 0x41, "OML IWD Version" },
- { 0x42, "RWL IWD Version" },
- { 0x43, "OML Function Map 1" },
- { 0x44, "OML Function Map 2" },
- { 0x45, "RSL Function Map 1" },
- { 0x46, "RSL Function Map 2" },
- { 0x47, "Extended Range Indicator" },
- { 0x48, "Request Indicators" },
- { 0x49, "DIP Alarm Condition Map" },
- { 0x4a, "ES Incoming" },
- { 0x4b, "ES Outgoing" },
- { 0x4e, "SES Incoming" },
- { 0x4f, "SES Outgoing" },
- { 0x50, "Replacement Unit Map Extension" },
- { 0x52, "UAS Incoming" },
- { 0x53, "UAS Outgoing" },
- { 0x58, "DF Incoming" },
- { 0x5a, "DF Outgoing" },
- { 0x5c, "SF" },
- { 0x60, "S Bits Setting" },
- { 0x61, "CRC-4 Use Option" },
- { 0x62, "T Parameter" },
- { 0x63, "N Parameter" },
- { 0x64, "N1 Parameter" },
- { 0x65, "N3 Parameter" },
- { 0x66, "N4 Parameter" },
- { 0x67, "P Parameter" },
- { 0x68, "Q Parameter" },
- { 0x69, "BI_Q1" },
- { 0x6a, "BI_Q2" },
- { 0x74, "ICM Boundary Parameters" },
- { 0x77, "AFT" },
- { 0x78, "AFT RAI" },
- { 0x79, "Link Supervision Control" },
- { 0x7a, "Link Supervision Filtering Time" },
- { 0x7b, "Call Supervision Time" },
- { 0x7c, "Interval Length UAS Incoming" },
- { 0x7d, "Interval Length UAS Outgoing" },
- { 0x7e, "ICM Channel Rate" },
- { 0x7f, "Attribute Identifier" },
- { 0x80, "FM Frequency List" },
- { 0x81, "FM Frequency Report" },
- { 0x82, "FM Percentile" },
- { 0x83, "FM Clear Indication" },
- { 0x84, "HW Info Signature" },
- { 0x85, "MO Record" },
- { 0x86, "TF Synchronisation Source" },
- { 0x87, "TTA" },
- { 0x88, "End Segment Number" },
- { 0x89, "Segment Number" },
- { 0x8a, "Capabilities Signature" },
- { 0x8c, "File Relation List" },
- { 0x90, "Negotiation Record I" },
- { 0x91, "Negotiation Record II" },
- { 0x92, "Encryption Algorithm" },
- { 0x94, "Interference Rejection Combining" },
- { 0x95, "Dedication Information" },
- { 0x97, "Feature Code" },
- { 0x98, "FS Offset" },
- { 0x99, "ESB Timeslot" },
- { 0x9a, "Master TG Instance" },
- { 0x9b, "Master TX Chain Delay" },
- { 0x9c, "External Condition Class 2 Extension" },
- { 0x9d, "TSs MO State" },
- { 0, NULL }
-};
-
-const struct value_string om2k_mo_class_short_vals[] = {
- { 0x01, "TRXC" },
- { 0x03, "TS" },
- { 0x04, "TF" },
- { 0x05, "IS" },
- { 0x06, "CON" },
- { 0x07, "DP" },
- { 0x0a, "CF" },
- { 0x0b, "TX" },
- { 0x0c, "RX" },
- { 0, NULL }
-};
-
-const struct value_string om2k_result_strings[] = {
- { 0x02, "Wrong state or out of sequence" },
- { 0x03, "File error" },
- { 0x04, "Fault, unspecified" },
- { 0x05, "Tuning fault" },
- { 0x06, "Protocol error" },
- { 0x07, "MO not connected" },
- { 0x08, "Parameter error" },
- { 0x09, "Optional function not supported" },
- { 0x0a, "Local access state LOCALLY DISCONNECTED" },
- { 0, NULL }
-};
-
-const struct value_string om2k_accordance_strings[] = {
- { 0x00, "Data according to request" },
- { 0x01, "Data not according to request" },
- { 0x02, "Inconsistent MO data" },
- { 0x03, "Capability constraint violation" },
- { 0, NULL }
-};
-
-const struct value_string om2k_mostate_vals[] = {
- { 0x00, "RESET" },
- { 0x01, "STARTED" },
- { 0x02, "ENABLED" },
- { 0x03, "DISABLED" },
- { 0, NULL }
-};
-
-/* entire decoded OM2K message (header + parsed TLV) */
-struct om2k_decoded_msg {
- struct abis_om2k_hdr o2h;
- uint16_t msg_type;
- struct tlv_parsed tp;
-};
-
-/* resolve the OM2000 Managed Object by BTS + MO Address */
-static struct om2k_mo *
-get_om2k_mo(struct gsm_bts *bts, const struct abis_om2k_mo *abis_mo)
-{
- struct om2k_mo *mo = NULL;
- struct gsm_bts_trx *trx;
-
- switch (abis_mo->class) {
- case OM2K_MO_CLS_CF:
- mo = &bts->rbs2000.cf.om2k_mo;
- break;
- case OM2K_MO_CLS_CON:
- mo = &bts->rbs2000.con.om2k_mo;
- break;
- case OM2K_MO_CLS_IS:
- mo = &bts->rbs2000.is.om2k_mo;
- break;
- case OM2K_MO_CLS_TF:
- mo = &bts->rbs2000.tf.om2k_mo;
- break;
-
- case OM2K_MO_CLS_TRXC:
- trx = gsm_bts_trx_num(bts, abis_mo->inst);
- if (!trx)
- return NULL;
- mo = &trx->rbs2000.trxc.om2k_mo;
- break;
- case OM2K_MO_CLS_TX:
- trx = gsm_bts_trx_num(bts, abis_mo->inst);
- if (!trx)
- return NULL;
- mo = &trx->rbs2000.tx.om2k_mo;
- break;
- case OM2K_MO_CLS_RX:
- trx = gsm_bts_trx_num(bts, abis_mo->inst);
- if (!trx)
- return NULL;
- mo = &trx->rbs2000.rx.om2k_mo;
- break;
- case OM2K_MO_CLS_TS:
- trx = gsm_bts_trx_num(bts, abis_mo->assoc_so);
- if (!trx)
- return NULL;
- if (abis_mo->inst >= ARRAY_SIZE(trx->ts))
- return NULL;
- mo = &trx->ts[abis_mo->inst].rbs2000.om2k_mo;
- break;
- default:
- return NULL;
- };
-
- return mo;
-}
-
-static struct msgb *om2k_msgb_alloc(void)
-{
- return msgb_alloc_headroom(OM_ALLOC_SIZE, OM_HEADROOM_SIZE,
- "OM2000");
-}
-
-static int abis_om2k_tlv_parse(struct tlv_parsed *tp, const uint8_t *buf, int len)
-{
- return tlv_parse(tp, &om2k_att_tlvdef, buf, len, 0, 0);
-}
-
-static int abis_om2k_msg_tlv_parse(struct tlv_parsed *tp, struct abis_om2k_hdr *oh)
-{
- return abis_om2k_tlv_parse(tp, oh->data, oh->om.length - 6);
-}
-
-/* decode/parse the message */
-static int om2k_decode_msg(struct om2k_decoded_msg *odm, struct msgb *msg)
-{
- struct abis_om2k_hdr *o2h = msgb_l2(msg);
- odm->msg_type = ntohs(o2h->msg_type);
- odm->o2h = *o2h;
- return abis_om2k_msg_tlv_parse(&odm->tp, o2h);
-}
-
-static char *om2k_mo_name(const struct abis_om2k_mo *mo)
-{
- static char mo_buf[64];
-
- memset(mo_buf, 0, sizeof(mo_buf));
- snprintf(mo_buf, sizeof(mo_buf), "%s/%02x/%02x/%02x",
- get_value_string(om2k_mo_class_short_vals, mo->class),
- mo->bts, mo->assoc_so, mo->inst);
- return mo_buf;
-}
-
-/* resolve the gsm_nm_state data structure for a given MO */
-static struct gsm_nm_state *
-mo2nm_state(struct gsm_bts *bts, const struct abis_om2k_mo *mo)
-{
- struct gsm_bts_trx *trx;
- struct gsm_nm_state *nm_state = NULL;
-
- switch (mo->class) {
- case OM2K_MO_CLS_TRXC:
- trx = gsm_bts_trx_num(bts, mo->inst);
- if (!trx)
- return NULL;
- nm_state = &trx->mo.nm_state;
- break;
- case OM2K_MO_CLS_TS:
- trx = gsm_bts_trx_num(bts, mo->assoc_so);
- if (!trx)
- return NULL;
- if (mo->inst >= ARRAY_SIZE(trx->ts))
- return NULL;
- nm_state = &trx->ts[mo->inst].mo.nm_state;
- break;
- case OM2K_MO_CLS_TF:
- nm_state = &bts->rbs2000.tf.mo.nm_state;
- break;
- case OM2K_MO_CLS_IS:
- nm_state = &bts->rbs2000.is.mo.nm_state;
- break;
- case OM2K_MO_CLS_CON:
- nm_state = &bts->rbs2000.con.mo.nm_state;
- break;
- case OM2K_MO_CLS_DP:
- nm_state = &bts->rbs2000.con.mo.nm_state;
- break;
- case OM2K_MO_CLS_CF:
- nm_state = &bts->mo.nm_state;
- break;
- case OM2K_MO_CLS_TX:
- trx = gsm_bts_trx_num(bts, mo->inst);
- if (!trx)
- return NULL;
- /* FIXME */
- break;
- case OM2K_MO_CLS_RX:
- trx = gsm_bts_trx_num(bts, mo->inst);
- if (!trx)
- return NULL;
- /* FIXME */
- break;
- }
-
- return nm_state;
-}
-
-static void *mo2obj(struct gsm_bts *bts, struct abis_om2k_mo *mo)
-{
- struct gsm_bts_trx *trx;
-
- switch (mo->class) {
- case OM2K_MO_CLS_TX:
- case OM2K_MO_CLS_RX:
- case OM2K_MO_CLS_TRXC:
- return gsm_bts_trx_num(bts, mo->inst);
- case OM2K_MO_CLS_TS:
- trx = gsm_bts_trx_num(bts, mo->assoc_so);
- if (!trx)
- return NULL;
- if (mo->inst >= ARRAY_SIZE(trx->ts))
- return NULL;
- return &trx->ts[mo->inst];
- case OM2K_MO_CLS_TF:
- case OM2K_MO_CLS_IS:
- case OM2K_MO_CLS_CON:
- case OM2K_MO_CLS_DP:
- case OM2K_MO_CLS_CF:
- return bts;
- }
-
- return NULL;
-}
-
-static void update_mo_state(struct gsm_bts *bts, struct abis_om2k_mo *mo,
- uint8_t mo_state)
-{
- struct gsm_nm_state *nm_state = mo2nm_state(bts, mo);
- struct gsm_nm_state new_state;
- struct nm_statechg_signal_data nsd;
-
- if (!nm_state)
- return;
-
- new_state = *nm_state;
- /* NOTICE: 12.21 Availability state values != OM2000 */
- new_state.availability = mo_state;
-
- memset(&nsd, 0, sizeof(nsd));
-
- nsd.bts = bts;
- nsd.obj = mo2obj(bts, mo);
- nsd.old_state = nm_state;
- nsd.new_state = &new_state;
- nsd.om2k_mo = mo;
-
- osmo_signal_dispatch(SS_NM, S_NM_STATECHG_ADM, &nsd);
-
- nm_state->availability = new_state.availability;
-}
-
-static void update_op_state(struct gsm_bts *bts, const struct abis_om2k_mo *mo,
- uint8_t op_state)
-{
- struct gsm_nm_state *nm_state = mo2nm_state(bts, mo);
- struct gsm_nm_state new_state;
-
- if (!nm_state)
- return;
-
- new_state = *nm_state;
- switch (op_state) {
- case 1:
- new_state.operational = NM_OPSTATE_ENABLED;
- break;
- case 0:
- new_state.operational = NM_OPSTATE_DISABLED;
- break;
- default:
- new_state.operational = NM_OPSTATE_NULL;
- break;
- }
-
- nm_state->operational = new_state.operational;
-}
-
-static int abis_om2k_sendmsg(struct gsm_bts *bts, struct msgb *msg)
-{
- struct abis_om2k_hdr *o2h;
- struct gsm_bts_trx *trx;
-
- msg->l2h = msg->data;
- o2h = (struct abis_om2k_hdr *) msg->l2h;
-
- /* Compute the length in the OML header */
- o2h->om.length = 6 + msgb_l2len(msg)-sizeof(*o2h);
-
- switch (o2h->mo.class) {
- case OM2K_MO_CLS_TRXC:
- case OM2K_MO_CLS_TX:
- case OM2K_MO_CLS_RX:
- /* Route through per-TRX OML Link to the appropriate TRX */
- trx = gsm_bts_trx_by_nr(bts, o2h->mo.inst);
- if (!trx) {
- LOGP(DNM, LOGL_ERROR, "MO=%s Tx Dropping msg to "
- "non-existing TRX\n", om2k_mo_name(&o2h->mo));
- return -ENODEV;
- }
- msg->dst = trx->oml_link;
- break;
- case OM2K_MO_CLS_TS:
- /* Route through per-TRX OML Link to the appropriate TRX */
- trx = gsm_bts_trx_by_nr(bts, o2h->mo.assoc_so);
- if (!trx) {
- LOGP(DNM, LOGL_ERROR, "MO=%s Tx Dropping msg to "
- "non-existing TRX\n", om2k_mo_name(&o2h->mo));
- return -ENODEV;
- }
- msg->dst = trx->oml_link;
- break;
- default:
- /* Route through the IXU/DXU OML Link */
- msg->dst = bts->oml_link;
- break;
- }
-
- return _abis_nm_sendmsg(msg);
-}
-
-static void fill_om2k_hdr(struct abis_om2k_hdr *o2h, const struct abis_om2k_mo *mo,
- uint16_t msg_type)
-{
- o2h->om.mdisc = ABIS_OM_MDISC_FOM;
- o2h->om.placement = ABIS_OM_PLACEMENT_ONLY;
- o2h->om.sequence = 0;
- /* We fill o2h->om.length later during om2k_sendmsg() */
- o2h->msg_type = htons(msg_type);
- memcpy(&o2h->mo, mo, sizeof(o2h->mo));
-}
-
-static int abis_om2k_cal_time_resp(struct gsm_bts *bts)
-{
- struct msgb *msg = om2k_msgb_alloc();
- struct abis_om2k_hdr *o2k;
- time_t tm_t;
- struct tm *tm;
-
- o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k));
- fill_om2k_hdr(o2k, &bts->rbs2000.cf.om2k_mo.addr,
- OM2K_MSGT_CAL_TIME_RESP);
-
- tm_t = time(NULL);
- tm = localtime(&tm_t);
-
- msgb_put_u8(msg, OM2K_DEI_CAL_TIME);
- msgb_put_u8(msg, tm->tm_year % 100);
- msgb_put_u8(msg, tm->tm_mon + 1);
- msgb_put_u8(msg, tm->tm_mday);
- msgb_put_u8(msg, tm->tm_hour);
- msgb_put_u8(msg, tm->tm_min);
- msgb_put_u8(msg, tm->tm_sec);
-
- return abis_om2k_sendmsg(bts, msg);
-}
-
-static int abis_om2k_tx_simple(struct gsm_bts *bts, const struct abis_om2k_mo *mo,
- uint8_t msg_type)
-{
- struct msgb *msg = om2k_msgb_alloc();
- struct abis_om2k_hdr *o2k;
-
- o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k));
- fill_om2k_hdr(o2k, mo, msg_type);
-
- DEBUGP(DNM, "Tx MO=%s %s\n", om2k_mo_name(mo),
- get_value_string(om2k_msgcode_vals, msg_type));
-
- return abis_om2k_sendmsg(bts, msg);
-}
-
-int abis_om2k_tx_reset_cmd(struct gsm_bts *bts, const struct abis_om2k_mo *mo)
-{
- return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_RESET_CMD);
-}
-
-int abis_om2k_tx_start_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo)
-{
- return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_START_REQ);
-}
-
-int abis_om2k_tx_status_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo)
-{
- return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_STATUS_REQ);
-}
-
-int abis_om2k_tx_connect_cmd(struct gsm_bts *bts, const struct abis_om2k_mo *mo)
-{
- return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_CONNECT_CMD);
-}
-
-int abis_om2k_tx_disconnect_cmd(struct gsm_bts *bts, const struct abis_om2k_mo *mo)
-{
- return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_DISCONNECT_CMD);
-}
-
-int abis_om2k_tx_test_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo)
-{
- return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_TEST_REQ);
-}
-
-int abis_om2k_tx_enable_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo)
-{
- return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_ENABLE_REQ);
-}
-
-int abis_om2k_tx_disable_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo)
-{
- return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_DISABLE_REQ);
-}
-
-int abis_om2k_tx_op_info(struct gsm_bts *bts, const struct abis_om2k_mo *mo,
- uint8_t operational)
-{
- struct msgb *msg = om2k_msgb_alloc();
- struct abis_om2k_hdr *o2k;
-
- o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k));
- fill_om2k_hdr(o2k, mo, OM2K_MSGT_OP_INFO);
-
- msgb_tv_put(msg, OM2K_DEI_OP_INFO, operational);
-
- DEBUGP(DNM, "Tx MO=%s %s\n", om2k_mo_name(mo),
- get_value_string(om2k_msgcode_vals, OM2K_MSGT_OP_INFO));
-
- /* we update the state here... and send the signal at ACK */
- update_op_state(bts, mo, operational);
-
- return abis_om2k_sendmsg(bts, msg);
-}
-
-int abis_om2k_tx_cap_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo)
-{
- return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_CAPA_REQ);
-}
-
-static void om2k_fill_is_conn_grp(struct om2k_is_conn_grp *grp, uint16_t icp1,
- uint16_t icp2, uint8_t cont_idx)
-{
- grp->icp1 = htons(icp1);
- grp->icp2 = htons(icp2);
- grp->cont_idx = cont_idx;
-}
-
-int abis_om2k_tx_is_conf_req(struct gsm_bts *bts)
-{
- struct msgb *msg = om2k_msgb_alloc();
- struct abis_om2k_hdr *o2k;
- struct is_conn_group *grp;
- unsigned int num_grps = 0, i = 0;
- struct om2k_is_conn_grp *cg;
-
- /* count number of groups in linked list */
- llist_for_each_entry(grp, &bts->rbs2000.is.conn_groups, list)
- num_grps++;
-
- if (!num_grps)
- return -EINVAL;
-
- /* allocate buffer for oml group array */
- cg = talloc_zero_array(bts, struct om2k_is_conn_grp, num_grps);
-
- /* fill array with data from linked list */
- llist_for_each_entry(grp, &bts->rbs2000.is.conn_groups, list)
- om2k_fill_is_conn_grp(&cg[i++], grp->icp1, grp->icp2, grp->ci);
-
- o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k));
- fill_om2k_hdr(o2k, &bts->rbs2000.is.om2k_mo.addr,
- OM2K_MSGT_IS_CONF_REQ);
-
- msgb_tv_put(msg, OM2K_DEI_LIST_NR, 1);
- msgb_tv_put(msg, OM2K_DEI_END_LIST_NR, 1);
-
- msgb_tlv_put(msg, OM2K_DEI_IS_CONN_LIST,
- num_grps * sizeof(*cg), (uint8_t *)cg);
-
- talloc_free(cg);
-
- DEBUGP(DNM, "Tx MO=%s %s\n",
- om2k_mo_name(&bts->rbs2000.is.om2k_mo.addr),
- get_value_string(om2k_msgcode_vals, OM2K_MSGT_IS_CONF_REQ));
-
- return abis_om2k_sendmsg(bts, msg);
-}
-
-int abis_om2k_tx_con_conf_req(struct gsm_bts *bts)
-{
- struct msgb *msg = om2k_msgb_alloc();
- struct abis_om2k_hdr *o2k;
- struct con_group *grp;
- unsigned int num_grps = 0;
-
- /* count number of groups in linked list */
- llist_for_each_entry(grp, &bts->rbs2000.con.conn_groups, list)
- num_grps++;
-
- if (!num_grps)
- return -EINVAL;
-
- /* first build the value part of the OM2K_DEI_CON_CONN_LIST DEI */
- msgb_put_u8(msg, num_grps);
- llist_for_each_entry(grp, &bts->rbs2000.con.conn_groups, list) {
- struct con_path *cp;
- unsigned int num_paths = 0;
- llist_for_each_entry(cp, &grp->paths, list)
- num_paths++;
- msgb_put_u8(msg, num_paths);
- llist_for_each_entry(cp, &grp->paths, list) {
- struct om2k_con_path *om2k_cp;
- om2k_cp = (struct om2k_con_path *) msgb_put(msg, sizeof(*om2k_cp));
- om2k_cp->ccp = htons(cp->ccp);
- om2k_cp->ci = cp->ci;
- om2k_cp->tag = cp->tag;
- om2k_cp->tei = cp->tei;
- }
- }
- msgb_push_u8(msg, msgb_length(msg));
- msgb_push_u8(msg, OM2K_DEI_CON_CONN_LIST);
-
- /* pre-pend the list number DEIs */
- msgb_tv_push(msg, OM2K_DEI_END_LIST_NR, 1);
- msgb_tv_push(msg, OM2K_DEI_LIST_NR, 1);
-
- /* pre-pend the OM2K header */
- o2k = (struct abis_om2k_hdr *) msgb_push(msg, sizeof(*o2k));
- fill_om2k_hdr(o2k, &bts->rbs2000.con.om2k_mo.addr,
- OM2K_MSGT_CON_CONF_REQ);
-
- DEBUGP(DNM, "Tx MO=%s %s\n",
- om2k_mo_name(&bts->rbs2000.con.om2k_mo.addr),
- get_value_string(om2k_msgcode_vals, OM2K_MSGT_CON_CONF_REQ));
-
- return abis_om2k_sendmsg(bts, msg);
-}
-
-static void om2k_trx_to_mo(struct abis_om2k_mo *mo,
- const struct gsm_bts_trx *trx,
- enum abis_om2k_mo_cls cls)
-{
- mo->class = cls;
- mo->bts = 0;
- mo->inst = trx->nr;
- mo->assoc_so = 255;
-}
-
-static void om2k_ts_to_mo(struct abis_om2k_mo *mo,
- const struct gsm_bts_trx_ts *ts)
-{
- mo->class = OM2K_MO_CLS_TS;
- mo->bts = 0;
- mo->inst = ts->nr;
- mo->assoc_so = ts->trx->nr;
-}
-
-/* Configure a Receiver MO */
-int abis_om2k_tx_rx_conf_req(struct gsm_bts_trx *trx)
-{
- struct msgb *msg = om2k_msgb_alloc();
- struct abis_om2k_hdr *o2k;
- struct abis_om2k_mo mo;
-
- om2k_trx_to_mo(&mo, trx, OM2K_MO_CLS_RX);
-
- o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k));
- fill_om2k_hdr(o2k, &mo, OM2K_MSGT_RX_CONF_REQ);
-
- msgb_tv16_put(msg, OM2K_DEI_FREQ_SPEC_RX, trx->arfcn);
- msgb_tv_put(msg, OM2K_DEI_RX_DIVERSITY, 0x02); /* A */
-
- return abis_om2k_sendmsg(trx->bts, msg);
-}
-
-/* Configure a Transmitter MO */
-int abis_om2k_tx_tx_conf_req(struct gsm_bts_trx *trx)
-{
- struct msgb *msg = om2k_msgb_alloc();
- struct abis_om2k_hdr *o2k;
- struct abis_om2k_mo mo;
-
- om2k_trx_to_mo(&mo, trx, OM2K_MO_CLS_TX);
-
- o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k));
- fill_om2k_hdr(o2k, &mo, OM2K_MSGT_TX_CONF_REQ);
-
- msgb_tv16_put(msg, OM2K_DEI_FREQ_SPEC_TX, trx->arfcn);
- msgb_tv_put(msg, OM2K_DEI_POWER, trx->nominal_power-trx->max_power_red);
- msgb_tv_put(msg, OM2K_DEI_FILLING_MARKER, 0); /* Filling enabled */
- msgb_tv_put(msg, OM2K_DEI_BCC, trx->bts->bsic & 0x7);
- /* Dedication Information is optional */
-
- return abis_om2k_sendmsg(trx->bts, msg);
-}
-
-enum abis_om2k_tf_mode {
- OM2K_TF_MODE_MASTER = 0x00,
- OM2K_TF_MODE_STANDALONE = 0x01,
- OM2K_TF_MODE_SLAVE = 0x02,
- OM2K_TF_MODE_UNDEFINED = 0xff,
-};
-
-static const uint8_t fs_offset_undef[5] = { 0xff, 0xff, 0xff, 0xff, 0xff };
-
-int abis_om2k_tx_tf_conf_req(struct gsm_bts *bts)
-{
- struct msgb *msg = om2k_msgb_alloc();
- struct abis_om2k_hdr *o2k;
-
- o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k));
- fill_om2k_hdr(o2k, &bts->rbs2000.tf.om2k_mo.addr,
- OM2K_MSGT_TF_CONF_REQ);
-
- msgb_tv_put(msg, OM2K_DEI_TF_MODE, OM2K_TF_MODE_STANDALONE);
- msgb_tv_put(msg, OM2K_DEI_TF_SYNC_SRC, 0x00);
- msgb_tv_fixed_put(msg, OM2K_DEI_FS_OFFSET,
- sizeof(fs_offset_undef), fs_offset_undef);
-
- DEBUGP(DNM, "Tx MO=%s %s\n",
- om2k_mo_name(&bts->rbs2000.tf.om2k_mo.addr),
- get_value_string(om2k_msgcode_vals, OM2K_MSGT_TF_CONF_REQ));
-
- return abis_om2k_sendmsg(bts, msg);
-}
-
-static uint8_t pchan2comb(enum gsm_phys_chan_config pchan)
-{
- switch (pchan) {
- case GSM_PCHAN_CCCH:
- return 4;
- case GSM_PCHAN_CCCH_SDCCH4:
- return 5;
- case GSM_PCHAN_SDCCH8_SACCH8C:
- return 3;
- case GSM_PCHAN_TCH_F:
- case GSM_PCHAN_TCH_H:
- case GSM_PCHAN_PDCH:
- case GSM_PCHAN_TCH_F_PDCH:
- case GSM_PCHAN_TCH_F_TCH_H_PDCH:
- return 8;
- default:
- return 0;
- }
-}
-
-static uint8_t ts2comb(struct gsm_bts_trx_ts *ts)
-{
- switch (ts->pchan) {
- case GSM_PCHAN_TCH_F_PDCH:
- LOGP(DNM, LOGL_ERROR, "%s pchan %s not intended for use"
- " with OM2000, use %s instead\n",
- gsm_ts_and_pchan_name(ts),
- gsm_pchan_name(GSM_PCHAN_TCH_F_PDCH),
- gsm_pchan_name(GSM_PCHAN_TCH_F_TCH_H_PDCH));
- /* If we allowed initialization of TCH/F_PDCH, it would fail
- * when we try to send the ip.access specific RSL PDCH Act
- * message for it. Rather fail completely right now: */
- return 0;
- case GSM_PCHAN_TCH_F_TCH_H_PDCH:
- return pchan2comb(GSM_PCHAN_TCH_F);
- default:
- return pchan2comb(ts->pchan);
- }
-}
-
-static int put_freq_list(uint8_t *buf, uint16_t arfcn)
-{
- buf[0] = 0x00; /* TX/RX address */
- buf[1] = (arfcn >> 8);
- buf[2] = (arfcn & 0xff);
-
- return 3;
-}
-
-/* Compute a frequency list in OM2000 fomrmat */
-static int om2k_gen_freq_list(uint8_t *list, struct gsm_bts_trx_ts *ts)
-{
- uint8_t *cur = list;
- int len;
-
- if (ts->hopping.enabled) {
- unsigned int i;
- for (i = 0; i < ts->hopping.arfcns.data_len*8; i++) {
- if (bitvec_get_bit_pos(&ts->hopping.arfcns, i))
- cur += put_freq_list(cur, i);
- }
- } else
- cur += put_freq_list(cur, ts->trx->arfcn);
-
- len = cur - list;
-
- return len;
-}
-
-const uint8_t icm_bound_params[] = { 0x02, 0x06, 0x0c, 0x16, 0x06 };
-
-int abis_om2k_tx_ts_conf_req(struct gsm_bts_trx_ts *ts)
-{
- struct msgb *msg = om2k_msgb_alloc();
- struct abis_om2k_hdr *o2k;
- struct abis_om2k_mo mo;
- uint8_t freq_list[64*3]; /* BA max size: 64 ARFCN */
- int freq_list_len;
-
- om2k_ts_to_mo(&mo, ts);
-
- memset(freq_list, 0, sizeof(freq_list));
- freq_list_len = om2k_gen_freq_list(freq_list, ts);
- if (freq_list_len < 0)
- return freq_list_len;
-
- o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k));
- fill_om2k_hdr(o2k, &mo, OM2K_MSGT_TS_CONF_REQ);
-
- msgb_tv_put(msg, OM2K_DEI_COMBINATION, ts2comb(ts));
- msgb_tv_put(msg, OM2K_DEI_TS_NR, ts->nr);
- msgb_tlv_put(msg, OM2K_DEI_FREQ_LIST, freq_list_len, freq_list);
- msgb_tv_put(msg, OM2K_DEI_HSN, ts->hopping.hsn);
- msgb_tv_put(msg, OM2K_DEI_MAIO, ts->hopping.maio);
- msgb_tv_put(msg, OM2K_DEI_BSIC, ts->trx->bts->bsic);
- msgb_tv_put(msg, OM2K_DEI_RX_DIVERSITY, 0x02); /* A */
- msgb_tv16_put(msg, OM2K_DEI_FN_OFFSET, 0);
- msgb_tv_put(msg, OM2K_DEI_EXT_RANGE, 0); /* Off */
- /* Optional: Interference Rejection Combining */
- msgb_tv_put(msg, OM2K_DEI_INTERF_REJ_COMB, 0x00);
- switch (ts->pchan) {
- case GSM_PCHAN_CCCH:
- msgb_tv_put(msg, OM2K_DEI_BA_PA_MFRMS, 0x06);
- msgb_tv_put(msg, OM2K_DEI_BS_AG_BKS_RES, 0x01);
- msgb_tv_put(msg, OM2K_DEI_DRX_DEV_MAX, 0x05);
- /* Repeat Paging/IMM.ASS: True, Allow Paging Type 3: Yes, Page for 5 seconds (default) */
- msgb_tv_put(msg, OM2K_DEI_CCCH_OPTIONS, 0x01);
- break;
- case GSM_PCHAN_CCCH_SDCCH4:
- msgb_tv_put(msg, OM2K_DEI_T3105, ts->trx->bts->network->T3105 / 10);
- msgb_tv_put(msg, OM2K_DEI_NY1, 35);
- msgb_tv_put(msg, OM2K_DEI_BA_PA_MFRMS, 0x06);
- msgb_tv_put(msg, OM2K_DEI_CBCH_INDICATOR, 0);
- msgb_tv_put(msg, OM2K_DEI_TSC, gsm_ts_tsc(ts));
- msgb_tv_put(msg, OM2K_DEI_BS_AG_BKS_RES, 0x01);
- msgb_tv_put(msg, OM2K_DEI_ICM_INDICATOR, 0);
- msgb_tv_put(msg, OM2K_DEI_DRX_DEV_MAX, 0x05);
- /* Repeat Paging/IMM.ASS: True, Allow Paging Type 3: Yes, Page for 5 seconds (default) */
- msgb_tv_put(msg, OM2K_DEI_CCCH_OPTIONS, 0x01);
- msgb_tv_fixed_put(msg, OM2K_DEI_ICM_BOUND_PARAMS,
- sizeof(icm_bound_params), icm_bound_params);
- break;
- case GSM_PCHAN_SDCCH8_SACCH8C:
- msgb_tv_put(msg, OM2K_DEI_T3105, ts->trx->bts->network->T3105 / 10);
- msgb_tv_put(msg, OM2K_DEI_NY1, 35);
- msgb_tv_put(msg, OM2K_DEI_CBCH_INDICATOR, 0);
- msgb_tv_put(msg, OM2K_DEI_TSC, gsm_ts_tsc(ts));
- /* Disable RF RESOURCE INDICATION on idle channels */
- msgb_tv_put(msg, OM2K_DEI_ICM_INDICATOR, 0);
- msgb_tv_fixed_put(msg, OM2K_DEI_ICM_BOUND_PARAMS,
- sizeof(icm_bound_params), icm_bound_params);
- break;
- default:
- msgb_tv_put(msg, OM2K_DEI_T3105, ts->trx->bts->network->T3105 / 10);
- msgb_tv_put(msg, OM2K_DEI_NY1, 35);
- msgb_tv_put(msg, OM2K_DEI_TSC, gsm_ts_tsc(ts));
- /* Disable RF RESOURCE INDICATION on idle channels */
- msgb_tv_put(msg, OM2K_DEI_ICM_INDICATOR, 0);
- msgb_tv_fixed_put(msg, OM2K_DEI_ICM_BOUND_PARAMS,
- sizeof(icm_bound_params), icm_bound_params);
- msgb_tv_put(msg, OM2K_DEI_TTA, 10); /* Timer for Time Alignment */
- if (ts->pchan == GSM_PCHAN_TCH_H)
- msgb_tv_put(msg, OM2K_DEI_ICM_CHAN_RATE, 1); /* TCH/H */
- else
- msgb_tv_put(msg, OM2K_DEI_ICM_CHAN_RATE, 0); /* TCH/F */
- msgb_tv_put(msg, OM2K_DEI_LSC, 1); /* enabled */
- msgb_tv_put(msg, OM2K_DEI_LSC_FILT_TIME, 10); /* units of 100ms */
- msgb_tv_put(msg, OM2K_DEI_CALL_SUPV_TIME, 8);
- msgb_tv_put(msg, OM2K_DEI_ENCR_ALG, 0x00);
- /* Not sure what those below mean */
- msgb_tv_put(msg, 0x9e, 0x00);
- msgb_tv_put(msg, 0x9f, 0x37);
- msgb_tv_put(msg, 0xa0, 0x01);
- break;
- }
-
- DEBUGP(DNM, "Tx MO=%s %s\n",
- om2k_mo_name(&mo),
- get_value_string(om2k_msgcode_vals, OM2K_MSGT_TS_CONF_REQ));
-
- return abis_om2k_sendmsg(ts->trx->bts, msg);
-}
-
-
-/***********************************************************************
- * OM2000 Managed Object (MO) FSM
- ***********************************************************************/
-
-#define S(x) (1 << (x))
-
-enum om2k_event_name {
- OM2K_MO_EVT_START,
- OM2K_MO_EVT_RX_CONN_COMPL,
- OM2K_MO_EVT_RX_RESET_COMPL,
- OM2K_MO_EVT_RX_START_REQ_ACCEPT,
- OM2K_MO_EVT_RX_START_RES,
- OM2K_MO_EVT_RX_CFG_REQ_ACCEPT,
- OM2K_MO_EVT_RX_CFG_RES,
- OM2K_MO_EVT_RX_ENA_REQ_ACCEPT,
- OM2K_MO_EVT_RX_ENA_RES,
- OM2K_MO_EVT_RX_OPINFO_ACC,
-};
-
-static const struct value_string om2k_event_names[] = {
- { OM2K_MO_EVT_START, "START" },
- { OM2K_MO_EVT_RX_CONN_COMPL, "RX-CONN-COMPL" },
- { OM2K_MO_EVT_RX_RESET_COMPL, "RX-RESET-COMPL" },
- { OM2K_MO_EVT_RX_START_REQ_ACCEPT, "RX-RESET-REQ-ACCEPT" },
- { OM2K_MO_EVT_RX_START_RES, "RX-START-RESULT" },
- { OM2K_MO_EVT_RX_CFG_REQ_ACCEPT, "RX-CFG-REQ-ACCEPT" },
- { OM2K_MO_EVT_RX_CFG_RES, "RX-CFG-RESULT" },
- { OM2K_MO_EVT_RX_ENA_REQ_ACCEPT, "RX-ENABLE-REQ-ACCEPT" },
- { OM2K_MO_EVT_RX_ENA_RES, "RX-ENABLE-RESULT" },
- { OM2K_MO_EVT_RX_OPINFO_ACC, "RX-OPINFO-ACCEPT" },
- { 0, NULL }
-};
-
-enum om2k_mo_fsm_state {
- OM2K_ST_INIT,
- OM2K_ST_WAIT_CONN_COMPL,
- OM2K_ST_WAIT_RES_COMPL,
- OM2K_ST_WAIT_START_ACCEPT,
- OM2K_ST_WAIT_START_RES,
- OM2K_ST_WAIT_CFG_ACCEPT,
- OM2K_ST_WAIT_CFG_RES,
- OM2K_ST_WAIT_ENABLE_ACCEPT,
- OM2K_ST_WAIT_ENABLE_RES,
- OM2K_ST_WAIT_OPINFO_ACCEPT,
- OM2K_ST_DONE,
- OM2K_ST_ERROR,
-};
-
-struct om2k_mo_fsm_priv {
- struct gsm_bts_trx *trx;
- struct om2k_mo *mo;
- uint8_t ts_nr;
-};
-
-static void om2k_mo_st_init(struct osmo_fsm_inst *fi, uint32_t event, void *data)
-{
- struct om2k_mo_fsm_priv *omfp = fi->priv;
-
- OSMO_ASSERT(event == OM2K_MO_EVT_START);
-
- switch (omfp->mo->addr.class) {
- case OM2K_MO_CLS_CF:
- /* no Connect required, is always connected */
- osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_START_ACCEPT,
- OM2K_TIMEOUT, 0);
- abis_om2k_tx_start_req(omfp->trx->bts, &omfp->mo->addr);
- break;
- case OM2K_MO_CLS_TRXC:
- /* no Connect required, start with Reset */
- osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_RES_COMPL,
- OM2K_TIMEOUT, 0);
- abis_om2k_tx_reset_cmd(omfp->trx->bts, &omfp->mo->addr);
- break;
- default:
- /* start with Connect */
- osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_CONN_COMPL,
- OM2K_TIMEOUT, 0);
- abis_om2k_tx_connect_cmd(omfp->trx->bts, &omfp->mo->addr);
- break;
- }
-}
-
-static void om2k_mo_st_wait_conn_compl(struct osmo_fsm_inst *fi, uint32_t event, void *data)
-{
- struct om2k_mo_fsm_priv *omfp = fi->priv;
-
- switch (omfp->mo->addr.class) {
-#if 0
- case OM2K_MO_CLS_TF:
- /* skip the reset, hope that helps */
- osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_START_ACCEPT,
- OM2K_TIMEOUT, 0);
- abis_om2k_tx_start_req(omfp->trx->bts, &omfp->mo->addr);
- break;
-#endif
- default:
- osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_RES_COMPL,
- OM2K_TIMEOUT, 0);
- abis_om2k_tx_reset_cmd(omfp->trx->bts, &omfp->mo->addr);
- break;
- }
-}
-
-static void om2k_mo_st_wait_res_compl(struct osmo_fsm_inst *fi, uint32_t event, void *data)
-{
- struct om2k_mo_fsm_priv *omfp = fi->priv;
-
- osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_START_ACCEPT,
- OM2K_TIMEOUT, 0);
- abis_om2k_tx_start_req(omfp->trx->bts, &omfp->mo->addr);
-}
-
-static void om2k_mo_st_wait_start_accept(struct osmo_fsm_inst *fi, uint32_t event, void *data)
-{
- struct om2k_decoded_msg *omd = data;
-
- switch (omd->msg_type) {
- case OM2K_MSGT_START_REQ_ACK:
- osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_START_RES,
- OM2K_TIMEOUT, 0);
- break;
- case OM2K_MSGT_START_REQ_REJ:
- osmo_fsm_inst_state_chg(fi, OM2K_ST_ERROR, 0, 0);
- break;
- }
-}
-
-static void om2k_mo_st_wait_start_res(struct osmo_fsm_inst *fi, uint32_t event, void *data)
-{
- struct om2k_mo_fsm_priv *omfp = fi->priv;
- struct gsm_bts_trx_ts *ts;
-
- switch (omfp->mo->addr.class) {
- case OM2K_MO_CLS_CF:
- case OM2K_MO_CLS_TRXC:
- /* Transition directly to Operational Info */
- osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_OPINFO_ACCEPT,
- OM2K_TIMEOUT, 0);
- abis_om2k_tx_op_info(omfp->trx->bts, &omfp->mo->addr, 1);
- return;
- case OM2K_MO_CLS_DP:
- /* Transition directoy to WAIT_ENABLE_ACCEPT */
- osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_ENABLE_ACCEPT,
- OM2K_TIMEOUT, 0);
- abis_om2k_tx_enable_req(omfp->trx->bts, &omfp->mo->addr);
- return;
-#if 0
- case OM2K_MO_CLS_TF:
- /* skip the config, hope that helps speeding things up */
- osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_ENABLE_ACCEPT,
- OM2K_TIMEOUT, 0);
- abis_om2k_tx_enable_req(omfp->trx->bts, &omfp->mo->addr);
- return;
-#endif
- }
-
- osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_CFG_ACCEPT,
- OM2K_TIMEOUT, 0);
- switch (omfp->mo->addr.class) {
- case OM2K_MO_CLS_TF:
- abis_om2k_tx_tf_conf_req(omfp->trx->bts);
- break;
- case OM2K_MO_CLS_IS:
- abis_om2k_tx_is_conf_req(omfp->trx->bts);
- break;
- case OM2K_MO_CLS_CON:
- abis_om2k_tx_con_conf_req(omfp->trx->bts);
- break;
- case OM2K_MO_CLS_TX:
- abis_om2k_tx_tx_conf_req(omfp->trx);
- break;
- case OM2K_MO_CLS_RX:
- abis_om2k_tx_rx_conf_req(omfp->trx);
- break;
- case OM2K_MO_CLS_TS:
- ts = mo2obj(omfp->trx->bts, &omfp->mo->addr);
- abis_om2k_tx_ts_conf_req(ts);
- break;
- }
-}
-
-static void om2k_mo_st_wait_cfg_accept(struct osmo_fsm_inst *fi, uint32_t event, void *data)
-{
- struct om2k_mo_fsm_priv *omfp = fi->priv;
- uint32_t timeout = OM2K_TIMEOUT;
-
- if (omfp->mo->addr.class == OM2K_MO_CLS_TF)
- timeout = 600;
-
- osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_CFG_RES, timeout, 0);
-}
-
-static void om2k_mo_st_wait_cfg_res(struct osmo_fsm_inst *fi, uint32_t event, void *data)
-{
- struct om2k_mo_fsm_priv *omfp = fi->priv;
- struct om2k_decoded_msg *omd = data;
- uint8_t accordance;
-
- if (!TLVP_PRESENT(&omd->tp, OM2K_DEI_ACCORDANCE_IND)) {
- osmo_fsm_inst_state_chg(fi, OM2K_ST_ERROR, 0, 0);
- return;
- }
- accordance = *TLVP_VAL(&omd->tp, OM2K_DEI_ACCORDANCE_IND);
-
- if (accordance != 0) {
- /* accordance not OK */
- osmo_fsm_inst_state_chg(fi, OM2K_ST_ERROR, 0, 0);
- return;
- }
-
- osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_ENABLE_ACCEPT,
- OM2K_TIMEOUT, 0);
- abis_om2k_tx_enable_req(omfp->trx->bts, &omfp->mo->addr);
-}
-
-static void om2k_mo_st_wait_enable_accept(struct osmo_fsm_inst *fi, uint32_t event, void *data)
-{
- struct om2k_mo_fsm_priv *omfp = fi->priv;
- struct om2k_decoded_msg *omd = data;
-
- switch (omd->msg_type) {
- case OM2K_MSGT_ENABLE_REQ_REJ:
- osmo_fsm_inst_state_chg(fi, OM2K_ST_ERROR, 0, 0);
- break;
- case OM2K_MSGT_ENABLE_REQ_ACK:
- if (omfp->mo->addr.class == OM2K_MO_CLS_IS &&
- omfp->trx->bts->rbs2000.use_superchannel)
- e1inp_ericsson_set_altc(omfp->trx->bts->oml_link->ts->line, 1);
- osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_ENABLE_RES,
- OM2K_TIMEOUT, 0);
- }
-}
-
-static void om2k_mo_st_wait_enable_res(struct osmo_fsm_inst *fi, uint32_t event, void *data)
-{
- struct om2k_mo_fsm_priv *omfp = fi->priv;
- //struct om2k_decoded_msg *omd = data;
- /* TODO: check if state is actually enabled now? */
-
- osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_OPINFO_ACCEPT,
- OM2K_TIMEOUT, 0);
- abis_om2k_tx_op_info(omfp->trx->bts, &omfp->mo->addr, 1);
-}
-
-static void om2k_mo_st_wait_opinfo_accept(struct osmo_fsm_inst *fi, uint32_t event, void *data)
-{
- struct om2k_mo_fsm_priv *omfp = fi->priv;
-
- /* if we have just received opinfo accept for the timeslot,
- * start dynamic TCH switching procedures */
- if (omfp->mo->addr.class == OM2K_MO_CLS_TS) {
- struct gsm_bts_trx_ts *ts;
- ts = mo2obj(omfp->trx->bts, &omfp->mo->addr);
- dyn_ts_init(ts);
- }
- osmo_fsm_inst_state_chg(fi, OM2K_ST_DONE, 0, 0);
-}
-
-static void om2k_mo_s_done_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
-{
- struct om2k_mo_fsm_priv *omfp = fi->priv;
- omfp->mo->fsm = NULL;
- osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
-}
-
-static void om2k_mo_s_error_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
-{
- struct om2k_mo_fsm_priv *omfp = fi->priv;
-
- omfp->mo->fsm = NULL;
- osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
-}
-
-static const struct osmo_fsm_state om2k_is_states[] = {
- [OM2K_ST_INIT] = {
- .name = "INIT",
- .in_event_mask = S(OM2K_MO_EVT_START),
- .out_state_mask = S(OM2K_ST_DONE) |
- S(OM2K_ST_ERROR) |
- S(OM2K_ST_WAIT_CONN_COMPL) |
- S(OM2K_ST_WAIT_START_ACCEPT) |
- S(OM2K_ST_WAIT_RES_COMPL),
- .action = om2k_mo_st_init,
- },
- [OM2K_ST_WAIT_CONN_COMPL] = {
- .name = "WAIT-CONN-COMPL",
- .in_event_mask = S(OM2K_MO_EVT_RX_CONN_COMPL),
- .out_state_mask = S(OM2K_ST_DONE) |
- S(OM2K_ST_ERROR) |
- S(OM2K_ST_WAIT_START_ACCEPT) |
- S(OM2K_ST_WAIT_RES_COMPL),
- .action = om2k_mo_st_wait_conn_compl,
- },
- [OM2K_ST_WAIT_RES_COMPL] = {
- .name = "WAIT-RES-COMPL",
- .in_event_mask = S(OM2K_MO_EVT_RX_RESET_COMPL),
- .out_state_mask = S(OM2K_ST_DONE) |
- S(OM2K_ST_ERROR) |
- S(OM2K_ST_WAIT_START_ACCEPT),
- .action = om2k_mo_st_wait_res_compl,
- },
- [OM2K_ST_WAIT_START_ACCEPT] = {
- .name = "WAIT-START-ACCEPT",
- .in_event_mask = S(OM2K_MO_EVT_RX_START_REQ_ACCEPT),
- .out_state_mask = S(OM2K_ST_DONE) |
- S(OM2K_ST_ERROR) |
- S(OM2K_ST_WAIT_START_RES),
- .action =om2k_mo_st_wait_start_accept,
- },
- [OM2K_ST_WAIT_START_RES] = {
- .name = "WAIT-START-RES",
- .in_event_mask = S(OM2K_MO_EVT_RX_START_RES),
- .out_state_mask = S(OM2K_ST_DONE) |
- S(OM2K_ST_ERROR) |
- S(OM2K_ST_WAIT_CFG_ACCEPT) |
- S(OM2K_ST_WAIT_OPINFO_ACCEPT),
- .action = om2k_mo_st_wait_start_res,
- },
- [OM2K_ST_WAIT_CFG_ACCEPT] = {
- .name = "WAIT-CFG-ACCEPT",
- .in_event_mask = S(OM2K_MO_EVT_RX_CFG_REQ_ACCEPT),
- .out_state_mask = S(OM2K_ST_DONE) |
- S(OM2K_ST_ERROR) |
- S(OM2K_ST_WAIT_CFG_RES),
- .action = om2k_mo_st_wait_cfg_accept,
- },
- [OM2K_ST_WAIT_CFG_RES] = {
- .name = "WAIT-CFG-RES",
- .in_event_mask = S(OM2K_MO_EVT_RX_CFG_RES),
- .out_state_mask = S(OM2K_ST_DONE) |
- S(OM2K_ST_ERROR) |
- S(OM2K_ST_WAIT_ENABLE_ACCEPT),
- .action = om2k_mo_st_wait_cfg_res,
- },
- [OM2K_ST_WAIT_ENABLE_ACCEPT] = {
- .name = "WAIT-ENABLE-ACCEPT",
- .in_event_mask = S(OM2K_MO_EVT_RX_ENA_REQ_ACCEPT),
- .out_state_mask = S(OM2K_ST_DONE) |
- S(OM2K_ST_ERROR) |
- S(OM2K_ST_WAIT_ENABLE_RES),
- .action = om2k_mo_st_wait_enable_accept,
- },
- [OM2K_ST_WAIT_ENABLE_RES] = {
- .name = "WAIT-ENABLE-RES",
- .in_event_mask = S(OM2K_MO_EVT_RX_ENA_RES),
- .out_state_mask = S(OM2K_ST_DONE) |
- S(OM2K_ST_ERROR) |
- S(OM2K_ST_WAIT_OPINFO_ACCEPT),
- .action = om2k_mo_st_wait_enable_res,
- },
- [OM2K_ST_WAIT_OPINFO_ACCEPT] = {
- .name = "WAIT-OPINFO-ACCEPT",
- .in_event_mask = S(OM2K_MO_EVT_RX_OPINFO_ACC),
- .out_state_mask = S(OM2K_ST_DONE) |
- S(OM2K_ST_ERROR),
- .action = om2k_mo_st_wait_opinfo_accept,
- },
- [OM2K_ST_DONE] = {
- .name = "DONE",
- .in_event_mask = 0,
- .out_state_mask = 0,
- .onenter = om2k_mo_s_done_onenter,
- },
- [OM2K_ST_ERROR] = {
- .name = "ERROR",
- .in_event_mask = 0,
- .out_state_mask = 0,
- .onenter = om2k_mo_s_error_onenter,
- },
-
-};
-
-static int om2k_mo_timer_cb(struct osmo_fsm_inst *fi)
-{
- osmo_fsm_inst_state_chg(fi, OM2K_ST_ERROR, 0, 0);
- return 0;
-}
-
-static struct osmo_fsm om2k_mo_fsm = {
- .name = "OM2000-MO",
- .states = om2k_is_states,
- .num_states = ARRAY_SIZE(om2k_is_states),
- .log_subsys = DNM,
- .event_names = om2k_event_names,
- .timer_cb = om2k_mo_timer_cb,
-};
-
-struct osmo_fsm_inst *om2k_mo_fsm_start(struct osmo_fsm_inst *parent,
- uint32_t term_event,
- struct gsm_bts_trx *trx, struct om2k_mo *mo)
-{
- struct osmo_fsm_inst *fi;
- struct om2k_mo_fsm_priv *omfp;
- char idbuf[64];
-
- snprintf(idbuf, sizeof(idbuf), "%s-%s", parent->id,
- om2k_mo_name(&mo->addr));
-
- fi = osmo_fsm_inst_alloc_child_id(&om2k_mo_fsm, parent,
- term_event, idbuf);
- if (!fi)
- return NULL;
-
- mo->fsm = fi;
- omfp = talloc_zero(fi, struct om2k_mo_fsm_priv);
- omfp->mo = mo;
- omfp->trx = trx;
- fi->priv = omfp;
-
- osmo_fsm_inst_dispatch(fi, OM2K_MO_EVT_START, NULL);
-
- return fi;
-}
-
-int om2k_mo_fsm_recvmsg(struct gsm_bts *bts, struct om2k_mo *mo,
- struct om2k_decoded_msg *odm)
-{
- switch (odm->msg_type) {
- case OM2K_MSGT_CONNECT_COMPL:
- case OM2K_MSGT_CONNECT_REJ:
- osmo_fsm_inst_dispatch(mo->fsm,
- OM2K_MO_EVT_RX_CONN_COMPL, odm);
- break;
-
- case OM2K_MSGT_RESET_COMPL:
- case OM2K_MSGT_RESET_REJ:
- osmo_fsm_inst_dispatch(mo->fsm,
- OM2K_MO_EVT_RX_RESET_COMPL, odm);
- break;
-
- case OM2K_MSGT_START_REQ_ACK:
- case OM2K_MSGT_START_REQ_REJ:
- osmo_fsm_inst_dispatch(mo->fsm,
- OM2K_MO_EVT_RX_START_REQ_ACCEPT, odm);
- break;
-
- case OM2K_MSGT_START_RES:
- osmo_fsm_inst_dispatch(mo->fsm,
- OM2K_MO_EVT_RX_START_RES, odm);
- break;
-
- case OM2K_MSGT_CON_CONF_REQ_ACK:
- case OM2K_MSGT_IS_CONF_REQ_ACK:
- case OM2K_MSGT_RX_CONF_REQ_ACK:
- case OM2K_MSGT_TF_CONF_REQ_ACK:
- case OM2K_MSGT_TS_CONF_REQ_ACK:
- case OM2K_MSGT_TX_CONF_REQ_ACK:
- osmo_fsm_inst_dispatch(mo->fsm,
- OM2K_MO_EVT_RX_CFG_REQ_ACCEPT, odm);
- break;
-
- case OM2K_MSGT_CON_CONF_RES:
- case OM2K_MSGT_IS_CONF_RES:
- case OM2K_MSGT_RX_CONF_RES:
- case OM2K_MSGT_TF_CONF_RES:
- case OM2K_MSGT_TS_CONF_RES:
- case OM2K_MSGT_TX_CONF_RES:
- osmo_fsm_inst_dispatch(mo->fsm,
- OM2K_MO_EVT_RX_CFG_RES, odm);
- break;
-
- case OM2K_MSGT_ENABLE_REQ_ACK:
- case OM2K_MSGT_ENABLE_REQ_REJ:
- osmo_fsm_inst_dispatch(mo->fsm,
- OM2K_MO_EVT_RX_ENA_REQ_ACCEPT, odm);
- break;
- case OM2K_MSGT_ENABLE_RES:
- osmo_fsm_inst_dispatch(mo->fsm,
- OM2K_MO_EVT_RX_ENA_RES, odm);
- break;
-
- case OM2K_MSGT_OP_INFO_ACK:
- case OM2K_MSGT_OP_INFO_REJ:
- osmo_fsm_inst_dispatch(mo->fsm,
- OM2K_MO_EVT_RX_OPINFO_ACC, odm);
- break;
- default:
- return -1;
- }
-
- return 0;
-}
-
-/***********************************************************************
- * OM2000 TRX Finite State Machine, initializes TRXC and all siblings
- ***********************************************************************/
-
-enum om2k_trx_event {
- OM2K_TRX_EVT_START,
- OM2K_TRX_EVT_TRXC_DONE,
- OM2K_TRX_EVT_TX_DONE,
- OM2K_TRX_EVT_RX_DONE,
- OM2K_TRX_EVT_TS_DONE,
- OM2K_TRX_EVT_STOP,
-};
-
-static struct value_string om2k_trx_events[] = {
- { OM2K_TRX_EVT_START, "START" },
- { OM2K_TRX_EVT_TRXC_DONE, "TRXC-DONE" },
- { OM2K_TRX_EVT_TX_DONE, "TX-DONE" },
- { OM2K_TRX_EVT_RX_DONE, "RX-DONE" },
- { OM2K_TRX_EVT_TS_DONE, "TS-DONE" },
- { OM2K_TRX_EVT_STOP, "STOP" },
- { 0, NULL }
-};
-
-enum om2k_trx_state {
- OM2K_TRX_S_INIT,
- OM2K_TRX_S_WAIT_TRXC,
- OM2K_TRX_S_WAIT_TX,
- OM2K_TRX_S_WAIT_RX,
- OM2K_TRX_S_WAIT_TS,
- OM2K_TRX_S_DONE,
- OM2K_TRX_S_ERROR
-};
-
-struct om2k_trx_fsm_priv {
- struct gsm_bts_trx *trx;
- uint8_t next_ts_nr;
-};
-
-static void om2k_trx_s_init(struct osmo_fsm_inst *fi, uint32_t event, void *data)
-{
- struct om2k_trx_fsm_priv *otfp = fi->priv;
-
- /* First initialize TRXC */
- osmo_fsm_inst_state_chg(fi, OM2K_TRX_S_WAIT_TRXC,
- TRX_FSM_TIMEOUT, 0);
- om2k_mo_fsm_start(fi, OM2K_TRX_EVT_TRXC_DONE, otfp->trx,
- &otfp->trx->rbs2000.trxc.om2k_mo);
-}
-
-static void om2k_trx_s_wait_trxc(struct osmo_fsm_inst *fi, uint32_t event, void *data)
-{
- struct om2k_trx_fsm_priv *otfp = fi->priv;
-
- /* Initialize TX after TRXC */
- osmo_fsm_inst_state_chg(fi, OM2K_TRX_S_WAIT_TX,
- TRX_FSM_TIMEOUT, 0);
- om2k_mo_fsm_start(fi, OM2K_TRX_EVT_TX_DONE, otfp->trx,
- &otfp->trx->rbs2000.tx.om2k_mo);
-}
-
-static void om2k_trx_s_wait_tx(struct osmo_fsm_inst *fi, uint32_t event, void *data)
-{
- struct om2k_trx_fsm_priv *otfp = fi->priv;
-
- /* Initialize RX after TX */
- osmo_fsm_inst_state_chg(fi, OM2K_TRX_S_WAIT_RX,
- TRX_FSM_TIMEOUT, 0);
- om2k_mo_fsm_start(fi, OM2K_TRX_EVT_RX_DONE, otfp->trx,
- &otfp->trx->rbs2000.rx.om2k_mo);
-}
-
-static void om2k_trx_s_wait_rx(struct osmo_fsm_inst *fi, uint32_t event, void *data)
-{
- struct om2k_trx_fsm_priv *otfp = fi->priv;
- struct gsm_bts_trx_ts *ts;
-
- /* Initialize Timeslots after TX */
- osmo_fsm_inst_state_chg(fi, OM2K_TRX_S_WAIT_TS,
- TRX_FSM_TIMEOUT, 0);
- otfp->next_ts_nr = 0;
- ts = &otfp->trx->ts[otfp->next_ts_nr++];
- om2k_mo_fsm_start(fi, OM2K_TRX_EVT_TS_DONE, otfp->trx,
- &ts->rbs2000.om2k_mo);
-}
-
-static void om2k_trx_s_wait_ts(struct osmo_fsm_inst *fi, uint32_t event, void *data)
-{
- struct om2k_trx_fsm_priv *otfp = fi->priv;
- struct gsm_bts_trx_ts *ts;
-
- if (otfp->next_ts_nr < 8) {
- /* iterate to the next timeslot */
- ts = &otfp->trx->ts[otfp->next_ts_nr++];
- om2k_mo_fsm_start(fi, OM2K_TRX_EVT_TS_DONE, otfp->trx,
- &ts->rbs2000.om2k_mo);
- } else {
- /* only after all 8 TS */
- osmo_fsm_inst_state_chg(fi, OM2K_TRX_S_DONE, 0, 0);
- }
-}
-
-static void om2k_trx_s_done_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
-{
- struct om2k_trx_fsm_priv *otfp = fi->priv;
- gsm_bts_trx_set_system_infos(otfp->trx);
- osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
-}
-
-static const struct osmo_fsm_state om2k_trx_states[] = {
- [OM2K_TRX_S_INIT] = {
- .in_event_mask = S(OM2K_TRX_EVT_START),
- .out_state_mask = S(OM2K_TRX_S_WAIT_TRXC),
- .name = "INIT",
- .action = om2k_trx_s_init,
- },
- [OM2K_TRX_S_WAIT_TRXC] = {
- .in_event_mask = S(OM2K_TRX_EVT_TRXC_DONE),
- .out_state_mask = S(OM2K_TRX_S_ERROR) |
- S(OM2K_TRX_S_WAIT_TX),
- .name = "WAIT-TRXC",
- .action = om2k_trx_s_wait_trxc,
- },
- [OM2K_TRX_S_WAIT_TX] = {
- .in_event_mask = S(OM2K_TRX_EVT_TX_DONE),
- .out_state_mask = S(OM2K_TRX_S_ERROR) |
- S(OM2K_TRX_S_WAIT_RX),
- .name = "WAIT-TX",
- .action = om2k_trx_s_wait_tx,
- },
- [OM2K_TRX_S_WAIT_RX] = {
- .in_event_mask = S(OM2K_TRX_EVT_RX_DONE),
- .out_state_mask = S(OM2K_TRX_S_ERROR) |
- S(OM2K_TRX_S_WAIT_TS),
- .name = "WAIT-RX",
- .action = om2k_trx_s_wait_rx,
- },
- [OM2K_TRX_S_WAIT_TS] = {
- .in_event_mask = S(OM2K_TRX_EVT_TS_DONE),
- .out_state_mask = S(OM2K_TRX_S_ERROR) |
- S(OM2K_TRX_S_DONE),
- .name = "WAIT-TS",
- .action = om2k_trx_s_wait_ts,
- },
- [OM2K_TRX_S_DONE] = {
- .name = "DONE",
- .onenter = om2k_trx_s_done_onenter,
- },
- [OM2K_TRX_S_ERROR] = {
- .name = "ERROR",
- },
-};
-
-static int om2k_trx_timer_cb(struct osmo_fsm_inst *fi)
-{
- osmo_fsm_inst_state_chg(fi, OM2K_TRX_S_ERROR, 0, 0);
- return 0;
-}
-
-static struct osmo_fsm om2k_trx_fsm = {
- .name = "OM2000-TRX",
- .states = om2k_trx_states,
- .num_states = ARRAY_SIZE(om2k_trx_states),
- .log_subsys = DNM,
- .event_names = om2k_trx_events,
- .timer_cb = om2k_trx_timer_cb,
-};
-
-struct osmo_fsm_inst *om2k_trx_fsm_start(struct osmo_fsm_inst *parent,
- struct gsm_bts_trx *trx,
- uint32_t term_event)
-{
- struct osmo_fsm_inst *fi;
- struct om2k_trx_fsm_priv *otfp;
- char idbuf[32];
-
- snprintf(idbuf, sizeof(idbuf), "%u/%u", trx->bts->nr, trx->nr);
-
- fi = osmo_fsm_inst_alloc_child_id(&om2k_trx_fsm, parent, term_event,
- idbuf);
- if (!fi)
- return NULL;
-
- otfp = talloc_zero(fi, struct om2k_trx_fsm_priv);
- otfp->trx = trx;
- fi->priv = otfp;
-
- osmo_fsm_inst_dispatch(fi, OM2K_TRX_EVT_START, NULL);
-
- return fi;
-}
-
-
-/***********************************************************************
- * OM2000 BTS Finite State Machine, initializes CF and all siblings
- ***********************************************************************/
-
-enum om2k_bts_event {
- OM2K_BTS_EVT_START,
- OM2K_BTS_EVT_CF_DONE,
- OM2K_BTS_EVT_IS_DONE,
- OM2K_BTS_EVT_CON_DONE,
- OM2K_BTS_EVT_TF_DONE,
- OM2K_BTS_EVT_TRX_DONE,
- OM2K_BTS_EVT_STOP,
-};
-
-static const struct value_string om2k_bts_events[] = {
- { OM2K_BTS_EVT_START, "START" },
- { OM2K_BTS_EVT_CF_DONE, "CF-DONE" },
- { OM2K_BTS_EVT_IS_DONE, "IS-DONE" },
- { OM2K_BTS_EVT_CON_DONE, "CON-DONE" },
- { OM2K_BTS_EVT_TF_DONE, "TF-DONE" },
- { OM2K_BTS_EVT_TRX_DONE, "TRX-DONE" },
- { OM2K_BTS_EVT_STOP, "STOP" },
- { 0, NULL }
-};
-
-enum om2k_bts_state {
- OM2K_BTS_S_INIT,
- OM2K_BTS_S_WAIT_CF,
- OM2K_BTS_S_WAIT_IS,
- OM2K_BTS_S_WAIT_CON,
- OM2K_BTS_S_WAIT_TF,
- OM2K_BTS_S_WAIT_TRX,
- OM2K_BTS_S_DONE,
- OM2K_BTS_S_ERROR,
-};
-
-struct om2k_bts_fsm_priv {
- struct gsm_bts *bts;
- uint8_t next_trx_nr;
-};
-
-static void om2k_bts_s_init(struct osmo_fsm_inst *fi, uint32_t event, void *data)
-{
- struct om2k_bts_fsm_priv *obfp = fi->priv;
- struct gsm_bts *bts = obfp->bts;
-
- OSMO_ASSERT(event == OM2K_BTS_EVT_START);
- osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_CF,
- BTS_FSM_TIMEOUT, 0);
- om2k_mo_fsm_start(fi, OM2K_BTS_EVT_CF_DONE, bts->c0,
- &bts->rbs2000.cf.om2k_mo);
-}
-
-static void om2k_bts_s_wait_cf(struct osmo_fsm_inst *fi, uint32_t event, void *data)
-{
- struct om2k_bts_fsm_priv *obfp = fi->priv;
- struct gsm_bts *bts = obfp->bts;
-
- OSMO_ASSERT(event == OM2K_BTS_EVT_CF_DONE);
- /* TF can take a long time to initialize, wait for 10min */
- osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_TF, 600, 0);
- om2k_mo_fsm_start(fi, OM2K_BTS_EVT_TF_DONE, bts->c0,
- &bts->rbs2000.tf.om2k_mo);
-}
-
-static void om2k_bts_s_wait_tf(struct osmo_fsm_inst *fi, uint32_t event, void *data)
-{
- struct om2k_bts_fsm_priv *obfp = fi->priv;
- struct gsm_bts *bts = obfp->bts;
-
- OSMO_ASSERT(event == OM2K_BTS_EVT_TF_DONE);
-
- osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_CON,
- BTS_FSM_TIMEOUT, 0);
- om2k_mo_fsm_start(fi, OM2K_BTS_EVT_CON_DONE, bts->c0,
- &bts->rbs2000.con.om2k_mo);
-}
-
-static void om2k_bts_s_wait_con(struct osmo_fsm_inst *fi, uint32_t event, void *data)
-{
- struct om2k_bts_fsm_priv *obfp = fi->priv;
- struct gsm_bts *bts = obfp->bts;
-
- OSMO_ASSERT(event == OM2K_BTS_EVT_CON_DONE);
-
- osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_IS,
- BTS_FSM_TIMEOUT, 0);
- om2k_mo_fsm_start(fi, OM2K_BTS_EVT_IS_DONE, bts->c0,
- &bts->rbs2000.is.om2k_mo);
-}
-
-static void om2k_bts_s_wait_is(struct osmo_fsm_inst *fi, uint32_t event, void *data)
-{
- struct om2k_bts_fsm_priv *obfp = fi->priv;
- struct gsm_bts_trx *trx;
-
- OSMO_ASSERT(event == OM2K_BTS_EVT_IS_DONE);
-
- osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_TRX,
- BTS_FSM_TIMEOUT, 0);
- obfp->next_trx_nr = 0;
- trx = gsm_bts_trx_num(obfp->bts, obfp->next_trx_nr++);
- om2k_trx_fsm_start(fi, trx, OM2K_BTS_EVT_TRX_DONE);
-}
-
-static void om2k_bts_s_wait_trx(struct osmo_fsm_inst *fi, uint32_t event, void *data)
-{
- struct om2k_bts_fsm_priv *obfp = fi->priv;
-
- OSMO_ASSERT(event == OM2K_BTS_EVT_TRX_DONE);
-
- if (obfp->next_trx_nr < obfp->bts->num_trx) {
- struct gsm_bts_trx *trx;
- trx = gsm_bts_trx_num(obfp->bts, obfp->next_trx_nr++);
- om2k_trx_fsm_start(fi, trx, OM2K_BTS_EVT_TRX_DONE);
- } else {
- osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_DONE, 0, 0);
- }
-}
-
-static void om2k_bts_s_done_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
-{
- osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
-}
-
-static const struct osmo_fsm_state om2k_bts_states[] = {
- [OM2K_BTS_S_INIT] = {
- .in_event_mask = S(OM2K_BTS_EVT_START),
- .out_state_mask = S(OM2K_BTS_S_WAIT_CF),
- .name = "INIT",
- .action = om2k_bts_s_init,
- },
- [OM2K_BTS_S_WAIT_CF] = {
- .in_event_mask = S(OM2K_BTS_EVT_CF_DONE),
- .out_state_mask = S(OM2K_BTS_S_ERROR) |
- S(OM2K_BTS_S_WAIT_TF),
- .name = "WAIT-CF",
- .action = om2k_bts_s_wait_cf,
- },
- [OM2K_BTS_S_WAIT_TF] = {
- .in_event_mask = S(OM2K_BTS_EVT_TF_DONE),
- .out_state_mask = S(OM2K_BTS_S_ERROR) |
- S(OM2K_BTS_S_WAIT_CON),
- .name = "WAIT-TF",
- .action = om2k_bts_s_wait_tf,
- },
- [OM2K_BTS_S_WAIT_CON] = {
- .in_event_mask = S(OM2K_BTS_EVT_CON_DONE),
- .out_state_mask = S(OM2K_BTS_S_ERROR) |
- S(OM2K_BTS_S_WAIT_IS),
- .name = "WAIT-CON",
- .action = om2k_bts_s_wait_con,
- },
- [OM2K_BTS_S_WAIT_IS] = {
- .in_event_mask = S(OM2K_BTS_EVT_IS_DONE),
- .out_state_mask = S(OM2K_BTS_S_ERROR) |
- S(OM2K_BTS_S_WAIT_TRX),
- .name = "WAIT-IS",
- .action = om2k_bts_s_wait_is,
- },
- [OM2K_BTS_S_WAIT_TRX] = {
- .in_event_mask = S(OM2K_BTS_EVT_TRX_DONE),
- .out_state_mask = S(OM2K_BTS_S_ERROR) |
- S(OM2K_BTS_S_DONE),
- .name = "WAIT-TRX",
- .action = om2k_bts_s_wait_trx,
- },
- [OM2K_BTS_S_DONE] = {
- .name = "DONE",
- .onenter = om2k_bts_s_done_onenter,
- },
- [OM2K_BTS_S_ERROR] = {
- .name = "ERROR",
- },
-};
-
-static int om2k_bts_timer_cb(struct osmo_fsm_inst *fi)
-{
- osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_ERROR, 0, 0);
- return 0;
-}
-
-static struct osmo_fsm om2k_bts_fsm = {
- .name = "OM2000-BTS",
- .states = om2k_bts_states,
- .num_states = ARRAY_SIZE(om2k_bts_states),
- .log_subsys = DNM,
- .event_names = om2k_bts_events,
- .timer_cb = om2k_bts_timer_cb,
-};
-
-struct osmo_fsm_inst *
-om2k_bts_fsm_start(struct gsm_bts *bts)
-{
- struct osmo_fsm_inst *fi;
- struct om2k_bts_fsm_priv *obfp;
- char idbuf[16];
-
- snprintf(idbuf, sizeof(idbuf), "%u", bts->nr);
-
- fi = osmo_fsm_inst_alloc(&om2k_bts_fsm, bts, NULL,
- LOGL_DEBUG, idbuf);
- if (!fi)
- return NULL;
- fi->priv = obfp = talloc_zero(fi, struct om2k_bts_fsm_priv);
- obfp->bts = bts;
-
- osmo_fsm_inst_dispatch(fi, OM2K_BTS_EVT_START, NULL);
-
- return fi;
-}
-
-
-/***********************************************************************
- * OM2000 Negotiation
- ***********************************************************************/
-
-static int abis_om2k_tx_negot_req_ack(struct gsm_bts *bts, const struct abis_om2k_mo *mo,
- uint8_t *data, unsigned int len)
-{
- struct msgb *msg = om2k_msgb_alloc();
- struct abis_om2k_hdr *o2k;
-
- o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k));
- fill_om2k_hdr(o2k, mo, OM2K_MSGT_NEGOT_REQ_ACK);
-
- msgb_tlv_put(msg, OM2K_DEI_NEGOT_REC2, len, data);
-
- DEBUGP(DNM, "Tx MO=%s %s\n", om2k_mo_name(mo),
- get_value_string(om2k_msgcode_vals, OM2K_MSGT_NEGOT_REQ_ACK));
-
- return abis_om2k_sendmsg(bts, msg);
-}
-
-struct iwd_version {
- uint8_t gen_char[3+1];
- uint8_t rev_char[3+1];
-};
-
-struct iwd_type {
- uint8_t num_vers;
- struct iwd_version v[8];
-};
-
-static int om2k_rx_negot_req(struct msgb *msg)
-{
- struct e1inp_sign_link *sign_link = (struct e1inp_sign_link *)msg->dst;
- struct abis_om2k_hdr *o2h = msgb_l2(msg);
- struct iwd_type iwd_types[16];
- uint8_t num_iwd_types = o2h->data[2];
- uint8_t *cur = o2h->data+3;
- unsigned int i, v;
-
- uint8_t out_buf[1024];
- uint8_t *out_cur = out_buf+1;
- uint8_t out_num_types = 0;
-
- memset(iwd_types, 0, sizeof(iwd_types));
-
- /* Parse the RBS-supported IWD versions into iwd_types array */
- for (i = 0; i < num_iwd_types; i++) {
- uint8_t num_versions = *cur++;
- uint8_t iwd_type = *cur++;
-
- iwd_types[iwd_type].num_vers = num_versions;
-
- for (v = 0; v < num_versions; v++) {
- struct iwd_version *iwd_v = &iwd_types[iwd_type].v[v];
-
- memcpy(iwd_v->gen_char, cur, 3);
- cur += 3;
- memcpy(iwd_v->rev_char, cur, 3);
- cur += 3;
-
- DEBUGP(DNM, "\tIWD Type %u Gen %s Rev %s\n", iwd_type,
- iwd_v->gen_char, iwd_v->rev_char);
- }
- }
-
- /* Select the last version for each IWD type */
- for (i = 0; i < ARRAY_SIZE(iwd_types); i++) {
- struct iwd_type *type = &iwd_types[i];
- struct iwd_version *last_v;
-
- if (type->num_vers == 0)
- continue;
-
- out_num_types++;
-
- last_v = &type->v[type->num_vers-1];
-
- *out_cur++ = i;
- memcpy(out_cur, last_v->gen_char, 3);
- out_cur += 3;
- memcpy(out_cur, last_v->rev_char, 3);
- out_cur += 3;
- }
-
- out_buf[0] = out_num_types;
-
- return abis_om2k_tx_negot_req_ack(sign_link->trx->bts, &o2h->mo, out_buf, out_cur - out_buf);
-}
-
-
-/***********************************************************************
- * OM2000 Receive Message Handler
- ***********************************************************************/
-
-static int om2k_rx_nack(struct msgb *msg)
-{
- struct abis_om2k_hdr *o2h = msgb_l2(msg);
- uint16_t msg_type = ntohs(o2h->msg_type);
- struct tlv_parsed tp;
-
- LOGP(DNM, LOGL_ERROR, "Rx MO=%s %s", om2k_mo_name(&o2h->mo),
- get_value_string(om2k_msgcode_vals, msg_type));
-
- abis_om2k_msg_tlv_parse(&tp, o2h);
- if (TLVP_PRESENT(&tp, OM2K_DEI_REASON_CODE))
- LOGPC(DNM, LOGL_ERROR, ", Reason 0x%02x",
- *TLVP_VAL(&tp, OM2K_DEI_REASON_CODE));
-
- if (TLVP_PRESENT(&tp, OM2K_DEI_RESULT_CODE))
- LOGPC(DNM, LOGL_ERROR, ", Result %s",
- get_value_string(om2k_result_strings,
- *TLVP_VAL(&tp, OM2K_DEI_RESULT_CODE)));
- LOGPC(DNM, LOGL_ERROR, "\n");
-
- return 0;
-}
-
-static int process_mo_state(struct gsm_bts *bts, struct om2k_decoded_msg *odm)
-{
- uint8_t mo_state;
-
- if (!TLVP_PRESENT(&odm->tp, OM2K_DEI_MO_STATE))
- return -EIO;
- mo_state = *TLVP_VAL(&odm->tp, OM2K_DEI_MO_STATE);
-
- LOGP(DNM, LOGL_DEBUG, "Rx MO=%s %s, MO State: %s\n",
- om2k_mo_name(&odm->o2h.mo),
- get_value_string(om2k_msgcode_vals, odm->msg_type),
- get_value_string(om2k_mostate_vals, mo_state));
-
- /* Throw error message in case we see an enable rsponse that does
- * not yield an enabled mo-state */
- if (odm->msg_type == OM2K_MSGT_ENABLE_RES
- && mo_state != OM2K_MO_S_ENABLED) {
- LOGP(DNM, LOGL_ERROR,
- "Rx MO=%s %s Failed to enable MO State!\n",
- om2k_mo_name(&odm->o2h.mo),
- get_value_string(om2k_msgcode_vals, odm->msg_type));
- }
-
- update_mo_state(bts, &odm->o2h.mo, mo_state);
-
- return 0;
-}
-
-/* Display fault report bits (helper function of display_fault_maps()) */
-static bool display_fault_bits(const uint8_t *vect, uint16_t len,
- uint8_t dei, const struct abis_om2k_mo *mo)
-{
- uint16_t i;
- int k;
- bool faults_present = false;
- int first = 1;
- char string[255];
-
- /* Check if errors are present at all */
- for (i = 0; i < len; i++)
- if (vect[i])
- faults_present = true;
- if (!faults_present)
- return false;
-
- sprintf(string, "Fault Report: %s (",
- get_value_string(om2k_attr_vals, dei));
-
- for (i = 0; i < len; i++) {
- for (k = 0; k < 8; k++) {
- if ((vect[i] >> k) & 1) {
- if (!first)
- sprintf(string + strlen(string), ",");
- sprintf(string + strlen(string), "%d", k + i*8);
- first = 0;
- }
- }
- }
-
- sprintf(string + strlen(string), ")\n");
- DEBUGP(DNM, "Rx MO=%s %s", om2k_mo_name(mo), string);
-
- return true;
-}
-
-/* Display fault report maps */
-static void display_fault_maps(const uint8_t *src, unsigned int src_len,
- const struct abis_om2k_mo *mo)
-{
- uint8_t tag;
- uint16_t tag_len;
- const uint8_t *val;
- int src_pos = 0;
- int rc;
- int tlv_count = 0;
- uint16_t msg_code;
- bool faults_present = false;
-
- /* Chop off header */
- src+=4;
- src_len-=4;
-
- /* Check message type */
- msg_code = (*src & 0xff) << 8;
- src++;
- src_len--;
- msg_code |= (*src & 0xff);
- src++;
- src_len--;
- if (msg_code != OM2K_MSGT_FAULT_REP) {
- LOGP(DNM, LOGL_ERROR, "Rx MO=%s Fault report: invalid message code!\n",
- om2k_mo_name(mo));
- return;
- }
-
- /* Chop off mo-interface */
- src += 4;
- src_len -= 4;
-
- /* Iterate over each TLV element */
- while (1) {
-
- /* Bail if an the maximum number of TLV fields
- * have been parsed */
- if (tlv_count >= 11) {
- LOGP(DNM, LOGL_ERROR,
- "Rx MO=%s Fault Report: too many tlv elements!\n",
- om2k_mo_name(mo));
- return;
- }
-
- /* Parse TLV field */
- rc = tlv_parse_one(&tag, &tag_len, &val, &om2k_att_tlvdef,
- src + src_pos, src_len - src_pos);
- if (rc > 0)
- src_pos += rc;
- else {
- LOGP(DNM, LOGL_ERROR,
- "Rx MO=%s Fault Report: invalid tlv element!\n",
- om2k_mo_name(mo));
- return;
- }
-
- switch (tag) {
- case OM2K_DEI_INT_FAULT_MAP_1A:
- case OM2K_DEI_INT_FAULT_MAP_1B:
- case OM2K_DEI_INT_FAULT_MAP_2A:
- case OM2K_DEI_EXT_COND_MAP_1:
- case OM2K_DEI_EXT_COND_MAP_2:
- case OM2K_DEI_REPL_UNIT_MAP:
- case OM2K_DEI_INT_FAULT_MAP_2A_EXT:
- case OM2K_DEI_EXT_COND_MAP_2_EXT:
- case OM2K_DEI_REPL_UNIT_MAP_EXT:
- faults_present |= display_fault_bits(val, tag_len,
- tag, mo);
- break;
- }
-
- /* Stop when no further TLV elements can be expected */
- if (src_len - src_pos < 2)
- break;
-
- tlv_count++;
- }
-
- if (!faults_present) {
- DEBUGP(DNM, "Rx MO=%s Fault Report: All faults ceased!\n",
- om2k_mo_name(mo));
- }
-}
-
-int abis_om2k_rcvmsg(struct msgb *msg)
-{
- struct e1inp_sign_link *sign_link = (struct e1inp_sign_link *)msg->dst;
- struct gsm_bts *bts = sign_link->trx->bts;
- struct abis_om2k_hdr *o2h = msgb_l2(msg);
- struct abis_om_hdr *oh = &o2h->om;
- uint16_t msg_type = ntohs(o2h->msg_type);
- struct om2k_decoded_msg odm;
- struct om2k_mo *mo;
- int rc = 0;
-
- /* Various consistency checks */
- if (oh->placement != ABIS_OM_PLACEMENT_ONLY) {
- LOGP(DNM, LOGL_ERROR, "ABIS OML placement 0x%x not supported\n",
- oh->placement);
- if (oh->placement != ABIS_OM_PLACEMENT_FIRST)
- return -EINVAL;
- }
- if (oh->sequence != 0) {
- LOGP(DNM, LOGL_ERROR, "ABIS OML sequence 0x%x != 0x00\n",
- oh->sequence);
- return -EINVAL;
- }
-
- msg->l3h = (unsigned char *)o2h + sizeof(*o2h);
-
- if (oh->mdisc != ABIS_OM_MDISC_FOM) {
- LOGP(DNM, LOGL_ERROR, "unknown ABIS OM2000 message discriminator 0x%x\n",
- oh->mdisc);
- return -EINVAL;
- }
-
- DEBUGP(DNM, "Rx MO=%s %s (%s)\n", om2k_mo_name(&o2h->mo),
- get_value_string(om2k_msgcode_vals, msg_type),
- osmo_hexdump(msg->l2h, msgb_l2len(msg)));
-
- om2k_decode_msg(&odm, msg);
-
- process_mo_state(bts, &odm);
-
- switch (msg_type) {
- case OM2K_MSGT_CAL_TIME_REQ:
- rc = abis_om2k_cal_time_resp(bts);
- break;
- case OM2K_MSGT_FAULT_REP:
- display_fault_maps(msg->l2h, msgb_l2len(msg), &o2h->mo);
- rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_FAULT_REP_ACK);
- break;
- case OM2K_MSGT_NEGOT_REQ:
- rc = om2k_rx_negot_req(msg);
- break;
- case OM2K_MSGT_START_RES:
- /* common processing here */
- rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_START_RES_ACK);
- /* below we dispatch into MO */
- break;
- case OM2K_MSGT_IS_CONF_RES:
- rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_IS_CONF_RES_ACK);
- break;
- case OM2K_MSGT_CON_CONF_RES:
- rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_CON_CONF_RES_ACK);
- break;
- case OM2K_MSGT_TX_CONF_RES:
- rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_TX_CONF_RES_ACK);
- break;
- case OM2K_MSGT_RX_CONF_RES:
- rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_RX_CONF_RES_ACK);
- break;
- case OM2K_MSGT_TS_CONF_RES:
- rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_TS_CONF_RES_ACK);
- break;
- case OM2K_MSGT_TF_CONF_RES:
- rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_TF_CONF_RES_ACK);
- break;
- case OM2K_MSGT_ENABLE_RES:
- rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_ENABLE_RES_ACK);
- break;
- case OM2K_MSGT_DISABLE_RES:
- rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_DISABLE_RES_ACK);
- break;
- case OM2K_MSGT_TEST_RES:
- rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_TEST_RES_ACK);
- break;
- case OM2K_MSGT_CAPA_RES:
- rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_CAPA_RES_ACK);
- break;
- /* ERrors */
- case OM2K_MSGT_START_REQ_REJ:
- case OM2K_MSGT_CONNECT_REJ:
- case OM2K_MSGT_OP_INFO_REJ:
- case OM2K_MSGT_DISCONNECT_REJ:
- case OM2K_MSGT_TEST_REQ_REJ:
- case OM2K_MSGT_CON_CONF_REQ_REJ:
- case OM2K_MSGT_IS_CONF_REQ_REJ:
- case OM2K_MSGT_TX_CONF_REQ_REJ:
- case OM2K_MSGT_RX_CONF_REQ_REJ:
- case OM2K_MSGT_TS_CONF_REQ_REJ:
- case OM2K_MSGT_TF_CONF_REQ_REJ:
- case OM2K_MSGT_ENABLE_REQ_REJ:
- case OM2K_MSGT_ALARM_STATUS_REQ_REJ:
- case OM2K_MSGT_DISABLE_REQ_REJ:
- rc = om2k_rx_nack(msg);
- break;
- }
-
- /* Resolve the MO for this message */
- mo = get_om2k_mo(bts, &o2h->mo);
- if (!mo) {
- LOGP(DNM, LOGL_ERROR, "Couldn't resolve MO for OM2K msg "
- "%s: %s\n", get_value_string(om2k_msgcode_vals, msg_type),
- msgb_hexdump(msg));
- return 0;
- }
- if (!mo->fsm) {
- LOGP(DNM, LOGL_ERROR, "MO object should not generate any message. fsm == NULL "
- "%s: %s\n", get_value_string(om2k_msgcode_vals, msg_type),
- msgb_hexdump(msg));
- return 0;
- }
-
- /* Dispatch message to that MO */
- om2k_mo_fsm_recvmsg(bts, mo, &odm);
-
- msgb_free(msg);
- return rc;
-}
-
-static void om2k_mo_init(struct om2k_mo *mo, uint8_t class,
- uint8_t bts_nr, uint8_t assoc_so, uint8_t inst)
-{
- mo->addr.class = class;
- mo->addr.bts = bts_nr;
- mo->addr.assoc_so = assoc_so;
- mo->addr.inst = inst;
-}
-
-/* initialize the OM2K_MO members of gsm_bts_trx and its timeslots */
-void abis_om2k_trx_init(struct gsm_bts_trx *trx)
-{
- struct gsm_bts *bts = trx->bts;
- unsigned int i;
-
- OSMO_ASSERT(bts->type == GSM_BTS_TYPE_RBS2000);
-
- om2k_mo_init(&trx->rbs2000.trxc.om2k_mo, OM2K_MO_CLS_TRXC,
- bts->nr, 255, trx->nr);
- om2k_mo_init(&trx->rbs2000.tx.om2k_mo, OM2K_MO_CLS_TX,
- bts->nr, 255, trx->nr);
- om2k_mo_init(&trx->rbs2000.rx.om2k_mo, OM2K_MO_CLS_RX,
- bts->nr, 255, trx->nr);
-
- for (i = 0; i < ARRAY_SIZE(trx->ts); i++) {
- om2k_mo_init(&trx->ts[i].rbs2000.om2k_mo, OM2K_MO_CLS_TS,
- bts->nr, trx->nr, i);
- }
-}
-
-/* initialize the OM2K_MO members of gsm_bts */
-void abis_om2k_bts_init(struct gsm_bts *bts)
-{
- OSMO_ASSERT(bts->type == GSM_BTS_TYPE_RBS2000);
-
- om2k_mo_init(&bts->rbs2000.cf.om2k_mo, OM2K_MO_CLS_CF,
- bts->nr, 0xFF, 0);
- om2k_mo_init(&bts->rbs2000.is.om2k_mo, OM2K_MO_CLS_IS,
- bts->nr, 0xFF, 0);
- om2k_mo_init(&bts->rbs2000.con.om2k_mo, OM2K_MO_CLS_CON,
- bts->nr, 0xFF, 0);
- om2k_mo_init(&bts->rbs2000.dp.om2k_mo, OM2K_MO_CLS_DP,
- bts->nr, 0xFF, 0);
- om2k_mo_init(&bts->rbs2000.tf.om2k_mo, OM2K_MO_CLS_TF,
- bts->nr, 0xFF, 0);
-}
-
-static __attribute__((constructor)) void abis_om2k_init(void)
-{
- osmo_fsm_register(&om2k_mo_fsm);
- osmo_fsm_register(&om2k_bts_fsm);
- osmo_fsm_register(&om2k_trx_fsm);
-}
diff --git a/openbsc/src/libbsc/abis_om2000_vty.c b/openbsc/src/libbsc/abis_om2000_vty.c
deleted file mode 100644
index a6bc4c78c..000000000
--- a/openbsc/src/libbsc/abis_om2000_vty.c
+++ /dev/null
@@ -1,609 +0,0 @@
-/* VTY interface for A-bis OM2000 */
-
-/* (C) 2010-2011 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 <openbsc/debug.h>
-#include <openbsc/signal.h>
-#include <openbsc/abis_om2000.h>
-#include <openbsc/vty.h>
-
-#include <osmocom/vty/vty.h>
-#include <osmocom/vty/command.h>
-#include <osmocom/vty/logging.h>
-#include <osmocom/vty/telnet_interface.h>
-
-extern struct gsm_network *bsc_gsmnet;
-
-static struct cmd_node om2k_node = {
- OM2K_NODE,
- "%s(om2k)# ",
- 1,
-};
-
-static struct cmd_node om2k_con_group_node = {
- OM2K_CON_GROUP_NODE,
- "%s(om2k-con-group)# ",
- 1,
-};
-
-struct con_group;
-
-struct oml_node_state {
- struct gsm_bts *bts;
- struct abis_om2k_mo mo;
- struct con_group *cg;
-};
-
-static int dummy_config_write(struct vty *v)
-{
- return CMD_SUCCESS;
-}
-
-/* FIXME: auto-generate those strings from the value_string lists */
-#define OM2K_OBJCLASS_VTY "(trxc|ts|tf|is|con|dp|cf|tx|rx)"
-#define OM2K_OBJCLASS_VTY_HELP "TRX Controller\n" \
- "Timeslot\n" \
- "Timing Function\n" \
- "Interface Switch\n" \
- "Abis Concentrator\n" \
- "Digital Path\n" \
- "Central Function\n" \
- "Transmitter\n" \
- "Receiver\n"
-
-DEFUN(om2k_class_inst, om2k_class_inst_cmd,
- "bts <0-255> om2000 class " OM2K_OBJCLASS_VTY
- " <0-255> <0-255> <0-255>",
- "BTS related commands\n" "BTS Number\n"
- "Manipulate the OM2000 managed objects\n"
- "Object Class\n" OM2K_OBJCLASS_VTY_HELP
- "BTS Number\n" "Associated SO Instance\n" "Instance Number\n")
-{
- struct gsm_bts *bts;
- struct oml_node_state *oms;
- int bts_nr = atoi(argv[0]);
-
- bts = gsm_bts_num(gsmnet_from_vty(vty), bts_nr);
- if (!bts) {
- vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- if (bts->type != GSM_BTS_TYPE_RBS2000) {
- vty_out(vty, "%% BTS %d not an Ericsson RBS%s",
- bts_nr, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- oms = talloc_zero(tall_bsc_ctx, struct oml_node_state);
- if (!oms)
- return CMD_WARNING;
-
- oms->bts = bts;
- oms->mo.class = get_string_value(om2k_mo_class_short_vals, argv[1]);
- oms->mo.bts = atoi(argv[2]);
- oms->mo.assoc_so = atoi(argv[3]);
- oms->mo.inst = atoi(argv[4]);
-
- vty->index = oms;
- vty->node = OM2K_NODE;
-
- return CMD_SUCCESS;
-
-}
-
-DEFUN(om2k_classnum_inst, om2k_classnum_inst_cmd,
- "bts <0-255> om2000 class <0-255> <0-255> <0-255> <0-255>",
- "BTS related commands\n" "BTS Number\n"
- "Manipulate the OML managed objects\n"
- "Object Class\n" "Object Class\n"
- "BTS Number\n" "Associated SO Instance\n" "Instance Number\n")
-{
- struct gsm_bts *bts;
- struct oml_node_state *oms;
- int bts_nr = atoi(argv[0]);
-
- bts = gsm_bts_num(gsmnet_from_vty(vty), bts_nr);
- if (!bts) {
- vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- oms = talloc_zero(tall_bsc_ctx, struct oml_node_state);
- if (!oms)
- return CMD_WARNING;
-
- oms->bts = bts;
- oms->mo.class = atoi(argv[1]);
- oms->mo.bts = atoi(argv[2]);
- oms->mo.assoc_so = atoi(argv[3]);
- oms->mo.inst = atoi(argv[4]);
-
- vty->index = oms;
- vty->node = OM2K_NODE;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(om2k_reset, om2k_reset_cmd,
- "reset-command",
- "Reset the MO\n")
-{
- struct oml_node_state *oms = vty->index;
-
- abis_om2k_tx_reset_cmd(oms->bts, &oms->mo);
- return CMD_SUCCESS;
-}
-
-DEFUN(om2k_start, om2k_start_cmd,
- "start-request",
- "Start the MO\n")
-{
- struct oml_node_state *oms = vty->index;
-
- abis_om2k_tx_start_req(oms->bts, &oms->mo);
- return CMD_SUCCESS;
-}
-
-DEFUN(om2k_status, om2k_status_cmd,
- "status-request",
- "Get the MO Status\n")
-{
- struct oml_node_state *oms = vty->index;
-
- abis_om2k_tx_status_req(oms->bts, &oms->mo);
- return CMD_SUCCESS;
-}
-
-DEFUN(om2k_connect, om2k_connect_cmd,
- "connect-command",
- "Connect the MO\n")
-{
- struct oml_node_state *oms = vty->index;
-
- abis_om2k_tx_connect_cmd(oms->bts, &oms->mo);
- return CMD_SUCCESS;
-}
-
-DEFUN(om2k_disconnect, om2k_disconnect_cmd,
- "disconnect-command",
- "Disconnect the MO\n")
-{
- struct oml_node_state *oms = vty->index;
-
- abis_om2k_tx_disconnect_cmd(oms->bts, &oms->mo);
- return CMD_SUCCESS;
-}
-
-DEFUN(om2k_enable, om2k_enable_cmd,
- "enable-request",
- "Enable the MO\n")
-{
- struct oml_node_state *oms = vty->index;
-
- abis_om2k_tx_enable_req(oms->bts, &oms->mo);
- return CMD_SUCCESS;
-}
-
-DEFUN(om2k_disable, om2k_disable_cmd,
- "disable-request",
- "Disable the MO\n")
-{
- struct oml_node_state *oms = vty->index;
-
- abis_om2k_tx_disable_req(oms->bts, &oms->mo);
- return CMD_SUCCESS;
-}
-
-DEFUN(om2k_op_info, om2k_op_info_cmd,
- "operational-info <0-1>",
- "Set operational information\n"
- "Set operational info to 0 or 1\n")
-{
- struct oml_node_state *oms = vty->index;
- int oper = atoi(argv[0]);
-
- abis_om2k_tx_op_info(oms->bts, &oms->mo, oper);
- return CMD_SUCCESS;
-}
-
-DEFUN(om2k_test, om2k_test_cmd,
- "test-request",
- "Test the MO\n")
-{
- struct oml_node_state *oms = vty->index;
-
- abis_om2k_tx_test_req(oms->bts, &oms->mo);
- return CMD_SUCCESS;
-}
-
-DEFUN(om2k_cap_req, om2k_cap_req_cmd,
- "capabilities-request",
- "Request MO capabilities\n")
-{
- struct oml_node_state *oms = vty->index;
-
- abis_om2k_tx_cap_req(oms->bts, &oms->mo);
- return CMD_SUCCESS;
-}
-
-static struct con_group *con_group_find_or_create(struct gsm_bts *bts, uint8_t cg)
-{
- struct con_group *ent;
-
- llist_for_each_entry(ent, &bts->rbs2000.con.conn_groups, list) {
- if (ent->cg == cg)
- return ent;
- }
-
- ent = talloc_zero(bts, struct con_group);
- ent->bts = bts;
- ent->cg = cg;
- INIT_LLIST_HEAD(&ent->paths);
- llist_add_tail(&ent->list, &bts->rbs2000.con.conn_groups);
-
- return ent;
-}
-
-static int con_group_del(struct gsm_bts *bts, uint8_t cg_id)
-{
- struct con_group *cg, *cg2;
-
- llist_for_each_entry_safe(cg, cg2, &bts->rbs2000.con.conn_groups, list) {
- if (cg->cg == cg_id) {
- llist_del(&cg->list);
- talloc_free(cg);
- return 0;
- };
- }
- return -ENOENT;
-}
-
-static void con_group_add_path(struct con_group *cg, uint16_t ccp,
- uint8_t ci, uint8_t tag, uint8_t tei)
-{
- struct con_path *cp = talloc_zero(cg, struct con_path);
-
- cp->ccp = ccp;
- cp->ci = ci;
- cp->tag = tag;
- cp->tei = tei;
- llist_add(&cp->list, &cg->paths);
-}
-
-static int con_group_del_path(struct con_group *cg, uint16_t ccp,
- uint8_t ci, uint8_t tag, uint8_t tei)
-{
- struct con_path *cp, *cp2;
- llist_for_each_entry_safe(cp, cp2, &cg->paths, list) {
- if (cp->ccp == ccp && cp->ci == ci && cp->tag == tag &&
- cp->tei == tei) {
- llist_del(&cp->list);
- talloc_free(cp);
- return 0;
- }
- }
- return -ENOENT;
-}
-
-DEFUN(cfg_om2k_con_group, cfg_om2k_con_group_cmd,
- "con-connection-group <1-31>",
- "Configure a CON (Concentrator) Connection Group\n"
- "CON Connection Group Number\n")
-{
- struct gsm_bts *bts = vty->index;
- struct con_group *cg;
- uint8_t cgid = atoi(argv[0]);
-
- if (bts->type != GSM_BTS_TYPE_RBS2000) {
- vty_out(vty, "%% CON MO only exists in RBS2000%s",
- VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- cg = con_group_find_or_create(bts, cgid);
- if (!cg) {
- vty_out(vty, "%% Cannot create CON Group%s",
- VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- vty->node = OM2K_CON_GROUP_NODE;
- vty->index = cg;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(del_om2k_con_group, del_om2k_con_group_cmd,
- "del-connection-group <1-31>",
- "Delete a CON (Concentrator) Connection Group\n"
- "CON Connection Group Number\n")
-{
- struct gsm_bts *bts = vty->index;
- int rc;
- uint8_t cgid = atoi(argv[0]);
-
- if (bts->type != GSM_BTS_TYPE_RBS2000) {
- vty_out(vty, "%% CON MO only exists in RBS2000%s",
- VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- rc = con_group_del(bts, cgid);
- if (rc != 0) {
- vty_out(vty, "%% Cannot delete CON Group%s",
- VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- return CMD_SUCCESS;
-}
-
-#define CON_PATH_HELP "CON Path (In/Out)\n" \
- "Add CON Path to Concentration Group\n" \
- "Delete CON Path from Concentration Group\n" \
- "CON Conection Point\n" \
- "Contiguity Index\n" \
-
-DEFUN(cfg_om2k_con_path_dec, cfg_om2k_con_path_dec_cmd,
- "con-path (add|del) <0-2047> <0-255> deconcentrated <0-63>",
- CON_PATH_HELP "De-concentrated in/outlet\n" "TEI Value\n")
-{
- struct con_group *cg = vty->index;
- uint16_t ccp = atoi(argv[1]);
- uint8_t ci = atoi(argv[2]);
- uint8_t tei = atoi(argv[3]);
-
- if (!strcmp(argv[0], "add"))
- con_group_add_path(cg, ccp, ci, 0, tei);
- else {
- if (con_group_del_path(cg, ccp, ci, 0, tei) < 0) {
- vty_out(vty, "%% No matching CON Path%s",
- VTY_NEWLINE);
- return CMD_WARNING;
- }
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_om2k_con_path_conc, cfg_om2k_con_path_conc_cmd,
- "con-path (add|del) <0-2047> <0-255> concentrated <1-16>",
- CON_PATH_HELP "Concentrated in/outlet\n" "Tag Number\n")
-{
- struct con_group *cg = vty->index;
- uint16_t ccp = atoi(argv[1]);
- uint8_t ci = atoi(argv[2]);
- uint8_t tag = atoi(argv[3]);
-
- if (!strcmp(argv[0], "add"))
- con_group_add_path(cg, ccp, ci, tag, 0xff);
- else {
- if (con_group_del_path(cg, ccp, ci, tag, 0xff) < 0) {
- vty_out(vty, "%% No matching CON list entry%s",
- VTY_NEWLINE);
- return CMD_WARNING;
- }
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_alt_mode, cfg_bts_alt_mode_cmd,
- "abis-lower-transport (single-timeslot|super-channel)",
- "Configure thee Abis Lower Transport\n"
- "Single Timeslot (classic Abis)\n"
- "SuperChannel (Packet Abis)\n")
-{
- struct gsm_bts *bts = vty->index;
- struct con_group *cg;
-
- if (bts->type != GSM_BTS_TYPE_RBS2000) {
- vty_out(vty, "%% Command only works for RBS2000%s",
- VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- if (!strcmp(argv[0], "super-channel"))
- bts->rbs2000.use_superchannel = 1;
- else
- bts->rbs2000.use_superchannel = 0;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_is_conn_list, cfg_bts_is_conn_list_cmd,
- "is-connection-list (add|del) <0-2047> <0-2047> <0-255>",
- "Interface Switch Connection List\n"
- "Add to IS list\n" "Delete from IS list\n"
- "ICP1\n" "ICP2\n" "Contiguity Index\n")
-{
- struct gsm_bts *bts = vty->index;
- uint16_t icp1 = atoi(argv[1]);
- uint16_t icp2 = atoi(argv[2]);
- uint8_t ci = atoi(argv[3]);
- struct is_conn_group *grp, *grp2;
-
- if (bts->type != GSM_BTS_TYPE_RBS2000) {
- vty_out(vty, "%% IS MO only exists in RBS2000%s",
- VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- if (!strcmp(argv[0], "add")) {
- grp = talloc_zero(bts, struct is_conn_group);
- grp->icp1 = icp1;
- grp->icp2 = icp2;
- grp->ci = ci;
- llist_add_tail(&grp->list, &bts->rbs2000.is.conn_groups);
- } else {
- llist_for_each_entry_safe(grp, grp2, &bts->rbs2000.is.conn_groups, list) {
- if (grp->icp1 == icp1 && grp->icp2 == icp2
- && grp->ci == ci) {
- llist_del(&grp->list);
- talloc_free(grp);
- return CMD_SUCCESS;
- }
- }
- vty_out(vty, "%% No matching IS Conn Group found!%s",
- VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- return CMD_SUCCESS;
-}
-
-
-DEFUN(om2k_conf_req, om2k_conf_req_cmd,
- "configuration-request",
- "Send the configuration request for current MO\n")
-{
- struct oml_node_state *oms = vty->index;
- struct gsm_bts *bts = oms->bts;
- struct gsm_bts_trx *trx = NULL;
- struct gsm_bts_trx_ts *ts = NULL;
-
- switch (oms->mo.class) {
- case OM2K_MO_CLS_IS:
- abis_om2k_tx_is_conf_req(bts);
- break;
- case OM2K_MO_CLS_TS:
- trx = gsm_bts_trx_by_nr(bts, oms->mo.assoc_so);
- if (!trx) {
- vty_out(vty, "%% BTS %u has no TRX %u%s", bts->nr,
- oms->mo.assoc_so, VTY_NEWLINE);
- return CMD_WARNING;
- }
- if (oms->mo.inst >= ARRAY_SIZE(trx->ts)) {
- vty_out(vty, "%% Timeslot %u out of range%s",
- oms->mo.inst, VTY_NEWLINE);
- return CMD_WARNING;
- }
- ts = &trx->ts[oms->mo.inst];
- abis_om2k_tx_ts_conf_req(ts);
- break;
- case OM2K_MO_CLS_RX:
- case OM2K_MO_CLS_TX:
- case OM2K_MO_CLS_TRXC:
- trx = gsm_bts_trx_by_nr(bts, oms->mo.inst);
- if (!trx) {
- vty_out(vty, "%% BTS %u has no TRX %u%s", bts->nr,
- oms->mo.inst, VTY_NEWLINE);
- return CMD_WARNING;
- }
- switch (oms->mo.class) {
- case OM2K_MO_CLS_RX:
- abis_om2k_tx_rx_conf_req(trx);
- break;
- case OM2K_MO_CLS_TX:
- abis_om2k_tx_tx_conf_req(trx);
- break;
- default:
- break;
- }
- break;
- case OM2K_MO_CLS_TF:
- abis_om2k_tx_tf_conf_req(bts);
- break;
- default:
- vty_out(vty, "%% Don't know how to configure MO%s",
- VTY_NEWLINE);
- }
-
- return CMD_SUCCESS;
-}
-
-static void dump_con_group(struct vty *vty, struct con_group *cg)
-{
- struct con_path *cp;
-
- llist_for_each_entry(cp, &cg->paths, list) {
- vty_out(vty, " con-path add %u %u ", cp->ccp, cp->ci);
- if (cp->tei == 0xff) {
- vty_out(vty, "concentrated %u%s", cp->tag,
- VTY_NEWLINE);
- } else {
- vty_out(vty, "deconcentrated %u%s", cp->tei,
- VTY_NEWLINE);
- }
- }
-}
-
-void abis_om2k_config_write_bts(struct vty *vty, struct gsm_bts *bts)
-{
- struct is_conn_group *igrp;
- struct con_group *cgrp;
-
- llist_for_each_entry(igrp, &bts->rbs2000.is.conn_groups, list)
- vty_out(vty, " is-connection-list add %u %u %u%s",
- igrp->icp1, igrp->icp2, igrp->ci, VTY_NEWLINE);
-
- llist_for_each_entry(cgrp, &bts->rbs2000.con.conn_groups, list) {
- vty_out(vty, " con-connection-group %u%s", cgrp->cg,
- VTY_NEWLINE);
- dump_con_group(vty, cgrp);
- }
- if (bts->rbs2000.use_superchannel)
- vty_out(vty, " abis-lower-transport super-channel%s",
- VTY_NEWLINE);
-}
-
-int abis_om2k_vty_init(void)
-{
- install_element(ENABLE_NODE, &om2k_class_inst_cmd);
- install_element(ENABLE_NODE, &om2k_classnum_inst_cmd);
- install_node(&om2k_node, dummy_config_write);
-
- vty_install_default(OM2K_NODE);
- install_element(OM2K_NODE, &om2k_reset_cmd);
- install_element(OM2K_NODE, &om2k_start_cmd);
- install_element(OM2K_NODE, &om2k_status_cmd);
- install_element(OM2K_NODE, &om2k_connect_cmd);
- install_element(OM2K_NODE, &om2k_disconnect_cmd);
- install_element(OM2K_NODE, &om2k_enable_cmd);
- install_element(OM2K_NODE, &om2k_disable_cmd);
- install_element(OM2K_NODE, &om2k_op_info_cmd);
- install_element(OM2K_NODE, &om2k_test_cmd);
- install_element(OM2K_NODE, &om2k_cap_req_cmd);
- install_element(OM2K_NODE, &om2k_conf_req_cmd);
-
- install_node(&om2k_con_group_node, dummy_config_write);
- vty_install_default(OM2K_CON_GROUP_NODE);
- install_element(OM2K_CON_GROUP_NODE, &cfg_om2k_con_path_dec_cmd);
- install_element(OM2K_CON_GROUP_NODE, &cfg_om2k_con_path_conc_cmd);
-
- install_element(BTS_NODE, &cfg_bts_is_conn_list_cmd);
- install_element(BTS_NODE, &cfg_bts_alt_mode_cmd);
- install_element(BTS_NODE, &cfg_om2k_con_group_cmd);
- install_element(BTS_NODE, &del_om2k_con_group_cmd);
-
- return 0;
-}
diff --git a/openbsc/src/libbsc/abis_rsl.c b/openbsc/src/libbsc/abis_rsl.c
deleted file mode 100644
index 6ae790f6c..000000000
--- a/openbsc/src/libbsc/abis_rsl.c
+++ /dev/null
@@ -1,2927 +0,0 @@
-/* GSM Radio Signalling Link messages on the A-bis interface
- * 3GPP TS 08.58 version 8.6.0 Release 1999 / ETSI TS 100 596 V8.6.0 */
-
-/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org>
- * (C) 2012 by Holger Hans Peter Freyther
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-
-#include <openbsc/gsm_data.h>
-#include <openbsc/gsm_04_08.h>
-#include <osmocom/gsm/gsm_utils.h>
-#include <openbsc/abis_rsl.h>
-#include <openbsc/chan_alloc.h>
-#include <openbsc/bsc_rll.h>
-#include <openbsc/debug.h>
-#include <osmocom/gsm/tlv.h>
-#include <osmocom/gsm/protocol/gsm_04_08.h>
-#include <osmocom/gsm/protocol/gsm_08_58.h>
-#include <openbsc/paging.h>
-#include <openbsc/signal.h>
-#include <openbsc/meas_rep.h>
-#include <openbsc/rtp_proxy.h>
-#include <openbsc/gsm_subscriber.h>
-#include <osmocom/abis/e1_input.h>
-#include <osmocom/gsm/rsl.h>
-#include <osmocom/core/talloc.h>
-#include <openbsc/pcu_if.h>
-
-#define RSL_ALLOC_SIZE 1024
-#define RSL_ALLOC_HEADROOM 128
-
-enum sacch_deact {
- SACCH_NONE,
- SACCH_DEACTIVATE,
-};
-
-static int rsl_send_imm_assignment(struct gsm_lchan *lchan);
-static void error_timeout_cb(void *data);
-static int dyn_ts_switchover_continue(struct gsm_bts_trx_ts *ts);
-static int dyn_ts_switchover_failed(struct gsm_bts_trx_ts *ts, int rc);
-static void dyn_ts_switchover_complete(struct gsm_lchan *lchan);
-
-static void send_lchan_signal(int sig_no, struct gsm_lchan *lchan,
- struct gsm_meas_rep *resp)
-{
- struct lchan_signal_data sig;
- sig.lchan = lchan;
- sig.mr = resp;
- osmo_signal_dispatch(SS_LCHAN, sig_no, &sig);
-}
-
-static void do_lchan_free(struct gsm_lchan *lchan)
-{
- /* We start the error timer to make the channel available again */
- if (lchan->state == LCHAN_S_REL_ERR) {
- osmo_timer_setup(&lchan->error_timer, error_timeout_cb, lchan);
- osmo_timer_schedule(&lchan->error_timer,
- lchan->ts->trx->bts->network->T3111 + 2, 0);
- } else {
- rsl_lchan_set_state(lchan, LCHAN_S_NONE);
- }
- lchan_free(lchan);
-}
-
-static void count_codecs(struct gsm_bts *bts, struct gsm_lchan *lchan)
-{
- OSMO_ASSERT(bts);
-
- if (lchan->type == GSM_LCHAN_TCH_H) {
- switch (lchan->tch_mode) {
- case GSM48_CMODE_SPEECH_AMR:
- rate_ctr_inc(&bts->network->bsc_ctrs->ctr[BSC_CTR_CODEC_AMR_H]);
- break;
- case GSM48_CMODE_SPEECH_V1:
- rate_ctr_inc(&bts->network->bsc_ctrs->ctr[BSC_CTR_CODEC_V1_HR]);
- break;
- default:
- break;
- }
- } else if (lchan->type == GSM_LCHAN_TCH_F) {
- switch (lchan->tch_mode) {
- case GSM48_CMODE_SPEECH_AMR:
- rate_ctr_inc(&bts->network->bsc_ctrs->ctr[BSC_CTR_CODEC_AMR_F]);
- break;
- case GSM48_CMODE_SPEECH_V1:
- rate_ctr_inc(&bts->network->bsc_ctrs->ctr[BSC_CTR_CODEC_V1_FR]);
- break;
- case GSM48_CMODE_SPEECH_EFR:
- rate_ctr_inc(&bts->network->bsc_ctrs->ctr[BSC_CTR_CODEC_EFR]);
- break;
- default:
- break;
- }
- }
-}
-
-static uint8_t mdisc_by_msgtype(uint8_t msg_type)
-{
- /* mask off the transparent bit ? */
- msg_type &= 0xfe;
-
- if ((msg_type & 0xf0) == 0x00)
- return ABIS_RSL_MDISC_RLL;
- if ((msg_type & 0xf0) == 0x10) {
- if (msg_type >= 0x19 && msg_type <= 0x22)
- return ABIS_RSL_MDISC_TRX;
- else
- return ABIS_RSL_MDISC_COM_CHAN;
- }
- if ((msg_type & 0xe0) == 0x20)
- return ABIS_RSL_MDISC_DED_CHAN;
-
- return ABIS_RSL_MDISC_LOC;
-}
-
-static inline void init_dchan_hdr(struct abis_rsl_dchan_hdr *dh,
- uint8_t msg_type)
-{
- dh->c.msg_discr = mdisc_by_msgtype(msg_type);
- dh->c.msg_type = msg_type;
- dh->ie_chan = RSL_IE_CHAN_NR;
-}
-
-/* call rsl_lchan_lookup and set the log context */
-static struct gsm_lchan *lchan_lookup(struct gsm_bts_trx *trx, uint8_t chan_nr,
- const char *log_name)
-{
- int rc;
- struct gsm_lchan *lchan = rsl_lchan_lookup(trx, chan_nr, &rc);
-
- if (!lchan) {
- LOGP(DRSL, LOGL_ERROR, "%sunknown chan_nr=0x%02x\n",
- log_name, chan_nr);
- return NULL;
- }
-
- if (rc < 0)
- LOGP(DRSL, LOGL_ERROR, "%s %smismatching chan_nr=0x%02x\n",
- gsm_ts_and_pchan_name(lchan->ts), log_name, chan_nr);
-
- if (lchan->conn)
- log_set_context(LOG_CTX_VLR_SUBSCR, lchan->conn->subscr);
-
- return lchan;
-}
-
-/* As per TS 03.03 Section 2.2, the IMSI has 'not more than 15 digits' */
-uint64_t str_to_imsi(const char *imsi_str)
-{
- uint64_t ret;
-
- ret = strtoull(imsi_str, NULL, 10);
-
- return ret;
-}
-
-static struct msgb *rsl_msgb_alloc(void)
-{
- return msgb_alloc_headroom(RSL_ALLOC_SIZE, RSL_ALLOC_HEADROOM,
- "RSL");
-}
-
-static void pad_macblock(uint8_t *out, const uint8_t *in, int len)
-{
- memcpy(out, in, len);
-
- if (len < GSM_MACBLOCK_LEN)
- memset(out+len, 0x2b, GSM_MACBLOCK_LEN - len);
-}
-
-/* Chapter 9.3.7: Encryption Information */
-static int build_encr_info(uint8_t *out, struct gsm_lchan *lchan)
-{
- *out++ = lchan->encr.alg_id & 0xff;
- if (lchan->encr.key_len)
- memcpy(out, lchan->encr.key, lchan->encr.key_len);
- return lchan->encr.key_len + 1;
-}
-
-static void print_rsl_cause(int lvl, const uint8_t *cause_v, uint8_t cause_len)
-{
- int i;
-
- LOGPC(DRSL, lvl, "CAUSE=0x%02x(%s) ",
- cause_v[0], rsl_err_name(cause_v[0]));
- for (i = 1; i < cause_len-1; i++)
- LOGPC(DRSL, lvl, "%02x ", cause_v[i]);
-}
-
-static void lchan_act_tmr_cb(void *data)
-{
- struct gsm_lchan *lchan = data;
-
- rsl_lchan_mark_broken(lchan, "activation timeout");
- lchan_free(lchan);
-}
-
-static void lchan_deact_tmr_cb(void *data)
-{
- struct gsm_lchan *lchan = data;
-
- rsl_lchan_mark_broken(lchan, "de-activation timeout");
- lchan_free(lchan);
-}
-
-
-/* Send a BCCH_INFO message as per Chapter 8.5.1 */
-int rsl_bcch_info(const struct gsm_bts_trx *trx, enum osmo_sysinfo_type si_type, const uint8_t *data, int len)
-{
- struct abis_rsl_dchan_hdr *dh;
- const struct gsm_bts *bts = trx->bts;
- struct msgb *msg = rsl_msgb_alloc();
- uint8_t type = osmo_sitype2rsl(si_type);
-
- if (bts->c0 != trx)
- LOGP(DRR, LOGL_ERROR, "Attempting to set BCCH SI%s on wrong BTS%u/TRX%u\n",
- get_value_string(osmo_sitype_strs, si_type), bts->nr, trx->nr);
-
- dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof*dh);
- init_dchan_hdr(dh, RSL_MT_BCCH_INFO);
- dh->chan_nr = RSL_CHAN_BCCH;
-
- if (trx->bts->type == GSM_BTS_TYPE_RBS2000
- && type == RSL_SYSTEM_INFO_13) {
- /* Ericsson proprietary encoding of SI13 */
- msgb_tv_put(msg, RSL_IE_SYSINFO_TYPE, RSL_ERIC_SYSTEM_INFO_13);
- msgb_tlv_put(msg, RSL_IE_FULL_BCCH_INFO, len, data);
- msgb_tv_put(msg, RSL_IE_ERIC_BCCH_MAPPING, 0x00);
- } else {
- /* Normal encoding */
- msgb_tv_put(msg, RSL_IE_SYSINFO_TYPE, type);
- msgb_tlv_put(msg, RSL_IE_FULL_BCCH_INFO, len, data);
- }
-
- msg->dst = trx->rsl_link;
-
- return abis_rsl_sendmsg(msg);
-}
-
-int rsl_sacch_filling(struct gsm_bts_trx *trx, uint8_t type,
- const uint8_t *data, int len)
-{
- struct abis_rsl_common_hdr *ch;
- struct msgb *msg = rsl_msgb_alloc();
-
- ch = (struct abis_rsl_common_hdr *) msgb_put(msg, sizeof(*ch));
- ch->msg_discr = ABIS_RSL_MDISC_TRX;
- ch->msg_type = RSL_MT_SACCH_FILL;
-
- msgb_tv_put(msg, RSL_IE_SYSINFO_TYPE, type);
- msgb_tl16v_put(msg, RSL_IE_L3_INFO, len, data);
-
- msg->dst = trx->rsl_link;
-
- return abis_rsl_sendmsg(msg);
-}
-
-int rsl_sacch_info_modify(struct gsm_lchan *lchan, uint8_t type,
- const uint8_t *data, int len)
-{
- struct abis_rsl_dchan_hdr *dh;
- struct msgb *msg = rsl_msgb_alloc();
- uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
-
- dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
- init_dchan_hdr(dh, RSL_MT_SACCH_INFO_MODIFY);
- dh->chan_nr = chan_nr;
-
- msgb_tv_put(msg, RSL_IE_SYSINFO_TYPE, type);
- msgb_tl16v_put(msg, RSL_IE_L3_INFO, len, data);
-
- msg->dst = lchan->ts->trx->rsl_link;
-
- return abis_rsl_sendmsg(msg);
-}
-
-int rsl_chan_bs_power_ctrl(struct gsm_lchan *lchan, unsigned int fpc, int db)
-{
- struct abis_rsl_dchan_hdr *dh;
- struct msgb *msg;
- uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
-
- db = abs(db);
- if (db > 30)
- return -EINVAL;
-
- msg = rsl_msgb_alloc();
-
- lchan->bs_power = db/2;
- if (fpc)
- lchan->bs_power |= 0x10;
-
- dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
- init_dchan_hdr(dh, RSL_MT_BS_POWER_CONTROL);
- dh->chan_nr = chan_nr;
-
- msgb_tv_put(msg, RSL_IE_BS_POWER, lchan->bs_power);
-
- msg->dst = lchan->ts->trx->rsl_link;
-
- return abis_rsl_sendmsg(msg);
-}
-
-int rsl_chan_ms_power_ctrl(struct gsm_lchan *lchan, unsigned int fpc, int dbm)
-{
- struct abis_rsl_dchan_hdr *dh;
- struct msgb *msg;
- uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
- int ctl_lvl;
-
- ctl_lvl = ms_pwr_ctl_lvl(lchan->ts->trx->bts->band, dbm);
- if (ctl_lvl < 0)
- return ctl_lvl;
-
- msg = rsl_msgb_alloc();
-
- lchan->ms_power = ctl_lvl;
-
- if (fpc)
- lchan->ms_power |= 0x20;
-
- dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
- init_dchan_hdr(dh, RSL_MT_MS_POWER_CONTROL);
- dh->chan_nr = chan_nr;
-
- msgb_tv_put(msg, RSL_IE_MS_POWER, lchan->ms_power);
-
- msg->dst = lchan->ts->trx->rsl_link;
-
- return abis_rsl_sendmsg(msg);
-}
-
-static int channel_mode_from_lchan(struct rsl_ie_chan_mode *cm,
- struct gsm_lchan *lchan)
-{
- memset(cm, 0, sizeof(*cm));
-
- /* FIXME: what to do with data calls ? */
- cm->dtx_dtu = 0;
- if (lchan->ts->trx->bts->dtxu != GSM48_DTX_SHALL_NOT_BE_USED)
- cm->dtx_dtu |= RSL_CMOD_DTXu;
- if (lchan->ts->trx->bts->dtxd)
- cm->dtx_dtu |= RSL_CMOD_DTXd;
-
- /* set TCH Speech/Data */
- cm->spd_ind = lchan->rsl_cmode;
-
- if (lchan->rsl_cmode == RSL_CMOD_SPD_SIGN &&
- lchan->tch_mode != GSM48_CMODE_SIGN)
- LOGP(DRSL, LOGL_ERROR, "unsupported: rsl_mode == signalling, "
- "but tch_mode != signalling\n");
-
- switch (lchan->type) {
- case GSM_LCHAN_SDCCH:
- cm->chan_rt = RSL_CMOD_CRT_SDCCH;
- break;
- case GSM_LCHAN_TCH_F:
- cm->chan_rt = RSL_CMOD_CRT_TCH_Bm;
- break;
- case GSM_LCHAN_TCH_H:
- cm->chan_rt = RSL_CMOD_CRT_TCH_Lm;
- break;
- case GSM_LCHAN_NONE:
- case GSM_LCHAN_UNKNOWN:
- default:
- LOGP(DRSL, LOGL_ERROR,
- "unsupported activation lchan->type %u %s\n",
- lchan->type, gsm_lchant_name(lchan->type));
- return -EINVAL;
- }
-
- switch (lchan->tch_mode) {
- case GSM48_CMODE_SIGN:
- cm->chan_rate = 0;
- break;
- case GSM48_CMODE_SPEECH_V1:
- cm->chan_rate = RSL_CMOD_SP_GSM1;
- break;
- case GSM48_CMODE_SPEECH_EFR:
- cm->chan_rate = RSL_CMOD_SP_GSM2;
- break;
- case GSM48_CMODE_SPEECH_AMR:
- cm->chan_rate = RSL_CMOD_SP_GSM3;
- break;
- case GSM48_CMODE_DATA_14k5:
- case GSM48_CMODE_DATA_12k0:
- case GSM48_CMODE_DATA_6k0:
- switch (lchan->csd_mode) {
- case LCHAN_CSD_M_NT:
- /* non-transparent CSD with RLP */
- switch (lchan->tch_mode) {
- case GSM48_CMODE_DATA_14k5:
- cm->chan_rate = RSL_CMOD_SP_NT_14k5;
- break;
- case GSM48_CMODE_DATA_12k0:
- cm->chan_rate = RSL_CMOD_SP_NT_12k0;
- break;
- case GSM48_CMODE_DATA_6k0:
- cm->chan_rate = RSL_CMOD_SP_NT_6k0;
- break;
- default:
- LOGP(DRSL, LOGL_ERROR,
- "unsupported lchan->tch_mode %u\n",
- lchan->tch_mode);
- return -EINVAL;
- }
- break;
- /* transparent data services below */
- case LCHAN_CSD_M_T_1200_75:
- cm->chan_rate = RSL_CMOD_CSD_T_1200_75;
- break;
- case LCHAN_CSD_M_T_600:
- cm->chan_rate = RSL_CMOD_CSD_T_600;
- break;
- case LCHAN_CSD_M_T_1200:
- cm->chan_rate = RSL_CMOD_CSD_T_1200;
- break;
- case LCHAN_CSD_M_T_2400:
- cm->chan_rate = RSL_CMOD_CSD_T_2400;
- break;
- case LCHAN_CSD_M_T_9600:
- cm->chan_rate = RSL_CMOD_CSD_T_9600;
- break;
- case LCHAN_CSD_M_T_14400:
- cm->chan_rate = RSL_CMOD_CSD_T_14400;
- break;
- case LCHAN_CSD_M_T_29000:
- cm->chan_rate = RSL_CMOD_CSD_T_29000;
- break;
- case LCHAN_CSD_M_T_32000:
- cm->chan_rate = RSL_CMOD_CSD_T_32000;
- break;
- default:
- LOGP(DRSL, LOGL_ERROR,
- "unsupported lchan->csd_mode %u\n",
- lchan->csd_mode);
- return -EINVAL;
- }
- break;
- default:
- LOGP(DRSL, LOGL_ERROR,
- "unsupported lchan->tch_mode %u\n",
- lchan->tch_mode);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static void mr_config_for_bts(struct gsm_lchan *lchan, struct msgb *msg)
-{
- if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR)
- msgb_tlv_put(msg, RSL_IE_MR_CONFIG, lchan->mr_bts_lv[0],
- lchan->mr_bts_lv + 1);
-}
-
-static enum gsm_phys_chan_config pchan_for_lchant(enum gsm_chan_t type)
-{
- switch (type) {
- case GSM_LCHAN_TCH_F:
- return GSM_PCHAN_TCH_F;
- case GSM_LCHAN_TCH_H:
- return GSM_PCHAN_TCH_H;
- case GSM_LCHAN_NONE:
- case GSM_LCHAN_PDTCH:
- /* TODO: so far lchan->type is NONE in PDCH mode. PDTCH is only
- * used in osmo-bts. Maybe set PDTCH and drop the NONE case
- * here. */
- return GSM_PCHAN_PDCH;
- default:
- return GSM_PCHAN_UNKNOWN;
- }
-}
-
-/*! Tx simplified channel activation message for non-standard PDCH type. */
-static int rsl_chan_activate_lchan_as_pdch(struct gsm_lchan *lchan)
-{
- struct msgb *msg;
- struct abis_rsl_dchan_hdr *dh;
-
- /* This might be called after release of the second lchan of a TCH/H,
- * but PDCH activation must always happen on the first lchan. Make sure
- * the calling code passes the correct lchan. */
- OSMO_ASSERT(lchan == lchan->ts->lchan);
-
- rsl_lchan_set_state(lchan, LCHAN_S_ACT_REQ);
-
- msg = rsl_msgb_alloc();
- dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
- init_dchan_hdr(dh, RSL_MT_CHAN_ACTIV);
- dh->chan_nr = gsm_lchan_as_pchan2chan_nr(lchan, GSM_PCHAN_PDCH);
-
- msgb_tv_put(msg, RSL_IE_ACT_TYPE, RSL_ACT_OSMO_PDCH);
-
- if (lchan->ts->trx->bts->type == GSM_BTS_TYPE_RBS2000 &&
- lchan->ts->trx->bts->rbs2000.use_superchannel) {
- const uint8_t eric_pgsl_tmr[] = { 30, 1 };
- msgb_tv_fixed_put(msg, RSL_IE_ERIC_PGSL_TIMERS,
- sizeof(eric_pgsl_tmr), eric_pgsl_tmr);
- }
-
- msg->dst = lchan->ts->trx->rsl_link;
-
- return abis_rsl_sendmsg(msg);
-}
-
-/* Chapter 8.4.1 */
-int rsl_chan_activate_lchan(struct gsm_lchan *lchan, uint8_t act_type,
- uint8_t ho_ref)
-{
- struct abis_rsl_dchan_hdr *dh;
- struct msgb *msg;
- int rc;
- uint8_t *len;
- uint8_t ta;
-
- struct rsl_ie_chan_mode cm;
- struct gsm48_chan_desc cd;
-
- /* If a TCH_F/PDCH TS is in PDCH mode, deactivate PDCH first. */
- if (lchan->ts->pchan == GSM_PCHAN_TCH_F_PDCH
- && (lchan->ts->flags & TS_F_PDCH_ACTIVE)) {
- /* store activation type and handover reference */
- lchan->dyn.act_type = act_type;
- lchan->dyn.ho_ref = ho_ref;
- return rsl_ipacc_pdch_activate(lchan->ts, 0);
- }
-
- /*
- * If necessary, release PDCH on dynamic TS. Note that sending a
- * release here is only necessary when in PDCH mode; for TCH types, an
- * RSL RF Chan Release is initiated by the BTS when a voice call ends,
- * so when we reach this, it will already be released. If a dyn TS is
- * in PDCH mode, it is still active and we need to initiate a release
- * from the BSC side here.
- *
- * If pchan_is != pchan_want, the PDCH has already been taken down and
- * the switchover now needs to enable the TCH lchan.
- *
- * To switch a dyn TS between TCH/H and TCH/F, it is sufficient to send
- * a chan activ with the new lchan type, because it will already be
- * released.
- */
- if (lchan->ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH
- && lchan->ts->dyn.pchan_is == GSM_PCHAN_PDCH
- && lchan->ts->dyn.pchan_is == lchan->ts->dyn.pchan_want) {
- enum gsm_phys_chan_config pchan_want;
- pchan_want = pchan_for_lchant(lchan->type);
- if (lchan->ts->dyn.pchan_is != pchan_want) {
- /*
- * Make sure to record on lchan[0] so that we'll find
- * it after the PDCH release.
- */
- struct gsm_lchan *lchan0 = lchan->ts->lchan;
- lchan0->dyn.act_type = act_type,
- lchan0->dyn.ho_ref = ho_ref;
- lchan0->dyn.rqd_ref = lchan->rqd_ref;
- lchan0->dyn.rqd_ta = lchan->rqd_ta;
- lchan->rqd_ref = NULL;
- lchan->rqd_ta = 0;
- DEBUGP(DRSL, "%s saved rqd_ref=%p ta=%u\n",
- gsm_lchan_name(lchan0), lchan0->rqd_ref,
- lchan0->rqd_ta);
- return dyn_ts_switchover_start(lchan->ts, pchan_want);
- }
- }
-
- DEBUGP(DRSL, "%s Tx RSL Channel Activate with act_type=%s\n",
- gsm_ts_and_pchan_name(lchan->ts),
- rsl_act_type_name(act_type));
-
- if (act_type == RSL_ACT_OSMO_PDCH) {
- if (lchan->ts->pchan != GSM_PCHAN_TCH_F_TCH_H_PDCH) {
- LOGP(DRSL, LOGL_ERROR,
- "%s PDCH channel activation only allowed on %s\n",
- gsm_ts_and_pchan_name(lchan->ts),
- gsm_pchan_name(GSM_PCHAN_TCH_F_TCH_H_PDCH));
- return -EINVAL;
- }
- return rsl_chan_activate_lchan_as_pdch(lchan);
- }
-
- if (lchan->ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH
- && lchan->ts->dyn.pchan_want == GSM_PCHAN_PDCH) {
- LOGP(DRSL, LOGL_ERROR,
- "%s Expected PDCH activation kind\n",
- gsm_ts_and_pchan_name(lchan->ts));
- return -EINVAL;
- }
-
- rc = channel_mode_from_lchan(&cm, lchan);
- if (rc < 0) {
- LOGP(DRSL, LOGL_ERROR,
- "%s Cannot find channel mode from lchan type\n",
- gsm_ts_and_pchan_name(lchan->ts));
- return rc;
- }
-
- rsl_lchan_set_state(lchan, LCHAN_S_ACT_REQ);
-
- ta = lchan->rqd_ta;
-
- /* BS11 requires TA shifted by 2 bits */
- if (lchan->ts->trx->bts->type == GSM_BTS_TYPE_BS11)
- ta <<= 2;
-
- memset(&cd, 0, sizeof(cd));
- gsm48_lchan2chan_desc(&cd, lchan);
-
- msg = rsl_msgb_alloc();
- dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
- init_dchan_hdr(dh, RSL_MT_CHAN_ACTIV);
-
- if (lchan->ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH)
- dh->chan_nr = gsm_lchan_as_pchan2chan_nr(
- lchan, lchan->ts->dyn.pchan_want);
- else
- dh->chan_nr = gsm_lchan2chan_nr(lchan);
-
- msgb_tv_put(msg, RSL_IE_ACT_TYPE, act_type);
- msgb_tlv_put(msg, RSL_IE_CHAN_MODE, sizeof(cm),
- (uint8_t *) &cm);
-
- /*
- * The Channel Identification is needed for Phase1 phones
- * and it contains the GSM48 Channel Description and the
- * Mobile Allocation. The GSM 08.58 asks for the Mobile
- * Allocation to have a length of zero. We are using the
- * msgb_l3len to calculate the length of both messages.
- */
- msgb_v_put(msg, RSL_IE_CHAN_IDENT);
- len = msgb_put(msg, 1);
- msgb_tv_fixed_put(msg, GSM48_IE_CHANDESC_2, sizeof(cd), (const uint8_t *) &cd);
-
- if (lchan->ts->hopping.enabled)
- msgb_tlv_put(msg, GSM48_IE_MA_AFTER, lchan->ts->hopping.ma_len,
- lchan->ts->hopping.ma_data);
- else
- msgb_tlv_put(msg, GSM48_IE_MA_AFTER, 0, NULL);
-
- /* update the calculated size */
- msg->l3h = len + 1;
- *len = msgb_l3len(msg);
-
- if (lchan->encr.alg_id > RSL_ENC_ALG_A5(0)) {
- uint8_t encr_info[MAX_A5_KEY_LEN+2];
- rc = build_encr_info(encr_info, lchan);
- if (rc > 0)
- msgb_tlv_put(msg, RSL_IE_ENCR_INFO, rc, encr_info);
- }
-
- switch (act_type) {
- case RSL_ACT_INTER_ASYNC:
- case RSL_ACT_INTER_SYNC:
- msgb_tv_put(msg, RSL_IE_HANDO_REF, ho_ref);
- break;
- default:
- break;
- }
-
- msgb_tv_put(msg, RSL_IE_BS_POWER, lchan->bs_power);
- msgb_tv_put(msg, RSL_IE_MS_POWER, lchan->ms_power);
- msgb_tv_put(msg, RSL_IE_TIMING_ADVANCE, ta);
- mr_config_for_bts(lchan, msg);
-
- msg->dst = lchan->ts->trx->rsl_link;
-
- return abis_rsl_sendmsg(msg);
-}
-
-/* Chapter 8.4.9: Modify channel mode on BTS side */
-int rsl_chan_mode_modify_req(struct gsm_lchan *lchan)
-{
- struct abis_rsl_dchan_hdr *dh;
- struct msgb *msg;
- int rc;
-
- uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
- struct rsl_ie_chan_mode cm;
-
- rc = channel_mode_from_lchan(&cm, lchan);
- if (rc < 0)
- return rc;
-
- msg = rsl_msgb_alloc();
- dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
- init_dchan_hdr(dh, RSL_MT_MODE_MODIFY_REQ);
- dh->chan_nr = chan_nr;
-
- msgb_tlv_put(msg, RSL_IE_CHAN_MODE, sizeof(cm),
- (uint8_t *) &cm);
-
- if (lchan->encr.alg_id > RSL_ENC_ALG_A5(0)) {
- uint8_t encr_info[MAX_A5_KEY_LEN+2];
- rc = build_encr_info(encr_info, lchan);
- if (rc > 0)
- msgb_tlv_put(msg, RSL_IE_ENCR_INFO, rc, encr_info);
- }
-
- mr_config_for_bts(lchan, msg);
-
- msg->dst = lchan->ts->trx->rsl_link;
-
- return abis_rsl_sendmsg(msg);
-}
-
-/* Chapter 8.4.6: Send the encryption command with given L3 info */
-int rsl_encryption_cmd(struct msgb *msg)
-{
- struct abis_rsl_dchan_hdr *dh;
- struct gsm_lchan *lchan = msg->lchan;
- uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
- uint8_t encr_info[MAX_A5_KEY_LEN+2];
- uint8_t l3_len = msg->len;
- int rc;
-
- /* First push the L3 IE tag and length */
- msgb_tv16_push(msg, RSL_IE_L3_INFO, l3_len);
-
- /* then the link identifier (SAPI0, main sign link) */
- msgb_tv_push(msg, RSL_IE_LINK_IDENT, 0);
-
- /* then encryption information */
- rc = build_encr_info(encr_info, lchan);
- if (rc <= 0)
- return rc;
- msgb_tlv_push(msg, RSL_IE_ENCR_INFO, rc, encr_info);
-
- /* and finally the DCHAN header */
- dh = (struct abis_rsl_dchan_hdr *) msgb_push(msg, sizeof(*dh));
- init_dchan_hdr(dh, RSL_MT_ENCR_CMD);
- dh->chan_nr = chan_nr;
-
- msg->dst = lchan->ts->trx->rsl_link;
-
- return abis_rsl_sendmsg(msg);
-}
-
-/* Chapter 8.4.5 / 4.6: Deactivate the SACCH after 04.08 RR CHAN RELEASE */
-int rsl_deact_sacch(struct gsm_lchan *lchan)
-{
- struct abis_rsl_dchan_hdr *dh;
- struct msgb *msg = rsl_msgb_alloc();
-
- dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
- init_dchan_hdr(dh, RSL_MT_DEACTIVATE_SACCH);
- dh->chan_nr = gsm_lchan2chan_nr(lchan);
-
- msg->lchan = lchan;
- msg->dst = lchan->ts->trx->rsl_link;
-
- DEBUGP(DRSL, "%s DEACTivate SACCH CMD\n", gsm_lchan_name(lchan));
-
- return abis_rsl_sendmsg(msg);
-}
-
-static bool dyn_ts_should_switch_to_pdch(struct gsm_bts_trx_ts *ts)
-{
- int ss;
-
- if (ts->pchan != GSM_PCHAN_TCH_F_TCH_H_PDCH)
- return false;
-
- if (ts->trx->bts->gprs.mode == BTS_GPRS_NONE)
- return false;
-
- /* Already in PDCH mode? */
- if (ts->dyn.pchan_is == GSM_PCHAN_PDCH)
- return false;
-
- /* See if all lchans are released. */
- for (ss = 0; ss < ts_subslots(ts); ss++) {
- struct gsm_lchan *lc = &ts->lchan[ss];
- if (lc->state != LCHAN_S_NONE) {
- DEBUGP(DRSL, "%s lchan %u still in use"
- " (type=%s,state=%s)\n",
- gsm_ts_and_pchan_name(ts), lc->nr,
- gsm_lchant_name(lc->type),
- gsm_lchans_name(lc->state));
- /* An lchan is still used. */
- return false;
- }
- }
-
- /* All channels are released, go to PDCH mode. */
- DEBUGP(DRSL, "%s back to PDCH\n",
- gsm_ts_and_pchan_name(ts));
- return true;
-}
-
-static void error_timeout_cb(void *data)
-{
- struct gsm_lchan *lchan = data;
- if (lchan->state != LCHAN_S_REL_ERR) {
- LOGP(DRSL, LOGL_ERROR, "%s error timeout but not in error state: %d\n",
- gsm_lchan_name(lchan), lchan->state);
- return;
- }
-
- /* go back to the none state */
- LOGP(DRSL, LOGL_INFO, "%s is back in operation.\n", gsm_lchan_name(lchan));
- rsl_lchan_set_state(lchan, LCHAN_S_NONE);
-
- /* Put PDCH channel back into PDCH mode, if GPRS is enabled */
- if (lchan->ts->pchan == GSM_PCHAN_TCH_F_PDCH
- && lchan->ts->trx->bts->gprs.mode != BTS_GPRS_NONE)
- rsl_ipacc_pdch_activate(lchan->ts, 1);
-
- if (dyn_ts_should_switch_to_pdch(lchan->ts))
- dyn_ts_switchover_start(lchan->ts, GSM_PCHAN_PDCH);
-}
-
-static int rsl_rx_rf_chan_rel_ack(struct gsm_lchan *lchan);
-
-/* Chapter 8.4.14 / 4.7: Tell BTS to release the radio channel */
-static int rsl_rf_chan_release(struct gsm_lchan *lchan, int error,
- enum sacch_deact deact_sacch)
-{
- struct abis_rsl_dchan_hdr *dh;
- struct msgb *msg;
- int rc;
-
- /* Stop timers that should lead to a channel release */
- osmo_timer_del(&lchan->T3109);
-
- if (lchan->state == LCHAN_S_REL_ERR) {
- LOGP(DRSL, LOGL_NOTICE, "%s is in error state, not sending release.\n",
- gsm_lchan_name(lchan));
- return -1;
- }
-
- msg = rsl_msgb_alloc();
- dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
- init_dchan_hdr(dh, RSL_MT_RF_CHAN_REL);
- dh->chan_nr = gsm_lchan2chan_nr(lchan);
-
- msg->lchan = lchan;
- msg->dst = lchan->ts->trx->rsl_link;
-
- if (error)
- DEBUGP(DRSL, "%s RF Channel Release due to error: %d\n",
- gsm_lchan_name(lchan), error);
- else
- DEBUGP(DRSL, "%s RF Channel Release\n", gsm_lchan_name(lchan));
-
- if (error) {
- /*
- * FIXME: GSM 04.08 gives us two options for the abnormal
- * chanel release. This can be either like in the non-existent
- * sub-lcuase 3.5.1 or for the main signalling link deactivate
- * the SACCH, start timer T3109 and consider the channel as
- * released.
- *
- * This code is doing the later for all raido links and not
- * only the main link. Right now all SAPIs are released on the
- * local end, the SACCH will be de-activated and right now the
- * T3111 will be started. First T3109 should be started and then
- * the T3111.
- *
- * TODO: Move this out of the function.
- */
-
- /*
- * sacch de-activate and "local end release"
- */
- if (deact_sacch == SACCH_DEACTIVATE)
- rsl_deact_sacch(lchan);
- rsl_release_sapis_from(lchan, 0, RSL_REL_LOCAL_END);
-
- /*
- * TODO: start T3109 now.
- */
- rsl_lchan_set_state(lchan, LCHAN_S_REL_ERR);
- }
-
- /* Start another timer or assume the BTS sends a ACK/NACK? */
- osmo_timer_setup(&lchan->act_timer, lchan_deact_tmr_cb, lchan);
- osmo_timer_schedule(&lchan->act_timer, 4, 0);
-
- rc = abis_rsl_sendmsg(msg);
-
- /* BTS will respond by RF CHAN REL ACK */
- return rc;
-}
-
-/*
- * Special handling for channel releases in the error case.
- */
-static int rsl_rf_chan_release_err(struct gsm_lchan *lchan)
-{
- enum sacch_deact sacch_deact;
- if (lchan->state != LCHAN_S_ACTIVE)
- return 0;
- switch (ts_pchan(lchan->ts)) {
- case GSM_PCHAN_TCH_F:
- case GSM_PCHAN_TCH_H:
- case GSM_PCHAN_CCCH_SDCCH4:
- case GSM_PCHAN_CCCH_SDCCH4_CBCH:
- case GSM_PCHAN_SDCCH8_SACCH8C:
- case GSM_PCHAN_SDCCH8_SACCH8C_CBCH:
- sacch_deact = SACCH_DEACTIVATE;
- break;
- default:
- sacch_deact = SACCH_NONE;
- break;
- }
- return rsl_rf_chan_release(lchan, 1, sacch_deact);
-}
-
-static int rsl_rx_rf_chan_rel_ack(struct gsm_lchan *lchan)
-{
- struct gsm_bts_trx_ts *ts = lchan->ts;
-
- DEBUGP(DRSL, "%s RF CHANNEL RELEASE ACK\n", gsm_lchan_name(lchan));
-
- /* Stop all pending timers */
- osmo_timer_del(&lchan->act_timer);
- osmo_timer_del(&lchan->T3111);
-
- /*
- * The BTS didn't respond within the timeout to our channel
- * release request and we have marked the channel as broken.
- * Now we do receive an ACK and let's be conservative. If it
- * is a sysmoBTS we know that only one RF Channel Release ACK
- * will be sent. So let's "repair" the channel.
- */
- if (lchan->state == LCHAN_S_BROKEN) {
- int do_free = is_sysmobts_v2(ts->trx->bts);
- LOGP(DRSL, LOGL_NOTICE,
- "%s CHAN REL ACK for broken channel. %s.\n",
- gsm_lchan_name(lchan),
- do_free ? "Releasing it" : "Keeping it broken");
- if (do_free)
- do_lchan_free(lchan);
- if (dyn_ts_should_switch_to_pdch(lchan->ts))
- dyn_ts_switchover_start(lchan->ts, GSM_PCHAN_PDCH);
- return 0;
- }
-
- if (lchan->state != LCHAN_S_REL_REQ && lchan->state != LCHAN_S_REL_ERR)
- LOGP(DRSL, LOGL_NOTICE, "%s CHAN REL ACK but state %s\n",
- gsm_lchan_name(lchan),
- gsm_lchans_name(lchan->state));
-
- do_lchan_free(lchan);
-
- /*
- * Check Osmocom RSL CHAN ACT style dynamic TCH/F_TCH/H_PDCH TS for pending
- * transitions in these cases:
- *
- * a) after PDCH was released due to switchover request, activate TCH.
- * BSC initiated this switchover, so dyn.pchan_is != pchan_want and
- * lchan->type has been set to the desired GSM_LCHAN_*.
- *
- * b) Voice call ended and a TCH is released. If the TS is now unused,
- * switch to PDCH. Here still dyn.pchan_is == dyn.pchan_want because
- * we're only just notified and may decide to switch to PDCH now.
- */
- if (ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH) {
- DEBUGP(DRSL, "%s Rx RSL Channel Release ack for lchan %u\n",
- gsm_ts_and_pchan_name(ts), lchan->nr);
-
- /* (a) */
- if (ts->dyn.pchan_is != ts->dyn.pchan_want)
- return dyn_ts_switchover_continue(ts);
-
- /* (b) */
- if (dyn_ts_should_switch_to_pdch(ts))
- return dyn_ts_switchover_start(ts, GSM_PCHAN_PDCH);
- }
-
- /*
- * Put a dynamic TCH/F_PDCH channel back to PDCH mode iff it was
- * released successfully. If in error, the PDCH ACT will follow after
- * T3111 in error_timeout_cb().
- *
- * Any state other than LCHAN_S_REL_ERR became LCHAN_S_NONE after above
- * do_lchan_free(). Assert this, because that's what ensures a PDCH ACT
- * on a TCH/F_PDCH TS in all cases.
- *
- * If GPRS is disabled, always skip the PDCH ACT.
- */
- OSMO_ASSERT(lchan->state == LCHAN_S_NONE
- || lchan->state == LCHAN_S_REL_ERR);
- if (ts->trx->bts->gprs.mode == BTS_GPRS_NONE)
- return 0;
- if (ts->pchan == GSM_PCHAN_TCH_F_PDCH
- && lchan->state == LCHAN_S_NONE)
- return rsl_ipacc_pdch_activate(ts, 1);
- return 0;
-}
-
-int rsl_paging_cmd(struct gsm_bts *bts, uint8_t paging_group, uint8_t len,
- uint8_t *ms_ident, uint8_t chan_needed, bool is_gprs)
-{
- struct abis_rsl_dchan_hdr *dh;
- struct msgb *msg = rsl_msgb_alloc();
-
- dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
- init_dchan_hdr(dh, RSL_MT_PAGING_CMD);
- dh->chan_nr = RSL_CHAN_PCH_AGCH;
-
- msgb_tv_put(msg, RSL_IE_PAGING_GROUP, paging_group);
- msgb_tlv_put(msg, RSL_IE_MS_IDENTITY, len-2, ms_ident+2);
- msgb_tv_put(msg, RSL_IE_CHAN_NEEDED, chan_needed);
-
- /* Ericsson wants to have this IE in case a paging message
- * relates to packet paging */
- if (bts->type == GSM_BTS_TYPE_RBS2000 && is_gprs)
- msgb_tv_put(msg, RSL_IE_ERIC_PACKET_PAG_IND, 0);
-
- msg->dst = bts->c0->rsl_link;
-
- return abis_rsl_sendmsg(msg);
-}
-
-int imsi_str2bcd(uint8_t *bcd_out, const char *str_in)
-{
- int i, len = strlen(str_in);
-
- for (i = 0; i < len; i++) {
- int num = str_in[i] - 0x30;
- if (num < 0 || num > 9)
- return -1;
- if (i % 2 == 0)
- bcd_out[i/2] = num;
- else
- bcd_out[i/2] |= (num << 4);
- }
-
- return 0;
-}
-
-/* Chapter 8.5.6 */
-struct msgb *rsl_imm_assign_cmd_common(struct gsm_bts *bts, uint8_t len, uint8_t *val)
-{
- struct msgb *msg = rsl_msgb_alloc();
- struct abis_rsl_dchan_hdr *dh;
- uint8_t buf[GSM_MACBLOCK_LEN];
-
- dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
- init_dchan_hdr(dh, RSL_MT_IMMEDIATE_ASSIGN_CMD);
- dh->chan_nr = RSL_CHAN_PCH_AGCH;
-
- switch (bts->type) {
- case GSM_BTS_TYPE_BS11:
- msgb_tlv_put(msg, RSL_IE_IMM_ASS_INFO, len, val);
- break;
- default:
- /* If phase 2, construct a FULL_IMM_ASS_INFO */
- pad_macblock(buf, val, len);
- msgb_tlv_put(msg, RSL_IE_FULL_IMM_ASS_INFO, GSM_MACBLOCK_LEN,
- buf);
- break;
- }
-
- msg->dst = bts->c0->rsl_link;
- return msg;
-}
-
-/* Chapter 8.5.6 */
-int rsl_imm_assign_cmd(struct gsm_bts *bts, uint8_t len, uint8_t *val)
-{
- struct msgb *msg = rsl_imm_assign_cmd_common(bts, len, val);
- if (!msg)
- return 1;
- return abis_rsl_sendmsg(msg);
-}
-
-/* Chapter 8.5.6 */
-int rsl_ericsson_imm_assign_cmd(struct gsm_bts *bts, uint32_t tlli, uint8_t len, uint8_t *val)
-{
- struct msgb *msg = rsl_imm_assign_cmd_common(bts, len, val);
- if (!msg)
- return 1;
-
- /* ericsson can handle a reference at the end of the message which is used in
- * the confirm message. The confirm message is only sent if the trailer is present */
- msgb_put_u8(msg, RSL_IE_ERIC_MOBILE_ID);
- msgb_put_u32(msg, tlli);
-
- return abis_rsl_sendmsg(msg);
-}
-
-/* Send Siemens specific MS RF Power Capability Indication */
-int rsl_siemens_mrpci(struct gsm_lchan *lchan, struct rsl_mrpci *mrpci)
-{
- struct msgb *msg = rsl_msgb_alloc();
- struct abis_rsl_dchan_hdr *dh;
-
- dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
- init_dchan_hdr(dh, RSL_MT_SIEMENS_MRPCI);
- dh->c.msg_discr = ABIS_RSL_MDISC_DED_CHAN;
- dh->chan_nr = gsm_lchan2chan_nr(lchan);
- msgb_tv_put(msg, RSL_IE_SIEMENS_MRPCI, *(uint8_t *)mrpci);
-
- DEBUGP(DRSL, "%s TX Siemens MRPCI 0x%02x\n",
- gsm_lchan_name(lchan), *(uint8_t *)mrpci);
-
- msg->dst = lchan->ts->trx->rsl_link;
-
- return abis_rsl_sendmsg(msg);
-}
-
-
-/* Send "DATA REQUEST" message with given L3 Info payload */
-/* Chapter 8.3.1 */
-int rsl_data_request(struct msgb *msg, uint8_t link_id)
-{
- if (msg->lchan == NULL) {
- LOGP(DRSL, LOGL_ERROR, "cannot send DATA REQUEST to unknown lchan\n");
- return -EINVAL;
- }
-
- rsl_rll_push_l3(msg, RSL_MT_DATA_REQ, gsm_lchan2chan_nr(msg->lchan),
- link_id, 1);
-
- msg->dst = msg->lchan->ts->trx->rsl_link;
-
- return abis_rsl_sendmsg(msg);
-}
-
-/* Send "ESTABLISH REQUEST" message with given L3 Info payload */
-/* Chapter 8.3.1 */
-int rsl_establish_request(struct gsm_lchan *lchan, uint8_t link_id)
-{
- struct msgb *msg;
-
- msg = rsl_rll_simple(RSL_MT_EST_REQ, gsm_lchan2chan_nr(lchan),
- link_id, 0);
- msg->dst = lchan->ts->trx->rsl_link;
-
- DEBUGP(DRLL, "%s RSL RLL ESTABLISH REQ (link_id=0x%02x)\n",
- gsm_lchan_name(lchan), link_id);
-
- return abis_rsl_sendmsg(msg);
-}
-
-static void rsl_handle_release(struct gsm_lchan *lchan);
-
-/* Special work handler to handle missing RSL_MT_REL_CONF message from
- * Nokia InSite BTS */
-static void lchan_rel_work_cb(void *data)
-{
- struct gsm_lchan *lchan = data;
- int sapi;
-
- for (sapi = 0; sapi < ARRAY_SIZE(lchan->sapis); ++sapi) {
- if (lchan->sapis[sapi] == LCHAN_SAPI_REL)
- lchan->sapis[sapi] = LCHAN_SAPI_UNUSED;
- }
- rsl_handle_release(lchan);
-}
-
-/* Chapter 8.3.7 Request the release of multiframe mode of RLL connection.
- This is what higher layers should call. The BTS then responds with
- RELEASE CONFIRM, which we in turn use to trigger RSL CHANNEL RELEASE,
- which in turn is acknowledged by RSL CHANNEL RELEASE ACK, which calls
- lchan_free() */
-int rsl_release_request(struct gsm_lchan *lchan, uint8_t link_id,
- enum rsl_rel_mode release_mode)
-{
-
- struct msgb *msg;
-
- msg = rsl_rll_simple(RSL_MT_REL_REQ, gsm_lchan2chan_nr(lchan),
- link_id, 0);
- /* 0 is normal release, 1 is local end */
- msgb_tv_put(msg, RSL_IE_RELEASE_MODE, release_mode);
-
- /* FIXME: start some timer in case we don't receive a REL ACK ? */
-
- msg->dst = lchan->ts->trx->rsl_link;
-
- DEBUGP(DRLL, "%s RSL RLL RELEASE REQ (link_id=0x%02x, reason=%u)\n",
- gsm_lchan_name(lchan), link_id, release_mode);
-
- abis_rsl_sendmsg(msg);
-
- /* Do not wait for Nokia BTS to send the confirm. */
- if (is_nokia_bts(lchan->ts->trx->bts)
- && lchan->ts->trx->bts->nokia.no_loc_rel_cnf
- && release_mode == RSL_REL_LOCAL_END) {
- DEBUGP(DRLL, "Scheduling release, becasuse Nokia InSite BTS does not send a RELease CONFirm.\n");
- lchan->sapis[link_id & 0x7] = LCHAN_SAPI_REL;
- osmo_timer_setup(&lchan->rel_work, lchan_rel_work_cb, lchan);
- osmo_timer_schedule(&lchan->rel_work, 0, 0);
- }
-
- return 0;
-}
-
-int rsl_lchan_mark_broken(struct gsm_lchan *lchan, const char *reason)
-{
- LOGP(DRSL, LOGL_ERROR, "%s %s lchan broken: %s\n",
- gsm_lchan_name(lchan), gsm_lchant_name(lchan->type), reason);
- rsl_lchan_set_state(lchan, LCHAN_S_BROKEN);
- lchan->broken_reason = reason;
- return 0;
-}
-
-int rsl_lchan_set_state(struct gsm_lchan *lchan, int state)
-{
- DEBUGP(DRSL, "%s state %s -> %s\n",
- gsm_lchan_name(lchan), gsm_lchans_name(lchan->state),
- gsm_lchans_name(state));
- lchan->state = state;
- return 0;
-}
-
-/* Chapter 8.4.2: Channel Activate Acknowledge */
-static int rsl_rx_chan_act_ack(struct msgb *msg)
-{
- struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg);
- struct gsm_lchan *lchan = msg->lchan;
- struct gsm_bts_trx_ts *ts = lchan->ts;
-
- /* BTS has confirmed channel activation, we now need
- * to assign the activated channel to the MS */
- if (rslh->ie_chan != RSL_IE_CHAN_NR)
- return -EINVAL;
-
- osmo_timer_del(&lchan->act_timer);
-
- if (lchan->state == LCHAN_S_BROKEN) {
- int do_release = is_sysmobts_v2(ts->trx->bts);
- LOGP(DRSL, LOGL_NOTICE, "%s CHAN ACT ACK for broken channel. %s\n",
- gsm_lchan_name(lchan),
- do_release ? "Releasing it" : "Keeping it broken");
- if (do_release) {
- talloc_free(lchan->rqd_ref);
- lchan->rqd_ref = NULL;
- lchan->rqd_ta = 0;
- rsl_lchan_set_state(msg->lchan, LCHAN_S_ACTIVE);
- if (ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH) {
- /*
- * lchan_act_tmr_cb() already called
- * lchan_free() and cleared the lchan->type, so
- * calling dyn_ts_switchover_complete() here
- * would not have the desired effect of
- * mimicking an activated lchan that we can
- * release. Instead hack the dyn ts state to
- * make sure that rsl_rx_rf_chan_rel_ack() will
- * switch back to PDCH, i.e. have pchan_is ==
- * pchan_want, both != GSM_PCHAN_PDCH:
- */
- ts->dyn.pchan_is = GSM_PCHAN_NONE;
- ts->dyn.pchan_want = GSM_PCHAN_NONE;
- }
- rsl_rf_chan_release(msg->lchan, 0, SACCH_NONE);
- }
- return 0;
- }
-
- if (lchan->state != LCHAN_S_ACT_REQ)
- LOGP(DRSL, LOGL_NOTICE, "%s CHAN ACT ACK, but state %s\n",
- gsm_lchan_name(lchan),
- gsm_lchans_name(lchan->state));
- rsl_lchan_set_state(lchan, LCHAN_S_ACTIVE);
-
- if (ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH)
- dyn_ts_switchover_complete(lchan);
-
- if (lchan->rqd_ref) {
- rsl_send_imm_assignment(lchan);
- talloc_free(lchan->rqd_ref);
- lchan->rqd_ref = NULL;
- lchan->rqd_ta = 0;
- }
-
- send_lchan_signal(S_LCHAN_ACTIVATE_ACK, lchan, NULL);
-
- /* Update bts attributes inside the PCU */
- if (ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH ||
- ts->pchan == GSM_PCHAN_TCH_F_PDCH ||
- ts->pchan == GSM_PCHAN_PDCH)
- pcu_info_update(ts->trx->bts);
-
- return 0;
-}
-
-/* Chapter 8.4.3: Channel Activate NACK */
-static int rsl_rx_chan_act_nack(struct msgb *msg)
-{
- struct abis_rsl_dchan_hdr *dh = msgb_l2(msg);
- struct tlv_parsed tp;
-
- osmo_timer_del(&msg->lchan->act_timer);
-
- if (msg->lchan->state == LCHAN_S_BROKEN) {
- LOGP(DRSL, LOGL_ERROR,
- "%s CHANNEL ACTIVATE NACK for broken channel.\n",
- gsm_lchan_name(msg->lchan));
- return -1;
- }
-
- LOGP(DRSL, LOGL_ERROR, "%s CHANNEL ACTIVATE NACK ",
- gsm_lchan_name(msg->lchan));
-
- /* BTS has rejected channel activation ?!? */
- if (dh->ie_chan != RSL_IE_CHAN_NR)
- return -EINVAL;
-
- rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh));
- if (TLVP_PRESENT(&tp, RSL_IE_CAUSE)) {
- const uint8_t *cause = TLVP_VAL(&tp, RSL_IE_CAUSE);
- print_rsl_cause(LOGL_ERROR, cause,
- TLVP_LEN(&tp, RSL_IE_CAUSE));
- msg->lchan->error_cause = *cause;
- if (*cause != RSL_ERR_RCH_ALR_ACTV_ALLOC) {
- rsl_lchan_mark_broken(msg->lchan, "NACK on activation");
- } else
- rsl_rf_chan_release(msg->lchan, 1, SACCH_DEACTIVATE);
-
- } else {
- rsl_lchan_mark_broken(msg->lchan, "NACK on activation no IE");
- }
-
- LOGPC(DRSL, LOGL_ERROR, "\n");
-
- send_lchan_signal(S_LCHAN_ACTIVATE_NACK, msg->lchan, NULL);
- return 0;
-}
-
-/* Chapter 8.4.4: Connection Failure Indication */
-static int rsl_rx_conn_fail(struct msgb *msg)
-{
- struct abis_rsl_dchan_hdr *dh = msgb_l2(msg);
- struct tlv_parsed tp;
-
- LOGP(DRSL, LOGL_NOTICE, "%s CONNECTION FAIL: RELEASING state %s ",
- gsm_lchan_name(msg->lchan),
- gsm_lchans_name(msg->lchan->state));
-
- rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh));
-
- if (TLVP_PRESENT(&tp, RSL_IE_CAUSE))
- print_rsl_cause(LOGL_NOTICE, TLVP_VAL(&tp, RSL_IE_CAUSE),
- TLVP_LEN(&tp, RSL_IE_CAUSE));
-
- LOGPC(DRSL, LOGL_NOTICE, "\n");
- rate_ctr_inc(&msg->lchan->ts->trx->bts->network->bsc_ctrs->ctr[BSC_CTR_CHAN_RF_FAIL]);
- return rsl_rf_chan_release_err(msg->lchan);
-}
-
-static void print_meas_rep_uni(struct gsm_meas_rep_unidir *mru,
- const char *prefix)
-{
- DEBUGPC(DMEAS, "RXL-FULL-%s=%3ddBm RXL-SUB-%s=%3ddBm ",
- prefix, rxlev2dbm(mru->full.rx_lev),
- prefix, rxlev2dbm(mru->sub.rx_lev));
- DEBUGPC(DMEAS, "RXQ-FULL-%s=%d RXQ-SUB-%s=%d ",
- prefix, mru->full.rx_qual, prefix, mru->sub.rx_qual);
-}
-
-static void print_meas_rep(struct gsm_lchan *lchan, struct gsm_meas_rep *mr)
-{
- int i;
- const char *name = "";
-
- if (lchan && lchan->conn) {
- if (lchan->conn->bsub)
- name = bsc_subscr_name(lchan->conn->bsub);
- else if (lchan->conn->subscr)
- name = lchan->conn->subscr->imsi;
- else
- name = lchan->name;
- }
-
- DEBUGP(DMEAS, "[%s] MEASUREMENT RESULT NR=%d ", name, mr->nr);
-
- if (mr->flags & MEAS_REP_F_DL_DTX)
- DEBUGPC(DMEAS, "DTXd ");
-
- print_meas_rep_uni(&mr->ul, "ul");
- DEBUGPC(DMEAS, "BS_POWER=%d ", mr->bs_power);
-
- if (mr->flags & MEAS_REP_F_MS_TO)
- DEBUGPC(DMEAS, "MS_TO=%d ", mr->ms_timing_offset);
-
- if (mr->flags & MEAS_REP_F_MS_L1) {
- DEBUGPC(DMEAS, "L1_MS_PWR=%3ddBm ", mr->ms_l1.pwr);
- DEBUGPC(DMEAS, "L1_FPC=%u ",
- mr->flags & MEAS_REP_F_FPC ? 1 : 0);
- DEBUGPC(DMEAS, "L1_TA=%u ", mr->ms_l1.ta);
- }
-
- if (mr->flags & MEAS_REP_F_UL_DTX)
- DEBUGPC(DMEAS, "DTXu ");
- if (mr->flags & MEAS_REP_F_BA1)
- DEBUGPC(DMEAS, "BA1 ");
- if (!(mr->flags & MEAS_REP_F_DL_VALID))
- DEBUGPC(DMEAS, "NOT VALID ");
- else
- print_meas_rep_uni(&mr->dl, "dl");
-
- DEBUGPC(DMEAS, "NUM_NEIGH=%u\n", mr->num_cell);
- if (mr->num_cell == 7)
- return;
- for (i = 0; i < mr->num_cell; i++) {
- struct gsm_meas_rep_cell *mrc = &mr->cell[i];
- DEBUGP(DMEAS, "IDX=%u ARFCN=%u BSIC=%u => %d dBm\n",
- mrc->neigh_idx, mrc->arfcn, mrc->bsic, rxlev2dbm(mrc->rxlev));
- }
-}
-
-static int rsl_rx_meas_res(struct msgb *msg)
-{
- struct abis_rsl_dchan_hdr *dh = msgb_l2(msg);
- struct tlv_parsed tp;
- struct gsm_meas_rep *mr = lchan_next_meas_rep(msg->lchan);
- uint8_t len;
- const uint8_t *val;
- int rc;
-
- /* check if this channel is actually active */
- /* FIXME: maybe this check should be way more generic/centralized */
- if (msg->lchan->state != LCHAN_S_ACTIVE) {
- LOGP(DRSL, LOGL_DEBUG, "%s: MEAS RES for inactive channel\n",
- gsm_lchan_name(msg->lchan));
- return 0;
- }
-
- memset(mr, 0, sizeof(*mr));
- mr->lchan = msg->lchan;
-
- rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh));
-
- if (!TLVP_PRESENT(&tp, RSL_IE_MEAS_RES_NR) ||
- !TLVP_PRESENT(&tp, RSL_IE_UPLINK_MEAS) ||
- !TLVP_PRESENT(&tp, RSL_IE_BS_POWER))
- return -EIO;
-
- /* Mandatory Parts */
- mr->nr = *TLVP_VAL(&tp, RSL_IE_MEAS_RES_NR);
-
- len = TLVP_LEN(&tp, RSL_IE_UPLINK_MEAS);
- val = TLVP_VAL(&tp, RSL_IE_UPLINK_MEAS);
- if (len >= 3) {
- if (val[0] & 0x40)
- mr->flags |= MEAS_REP_F_DL_DTX;
- mr->ul.full.rx_lev = val[0] & 0x3f;
- mr->ul.sub.rx_lev = val[1] & 0x3f;
- mr->ul.full.rx_qual = val[2]>>3 & 0x7;
- mr->ul.sub.rx_qual = val[2] & 0x7;
- }
-
- mr->bs_power = *TLVP_VAL(&tp, RSL_IE_BS_POWER);
-
- /* Optional Parts */
- if (TLVP_PRESENT(&tp, RSL_IE_MS_TIMING_OFFSET)) {
- /* According to 3GPP TS 48.058 § MS Timing Offset = Timing Offset field - 63 */
- mr->ms_timing_offset = *TLVP_VAL(&tp, RSL_IE_MS_TIMING_OFFSET) - 63;
- mr->flags |= MEAS_REP_F_MS_TO;
- }
-
- if (TLVP_PRESENT(&tp, RSL_IE_L1_INFO)) {
- struct e1inp_sign_link *sign_link = msg->dst;
-
- val = TLVP_VAL(&tp, RSL_IE_L1_INFO);
- mr->flags |= MEAS_REP_F_MS_L1;
- mr->ms_l1.pwr = ms_pwr_dbm(sign_link->trx->bts->band, val[0] >> 3);
- if (val[0] & 0x04)
- mr->flags |= MEAS_REP_F_FPC;
- mr->ms_l1.ta = val[1];
- /* BS11 and Nokia reports TA shifted by 2 bits */
- if (msg->lchan->ts->trx->bts->type == GSM_BTS_TYPE_BS11
- || msg->lchan->ts->trx->bts->type == GSM_BTS_TYPE_NOKIA_SITE)
- mr->ms_l1.ta >>= 2;
- }
- if (TLVP_PRESENT(&tp, RSL_IE_L3_INFO)) {
- msg->l3h = (uint8_t *) TLVP_VAL(&tp, RSL_IE_L3_INFO);
- rc = gsm48_parse_meas_rep(mr, msg);
- if (rc < 0)
- return rc;
- }
-
- print_meas_rep(msg->lchan, mr);
-
- send_lchan_signal(S_LCHAN_MEAS_REP, msg->lchan, mr);
-
- return 0;
-}
-
-/* Chapter 8.4.7 */
-static int rsl_rx_hando_det(struct msgb *msg)
-{
- struct abis_rsl_dchan_hdr *dh = msgb_l2(msg);
- struct tlv_parsed tp;
-
- DEBUGP(DRSL, "%s HANDOVER DETECT ", gsm_lchan_name(msg->lchan));
-
- rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh));
-
- if (TLVP_PRESENT(&tp, RSL_IE_ACCESS_DELAY))
- DEBUGPC(DRSL, "access delay = %u\n",
- *TLVP_VAL(&tp, RSL_IE_ACCESS_DELAY));
- else
- DEBUGPC(DRSL, "\n");
-
- send_lchan_signal(S_LCHAN_HANDOVER_DETECT, msg->lchan, NULL);
-
- return 0;
-}
-
-static bool lchan_may_change_pdch(struct gsm_lchan *lchan, bool pdch_act)
-{
- struct gsm_bts_trx_ts *ts;
-
- OSMO_ASSERT(lchan);
-
- ts = lchan->ts;
- OSMO_ASSERT(ts);
- OSMO_ASSERT(ts->trx);
- OSMO_ASSERT(ts->trx->bts);
-
- if (lchan->ts->pchan != GSM_PCHAN_TCH_F_PDCH) {
- LOGP(DRSL, LOGL_ERROR, "%s pchan=%s Rx PDCH %s ACK"
- " for channel that is no TCH/F_PDCH\n",
- gsm_lchan_name(lchan),
- gsm_pchan_name(ts->pchan),
- pdch_act? "ACT" : "DEACT");
- return false;
- }
-
- if (lchan->state != LCHAN_S_NONE) {
- LOGP(DRSL, LOGL_ERROR, "%s pchan=%s Rx PDCH %s ACK"
- " in unexpected state: %s\n",
- gsm_lchan_name(lchan),
- gsm_pchan_name(ts->pchan),
- pdch_act? "ACT" : "DEACT",
- gsm_lchans_name(lchan->state));
- return false;
- }
- return true;
-}
-
-static int rsl_rx_pdch_act_ack(struct msgb *msg)
-{
- if (!lchan_may_change_pdch(msg->lchan, true))
- return -EINVAL;
-
- msg->lchan->ts->flags |= TS_F_PDCH_ACTIVE;
- msg->lchan->ts->flags &= ~TS_F_PDCH_ACT_PENDING;
-
- return 0;
-}
-
-static int rsl_rx_pdch_deact_ack(struct msgb *msg)
-{
- if (!lchan_may_change_pdch(msg->lchan, false))
- return -EINVAL;
-
- msg->lchan->ts->flags &= ~TS_F_PDCH_ACTIVE;
- msg->lchan->ts->flags &= ~TS_F_PDCH_DEACT_PENDING;
-
- rsl_chan_activate_lchan(msg->lchan, msg->lchan->dyn.act_type,
- msg->lchan->dyn.ho_ref);
-
- return 0;
-}
-
-static int abis_rsl_rx_dchan(struct msgb *msg)
-{
- struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg);
- int rc = 0;
- char *ts_name;
- struct e1inp_sign_link *sign_link = msg->dst;
-
- msg->lchan = lchan_lookup(sign_link->trx, rslh->chan_nr,
- "Abis RSL rx DCHAN: ");
- if (!msg->lchan)
- return -1;
- ts_name = gsm_lchan_name(msg->lchan);
-
- switch (rslh->c.msg_type) {
- case RSL_MT_CHAN_ACTIV_ACK:
- DEBUGP(DRSL, "%s CHANNEL ACTIVATE ACK\n", ts_name);
- rc = rsl_rx_chan_act_ack(msg);
- count_codecs(sign_link->trx->bts, msg->lchan);
- break;
- case RSL_MT_CHAN_ACTIV_NACK:
- rc = rsl_rx_chan_act_nack(msg);
- break;
- case RSL_MT_CONN_FAIL:
- rc = rsl_rx_conn_fail(msg);
- break;
- case RSL_MT_MEAS_RES:
- rc = rsl_rx_meas_res(msg);
- break;
- case RSL_MT_HANDO_DET:
- rc = rsl_rx_hando_det(msg);
- break;
- case RSL_MT_RF_CHAN_REL_ACK:
- rc = rsl_rx_rf_chan_rel_ack(msg->lchan);
- break;
- case RSL_MT_MODE_MODIFY_ACK:
- count_codecs(sign_link->trx->bts, msg->lchan);
- DEBUGP(DRSL, "%s CHANNEL MODE MODIFY ACK\n", ts_name);
- break;
- case RSL_MT_MODE_MODIFY_NACK:
- LOGP(DRSL, LOGL_ERROR, "%s CHANNEL MODE MODIFY NACK\n", ts_name);
- break;
- case RSL_MT_IPAC_PDCH_ACT_ACK:
- DEBUGP(DRSL, "%s IPAC PDCH ACT ACK\n", ts_name);
- rc = rsl_rx_pdch_act_ack(msg);
- break;
- case RSL_MT_IPAC_PDCH_ACT_NACK:
- LOGP(DRSL, LOGL_ERROR, "%s IPAC PDCH ACT NACK\n", ts_name);
- break;
- case RSL_MT_IPAC_PDCH_DEACT_ACK:
- DEBUGP(DRSL, "%s IPAC PDCH DEACT ACK\n", ts_name);
- rc = rsl_rx_pdch_deact_ack(msg);
- break;
- case RSL_MT_IPAC_PDCH_DEACT_NACK:
- LOGP(DRSL, LOGL_ERROR, "%s IPAC PDCH DEACT NACK\n", ts_name);
- break;
- case RSL_MT_PHY_CONTEXT_CONF:
- case RSL_MT_PREPROC_MEAS_RES:
- case RSL_MT_TALKER_DET:
- case RSL_MT_LISTENER_DET:
- case RSL_MT_REMOTE_CODEC_CONF_REP:
- case RSL_MT_MR_CODEC_MOD_ACK:
- case RSL_MT_MR_CODEC_MOD_NACK:
- case RSL_MT_MR_CODEC_MOD_PER:
- LOGP(DRSL, LOGL_NOTICE, "%s Unimplemented Abis RSL DChan "
- "msg 0x%02x\n", ts_name, rslh->c.msg_type);
- break;
- default:
- LOGP(DRSL, LOGL_NOTICE, "%s unknown Abis RSL DChan msg 0x%02x\n",
- ts_name, rslh->c.msg_type);
- return -EINVAL;
- }
-
- return rc;
-}
-
-static int rsl_rx_error_rep(struct msgb *msg)
-{
- struct abis_rsl_common_hdr *rslh = msgb_l2(msg);
- struct tlv_parsed tp;
- struct e1inp_sign_link *sign_link = msg->dst;
-
- LOGP(DRSL, LOGL_ERROR, "%s ERROR REPORT ", gsm_trx_name(sign_link->trx));
-
- rsl_tlv_parse(&tp, rslh->data, msgb_l2len(msg)-sizeof(*rslh));
-
- if (TLVP_PRESENT(&tp, RSL_IE_CAUSE))
- print_rsl_cause(LOGL_ERROR, TLVP_VAL(&tp, RSL_IE_CAUSE),
- TLVP_LEN(&tp, RSL_IE_CAUSE));
-
- LOGPC(DRSL, LOGL_ERROR, "\n");
-
- return 0;
-}
-
-static int abis_rsl_rx_trx(struct msgb *msg)
-{
- struct abis_rsl_common_hdr *rslh = msgb_l2(msg);
- struct e1inp_sign_link *sign_link = msg->dst;
- int rc = 0;
-
- switch (rslh->msg_type) {
- case RSL_MT_ERROR_REPORT:
- rc = rsl_rx_error_rep(msg);
- break;
- case RSL_MT_RF_RES_IND:
- /* interference on idle channels of TRX */
- //DEBUGP(DRSL, "%s RF Resource Indication\n", gsm_trx_name(sign_link->trx));
- break;
- case RSL_MT_OVERLOAD:
- /* indicate CCCH / ACCH / processor overload */
- LOGP(DRSL, LOGL_ERROR, "%s CCCH/ACCH/CPU Overload\n",
- gsm_trx_name(sign_link->trx));
- break;
- case 0x42: /* Nokia specific: SI End ACK */
- LOGP(DRSL, LOGL_INFO, "Nokia SI End ACK\n");
- break;
- case 0x43: /* Nokia specific: SI End NACK */
- LOGP(DRSL, LOGL_INFO, "Nokia SI End NACK\n");
- break;
- default:
- LOGP(DRSL, LOGL_NOTICE, "%s Unknown Abis RSL TRX message "
- "type 0x%02x\n", gsm_trx_name(sign_link->trx), rslh->msg_type);
- return -EINVAL;
- }
- return rc;
-}
-
-/* If T3101 expires, we never received a response to IMMEDIATE ASSIGN */
-static void t3101_expired(void *data)
-{
- struct gsm_lchan *lchan = data;
- LOGP(DRSL, LOGL_NOTICE,
- "%s T3101 expired: no response to IMMEDIATE ASSIGN\n",
- gsm_lchan_name(lchan));
- rsl_rf_chan_release(lchan, 1, SACCH_DEACTIVATE);
-}
-
-/* If T3111 expires, we will send the RF Channel Request */
-static void t3111_expired(void *data)
-{
- struct gsm_lchan *lchan = data;
- LOGP(DRSL, LOGL_NOTICE,
- "%s T3111 expired: releasing RF Channel\n",
- gsm_lchan_name(lchan));
- rsl_rf_chan_release(lchan, 0, SACCH_NONE);
-}
-
-/* If T3109 expires the MS has not send a UA/UM do the error release */
-static void t3109_expired(void *data)
-{
- struct gsm_lchan *lchan = data;
-
- LOGP(DRSL, LOGL_ERROR,
- "%s SACCH deactivation timeout.\n", gsm_lchan_name(lchan));
- rsl_rf_chan_release(lchan, 1, SACCH_NONE);
-}
-
-/* Format an IMM ASS REJ according to 04.08 Chapter 9.1.20 */
-static int rsl_send_imm_ass_rej(struct gsm_bts *bts,
- unsigned int num_req_refs,
- struct gsm48_req_ref *rqd_refs,
- uint8_t wait_ind)
-{
- uint8_t buf[GSM_MACBLOCK_LEN];
- struct gsm48_imm_ass_rej *iar = (struct gsm48_imm_ass_rej *)buf;
-
- /* create IMMEDIATE ASSIGN REJECT 04.08 message */
- memset(iar, 0, sizeof(*iar));
- iar->proto_discr = GSM48_PDISC_RR;
- iar->msg_type = GSM48_MT_RR_IMM_ASS_REJ;
- iar->page_mode = GSM48_PM_SAME;
-
- memcpy(&iar->req_ref1, &rqd_refs[0], sizeof(iar->req_ref1));
- iar->wait_ind1 = wait_ind;
-
- if (num_req_refs >= 2)
- memcpy(&iar->req_ref2, &rqd_refs[1], sizeof(iar->req_ref2));
- else
- memcpy(&iar->req_ref2, &rqd_refs[0], sizeof(iar->req_ref2));
- iar->wait_ind2 = wait_ind;
-
- if (num_req_refs >= 3)
- memcpy(&iar->req_ref3, &rqd_refs[2], sizeof(iar->req_ref3));
- else
- memcpy(&iar->req_ref3, &rqd_refs[0], sizeof(iar->req_ref3));
- iar->wait_ind3 = wait_ind;
-
- if (num_req_refs >= 4)
- memcpy(&iar->req_ref4, &rqd_refs[3], sizeof(iar->req_ref4));
- else
- memcpy(&iar->req_ref4, &rqd_refs[0], sizeof(iar->req_ref4));
- iar->wait_ind4 = wait_ind;
-
- /* we need to subtract 1 byte from sizeof(*iar) since ia includes the l2_plen field */
- iar->l2_plen = GSM48_LEN2PLEN((sizeof(*iar)-1));
-
- return rsl_imm_assign_cmd(bts, sizeof(*iar), (uint8_t *) iar);
-}
-
-/* Handle packet channel rach requests */
-static int rsl_rx_pchan_rqd(struct msgb *msg, struct gsm_bts *bts)
-{
- struct gsm48_req_ref *rqd_ref;
- struct abis_rsl_dchan_hdr *rqd_hdr = msgb_l2(msg);
- rqd_ref = (struct gsm48_req_ref *) &rqd_hdr->data[1];
- uint8_t ra = rqd_ref->ra;
- uint8_t t1, t2, t3;
- uint32_t fn;
- uint8_t rqd_ta;
- uint8_t is_11bit;
-
- /* Process rach request and forward contained information to PCU */
- if (ra == 0x7F) {
- is_11bit = 1;
-
- /* FIXME: Also handle 11 bit rach requests */
- LOGP(DRSL, LOGL_ERROR, "BTS %d eleven bit access burst not supported yet!\n",bts->nr);
- return -EINVAL;
- } else {
- is_11bit = 0;
- t1 = rqd_ref->t1;
- t2 = rqd_ref->t2;
- t3 = rqd_ref->t3_low | (rqd_ref->t3_high << 3);
- fn = (51 * ((t3-t2) % 26) + t3 + 51 * 26 * t1);
-
- rqd_ta = rqd_hdr->data[sizeof(struct gsm48_req_ref)+2];
- }
-
- return pcu_tx_rach_ind(bts, rqd_ta, ra, fn, is_11bit,
- GSM_L1_BURST_TYPE_ACCESS_0);
-}
-
-/* MS has requested a channel on the RACH */
-static int rsl_rx_chan_rqd(struct msgb *msg)
-{
- struct e1inp_sign_link *sign_link = msg->dst;
- struct gsm_bts *bts = sign_link->trx->bts;
- struct abis_rsl_dchan_hdr *rqd_hdr = msgb_l2(msg);
- struct gsm48_req_ref *rqd_ref;
- enum gsm_chan_t lctype;
- enum gsm_chreq_reason_t chreq_reason;
- struct gsm_lchan *lchan;
- uint8_t rqd_ta;
- int is_lu;
-
- uint16_t arfcn;
- uint8_t subch;
-
- /* parse request reference to be used in immediate assign */
- if (rqd_hdr->data[0] != RSL_IE_REQ_REFERENCE)
- return -EINVAL;
-
- rqd_ref = (struct gsm48_req_ref *) &rqd_hdr->data[1];
-
- /* parse access delay and use as TA */
- if (rqd_hdr->data[sizeof(struct gsm48_req_ref)+1] != RSL_IE_ACCESS_DELAY)
- return -EINVAL;
- rqd_ta = rqd_hdr->data[sizeof(struct gsm48_req_ref)+2];
-
- /* Determine channel request cause code */
- chreq_reason = get_reason_by_chreq(rqd_ref->ra, bts->network->neci);
- LOGP(DRSL, LOGL_NOTICE, "BTS %d CHAN RQD: reason: %s (ra=0x%02x, neci=0x%02x, chreq_reason=0x%02x)\n",
- msg->lchan->ts->trx->bts->nr,
- get_value_string(gsm_chreq_descs, chreq_reason),
- rqd_ref->ra, bts->network->neci, chreq_reason);
-
- /* Handle PDCH related rach requests (in case of BSC-co-located-PCU */
- if (chreq_reason == GSM_CHREQ_REASON_PDCH)
- return rsl_rx_pchan_rqd(msg, bts);
-
- /* determine channel type (SDCCH/TCH_F/TCH_H) based on
- * request reference RA */
- lctype = get_ctype_by_chreq(bts->network, rqd_ref->ra);
-
- rate_ctr_inc(&bts->network->bsc_ctrs->ctr[BSC_CTR_CHREQ_TOTAL]);
-
- /*
- * We want LOCATION UPDATES to succeed and will assign a TCH
- * if we have no SDCCH available.
- */
- is_lu = !!(chreq_reason == GSM_CHREQ_REASON_LOCATION_UPD);
-
- /* check availability / allocate channel */
- lchan = lchan_alloc(bts, lctype, is_lu);
- if (!lchan) {
- LOGP(DRSL, LOGL_NOTICE, "BTS %d CHAN RQD: no resources for %s 0x%x\n",
- msg->lchan->ts->trx->bts->nr, gsm_lchant_name(lctype), rqd_ref->ra);
- rate_ctr_inc(&bts->network->bsc_ctrs->ctr[BSC_CTR_CHREQ_NO_CHANNEL]);
- /* FIXME gather multiple CHAN RQD and reject up to 4 at the same time */
- if (bts->network->T3122)
- rsl_send_imm_ass_rej(bts, 1, rqd_ref, bts->network->T3122 & 0xff);
- return 0;
- }
-
- /*
- * Expecting lchan state to be NONE, except for dyn TS in PDCH mode.
- * Those are expected to be ACTIVE: the PDCH release will be sent from
- * rsl_chan_activate_lchan() below.
- */
- if (lchan->state != LCHAN_S_NONE
- && !(lchan->ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH
- && lchan->ts->dyn.pchan_is == GSM_PCHAN_PDCH
- && lchan->state == LCHAN_S_ACTIVE))
- LOGP(DRSL, LOGL_NOTICE, "%s lchan_alloc() returned channel "
- "in state %s\n", gsm_lchan_name(lchan),
- gsm_lchans_name(lchan->state));
-
- /* save the RACH data as we need it after the CHAN ACT ACK */
- lchan->rqd_ref = talloc_zero(bts, struct gsm48_req_ref);
- if (!lchan->rqd_ref) {
- LOGP(DRSL, LOGL_ERROR, "Failed to allocate gsm48_req_ref.\n");
- lchan_free(lchan);
- return -ENOMEM;
- }
-
- memcpy(lchan->rqd_ref, rqd_ref, sizeof(*rqd_ref));
- lchan->rqd_ta = rqd_ta;
-
- arfcn = lchan->ts->trx->arfcn;
- subch = lchan->nr;
-
- lchan->encr.alg_id = RSL_ENC_ALG_A5(0); /* no encryption */
- lchan->ms_power = ms_pwr_ctl_lvl(bts->band, bts->ms_max_power);
- lchan->bs_power = 0; /* 0dB reduction, output power = Pn */
- lchan->rsl_cmode = RSL_CMOD_SPD_SIGN;
- lchan->tch_mode = GSM48_CMODE_SIGN;
-
- /* Start another timer or assume the BTS sends a ACK/NACK? */
- osmo_timer_setup(&lchan->act_timer, lchan_act_tmr_cb, lchan);
- osmo_timer_schedule(&lchan->act_timer, 4, 0);
-
- DEBUGP(DRSL, "%s Activating ARFCN(%u) SS(%u) lctype %s "
- "r=%s ra=0x%02x ta=%d\n", gsm_lchan_name(lchan), arfcn, subch,
- gsm_lchant_name(lchan->type), gsm_chreq_name(chreq_reason),
- rqd_ref->ra, rqd_ta);
-
- rsl_chan_activate_lchan(lchan, RSL_ACT_INTRA_IMM_ASS, 0);
-
- return 0;
-}
-
-static int rsl_send_imm_assignment(struct gsm_lchan *lchan)
-{
- struct gsm_bts *bts = lchan->ts->trx->bts;
- uint8_t buf[GSM_MACBLOCK_LEN];
- struct gsm48_imm_ass *ia = (struct gsm48_imm_ass *) buf;
-
- /* create IMMEDIATE ASSIGN 04.08 messge */
- memset(ia, 0, sizeof(*ia));
- /* we set ia->l2_plen once we know the length of the MA below */
- ia->proto_discr = GSM48_PDISC_RR;
- ia->msg_type = GSM48_MT_RR_IMM_ASS;
- ia->page_mode = GSM48_PM_SAME;
- gsm48_lchan2chan_desc(&ia->chan_desc, lchan);
-
- /* use request reference extracted from CHAN_RQD */
- memcpy(&ia->req_ref, lchan->rqd_ref, sizeof(ia->req_ref));
- ia->timing_advance = lchan->rqd_ta;
- if (!lchan->ts->hopping.enabled) {
- ia->mob_alloc_len = 0;
- } else {
- ia->mob_alloc_len = lchan->ts->hopping.ma_len;
- memcpy(ia->mob_alloc, lchan->ts->hopping.ma_data, ia->mob_alloc_len);
- }
- /* we need to subtract 1 byte from sizeof(*ia) since ia includes the l2_plen field */
- ia->l2_plen = GSM48_LEN2PLEN((sizeof(*ia)-1) + ia->mob_alloc_len);
-
- /* Start timer T3101 to wait for GSM48_MT_RR_PAG_RESP */
- osmo_timer_setup(&lchan->T3101, t3101_expired, lchan);
- osmo_timer_schedule(&lchan->T3101, bts->network->T3101, 0);
-
- /* send IMMEDIATE ASSIGN CMD on RSL to BTS (to send on CCCH to MS) */
- return rsl_imm_assign_cmd(bts, sizeof(*ia)+ia->mob_alloc_len, (uint8_t *) ia);
-}
-
-/* current load on the CCCH */
-static int rsl_rx_ccch_load(struct msgb *msg)
-{
- struct e1inp_sign_link *sign_link = msg->dst;
- struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg);
- struct ccch_signal_data sd;
-
- sd.bts = sign_link->trx->bts;
- sd.rach_slot_count = -1;
- sd.rach_busy_count = -1;
- sd.rach_access_count = -1;
-
- switch (rslh->data[0]) {
- case RSL_IE_PAGING_LOAD:
- sd.pg_buf_space = rslh->data[1] << 8 | rslh->data[2];
- if (is_ipaccess_bts(sign_link->trx->bts) && sd.pg_buf_space == 0xffff) {
- /* paging load below configured threshold, use 50 as default */
- sd.pg_buf_space = 50;
- }
- paging_update_buffer_space(sign_link->trx->bts, sd.pg_buf_space);
- osmo_signal_dispatch(SS_CCCH, S_CCCH_PAGING_LOAD, &sd);
- break;
- case RSL_IE_RACH_LOAD:
- if (msg->data_len >= 7) {
- sd.rach_slot_count = rslh->data[2] << 8 | rslh->data[3];
- sd.rach_busy_count = rslh->data[4] << 8 | rslh->data[5];
- sd.rach_access_count = rslh->data[6] << 8 | rslh->data[7];
- osmo_signal_dispatch(SS_CCCH, S_CCCH_RACH_LOAD, &sd);
- }
- break;
- default:
- break;
- }
-
- return 0;
-}
-
-static int abis_rsl_rx_cchan(struct msgb *msg)
-{
- struct e1inp_sign_link *sign_link = msg->dst;
- struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg);
- int rc = 0;
- uint32_t tlli;
-
- msg->lchan = lchan_lookup(sign_link->trx, rslh->chan_nr,
- "Abis RSL rx CCHAN: ");
-
- switch (rslh->c.msg_type) {
- case RSL_MT_CHAN_RQD:
- /* MS has requested a channel on the RACH */
- rc = rsl_rx_chan_rqd(msg);
- break;
- case RSL_MT_CCCH_LOAD_IND:
- /* current load on the CCCH */
- rc = rsl_rx_ccch_load(msg);
- break;
- case RSL_MT_DELETE_IND:
- /* CCCH overloaded, IMM_ASSIGN was dropped */
- case RSL_MT_CBCH_LOAD_IND:
- /* current load on the CBCH */
- LOGP(DRSL, LOGL_NOTICE, "Unimplemented Abis RSL TRX message "
- "type 0x%02x\n", rslh->c.msg_type);
- break;
- case 0x10: /* Ericsson specific: Immediate Assign Sent */
- /* FIXME: Replace the messy message parsing below
- * with proper TV parser */
- LOGP(DRSL, LOGL_INFO, "IMM.ass sent\n");
- if(msg->len < 9)
- LOGP(DRSL, LOGL_ERROR, "short IMM.ass sent message!\n");
- else if(msg->data[4] != 0xf1)
- LOGP(DRSL, LOGL_ERROR, "unsupported IMM.ass message format! (please fix)\n");
- else {
- msgb_pull(msg, 5); /* drop previous data to use msg_pull_u32 */
- tlli = msgb_pull_u32(msg);
- pcu_tx_imm_ass_sent(sign_link->trx->bts, tlli);
- }
- break;
- default:
- LOGP(DRSL, LOGL_NOTICE, "Unknown Abis RSL TRX message type "
- "0x%02x\n", rslh->c.msg_type);
- return -EINVAL;
- }
-
- return rc;
-}
-
-static int rsl_rx_rll_err_ind(struct msgb *msg)
-{
- struct tlv_parsed tp;
- struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
- uint8_t rlm_cause;
-
- rsl_tlv_parse(&tp, rllh->data, msgb_l2len(msg) - sizeof(*rllh));
- if (!TLVP_PRESENT(&tp, RSL_IE_RLM_CAUSE)) {
- LOGP(DRLL, LOGL_ERROR,
- "%s ERROR INDICATION without mandantory cause.\n",
- gsm_lchan_name(msg->lchan));
- return -1;
- }
-
- rlm_cause = *TLVP_VAL(&tp, RSL_IE_RLM_CAUSE);
- LOGP(DRLL, LOGL_ERROR, "%s ERROR INDICATION cause=%s in state=%s\n",
- gsm_lchan_name(msg->lchan),
- rsl_rlm_cause_name(rlm_cause),
- gsm_lchans_name(msg->lchan->state));
-
- rll_indication(msg->lchan, rllh->link_id, BSC_RLLR_IND_ERR_IND);
-
- if (rlm_cause == RLL_CAUSE_T200_EXPIRED) {
- rate_ctr_inc(&msg->lchan->ts->trx->bts->network->bsc_ctrs->ctr[BSC_CTR_CHAN_RLL_ERR]);
- return rsl_rf_chan_release_err(msg->lchan);
- }
-
- return 0;
-}
-
-static void rsl_handle_release(struct gsm_lchan *lchan)
-{
- int sapi;
- struct gsm_bts *bts;
-
- /*
- * Maybe only one link/SAPI was releasd or the error handling
- * was activated. Just return now and let the other code handle
- * it.
- */
- if (lchan->state != LCHAN_S_REL_REQ)
- return;
-
- for (sapi = 0; sapi < ARRAY_SIZE(lchan->sapis); ++sapi) {
- if (lchan->sapis[sapi] == LCHAN_SAPI_UNUSED)
- continue;
- LOGP(DRSL, LOGL_DEBUG, "%s waiting for SAPI=%d to be released.\n",
- gsm_lchan_name(lchan), sapi);
- return;
- }
-
-
- /* Stop T3109 and wait for T3111 before re-using the channel */
- osmo_timer_del(&lchan->T3109);
- osmo_timer_setup(&lchan->T3111, t3111_expired, lchan);
- bts = lchan->ts->trx->bts;
- osmo_timer_schedule(&lchan->T3111, bts->network->T3111, 0);
-}
-
-/* ESTABLISH INDICATION, LOCATION AREA UPDATE REQUEST
- 0x02, 0x06,
- 0x01, 0x20,
- 0x02, 0x00,
- 0x0b, 0x00, 0x0f, 0x05, 0x08, ... */
-
-static int abis_rsl_rx_rll(struct msgb *msg)
-{
- struct e1inp_sign_link *sign_link = msg->dst;
- struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
- int rc = 0;
- char *ts_name;
- uint8_t sapi = rllh->link_id & 7;
-
- msg->lchan = lchan_lookup(sign_link->trx, rllh->chan_nr,
- "Abis RSL rx RLL: ");
- ts_name = gsm_lchan_name(msg->lchan);
- DEBUGP(DRLL, "%s SAPI=%u ", ts_name, sapi);
-
- switch (rllh->c.msg_type) {
- case RSL_MT_DATA_IND:
- DEBUGPC(DRLL, "DATA INDICATION\n");
- if (msgb_l2len(msg) >
- sizeof(struct abis_rsl_common_hdr) + sizeof(*rllh) &&
- rllh->data[0] == RSL_IE_L3_INFO) {
- msg->l3h = &rllh->data[3];
- return gsm0408_rcvmsg(msg, rllh->link_id);
- }
- break;
- case RSL_MT_EST_IND:
- DEBUGPC(DRLL, "ESTABLISH INDICATION\n");
- /* lchan is established, stop T3101 */
- msg->lchan->sapis[rllh->link_id & 0x7] = LCHAN_SAPI_MS;
- osmo_timer_del(&msg->lchan->T3101);
- if (msgb_l2len(msg) >
- sizeof(struct abis_rsl_common_hdr) + sizeof(*rllh) &&
- rllh->data[0] == RSL_IE_L3_INFO) {
- msg->l3h = &rllh->data[3];
- return gsm0408_rcvmsg(msg, rllh->link_id);
- }
- break;
- case RSL_MT_EST_CONF:
- DEBUGPC(DRLL, "ESTABLISH CONFIRM\n");
- msg->lchan->sapis[rllh->link_id & 0x7] = LCHAN_SAPI_NET;
- rll_indication(msg->lchan, rllh->link_id,
- BSC_RLLR_IND_EST_CONF);
- break;
- case RSL_MT_REL_IND:
- /* BTS informs us of having received DISC from MS */
- DEBUGPC(DRLL, "RELEASE INDICATION\n");
- msg->lchan->sapis[rllh->link_id & 0x7] = LCHAN_SAPI_UNUSED;
- rll_indication(msg->lchan, rllh->link_id,
- BSC_RLLR_IND_REL_IND);
- rsl_handle_release(msg->lchan);
- break;
- case RSL_MT_REL_CONF:
- /* BTS informs us of having received UA from MS,
- * in response to DISC that we've sent earlier */
- DEBUGPC(DRLL, "RELEASE CONFIRMATION\n");
- msg->lchan->sapis[rllh->link_id & 0x7] = LCHAN_SAPI_UNUSED;
- rsl_handle_release(msg->lchan);
- break;
- case RSL_MT_ERROR_IND:
- DEBUGPC(DRLL, "ERROR INDICATION\n");
- rc = rsl_rx_rll_err_ind(msg);
- break;
- case RSL_MT_UNIT_DATA_IND:
- DEBUGPC(DRLL, "UNIT DATA INDICATION\n");
- LOGP(DRLL, LOGL_NOTICE, "unimplemented Abis RLL message "
- "type 0x%02x\n", rllh->c.msg_type);
- break;
- default:
- DEBUGPC(DRLL, "UNKNOWN\n");
- LOGP(DRLL, LOGL_NOTICE, "unknown Abis RLL message "
- "type 0x%02x\n", rllh->c.msg_type);
- }
- return rc;
-}
-
-static uint8_t ipa_smod_s_for_lchan(struct gsm_lchan *lchan)
-{
- switch (lchan->tch_mode) {
- case GSM48_CMODE_SPEECH_V1:
- switch (lchan->type) {
- case GSM_LCHAN_TCH_F:
- return 0x00;
- case GSM_LCHAN_TCH_H:
- return 0x03;
- default:
- break;
- }
- break;
- case GSM48_CMODE_SPEECH_EFR:
- switch (lchan->type) {
- case GSM_LCHAN_TCH_F:
- return 0x01;
- /* there's no half-rate EFR */
- default:
- break;
- }
- break;
- case GSM48_CMODE_SPEECH_AMR:
- switch (lchan->type) {
- case GSM_LCHAN_TCH_F:
- return 0x02;
- case GSM_LCHAN_TCH_H:
- return 0x05;
- default:
- break;
- }
- break;
- default:
- break;
- }
- LOGP(DRSL, LOGL_ERROR, "Cannot determine ip.access speech mode for "
- "tch_mode == 0x%02x\n", lchan->tch_mode);
- return 0;
-}
-
-static uint8_t ipa_rtp_pt_for_lchan(struct gsm_lchan *lchan)
-{
- switch (lchan->tch_mode) {
- case GSM48_CMODE_SPEECH_V1:
- switch (lchan->type) {
- case GSM_LCHAN_TCH_F:
- return RTP_PT_GSM_FULL;
- case GSM_LCHAN_TCH_H:
- return RTP_PT_GSM_HALF;
- default:
- break;
- }
- break;
- case GSM48_CMODE_SPEECH_EFR:
- switch (lchan->type) {
- case GSM_LCHAN_TCH_F:
- return RTP_PT_GSM_EFR;
- /* there's no half-rate EFR */
- default:
- break;
- }
- break;
- case GSM48_CMODE_SPEECH_AMR:
- switch (lchan->type) {
- case GSM_LCHAN_TCH_F:
- case GSM_LCHAN_TCH_H:
- return RTP_PT_AMR;
- default:
- break;
- }
- break;
- default:
- break;
- }
- LOGP(DRSL, LOGL_ERROR, "Cannot determine ip.access rtp payload type for "
- "tch_mode == 0x%02x\n & lchan_type == %d",
- lchan->tch_mode, lchan->type);
- return 0;
-}
-
-/* ip.access specific RSL extensions */
-static void ipac_parse_rtp(struct gsm_lchan *lchan, struct tlv_parsed *tv)
-{
- struct in_addr ip;
- uint16_t port, conn_id;
-
- if (TLVP_PRESENT(tv, RSL_IE_IPAC_LOCAL_IP)) {
- ip.s_addr = tlvp_val32_unal(tv, RSL_IE_IPAC_LOCAL_IP);
- DEBUGPC(DRSL, "LOCAL_IP=%s ", inet_ntoa(ip));
- lchan->abis_ip.bound_ip = ntohl(ip.s_addr);
- }
-
- if (TLVP_PRESENT(tv, RSL_IE_IPAC_LOCAL_PORT)) {
- port = tlvp_val16_unal(tv, RSL_IE_IPAC_LOCAL_PORT);
- port = ntohs(port);
- DEBUGPC(DRSL, "LOCAL_PORT=%u ", port);
- lchan->abis_ip.bound_port = port;
- }
-
- if (TLVP_PRESENT(tv, RSL_IE_IPAC_CONN_ID)) {
- conn_id = tlvp_val16_unal(tv, RSL_IE_IPAC_CONN_ID);
- conn_id = ntohs(conn_id);
- DEBUGPC(DRSL, "CON_ID=%u ", conn_id);
- lchan->abis_ip.conn_id = conn_id;
- }
-
- if (TLVP_PRESENT(tv, RSL_IE_IPAC_RTP_PAYLOAD2)) {
- lchan->abis_ip.rtp_payload2 =
- *TLVP_VAL(tv, RSL_IE_IPAC_RTP_PAYLOAD2);
- DEBUGPC(DRSL, "RTP_PAYLOAD2=0x%02x ",
- lchan->abis_ip.rtp_payload2);
- }
-
- if (TLVP_PRESENT(tv, RSL_IE_IPAC_SPEECH_MODE)) {
- lchan->abis_ip.speech_mode =
- *TLVP_VAL(tv, RSL_IE_IPAC_SPEECH_MODE);
- DEBUGPC(DRSL, "speech_mode=0x%02x ",
- lchan->abis_ip.speech_mode);
- }
-
- if (TLVP_PRESENT(tv, RSL_IE_IPAC_REMOTE_IP)) {
- ip.s_addr = tlvp_val32_unal(tv, RSL_IE_IPAC_REMOTE_IP);
- DEBUGPC(DRSL, "REMOTE_IP=%s ", inet_ntoa(ip));
- lchan->abis_ip.connect_ip = ntohl(ip.s_addr);
- }
-
- if (TLVP_PRESENT(tv, RSL_IE_IPAC_REMOTE_PORT)) {
- port = tlvp_val16_unal(tv, RSL_IE_IPAC_REMOTE_PORT);
- port = ntohs(port);
- DEBUGPC(DRSL, "REMOTE_PORT=%u ", port);
- lchan->abis_ip.connect_port = port;
- }
-}
-
-/*! \brief Issue IPA RSL CRCX to configure RTP on BTS side
- * \param[in] lchan Logical Channel for which we issue CRCX
- */
-int rsl_ipacc_crcx(struct gsm_lchan *lchan)
-{
- struct msgb *msg = rsl_msgb_alloc();
- struct abis_rsl_dchan_hdr *dh;
-
- dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
- init_dchan_hdr(dh, RSL_MT_IPAC_CRCX);
- dh->c.msg_discr = ABIS_RSL_MDISC_IPACCESS;
- dh->chan_nr = gsm_lchan2chan_nr(lchan);
-
- /* 0x1- == receive-only, 0x-1 == EFR codec */
- lchan->abis_ip.speech_mode = 0x10 | ipa_smod_s_for_lchan(lchan);
- lchan->abis_ip.rtp_payload = ipa_rtp_pt_for_lchan(lchan);
- msgb_tv_put(msg, RSL_IE_IPAC_SPEECH_MODE, lchan->abis_ip.speech_mode);
- msgb_tv_put(msg, RSL_IE_IPAC_RTP_PAYLOAD, lchan->abis_ip.rtp_payload);
-
- DEBUGP(DRSL, "%s IPAC_BIND speech_mode=0x%02x RTP_PAYLOAD=%d\n",
- gsm_lchan_name(lchan), lchan->abis_ip.speech_mode,
- lchan->abis_ip.rtp_payload);
-
- msg->dst = lchan->ts->trx->rsl_link;
-
- return abis_rsl_sendmsg(msg);
-}
-
-/*! \brief Issue IPA RSL MDCX to configure MGW-side of RTP
- * \param[in] lchan Logical Channel for which we issue MDCX
- * \param[in] ip Remote (MGW) IP address for RTP
- * \param[in] port Remote (MGW) UDP port number for RTP
- * \param[in] rtp_payload2 Contents of RTP PAYLOAD 2 IE
- */
-int rsl_ipacc_mdcx(struct gsm_lchan *lchan, uint32_t ip, uint16_t port,
- uint8_t rtp_payload2)
-{
- struct msgb *msg = rsl_msgb_alloc();
- struct abis_rsl_dchan_hdr *dh;
- uint32_t *att_ip;
- struct in_addr ia;
-
- dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
- init_dchan_hdr(dh, RSL_MT_IPAC_MDCX);
- dh->c.msg_discr = ABIS_RSL_MDISC_IPACCESS;
- dh->chan_nr = gsm_lchan2chan_nr(lchan);
-
- /* we need to store these now as MDCX_ACK does not return them :( */
- lchan->abis_ip.rtp_payload2 = rtp_payload2;
- lchan->abis_ip.connect_port = port;
- lchan->abis_ip.connect_ip = ip;
-
- /* 0x0- == both directions, 0x-1 == EFR codec */
- lchan->abis_ip.speech_mode = 0x00 | ipa_smod_s_for_lchan(lchan);
- lchan->abis_ip.rtp_payload = ipa_rtp_pt_for_lchan(lchan);
-
- ia.s_addr = htonl(ip);
- DEBUGP(DRSL, "%s IPAC_MDCX IP=%s PORT=%d RTP_PAYLOAD=%d RTP_PAYLOAD2=%d "
- "CONN_ID=%d speech_mode=0x%02x\n", gsm_lchan_name(lchan),
- inet_ntoa(ia), port, lchan->abis_ip.rtp_payload, rtp_payload2,
- lchan->abis_ip.conn_id, lchan->abis_ip.speech_mode);
-
- msgb_tv16_put(msg, RSL_IE_IPAC_CONN_ID, lchan->abis_ip.conn_id);
- msgb_v_put(msg, RSL_IE_IPAC_REMOTE_IP);
- att_ip = (uint32_t *) msgb_put(msg, sizeof(ip));
- *att_ip = ia.s_addr;
- msgb_tv16_put(msg, RSL_IE_IPAC_REMOTE_PORT, port);
- msgb_tv_put(msg, RSL_IE_IPAC_SPEECH_MODE, lchan->abis_ip.speech_mode);
- msgb_tv_put(msg, RSL_IE_IPAC_RTP_PAYLOAD, lchan->abis_ip.rtp_payload);
- if (rtp_payload2)
- msgb_tv_put(msg, RSL_IE_IPAC_RTP_PAYLOAD2, rtp_payload2);
-
- msg->dst = lchan->ts->trx->rsl_link;
-
- return abis_rsl_sendmsg(msg);
-}
-
-/* tell BTS to connect RTP stream to our local RTP socket */
-int rsl_ipacc_mdcx_to_rtpsock(struct gsm_lchan *lchan)
-{
- struct rtp_socket *rs = lchan->abis_ip.rtp_socket;
- int rc;
-
- rc = rsl_ipacc_mdcx(lchan, ntohl(rs->rtp.sin_local.sin_addr.s_addr),
- ntohs(rs->rtp.sin_local.sin_port),
- /* FIXME: use RTP payload of bound socket, not BTS*/
- lchan->abis_ip.rtp_payload2);
-
- return rc;
-}
-
-int rsl_ipacc_pdch_activate(struct gsm_bts_trx_ts *ts, int act)
-{
- struct msgb *msg = rsl_msgb_alloc();
- struct abis_rsl_dchan_hdr *dh;
- uint8_t msg_type;
-
- if (ts->flags & TS_F_PDCH_PENDING_MASK) {
- LOGP(DRSL, LOGL_ERROR,
- "%s PDCH %s requested, but a PDCH%s%s is still pending\n",
- gsm_ts_name(ts),
- act ? "ACT" : "DEACT",
- ts->flags & TS_F_PDCH_ACT_PENDING? " ACT" : "",
- ts->flags & TS_F_PDCH_DEACT_PENDING? " DEACT" : "");
- return -EINVAL;
- }
-
- if (act){
- /* Callers should heed the GPRS mode. */
- OSMO_ASSERT(ts->trx->bts->gprs.mode != BTS_GPRS_NONE);
- msg_type = RSL_MT_IPAC_PDCH_ACT;
- ts->flags |= TS_F_PDCH_ACT_PENDING;
- } else {
- msg_type = RSL_MT_IPAC_PDCH_DEACT;
- ts->flags |= TS_F_PDCH_DEACT_PENDING;
- }
- /* TODO add timeout to cancel PDCH DE/ACT */
-
- dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
- init_dchan_hdr(dh, msg_type);
- dh->c.msg_discr = ABIS_RSL_MDISC_DED_CHAN;
- dh->chan_nr = gsm_pchan2chan_nr(GSM_PCHAN_PDCH, ts->nr, 0);
-
- DEBUGP(DRSL, "%s IPAC PDCH %sACT\n", gsm_ts_name(ts),
- act ? "" : "DE");
-
- msg->dst = ts->trx->rsl_link;
-
- return abis_rsl_sendmsg(msg);
-}
-
-static int abis_rsl_rx_ipacc_crcx_ack(struct msgb *msg)
-{
- struct abis_rsl_dchan_hdr *dh = msgb_l2(msg);
- struct tlv_parsed tv;
- struct gsm_lchan *lchan = msg->lchan;
-
- /* the BTS has acknowledged a local bind, it now tells us the IP
- * address and port number to which it has bound the given logical
- * channel */
-
- rsl_tlv_parse(&tv, dh->data, msgb_l2len(msg)-sizeof(*dh));
- if (!TLVP_PRESENT(&tv, RSL_IE_IPAC_LOCAL_PORT) ||
- !TLVP_PRESENT(&tv, RSL_IE_IPAC_LOCAL_IP) ||
- !TLVP_PRESENT(&tv, RSL_IE_IPAC_CONN_ID)) {
- LOGP(DRSL, LOGL_NOTICE, "mandatory IE missing");
- return -EINVAL;
- }
-
- ipac_parse_rtp(lchan, &tv);
-
- osmo_signal_dispatch(SS_ABISIP, S_ABISIP_CRCX_ACK, msg->lchan);
-
- return 0;
-}
-
-static int abis_rsl_rx_ipacc_mdcx_ack(struct msgb *msg)
-{
- struct abis_rsl_dchan_hdr *dh = msgb_l2(msg);
- struct tlv_parsed tv;
- struct gsm_lchan *lchan = msg->lchan;
-
- /* the BTS has acknowledged a remote connect request and
- * it now tells us the IP address and port number to which it has
- * connected the given logical channel */
-
- rsl_tlv_parse(&tv, dh->data, msgb_l2len(msg)-sizeof(*dh));
- ipac_parse_rtp(lchan, &tv);
- osmo_signal_dispatch(SS_ABISIP, S_ABISIP_MDCX_ACK, msg->lchan);
-
- return 0;
-}
-
-static int abis_rsl_rx_ipacc_dlcx_ind(struct msgb *msg)
-{
- struct abis_rsl_dchan_hdr *dh = msgb_l2(msg);
- struct tlv_parsed tv;
-
- rsl_tlv_parse(&tv, dh->data, msgb_l2len(msg)-sizeof(*dh));
-
- if (TLVP_PRESENT(&tv, RSL_IE_CAUSE))
- print_rsl_cause(LOGL_DEBUG, TLVP_VAL(&tv, RSL_IE_CAUSE),
- TLVP_LEN(&tv, RSL_IE_CAUSE));
-
- osmo_signal_dispatch(SS_ABISIP, S_ABISIP_DLCX_IND, msg->lchan);
-
- return 0;
-}
-
-static int abis_rsl_rx_ipacc(struct msgb *msg)
-{
- struct e1inp_sign_link *sign_link = msg->dst;
- struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
- char *ts_name;
- int rc = 0;
-
- msg->lchan = lchan_lookup(sign_link->trx, rllh->chan_nr,
- "Abis RSL rx IPACC: ");
- ts_name = gsm_lchan_name(msg->lchan);
-
- switch (rllh->c.msg_type) {
- case RSL_MT_IPAC_CRCX_ACK:
- DEBUGP(DRSL, "%s IPAC_CRCX_ACK ", ts_name);
- rc = abis_rsl_rx_ipacc_crcx_ack(msg);
- break;
- case RSL_MT_IPAC_CRCX_NACK:
- /* somehow the BTS was unable to bind the lchan to its local
- * port?!? */
- LOGP(DRSL, LOGL_ERROR, "%s IPAC_CRCX_NACK\n", ts_name);
- break;
- case RSL_MT_IPAC_MDCX_ACK:
- /* the BTS tells us that a connect operation was successful */
- DEBUGP(DRSL, "%s IPAC_MDCX_ACK ", ts_name);
- rc = abis_rsl_rx_ipacc_mdcx_ack(msg);
- break;
- case RSL_MT_IPAC_MDCX_NACK:
- /* somehow the BTS was unable to connect the lchan to a remote
- * port */
- LOGP(DRSL, LOGL_ERROR, "%s IPAC_MDCX_NACK\n", ts_name);
- break;
- case RSL_MT_IPAC_DLCX_IND:
- DEBUGP(DRSL, "%s IPAC_DLCX_IND ", ts_name);
- rc = abis_rsl_rx_ipacc_dlcx_ind(msg);
- break;
- default:
- LOGP(DRSL, LOGL_NOTICE, "Unknown ip.access msg_type 0x%02x\n",
- rllh->c.msg_type);
- break;
- }
- DEBUGPC(DRSL, "\n");
-
- return rc;
-}
-
-int dyn_ts_switchover_start(struct gsm_bts_trx_ts *ts,
- enum gsm_phys_chan_config to_pchan)
-{
- int ss;
- int rc = -EIO;
-
- OSMO_ASSERT(ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH);
- DEBUGP(DRSL, "%s starting switchover to %s\n",
- gsm_ts_and_pchan_name(ts), gsm_pchan_name(to_pchan));
-
- if (ts->dyn.pchan_is != ts->dyn.pchan_want) {
- LOGP(DRSL, LOGL_ERROR,
- "%s: Attempt to switch dynamic channel to %s,"
- " but is already in switchover.\n",
- gsm_ts_and_pchan_name(ts),
- gsm_pchan_name(to_pchan));
- return ts->dyn.pchan_want == to_pchan? 0 : -EAGAIN;
- }
-
- if (ts->dyn.pchan_is == to_pchan) {
- LOGP(DRSL, LOGL_INFO,
- "%s %s Already is in %s mode, cannot switchover.\n",
- gsm_ts_name(ts), gsm_pchan_name(ts->pchan),
- gsm_pchan_name(to_pchan));
- return -EINVAL;
- }
-
- /* Paranoia: let's make sure all is indeed released. */
- for (ss = 0; ss < ts_subslots(ts); ss++) {
- struct gsm_lchan *lc = &ts->lchan[ss];
- if (lc->state != LCHAN_S_NONE) {
- LOGP(DRSL, LOGL_ERROR,
- "%s Attempt to switch dynamic channel to %s,"
- " but is not fully released.\n",
- gsm_ts_and_pchan_name(ts),
- gsm_pchan_name(to_pchan));
- return -EAGAIN;
- }
- }
-
- /* Record that we're busy switching. */
- ts->dyn.pchan_want = to_pchan;
-
- /*
- * To switch from PDCH, we need to initiate the release from the BSC
- * side. dyn_ts_switchover_continue() will be called from
- * rsl_rx_rf_chan_rel_ack(). PDCH is always on lchan[0].
- */
- if (ts->dyn.pchan_is == GSM_PCHAN_PDCH) {
- rsl_lchan_set_state(ts->lchan, LCHAN_S_REL_REQ);
- rc = rsl_rf_chan_release(ts->lchan, 0, SACCH_NONE);
- if (rc) {
- LOGP(DRSL, LOGL_ERROR,
- "%s RSL RF Chan Release failed\n",
- gsm_ts_and_pchan_name(ts));
- return dyn_ts_switchover_failed(ts, rc);
- }
- return 0;
- }
-
- /*
- * To switch from TCH/F and TCH/H pchans, this has been called from
- * rsl_rx_rf_chan_rel_ack(), i.e. release is complete. Go ahead and
- * activate as new type. This will always be PDCH.
- */
- return dyn_ts_switchover_continue(ts);
-}
-
-static int dyn_ts_switchover_continue(struct gsm_bts_trx_ts *ts)
-{
- int rc;
- uint8_t act_type;
- uint8_t ho_ref;
- int ss;
- struct gsm_lchan *lchan;
-
- OSMO_ASSERT(ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH);
- DEBUGP(DRSL, "%s switchover: release complete,"
- " activating new pchan type\n",
- gsm_ts_and_pchan_name(ts));
-
- if (ts->dyn.pchan_is == ts->dyn.pchan_want) {
- LOGP(DRSL, LOGL_ERROR,
- "%s Requested to switchover dynamic channel to the"
- " same type it is already in.\n",
- gsm_ts_and_pchan_name(ts));
- return 0;
- }
-
- for (ss = 0; ss < ts_subslots(ts); ss++) {
- lchan = &ts->lchan[ss];
- if (lchan->rqd_ref) {
- LOGP(DRSL, LOGL_ERROR,
- "%s During dyn TS switchover, expecting no"
- " Request Reference to be pending. Discarding!\n",
- gsm_lchan_name(lchan));
- talloc_free(lchan->rqd_ref);
- lchan->rqd_ref = NULL;
- }
- }
-
- /*
- * When switching pchan modes, all lchans are unused. So always
- * activate whatever wants to be activated on the first lchan. (We
- * wouldn't remember to use lchan[1] across e.g. a PDCH deact anyway)
- */
- lchan = ts->lchan;
-
- /*
- * For TCH/x, the lchan->type has been set in lchan_alloc(), but it may
- * have been lost during channel release due to dynamic switchover.
- *
- * For PDCH, the lchan->type will actually remain NONE.
- * TODO: set GSM_LCHAN_PDTCH?
- */
- switch (ts->dyn.pchan_want) {
- case GSM_PCHAN_TCH_F:
- lchan->type = GSM_LCHAN_TCH_F;
- break;
- case GSM_PCHAN_TCH_H:
- lchan->type = GSM_LCHAN_TCH_H;
- break;
- case GSM_PCHAN_PDCH:
- lchan->type = GSM_LCHAN_NONE;
- break;
- default:
- LOGP(DRSL, LOGL_ERROR,
- "%s Invalid target pchan for dynamic TS\n",
- gsm_ts_and_pchan_name(ts));
- }
-
- act_type = (ts->dyn.pchan_want == GSM_PCHAN_PDCH)
- ? RSL_ACT_OSMO_PDCH
- : lchan->dyn.act_type;
- ho_ref = (ts->dyn.pchan_want == GSM_PCHAN_PDCH)
- ? 0
- : lchan->dyn.ho_ref;
-
- /* Fetch the rqd_ref back from before switchover started. */
- lchan->rqd_ref = lchan->dyn.rqd_ref;
- lchan->rqd_ta = lchan->dyn.rqd_ta;
- lchan->dyn.rqd_ref = NULL;
- lchan->dyn.rqd_ta = 0;
-
- /* During switchover, we have received a release ack, which means that
- * the act_timer has been stopped. Start the timer again so we mark
- * this channel broken if the activation ack comes too late. */
- osmo_timer_setup(&lchan->act_timer, lchan_act_tmr_cb, lchan);
- osmo_timer_schedule(&lchan->act_timer, 4, 0);
-
- rc = rsl_chan_activate_lchan(lchan, act_type, ho_ref);
- if (rc) {
- LOGP(DRSL, LOGL_ERROR,
- "%s RSL Chan Activate failed\n",
- gsm_ts_and_pchan_name(ts));
- return dyn_ts_switchover_failed(ts, rc);
- }
- return 0;
-}
-
-static int dyn_ts_switchover_failed(struct gsm_bts_trx_ts *ts, int rc)
-{
- ts->dyn.pchan_want = ts->dyn.pchan_is;
- LOGP(DRSL, LOGL_ERROR, "%s Error %d during dynamic channel switchover."
- " Going back to previous pchan.\n", gsm_ts_and_pchan_name(ts),
- rc);
- return rc;
-}
-
-static void dyn_ts_switchover_complete(struct gsm_lchan *lchan)
-{
- enum gsm_phys_chan_config pchan_act;
- enum gsm_phys_chan_config pchan_was;
- struct gsm_bts_trx_ts *ts = lchan->ts;
-
- OSMO_ASSERT(ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH);
-
- pchan_act = pchan_for_lchant(lchan->type);
- /*
- * Paranoia: do the types match?
- * In case of errors: we've received an act ack already, so what to do
- * about it? Logging the error should suffice for now.
- */
- if (pchan_act != ts->dyn.pchan_want)
- LOGP(DRSL, LOGL_ERROR,
- "%s Requested transition does not match lchan type %s\n",
- gsm_ts_and_pchan_name(ts),
- gsm_lchant_name(lchan->type));
-
- pchan_was = ts->dyn.pchan_is;
- ts->dyn.pchan_is = ts->dyn.pchan_want = pchan_act;
-
- if (pchan_was != ts->dyn.pchan_is)
- LOGP(DRSL, LOGL_INFO, "%s switchover from %s complete.\n",
- gsm_ts_and_pchan_name(ts), gsm_pchan_name(pchan_was));
-}
-
-/* Entry-point where L2 RSL from BTS enters */
-int abis_rsl_rcvmsg(struct msgb *msg)
-{
- struct abis_rsl_common_hdr *rslh;
- int rc = 0;
-
- if (!msg) {
- DEBUGP(DRSL, "Empty RSL msg?..\n");
- return -1;
- }
-
- if (msgb_l2len(msg) < sizeof(*rslh)) {
- DEBUGP(DRSL, "Truncated RSL message with l2len: %u\n", msgb_l2len(msg));
- msgb_free(msg);
- return -1;
- }
-
- rslh = msgb_l2(msg);
-
- switch (rslh->msg_discr & 0xfe) {
- case ABIS_RSL_MDISC_RLL:
- rc = abis_rsl_rx_rll(msg);
- break;
- case ABIS_RSL_MDISC_DED_CHAN:
- rc = abis_rsl_rx_dchan(msg);
- break;
- case ABIS_RSL_MDISC_COM_CHAN:
- rc = abis_rsl_rx_cchan(msg);
- break;
- case ABIS_RSL_MDISC_TRX:
- rc = abis_rsl_rx_trx(msg);
- break;
- case ABIS_RSL_MDISC_LOC:
- LOGP(DRSL, LOGL_NOTICE, "unimplemented RSL msg disc 0x%02x\n",
- rslh->msg_discr);
- break;
- case ABIS_RSL_MDISC_IPACCESS:
- rc = abis_rsl_rx_ipacc(msg);
- break;
- default:
- LOGP(DRSL, LOGL_NOTICE, "unknown RSL message discriminator "
- "0x%02x\n", rslh->msg_discr);
- rc = -EINVAL;
- }
- msgb_free(msg);
- return rc;
-}
-
-int rsl_sms_cb_command(struct gsm_bts *bts, uint8_t chan_number,
- struct rsl_ie_cb_cmd_type cb_command,
- const uint8_t *data, int len)
-{
- struct abis_rsl_dchan_hdr *dh;
- struct msgb *cb_cmd;
-
- cb_cmd = rsl_msgb_alloc();
- if (!cb_cmd)
- return -1;
-
- dh = (struct abis_rsl_dchan_hdr *) msgb_put(cb_cmd, sizeof(*dh));
- init_dchan_hdr(dh, RSL_MT_SMS_BC_CMD);
- dh->c.msg_discr = ABIS_RSL_MDISC_COM_CHAN;
- dh->chan_nr = chan_number; /* TODO: check the chan config */
-
- msgb_tv_put(cb_cmd, RSL_IE_CB_CMD_TYPE, *(uint8_t*)&cb_command);
- msgb_tlv_put(cb_cmd, RSL_IE_SMSCB_MSG, len, data);
-
- cb_cmd->dst = bts->c0->rsl_link;
-
- return abis_rsl_sendmsg(cb_cmd);
-}
-
-int rsl_nokia_si_begin(struct gsm_bts_trx *trx)
-{
- struct abis_rsl_common_hdr *ch;
- struct msgb *msg = rsl_msgb_alloc();
-
- ch = (struct abis_rsl_common_hdr *) msgb_put(msg, sizeof(*ch));
- ch->msg_discr = ABIS_RSL_MDISC_TRX;
- ch->msg_type = 0x40; /* Nokia SI Begin */
-
- msg->dst = trx->rsl_link;
-
- return abis_rsl_sendmsg(msg);
-}
-
-int rsl_nokia_si_end(struct gsm_bts_trx *trx)
-{
- struct abis_rsl_common_hdr *ch;
- struct msgb *msg = rsl_msgb_alloc();
-
- ch = (struct abis_rsl_common_hdr *) msgb_put(msg, sizeof(*ch));
- ch->msg_discr = ABIS_RSL_MDISC_TRX;
- ch->msg_type = 0x41; /* Nokia SI End */
-
- msgb_tv_put(msg, 0xFD, 0x00); /* Nokia Pagemode Info, No paging reorganisation required */
-
- msg->dst = trx->rsl_link;
-
- return abis_rsl_sendmsg(msg);
-}
-
-int rsl_bs_power_control(struct gsm_bts_trx *trx, uint8_t channel, uint8_t reduction)
-{
- struct abis_rsl_common_hdr *ch;
- struct msgb *msg = rsl_msgb_alloc();
-
- ch = (struct abis_rsl_common_hdr *) msgb_put(msg, sizeof(*ch));
- ch->msg_discr = ABIS_RSL_MDISC_DED_CHAN;
- ch->msg_type = RSL_MT_BS_POWER_CONTROL;
-
- msgb_tv_put(msg, RSL_IE_CHAN_NR, channel);
- msgb_tv_put(msg, RSL_IE_BS_POWER, reduction); /* reduction in 2dB steps */
-
- msg->dst = trx->rsl_link;
-
- return abis_rsl_sendmsg(msg);
-}
-
-/**
- * Release all allocated SAPIs starting from @param start and
- * release them with the given release mode. Once the release
- * confirmation arrives it will be attempted to release the
- * the RF channel.
- */
-int rsl_release_sapis_from(struct gsm_lchan *lchan, int start,
- enum rsl_rel_mode release_mode)
-{
- int no_sapi = 1;
- int sapi;
-
- for (sapi = start; sapi < ARRAY_SIZE(lchan->sapis); ++sapi) {
- uint8_t link_id;
- if (lchan->sapis[sapi] == LCHAN_SAPI_UNUSED)
- continue;
-
- link_id = sapi;
- if (lchan->type == GSM_LCHAN_TCH_F || lchan->type == GSM_LCHAN_TCH_H)
- link_id |= 0x40;
- rsl_release_request(lchan, link_id, release_mode);
- no_sapi = 0;
- }
-
- return no_sapi;
-}
-
-int rsl_start_t3109(struct gsm_lchan *lchan)
-{
- struct gsm_bts *bts = lchan->ts->trx->bts;
-
- /* Disabled, mostly legacy code */
- if (bts->network->T3109 == 0)
- return -1;
-
- osmo_timer_setup(&lchan->T3109, t3109_expired, lchan);
- osmo_timer_schedule(&lchan->T3109, bts->network->T3109, 0);
- return 0;
-}
-
-/**
- * \brief directly RF Channel Release the lchan
- *
- * When no SAPI was allocated, directly release the logical channel. This
- * should only be called from chan_alloc.c on channel release handling. In
- * case no SAPI was established the RF Channel can be directly released,
- */
-int rsl_direct_rf_release(struct gsm_lchan *lchan)
-{
- int i;
- for (i = 0; i < ARRAY_SIZE(lchan->sapis); ++i) {
- if (lchan->sapis[i] != LCHAN_SAPI_UNUSED) {
- LOGP(DRSL, LOGL_ERROR, "%s SAPI(%d) still allocated.\n",
- gsm_lchan_name(lchan), i);
- return -1;
- }
- }
-
- /* Now release it */
- return rsl_rf_chan_release(lchan, 0, SACCH_NONE);
-}
diff --git a/openbsc/src/libbsc/arfcn_range_encode.c b/openbsc/src/libbsc/arfcn_range_encode.c
deleted file mode 100644
index 9ca48407e..000000000
--- a/openbsc/src/libbsc/arfcn_range_encode.c
+++ /dev/null
@@ -1,330 +0,0 @@
-/* gsm 04.08 system information (si) encoding and decoding
- * 3gpp ts 04.08 version 7.21.0 release 1998 / etsi ts 100 940 v7.21.0 */
-
-/*
- * (C) 2012 Holger Hans Peter Freyther
- * (C) 2012 by On-Waves
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <openbsc/arfcn_range_encode.h>
-#include <openbsc/debug.h>
-
-#include <osmocom/gsm/protocol/gsm_04_08.h>
-
-#include <osmocom/core/utils.h>
-
-#include <errno.h>
-
-static inline int greatest_power_of_2_lesser_or_equal_to(int index)
-{
- int power_of_2 = 1;
-
- do {
- power_of_2 *= 2;
- } while (power_of_2 <= index);
-
- /* now go back one step */
- return power_of_2 / 2;
-}
-
-static inline int mod(int data, int range)
-{
- int res = data % range;
- while (res < 0)
- res += range;
- return res;
-}
-
-/**
- * Determine at which index to split the ARFCNs to create an
- * equally size partition for the given range. Return -1 if
- * no such partition exists.
- */
-int range_enc_find_index(enum gsm48_range range, const int *freqs, const int size)
-{
- int i, j, n;
-
- const int RANGE_DELTA = (range - 1) / 2;
-
- for (i = 0; i < size; ++i) {
- n = 0;
- for (j = 0; j < size; ++j) {
- if (mod(freqs[j] - freqs[i], range) <= RANGE_DELTA)
- n += 1;
- }
-
- if (n - 1 == (size - 1) / 2)
- return i;
- }
-
- return -1;
-}
-
-/**
- * Range encode the ARFCN list.
- * \param range The range to use.
- * \param arfcns The list of ARFCNs
- * \param size The size of the list of ARFCNs
- * \param out Place to store the W(i) output.
- */
-int range_enc_arfcns(enum gsm48_range range,
- const int *arfcns, int size, int *out,
- const int index)
-{
- int split_at;
- int i;
-
- /*
- * The below is a GNU extension and we can remove it when
- * we move to a quicksort like in-situ swap with the pivot.
- */
- int arfcns_left[size / 2];
- int arfcns_right[size / 2];
- int l_size;
- int r_size;
- int l_origin;
- int r_origin;
-
-
- /* Test the two recursion anchors and stop processing */
- if (size == 0)
- return 0;
-
- if (size == 1) {
- out[index] = 1 + arfcns[0];
- return 0;
- }
-
- /* Now do the processing */
- split_at = range_enc_find_index(range, arfcns, size);
- if (split_at < 0)
- return -EINVAL;
-
- /* we now know where to split */
- out[index] = 1 + arfcns[split_at];
-
- /* calculate the work that needs to be done for the leafs */
- l_origin = mod(arfcns[split_at] + ((range - 1) / 2) + 1, range);
- r_origin = mod(arfcns[split_at] + 1, range);
- for (i = 0, l_size = 0, r_size = 0; i < size; ++i) {
- if (mod(arfcns[i] - l_origin, range) < range / 2)
- arfcns_left[l_size++] = mod(arfcns[i] - l_origin, range);
- if (mod(arfcns[i] - r_origin, range) < range / 2)
- arfcns_right[r_size++] = mod(arfcns[i] - r_origin, range);
- }
-
- /*
- * Now recurse and we need to make this iterative... but as the
- * tree is balanced the stack will not be too deep.
- */
- if (l_size)
- range_enc_arfcns(range / 2, arfcns_left, l_size,
- out, index + greatest_power_of_2_lesser_or_equal_to(index + 1));
- if (r_size)
- range_enc_arfcns((range - 1) / 2, arfcns_right, r_size,
- out, index + (2 * greatest_power_of_2_lesser_or_equal_to(index + 1)));
- return 0;
-}
-
-/*
- * The easiest is to use f0 == arfcns[0]. This means that under certain
- * circumstances we can encode less ARFCNs than possible with an optimal f0.
- *
- * TODO: Solve the optimisation problem and pick f0 so that the max distance
- * is the smallest. Taking into account the modulo operation. I think picking
- * size/2 will be the optimal arfcn.
- */
-/**
- * This implements the range determination as described in GSM 04.08 J4. The
- * result will be a base frequency f0 and the range to use. Note that for range
- * 1024 encoding f0 always refers to ARFCN 0 even if it is not an element of
- * the arfcns list.
- *
- * \param[in] arfcns The input frequencies, they must be sorted, lowest number first
- * \param[in] size The length of the array
- * \param[out] f0 The selected F0 base frequency. It might not be inside the list
- */
-int range_enc_determine_range(const int *arfcns, const int size, int *f0)
-{
- int max = 0;
-
- /*
- * Go for the easiest. And pick arfcns[0] == f0.
- */
- max = arfcns[size - 1] - arfcns[0];
- *f0 = arfcns[0];
-
- if (max < 128 && size <= 29)
- return ARFCN_RANGE_128;
- if (max < 256 && size <= 22)
- return ARFCN_RANGE_256;
- if (max < 512 && size <= 18)
- return ARFCN_RANGE_512;
- if (max < 1024 && size <= 17) {
- *f0 = 0;
- return ARFCN_RANGE_1024;
- }
-
- return ARFCN_RANGE_INVALID;
-}
-
-static void write_orig_arfcn(uint8_t *chan_list, int f0)
-{
- chan_list[0] |= (f0 >> 9) & 1;
- chan_list[1] = (f0 >> 1);
- chan_list[2] = (f0 & 1) << 7;
-}
-
-static void write_all_wn(uint8_t *chan_list, int bit_offs,
- int *w, int w_size, int w1_len)
-{
- int octet_offs = 0; /* offset into chan_list */
- int wk_len = w1_len; /* encoding size in bits of w[k] */
- int k; /* 1 based */
- int level = 0; /* tree level, top level = 0 */
- int lvl_left = 1; /* nodes per tree level */
-
- /* W(2^i) to W(2^(i+1)-1) are on w1_len-i bits when present */
-
- for (k = 1; k <= w_size; k++) {
- int wk_left = wk_len;
- DEBUGP(DRR,
- "k=%d, wk_len=%d, offs=%d:%d, level=%d, "
- "lvl_left=%d\n",
- k, wk_len, octet_offs, bit_offs, level, lvl_left);
-
- while (wk_left > 0) {
- int cur_bits = 8 - bit_offs;
- int cur_mask;
- int wk_slice;
-
- if (cur_bits > wk_left)
- cur_bits = wk_left;
-
- cur_mask = ((1 << cur_bits) - 1);
-
- DEBUGP(DRR,
- " wk_left=%d, cur_bits=%d, offs=%d:%d\n",
- wk_left, cur_bits, octet_offs, bit_offs);
-
- /* advance */
- wk_left -= cur_bits;
- bit_offs += cur_bits;
-
- /* right aligned wk data for current out octet */
- wk_slice = (w[k-1] >> wk_left) & cur_mask;
-
- /* cur_bits now contains the number of bits
- * that are to be copied from wk to the chan_list.
- * wk_left is set to the number of bits that must
- * not yet be copied.
- * bit_offs points after the bit area that is going to
- * be overwritten:
- *
- * wk_left
- * |
- * v
- * wk: WWWWWWWWWWW
- * |||||<-- wk_slice, cur_bits=5
- * --WWWWW-
- * ^
- * |
- * bit_offs
- */
-
- DEBUGP(DRR,
- " wk=%02x, slice=%02x/%02x, cl=%02x\n",
- w[k-1], wk_slice, cur_mask, wk_slice << (8 - bit_offs));
-
- chan_list[octet_offs] &= ~(cur_mask << (8 - bit_offs));
- chan_list[octet_offs] |= wk_slice << (8 - bit_offs);
-
- /* adjust output */
- if (bit_offs == 8) {
- bit_offs = 0;
- octet_offs += 1;
- }
- }
-
- /* adjust bit sizes */
- lvl_left -= 1;
- if (!lvl_left) {
- /* completed tree level, advance to next */
- level += 1;
- lvl_left = 1 << level;
- wk_len -= 1;
- }
- }
-}
-
-int range_enc_range128(uint8_t *chan_list, int f0, int *w)
-{
- chan_list[0] = 0x8C;
- write_orig_arfcn(chan_list, f0);
-
- write_all_wn(&chan_list[2], 1, w, 28, 7);
- return 0;
-}
-
-int range_enc_range256(uint8_t *chan_list, int f0, int *w)
-{
- chan_list[0] = 0x8A;
- write_orig_arfcn(chan_list, f0);
-
- write_all_wn(&chan_list[2], 1, w, 21, 8);
- return 0;
-}
-
-int range_enc_range512(uint8_t *chan_list, int f0, int *w)
-{
- chan_list[0] = 0x88;
- write_orig_arfcn(chan_list, f0);
-
- write_all_wn(&chan_list[2], 1, w, 17, 9);
- return 0;
-}
-
-int range_enc_range1024(uint8_t *chan_list, int f0, int f0_included, int *w)
-{
- chan_list[0] = 0x80 | (f0_included << 2);
-
- write_all_wn(&chan_list[0], 6, w, 16, 10);
- return 0;
-}
-
-int range_enc_filter_arfcns(int *arfcns,
- const int size, const int f0, int *f0_included)
-{
- int i, j = 0;
- *f0_included = 0;
-
- for (i = 0; i < size; ++i) {
- /*
- * Appendix J.4 says the following:
- * All frequencies except F(0), minus F(0) + 1.
- * I assume we need to exclude it here.
- */
- if (arfcns[i] == f0) {
- *f0_included = 1;
- continue;
- }
-
- arfcns[j++] = mod(arfcns[i] - (f0 + 1), 1024);
- }
-
- return j;
-}
diff --git a/openbsc/src/libbsc/bsc_api.c b/openbsc/src/libbsc/bsc_api.c
deleted file mode 100644
index 7613cac90..000000000
--- a/openbsc/src/libbsc/bsc_api.c
+++ /dev/null
@@ -1,894 +0,0 @@
-/* GSM 08.08 like API for OpenBSC. The bridge from MSC to BSC */
-
-/* (C) 2010-2011 by Holger Hans Peter Freyther
- * (C) 2010-2011 by On-Waves
- * (C) 2009 by Harald Welte <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/bsc_api.h>
-#include <openbsc/bsc_rll.h>
-#include <openbsc/gsm_data.h>
-#include <openbsc/gsm_subscriber.h>
-#include <openbsc/signal.h>
-#include <openbsc/abis_rsl.h>
-#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 <osmocom/gsm/protocol/gsm_08_08.h>
-#include <osmocom/gsm/gsm48.h>
-
-#include <osmocom/core/talloc.h>
-
-#define GSM0808_T10_VALUE 6, 0
-
-
-static void rll_ind_cb(struct gsm_lchan *, uint8_t, void *, enum bsc_rllr_ind);
-static void send_sapi_reject(struct gsm_subscriber_connection *conn, int link_id);
-static void handle_release(struct gsm_subscriber_connection *conn, struct bsc_api *bsc, struct gsm_lchan *lchan);
-static void handle_chan_ack(struct gsm_subscriber_connection *conn, struct bsc_api *bsc, struct gsm_lchan *lchan);
-static void handle_chan_nack(struct gsm_subscriber_connection *conn, struct bsc_api *bsc, struct gsm_lchan *lchan);
-
-/* GSM 08.08 3.2.2.33 */
-static uint8_t lchan_to_chosen_channel(struct gsm_lchan *lchan)
-{
- uint8_t channel_mode = 0, channel = 0;
-
- switch (lchan->tch_mode) {
- case GSM48_CMODE_SPEECH_V1:
- case GSM48_CMODE_SPEECH_EFR:
- case GSM48_CMODE_SPEECH_AMR:
- channel_mode = 0x9;
- break;
- case GSM48_CMODE_SIGN:
- channel_mode = 0x8;
- break;
- case GSM48_CMODE_DATA_14k5:
- channel_mode = 0xe;
- break;
- case GSM48_CMODE_DATA_12k0:
- channel_mode = 0xb;
- break;
- case GSM48_CMODE_DATA_6k0:
- channel_mode = 0xc;
- break;
- case GSM48_CMODE_DATA_3k6:
- channel_mode = 0xd;
- break;
- }
-
- switch (lchan->type) {
- case GSM_LCHAN_NONE:
- channel = 0x0;
- break;
- case GSM_LCHAN_SDCCH:
- channel = 0x1;
- break;
- case GSM_LCHAN_TCH_F:
- channel = 0x8;
- break;
- case GSM_LCHAN_TCH_H:
- channel = 0x9;
- break;
- case GSM_LCHAN_UNKNOWN:
- default:
- LOGP(DMSC, LOGL_ERROR, "Unknown lchan type: %p\n", lchan);
- break;
- }
-
- return channel_mode << 4 | channel;
-}
-
-static uint8_t chan_mode_to_speech(struct gsm_lchan *lchan)
-{
- int mode = 0;
-
- switch (lchan->tch_mode) {
- case GSM48_CMODE_SPEECH_V1:
- mode = 1;
- break;
- case GSM48_CMODE_SPEECH_EFR:
- mode = 0x11;
- break;
- case GSM48_CMODE_SPEECH_AMR:
- mode = 0x21;
- break;
- case GSM48_CMODE_SIGN:
- case GSM48_CMODE_DATA_14k5:
- case GSM48_CMODE_DATA_12k0:
- case GSM48_CMODE_DATA_6k0:
- case GSM48_CMODE_DATA_3k6:
- default:
- LOGP(DMSC, LOGL_ERROR, "Using non speech mode: %d\n", mode);
- return 0;
- break;
- }
-
- /* assume to always do AMR HR on any TCH type */
- if (lchan->type == GSM_LCHAN_TCH_H ||
- lchan->tch_mode == GSM48_CMODE_SPEECH_AMR)
- mode |= 0x4;
-
- return mode;
-}
-
-static void assignment_t10_timeout(void *_conn)
-{
- struct bsc_api *api;
- struct gsm_subscriber_connection *conn =
- (struct gsm_subscriber_connection *) _conn;
-
- LOGP(DMSC, LOGL_ERROR, "Assignment T10 timeout on %p\n", conn);
-
- /*
- * normal release on the secondary channel but only if the
- * secondary_channel has not been released by the handle_chan_nack.
- */
- if (conn->secondary_lchan)
- lchan_release(conn->secondary_lchan, 0, RSL_REL_LOCAL_END);
- conn->secondary_lchan = NULL;
-
- /* inform them about the failure */
- api = conn->network->bsc_api;
- api->assign_fail(conn, GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE, NULL);
-}
-
-/*! \brief Determine and apply AMR multi-rate configuration to lchan
- * Determine which AMR multi-rate configuration to use and apply it to
- * the lchan (so it can be communicated to BTS and MS during channel
- * activation.
- * \param[in] conn subscriber connection (used to resolve bsc_api)
- * \param[out] lchan logical channel to which to apply mr config
- * \param[in] full_rate whether to use full-rate (1) or half-rate (0) config
- */
-static void handle_mr_config(struct gsm_subscriber_connection *conn,
- struct gsm_lchan *lchan, int full_rate)
-{
- struct bsc_api *api;
- api = conn->network->bsc_api;
- struct amr_multirate_conf *mr;
- struct gsm48_multi_rate_conf *mr_conf;
-
- /* BSC api override for this method, used in OsmoBSC mode with
- * bsc_mr_config() to use MSC-specific/specified configuration */
- if (api->mr_config)
- return api->mr_config(conn, lchan, full_rate);
-
- /* NITB case: use the BTS-specic multi-rate configuration from
- * the vty/configuration file */
- if (full_rate)
- mr = &lchan->ts->trx->bts->mr_full;
- else
- mr = &lchan->ts->trx->bts->mr_half;
-
- mr_conf = (struct gsm48_multi_rate_conf *) mr->gsm48_ie;
- mr_conf->ver = 1;
-
- /* default, if no AMR codec defined */
- if (!mr->gsm48_ie[1]) {
- mr_conf->icmi = 1;
- mr_conf->m5_90 = 1;
- }
- /* store encoded MR config IE lchan for both MS (uplink) and BTS
- * (downlink) directions */
- gsm48_multirate_config(lchan->mr_ms_lv, mr, mr->ms_mode);
- gsm48_multirate_config(lchan->mr_bts_lv, mr, mr->bts_mode);
-}
-
-/*
- * Start a new assignment and make sure that it is completed within T10 either
- * positively, negatively or by the timeout.
- *
- * 1.) allocate a new lchan
- * 2.) copy the encryption key and other data from the
- * old to the new channel.
- * 3.) RSL Channel Activate this channel and wait
- *
- * -> Signal handler for the LCHAN
- * 4.) Send GSM 04.08 assignment command to the MS
- *
- * -> Assignment Complete/Assignment Failure
- * 5.) Release the SDCCH, continue signalling on the new link
- */
-static int handle_new_assignment(struct gsm_subscriber_connection *conn, int chan_mode, int full_rate)
-{
- struct gsm_lchan *new_lchan;
- int chan_type;
-
- chan_type = full_rate ? GSM_LCHAN_TCH_F : GSM_LCHAN_TCH_H;
-
- new_lchan = lchan_alloc(conn->bts, chan_type, 0);
-
- if (!new_lchan) {
- LOGP(DMSC, LOGL_NOTICE, "No free channel.\n");
- return -1;
- }
-
- /* copy old data to the new channel */
- memcpy(&new_lchan->encr, &conn->lchan->encr, sizeof(new_lchan->encr));
- new_lchan->ms_power = conn->lchan->ms_power;
- new_lchan->bs_power = conn->lchan->bs_power;
- new_lchan->rqd_ta = conn->lchan->rqd_ta;
-
- /* copy new data to it */
- new_lchan->tch_mode = chan_mode;
- new_lchan->rsl_cmode = RSL_CMOD_SPD_SPEECH;
-
- /* handle AMR correctly */
- if (chan_mode == GSM48_CMODE_SPEECH_AMR)
- handle_mr_config(conn, new_lchan, full_rate);
-
- if (rsl_chan_activate_lchan(new_lchan, 0x1, 0) < 0) {
- LOGP(DHO, LOGL_ERROR, "could not activate channel\n");
- lchan_free(new_lchan);
- return -1;
- }
-
- /* remember that we have the channel */
- conn->secondary_lchan = new_lchan;
- new_lchan->conn = conn;
-
- rsl_lchan_set_state(new_lchan, LCHAN_S_ACT_REQ);
- return 0;
-}
-
-struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_lchan *lchan)
-{
- struct gsm_subscriber_connection *conn;
- struct gsm_network *net = lchan->ts->trx->bts->network;
-
- conn = talloc_zero(net, struct gsm_subscriber_connection);
- if (!conn)
- return NULL;
-
- conn->network = net;
- conn->lchan = lchan;
- conn->bts = lchan->ts->trx->bts;
- conn->via_ran = RAN_GERAN_A;
- lchan->conn = conn;
- llist_add_tail(&conn->entry, &net->subscr_conns);
- return conn;
-}
-
-void bsc_subscr_con_free(struct gsm_subscriber_connection *conn)
-{
- if (!conn)
- return;
-
-
- if (conn->subscr) {
- subscr_put(conn->subscr);
- conn->subscr = NULL;
- }
-
-
- if (conn->ho_lchan) {
- LOGP(DNM, LOGL_ERROR, "The ho_lchan should have been cleared.\n");
- conn->ho_lchan->conn = NULL;
- }
-
- if (conn->lchan) {
- LOGP(DNM, LOGL_ERROR, "The lchan should have been cleared.\n");
- conn->lchan->conn = NULL;
- }
-
- if (conn->secondary_lchan) {
- LOGP(DNM, LOGL_ERROR, "The secondary_lchan should have been cleared.\n");
- conn->secondary_lchan->conn = NULL;
- }
-
- llist_del(&conn->entry);
- talloc_free(conn);
-}
-
-int bsc_api_init(struct gsm_network *network, struct bsc_api *api)
-{
- network->bsc_api = api;
- return 0;
-}
-
-/*! \brief process incoming 08.08 DTAP from MSC (send via BTS to MS) */
-int gsm0808_submit_dtap(struct gsm_subscriber_connection *conn,
- struct msgb *msg, int link_id, int allow_sacch)
-{
- uint8_t sapi;
-
-
- if (!conn->lchan) {
- LOGP(DMSC, LOGL_ERROR,
- "Called submit dtap without an lchan.\n");
- msgb_free(msg);
- return -1;
- }
-
- sapi = link_id & 0x7;
- msg->lchan = conn->lchan;
- msg->dst = msg->lchan->ts->trx->rsl_link;
-
- /* If we are on a TCH and need to submit a SMS (on SAPI=3) we need to use the SACH */
- if (allow_sacch && sapi != 0) {
- if (conn->lchan->type == GSM_LCHAN_TCH_F || conn->lchan->type == GSM_LCHAN_TCH_H)
- link_id |= 0x40;
- }
-
- msg->l3h = msg->data;
- /* is requested SAPI already up? */
- if (conn->lchan->sapis[sapi] == LCHAN_SAPI_UNUSED) {
- /* Establish L2 for additional SAPI */
- OBSC_LINKID_CB(msg) = link_id;
- if (rll_establish(msg->lchan, sapi, rll_ind_cb, msg) != 0) {
- msgb_free(msg);
- send_sapi_reject(conn, link_id);
- return -1;
- }
- return 0;
- } else {
- /* Directly forward via RLL/RSL to BTS */
- return rsl_data_request(msg, link_id);
- }
-}
-
-/*
- * \brief Check if the given channel is compatible with the mode/fullrate
- */
-static int chan_compat_with_mode(struct gsm_lchan *lchan, int chan_mode, int full_rate)
-{
- switch (chan_mode) {
- case GSM48_CMODE_SIGN:
- /* signalling is always possible */
- return 1;
- case GSM48_CMODE_SPEECH_V1:
- case GSM48_CMODE_SPEECH_AMR:
- case GSM48_CMODE_DATA_3k6:
- case GSM48_CMODE_DATA_6k0:
- /* these services can all run on TCH/H, but we may have
- * an explicit override by the 'full_rate' argument */
- switch (lchan->type) {
- case GSM_LCHAN_TCH_F:
- return 1;
- case GSM_LCHAN_TCH_H:
- if (full_rate)
- return 0;
- else
- return 1;
- break;
- default:
- return 0;
- }
- break;
- case GSM48_CMODE_DATA_12k0:
- case GSM48_CMODE_DATA_14k5:
- case GSM48_CMODE_SPEECH_EFR:
- /* these services all explicitly require a TCH/F */
- if (lchan->type == GSM_LCHAN_TCH_F)
- return 1;
- else
- return 0;
- break;
- }
-
- return 0;
-}
-
-/**
- * Send a GSM08.08 Assignment Request. Right now this does not contain the
- * audio codec type or the allowed rates for the config. It is assumed that
- * this is for audio handling only. In case the current channel does not allow
- * the selected mode a new one will be allocated.
- *
- * TODO: Add multirate configuration, make it work for more than audio.
- */
-int gsm0808_assign_req(struct gsm_subscriber_connection *conn, int chan_mode, int full_rate)
-{
- struct bsc_api *api;
- api = conn->network->bsc_api;
-
- if (!chan_compat_with_mode(conn->lchan, chan_mode, full_rate)) {
- if (handle_new_assignment(conn, chan_mode, full_rate) != 0)
- goto error;
- } else {
- if (chan_mode == GSM48_CMODE_SPEECH_AMR)
- handle_mr_config(conn, conn->lchan, full_rate);
-
- LOGP(DMSC, LOGL_NOTICE,
- "Sending %s ChanModify for speech: %s on channel %s\n",
- gsm_lchan_name(conn->lchan),
- get_value_string(gsm48_chan_mode_names, chan_mode),
- get_value_string(gsm_chan_t_names, conn->lchan->type));
- gsm48_lchan_modify(conn->lchan, chan_mode);
- }
-
- /* we will now start the timer to complete the assignment */
- osmo_timer_setup(&conn->T10, assignment_t10_timeout, conn);
- osmo_timer_schedule(&conn->T10, GSM0808_T10_VALUE);
- return 0;
-
-error:
- api->assign_fail(conn, 0, NULL);
- return -1;
-}
-
-int gsm0808_page(struct gsm_bts *bts, unsigned int page_group, unsigned int mi_len,
- uint8_t *mi, int chan_type)
-{
- return rsl_paging_cmd(bts, page_group, mi_len, mi, chan_type, false);
-}
-
-static void handle_ass_compl(struct gsm_subscriber_connection *conn,
- struct msgb *msg)
-{
- struct gsm48_hdr *gh;
- struct bsc_api *api = conn->network->bsc_api;
-
- if (conn->secondary_lchan != msg->lchan) {
- LOGP(DMSC, LOGL_ERROR, "Assignment Compl should occur on second lchan.\n");
- return;
- }
-
- gh = msgb_l3(msg);
- if (msgb_l3len(msg) - sizeof(*gh) != 1) {
- LOGP(DMSC, LOGL_ERROR, "Assignment Compl invalid: %zu\n",
- msgb_l3len(msg) - sizeof(*gh));
- return;
- }
-
- /* switch TRAU muxer for E1 based BTS from one channel to another */
- if (is_e1_bts(conn->bts))
- switch_trau_mux(conn->lchan, conn->secondary_lchan);
-
- /* swap channels */
- osmo_timer_del(&conn->T10);
-
- lchan_release(conn->lchan, 0, RSL_REL_LOCAL_END);
- conn->lchan = conn->secondary_lchan;
- conn->secondary_lchan = NULL;
-
- if (is_ipaccess_bts(conn->bts) && conn->lchan->tch_mode != GSM48_CMODE_SIGN)
- rsl_ipacc_crcx(conn->lchan);
-
- api->assign_compl(conn, gh->data[0],
- lchan_to_chosen_channel(conn->lchan),
- conn->lchan->encr.alg_id,
- chan_mode_to_speech(conn->lchan));
-}
-
-static void handle_ass_fail(struct gsm_subscriber_connection *conn,
- struct msgb *msg)
-{
- struct bsc_api *api = conn->network->bsc_api;
- uint8_t *rr_failure;
- struct gsm48_hdr *gh;
-
-
- if (conn->lchan != msg->lchan) {
- LOGP(DMSC, LOGL_ERROR, "Assignment failure should occur on primary lchan.\n");
- return;
- }
-
- /* stop the timer and release it */
- osmo_timer_del(&conn->T10);
- lchan_release(conn->secondary_lchan, 0, RSL_REL_LOCAL_END);
- conn->secondary_lchan = NULL;
-
- gh = msgb_l3(msg);
- if (msgb_l3len(msg) - sizeof(*gh) != 1) {
- LOGP(DMSC, LOGL_ERROR, "assignment failure unhandled: %zu\n",
- msgb_l3len(msg) - sizeof(*gh));
- rr_failure = NULL;
- } else {
- rr_failure = &gh->data[0];
- }
-
- api->assign_fail(conn,
- GSM0808_CAUSE_RADIO_INTERFACE_MESSAGE_FAILURE,
- rr_failure);
-}
-
-static void handle_classmark_chg(struct gsm_subscriber_connection *conn,
- struct msgb *msg)
-{
- struct bsc_api *api = msg->lchan->ts->trx->bts->network->bsc_api;
- struct gsm48_hdr *gh = msgb_l3(msg);
- unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
- uint8_t cm2_len, cm3_len = 0;
- uint8_t *cm2, *cm3 = NULL;
-
- DEBUGP(DRR, "CLASSMARK CHANGE ");
-
- /* classmark 2 */
- cm2_len = gh->data[0];
- cm2 = &gh->data[1];
- DEBUGPC(DRR, "CM2(len=%u) ", cm2_len);
-
- if (payload_len > cm2_len + 1) {
- /* we must have a classmark3 */
- if (gh->data[cm2_len+1] != 0x20) {
- DEBUGPC(DRR, "ERR CM3 TAG\n");
- return;
- }
- if (cm2_len > 3) {
- DEBUGPC(DRR, "CM2 too long!\n");
- return;
- }
-
- cm3_len = gh->data[cm2_len+2];
- cm3 = &gh->data[cm2_len+3];
- if (cm3_len > 14) {
- DEBUGPC(DRR, "CM3 len %u too long!\n", cm3_len);
- return;
- }
- DEBUGPC(DRR, "CM3(len=%u)\n", cm3_len);
- }
- api->classmark_chg(conn, cm2, cm2_len, cm3, cm3_len);
-}
-
-/* Chapter 9.1.16 Handover complete */
-static void handle_rr_ho_compl(struct msgb *msg)
-{
- struct lchan_signal_data sig;
- struct gsm48_hdr *gh = msgb_l3(msg);
-
- DEBUGP(DRR, "HANDOVER COMPLETE cause = %s\n",
- rr_cause_name(gh->data[0]));
-
- sig.lchan = msg->lchan;
- sig.mr = NULL;
- osmo_signal_dispatch(SS_LCHAN, S_LCHAN_HANDOVER_COMPL, &sig);
- /* FIXME: release old channel */
-}
-
-/* Chapter 9.1.17 Handover Failure */
-static void handle_rr_ho_fail(struct msgb *msg)
-{
- struct lchan_signal_data sig;
- struct gsm48_hdr *gh = msgb_l3(msg);
-
- DEBUGP(DRR, "HANDOVER FAILED cause = %s\n",
- rr_cause_name(gh->data[0]));
-
- sig.lchan = msg->lchan;
- sig.mr = NULL;
- osmo_signal_dispatch(SS_LCHAN, S_LCHAN_HANDOVER_FAIL, &sig);
- /* FIXME: release allocated new channel */
-}
-
-
-static void dispatch_dtap(struct gsm_subscriber_connection *conn,
- uint8_t link_id, struct msgb *msg)
-{
- struct bsc_api *api = msg->lchan->ts->trx->bts->network->bsc_api;
- struct gsm48_hdr *gh;
- uint8_t pdisc;
- uint8_t msg_type;
- int rc;
-
- if (msgb_l3len(msg) < sizeof(*gh)) {
- LOGP(DMSC, LOGL_ERROR, "Message too short for a GSM48 header.\n");
- return;
- }
-
- gh = msgb_l3(msg);
- pdisc = gsm48_hdr_pdisc(gh);
- msg_type = gsm48_hdr_msg_type(gh);
-
- /* the idea is to handle all RR messages here, and only hand
- * MM/CC/SMS-CP/LCS up to the MSC. Some messages like PAGING
- * RESPONSE or CM SERVICE REQUEST will not be covered here, as
- * they are only possible in the first L3 message of each L2
- * channel, i.e. 'conn' will not exist and gsm0408_rcvmsg()
- * will call api->compl_l3() for it */
- switch (pdisc) {
- case GSM48_PDISC_RR:
- switch (msg_type) {
- case GSM48_MT_RR_GPRS_SUSP_REQ:
- DEBUGP(DRR, "%s\n",
- gsm48_rr_msg_name(GSM48_MT_RR_GPRS_SUSP_REQ));
- break;
- case GSM48_MT_RR_STATUS:
- LOGP(DRR, LOGL_NOTICE, "%s (cause: %s)\n",
- gsm48_rr_msg_name(GSM48_MT_RR_GPRS_SUSP_REQ),
- rr_cause_name(gh->data[0]));
- break;
- case GSM48_MT_RR_MEAS_REP:
- /* This shouldn't actually end up here, as RSL treats
- * L3 Info of 08.58 MEASUREMENT REPORT different by calling
- * directly into gsm48_parse_meas_rep */
- LOGP(DMEAS, LOGL_ERROR, "DIRECT GSM48 MEASUREMENT REPORT ?!? ");
- break;
- case GSM48_MT_RR_HANDO_COMPL:
- handle_rr_ho_compl(msg);
- break;
- case GSM48_MT_RR_HANDO_FAIL:
- handle_rr_ho_fail(msg);
- break;
- case GSM48_MT_RR_CIPH_M_COMPL:
- if (api->cipher_mode_compl)
- api->cipher_mode_compl(conn, msg,
- conn->lchan->encr.alg_id);
- break;
- case GSM48_MT_RR_ASS_COMPL:
- handle_ass_compl(conn, msg);
- break;
- case GSM48_MT_RR_ASS_FAIL:
- handle_ass_fail(conn, msg);
- break;
- case GSM48_MT_RR_CHAN_MODE_MODIF_ACK:
- osmo_timer_del(&conn->T10);
- rc = gsm48_rx_rr_modif_ack(msg);
- if (rc < 0) {
- api->assign_fail(conn,
- GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE,
- NULL);
- } else if (rc >= 0) {
- api->assign_compl(conn, 0,
- lchan_to_chosen_channel(conn->lchan),
- conn->lchan->encr.alg_id,
- chan_mode_to_speech(conn->lchan));
- }
- break;
- case GSM48_MT_RR_CLSM_CHG:
- handle_classmark_chg(conn, msg);
- break;
- case GSM48_MT_RR_APP_INFO:
- /* Passing RR APP INFO to MSC, not quite
- * according to spec */
- if (api->dtap)
- api->dtap(conn, link_id, msg);
- break;
- default:
- /* Normally, a MSC should never receive RR
- * messages, but we'd rather forward what we
- * don't know than drop it... */
- LOGP(DRR, LOGL_NOTICE,
- "BSC: Passing %s 04.08 RR message to MSC\n",
- gsm48_rr_msg_name(msg_type));
- if (api->dtap)
- api->dtap(conn, link_id, msg);
- }
- break;
- default:
- if (api->dtap)
- api->dtap(conn, link_id, msg);
- break;
- }
-}
-
-/*! \brief RSL has received a DATA INDICATION with L3 from MS */
-int gsm0408_rcvmsg(struct msgb *msg, uint8_t link_id)
-{
- int rc;
- struct bsc_api *api = msg->lchan->ts->trx->bts->network->bsc_api;
- struct gsm_lchan *lchan;
-
- lchan = msg->lchan;
- if (lchan->state != LCHAN_S_ACTIVE) {
- LOGP(DRSL, LOGL_INFO, "Got data in non active state(%s), "
- "discarding.\n", gsm_lchans_name(lchan->state));
- return -1;
- }
-
-
- if (lchan->conn) {
- /* if we already have a connection, forward via DTAP to
- * MSC */
- dispatch_dtap(lchan->conn, link_id, msg);
- } else {
- /* allocate a new connection */
- rc = BSC_API_CONN_POL_REJECT;
- lchan->conn = bsc_subscr_con_allocate(msg->lchan);
- if (!lchan->conn) {
- lchan_release(lchan, 1, RSL_REL_NORMAL);
- return -1;
- }
-
- /* fwd via bsc_api to send COMPLETE L3 INFO to MSC */
- rc = api->compl_l3(lchan->conn, msg, 0);
-
- if (rc != BSC_API_CONN_POL_ACCEPT) {
- lchan->conn->lchan = NULL;
- bsc_subscr_con_free(lchan->conn);
- lchan_release(lchan, 1, RSL_REL_NORMAL);
- }
- }
-
- return 0;
-}
-
-/*! \brief We received a GSM 08.08 CIPHER MODE from the MSC */
-int gsm0808_cipher_mode(struct gsm_subscriber_connection *conn, int cipher,
- const uint8_t *key, int len, int include_imeisv)
-{
- if (cipher > 0 && key == NULL) {
- LOGP(DRSL, LOGL_ERROR, "Need to have an encrytpion key.\n");
- return -1;
- }
-
- if (len > MAX_A5_KEY_LEN) {
- LOGP(DRSL, LOGL_ERROR, "The key is too long: %d\n", len);
- return -1;
- }
-
- conn->lchan->encr.alg_id = RSL_ENC_ALG_A5(cipher);
- if (key) {
- conn->lchan->encr.key_len = len;
- memcpy(conn->lchan->encr.key, key, len);
- }
-
- return gsm48_send_rr_ciph_mode(conn->lchan, include_imeisv);
-}
-
-/*
- * Release all occupied RF Channels but stay around for more.
- */
-int gsm0808_clear(struct gsm_subscriber_connection *conn)
-{
- if (conn->ho_lchan)
- bsc_clear_handover(conn, 1);
-
- if (conn->secondary_lchan)
- lchan_release(conn->secondary_lchan, 0, RSL_REL_LOCAL_END);
-
- if (conn->lchan)
- lchan_release(conn->lchan, 1, RSL_REL_NORMAL);
-
- conn->lchan = NULL;
- conn->secondary_lchan = NULL;
- conn->ho_lchan = NULL;
- conn->bts = NULL;
-
- osmo_timer_del(&conn->T10);
-
- return 0;
-}
-
-static void send_sapi_reject(struct gsm_subscriber_connection *conn, int link_id)
-{
- struct bsc_api *api;
-
- if (!conn)
- return;
-
- api = conn->network->bsc_api;
- if (!api || !api->sapi_n_reject)
- return;
-
- api->sapi_n_reject(conn, link_id);
-}
-
-static void rll_ind_cb(struct gsm_lchan *lchan, uint8_t link_id, void *_data, enum bsc_rllr_ind rllr_ind)
-{
- struct msgb *msg = _data;
-
- /*
- * There seems to be a small window that the RLL timer can
- * fire after a lchan_release call and before the S_CHALLOC_FREED
- * is called. Check if a conn is set before proceeding.
- */
- if (!lchan->conn)
- return;
-
- switch (rllr_ind) {
- case BSC_RLLR_IND_EST_CONF:
- rsl_data_request(msg, OBSC_LINKID_CB(msg));
- break;
- case BSC_RLLR_IND_REL_IND:
- case BSC_RLLR_IND_ERR_IND:
- case BSC_RLLR_IND_TIMEOUT:
- send_sapi_reject(lchan->conn, OBSC_LINKID_CB(msg));
- msgb_free(msg);
- break;
- }
-}
-
-static int bsc_handle_lchan_signal(unsigned int subsys, unsigned int signal,
- void *handler_data, void *signal_data)
-{
- struct bsc_api *bsc;
- struct gsm_lchan *lchan;
- struct lchan_signal_data *lchan_data;
-
- if (subsys != SS_LCHAN)
- return 0;
-
-
- lchan_data = signal_data;
- if (!lchan_data->lchan || !lchan_data->lchan->conn)
- return 0;
-
- lchan = lchan_data->lchan;
- bsc = lchan->ts->trx->bts->network->bsc_api;
- if (!bsc)
- return 0;
-
- switch (signal) {
- case S_LCHAN_UNEXPECTED_RELEASE:
- handle_release(lchan->conn, bsc, lchan);
- break;
- case S_LCHAN_ACTIVATE_ACK:
- handle_chan_ack(lchan->conn, bsc, lchan);
- break;
- case S_LCHAN_ACTIVATE_NACK:
- handle_chan_nack(lchan->conn, bsc, lchan);
- break;
- }
-
- return 0;
-}
-
-static void handle_release(struct gsm_subscriber_connection *conn,
- struct bsc_api *bsc, struct gsm_lchan *lchan)
-{
- int destruct = 1;
-
- if (conn->secondary_lchan == lchan) {
- osmo_timer_del(&conn->T10);
- conn->secondary_lchan = NULL;
-
- bsc->assign_fail(conn,
- GSM0808_CAUSE_RADIO_INTERFACE_FAILURE,
- NULL);
- }
-
- /* clear the connection now */
- if (bsc->clear_request)
- destruct = bsc->clear_request(conn, 0);
-
- /* now give up all channels */
- if (conn->lchan == lchan)
- conn->lchan = NULL;
- if (conn->ho_lchan == lchan) {
- bsc_clear_handover(conn, 0);
- conn->ho_lchan = NULL;
- }
- lchan->conn = NULL;
-
- gsm0808_clear(conn);
-
- if (destruct)
- bsc_subscr_con_free(conn);
-}
-
-static void handle_chan_ack(struct gsm_subscriber_connection *conn,
- struct bsc_api *api, struct gsm_lchan *lchan)
-{
- if (conn->secondary_lchan != lchan)
- return;
-
- LOGP(DMSC, LOGL_NOTICE, "Sending assignment on chan: %p\n", lchan);
- gsm48_send_rr_ass_cmd(conn->lchan, lchan, lchan->ms_power);
-}
-
-static void handle_chan_nack(struct gsm_subscriber_connection *conn,
- struct bsc_api *api, struct gsm_lchan *lchan)
-{
- if (conn->secondary_lchan != lchan)
- return;
-
- LOGP(DMSC, LOGL_ERROR, "Channel activation failed. Waiting for timeout now\n");
- conn->secondary_lchan->conn = NULL;
- conn->secondary_lchan = NULL;
-}
-
-static __attribute__((constructor)) void on_dso_load_bsc(void)
-{
- osmo_signal_register_handler(SS_LCHAN, bsc_handle_lchan_signal, NULL);
-}
-
diff --git a/openbsc/src/libbsc/bsc_ctrl_commands.c b/openbsc/src/libbsc/bsc_ctrl_commands.c
deleted file mode 100644
index 641fe2bf6..000000000
--- a/openbsc/src/libbsc/bsc_ctrl_commands.c
+++ /dev/null
@@ -1,459 +0,0 @@
-/*
- * (C) 2013-2015 by Holger Hans Peter Freyther
- * (C) 2013-2015 by sysmocom s.f.m.c. GmbH
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-#include <errno.h>
-#include <time.h>
-
-#include <osmocom/ctrl/control_cmd.h>
-#include <openbsc/ipaccess.h>
-#include <openbsc/gsm_data.h>
-#include <openbsc/abis_nm.h>
-#include <openbsc/debug.h>
-#include <openbsc/chan_alloc.h>
-#include <openbsc/osmo_bsc_rf.h>
-#include <openbsc/bsc_msc_data.h>
-
-#define CTRL_CMD_VTY_STRING(cmdname, cmdstr, dtype, element) \
- CTRL_HELPER_GET_STRING(cmdname, dtype, element) \
- CTRL_HELPER_SET_STRING(cmdname, dtype, element) \
-static struct ctrl_cmd_element cmd_##cmdname = { \
- .name = cmdstr, \
- .get = get_##cmdname, \
- .set = set_##cmdname, \
- .verify = verify_vty_description_string, \
-}
-
-/**
- * Check that there are no newlines or comments or other things
- * that could make the VTY configuration unparsable.
- */
-static int verify_vty_description_string(struct ctrl_cmd *cmd,
- const char *value, void *data)
-{
- int i;
- const size_t len = strlen(value);
-
- for (i = 0; i < len; ++i) {
- switch(value[i]) {
- case '#':
- case '\n':
- case '\r':
- cmd->reply = "String includes illegal character";
- return -1;
- default:
- break;
- }
- }
-
- return 0;
-}
-
-CTRL_CMD_DEFINE_RANGE(net_mnc, "mnc", struct gsm_network, network_code, 0, 999);
-CTRL_CMD_DEFINE_RANGE(net_mcc, "mcc", struct gsm_network, country_code, 1, 999);
-CTRL_CMD_VTY_STRING(net_short_name, "short-name", struct gsm_network, name_short);
-CTRL_CMD_VTY_STRING(net_long_name, "long-name", struct gsm_network, name_long);
-
-static int set_net_apply_config(struct ctrl_cmd *cmd, void *data)
-{
- struct gsm_network *net = cmd->node;
- struct gsm_bts *bts;
-
- llist_for_each_entry(bts, &net->bts_list, list) {
- if (!is_ipaccess_bts(bts))
- continue;
-
- /*
- * The ip.access nanoBTS seems to be unrelaible on BSSGP
- * so let's us just reboot it. For the sysmoBTS we can just
- * restart the process as all state is gone.
- */
- if (!is_sysmobts_v2(bts) && strcmp(cmd->value, "restart") == 0) {
- struct gsm_bts_trx *trx;
- llist_for_each_entry_reverse(trx, &bts->trx_list, list)
- abis_nm_ipaccess_restart(trx);
- } else
- ipaccess_drop_oml(bts);
- }
-
- cmd->reply = "Tried to drop the BTS";
- return CTRL_CMD_REPLY;
-}
-
-CTRL_CMD_DEFINE_WO_NOVRF(net_apply_config, "apply-configuration");
-
-static int verify_net_mcc_mnc_apply(struct ctrl_cmd *cmd, const char *value, void *d)
-{
- char *tmp, *saveptr, *mcc, *mnc;
-
- tmp = talloc_strdup(cmd, value);
- if (!tmp)
- return 1;
-
- mcc = strtok_r(tmp, ",", &saveptr);
- mnc = strtok_r(NULL, ",", &saveptr);
- talloc_free(tmp);
-
- if (!mcc || !mnc)
- return 1;
- return 0;
-}
-
-static int set_net_mcc_mnc_apply(struct ctrl_cmd *cmd, void *data)
-{
- struct gsm_network *net = cmd->node;
- char *tmp, *saveptr, *mcc_str, *mnc_str;
- int mcc, mnc;
-
- tmp = talloc_strdup(cmd, cmd->value);
- if (!tmp)
- goto oom;
-
-
- mcc_str = strtok_r(tmp, ",", &saveptr);
- mnc_str = strtok_r(NULL, ",", &saveptr);
-
- mcc = atoi(mcc_str);
- mnc = atoi(mnc_str);
-
- talloc_free(tmp);
-
- if (net->network_code == mnc && net->country_code == mcc) {
- cmd->reply = "Nothing changed";
- return CTRL_CMD_REPLY;
- }
-
- net->network_code = mnc;
- net->country_code = mcc;
-
- return set_net_apply_config(cmd, data);
-
-oom:
- cmd->reply = "OOM";
- return CTRL_CMD_ERROR;
-}
-CTRL_CMD_DEFINE_WO(net_mcc_mnc_apply, "mcc-mnc-apply");
-
-/* BTS related commands below */
-CTRL_CMD_DEFINE_RANGE(bts_lac, "location-area-code", struct gsm_bts, location_area_code, 0, 65535);
-CTRL_CMD_DEFINE_RANGE(bts_ci, "cell-identity", struct gsm_bts, cell_identity, 0, 65535);
-
-static int set_bts_apply_config(struct ctrl_cmd *cmd, void *data)
-{
- struct gsm_bts *bts = cmd->node;
-
- if (!is_ipaccess_bts(bts)) {
- cmd->reply = "BTS is not IP based";
- return CTRL_CMD_ERROR;
- }
-
- ipaccess_drop_oml(bts);
- cmd->reply = "Tried to drop the BTS";
- return CTRL_CMD_REPLY;
-}
-
-CTRL_CMD_DEFINE_WO_NOVRF(bts_apply_config, "apply-configuration");
-
-static int set_bts_si(struct ctrl_cmd *cmd, void *data)
-{
- struct gsm_bts *bts = cmd->node;
- int rc;
-
- rc = gsm_bts_set_system_infos(bts);
- if (rc != 0) {
- cmd->reply = "Failed to generate SI";
- return CTRL_CMD_ERROR;
- }
-
- cmd->reply = "Generated new System Information";
- return CTRL_CMD_REPLY;
-}
-CTRL_CMD_DEFINE_WO_NOVRF(bts_si, "send-new-system-informations");
-
-static int get_bts_chan_load(struct ctrl_cmd *cmd, void *data)
-{
- int i;
- struct pchan_load pl;
- struct gsm_bts *bts;
- const char *space = "";
-
- bts = cmd->node;
- memset(&pl, 0, sizeof(pl));
- bts_chan_load(&pl, bts);
-
- cmd->reply = talloc_strdup(cmd, "");
-
- for (i = 0; i < ARRAY_SIZE(pl.pchan); ++i) {
- const struct load_counter *lc = &pl.pchan[i];
-
- /* These can never have user load */
- if (i == GSM_PCHAN_NONE)
- continue;
- if (i == GSM_PCHAN_CCCH)
- continue;
- if (i == GSM_PCHAN_PDCH)
- continue;
- if (i == GSM_PCHAN_UNKNOWN)
- continue;
-
- cmd->reply = talloc_asprintf_append(cmd->reply,
- "%s%s,%u,%u",
- space, gsm_pchan_name(i), lc->used, lc->total);
- if (!cmd->reply)
- goto error;
- space = " ";
- }
-
- return CTRL_CMD_REPLY;
-
-error:
- cmd->reply = "Memory allocation failure";
- return CTRL_CMD_ERROR;
-}
-
-CTRL_CMD_DEFINE_RO(bts_chan_load, "channel-load");
-
-static int get_bts_oml_conn(struct ctrl_cmd *cmd, void *data)
-{
- struct gsm_bts *bts = cmd->node;
-
- cmd->reply = bts->oml_link ? "connected" : "disconnected";
- return CTRL_CMD_REPLY;
-}
-
-CTRL_CMD_DEFINE_RO(bts_oml_conn, "oml-connection-state");
-
-static int verify_bts_gprs_mode(struct ctrl_cmd *cmd, const char *value, void *_data)
-{
- int valid;
- enum bts_gprs_mode mode;
- struct gsm_bts *bts = cmd->node;
-
- mode = bts_gprs_mode_parse(value, &valid);
- if (!valid) {
- cmd->reply = "Mode is not known";
- return 1;
- }
-
- if (!bts_gprs_mode_is_compat(bts, mode)) {
- cmd->reply = "bts does not support this mode";
- return 1;
- }
-
- return 0;
-}
-
-static int get_bts_gprs_mode(struct ctrl_cmd *cmd, void *data)
-{
- struct gsm_bts *bts = cmd->node;
-
- cmd->reply = talloc_strdup(cmd, bts_gprs_mode_name(bts->gprs.mode));
- return CTRL_CMD_REPLY;
-}
-
-static int set_bts_gprs_mode(struct ctrl_cmd *cmd, void *data)
-{
- struct gsm_bts *bts = cmd->node;
-
- bts->gprs.mode = bts_gprs_mode_parse(cmd->value, NULL);
- return get_bts_gprs_mode(cmd, data);
-}
-
-CTRL_CMD_DEFINE(bts_gprs_mode, "gprs-mode");
-
-static int get_bts_rf_state(struct ctrl_cmd *cmd, void *data)
-{
- const char *oper, *admin, *policy;
- struct gsm_bts *bts = cmd->node;
-
- if (!bts) {
- cmd->reply = "bts not found.";
- return CTRL_CMD_ERROR;
- }
-
- oper = osmo_bsc_rf_get_opstate_name(osmo_bsc_rf_get_opstate_by_bts(bts));
- admin = osmo_bsc_rf_get_adminstate_name(osmo_bsc_rf_get_adminstate_by_bts(bts));
- policy = osmo_bsc_rf_get_policy_name(osmo_bsc_rf_get_policy_by_bts(bts));
-
- cmd->reply = talloc_asprintf(cmd, "%s,%s,%s", oper, admin, policy);
- if (!cmd->reply) {
- cmd->reply = "OOM.";
- return CTRL_CMD_ERROR;
- }
-
- return CTRL_CMD_REPLY;
-}
-CTRL_CMD_DEFINE_RO(bts_rf_state, "rf_state");
-
-static int get_net_rf_lock(struct ctrl_cmd *cmd, void *data)
-{
- struct gsm_network *net = cmd->node;
- struct gsm_bts *bts;
- const char *policy_name;
-
- policy_name = osmo_bsc_rf_get_policy_name(net->bsc_data->rf_ctrl->policy);
-
- llist_for_each_entry(bts, &net->bts_list, list) {
- struct gsm_bts_trx *trx;
-
- /* Exclude the BTS from the global lock */
- if (bts->excl_from_rf_lock)
- continue;
-
- llist_for_each_entry(trx, &bts->trx_list, list) {
- if (trx->mo.nm_state.availability == NM_AVSTATE_OK &&
- trx->mo.nm_state.operational != NM_OPSTATE_DISABLED) {
- cmd->reply = talloc_asprintf(cmd,
- "state=on,policy=%s,bts=%u,trx=%u",
- policy_name, bts->nr, trx->nr);
- return CTRL_CMD_REPLY;
- }
- }
- }
-
- cmd->reply = talloc_asprintf(cmd, "state=off,policy=%s",
- policy_name);
- return CTRL_CMD_REPLY;
-}
-
-#define TIME_FORMAT_RFC2822 "%a, %d %b %Y %T %z"
-
-static int set_net_rf_lock(struct ctrl_cmd *cmd, void *data)
-{
- int locked = atoi(cmd->value);
- struct gsm_network *net = cmd->node;
- time_t now = time(NULL);
- char now_buf[64];
- struct osmo_bsc_rf *rf;
-
- if (!net) {
- cmd->reply = "net not found.";
- return CTRL_CMD_ERROR;
- }
-
- rf = net->bsc_data->rf_ctrl;
-
- if (!rf) {
- cmd->reply = "RF Ctrl is not enabled in the BSC Configuration";
- return CTRL_CMD_ERROR;
- }
-
- talloc_free(rf->last_rf_lock_ctrl_command);
- strftime(now_buf, sizeof(now_buf), TIME_FORMAT_RFC2822, gmtime(&now));
- rf->last_rf_lock_ctrl_command =
- talloc_asprintf(rf, "rf_locked %u (%s)", locked, now_buf);
-
- osmo_bsc_rf_schedule_lock(rf, locked == 1 ? '0' : '1');
-
- cmd->reply = talloc_asprintf(cmd, "%u", locked);
- if (!cmd->reply) {
- cmd->reply = "OOM.";
- return CTRL_CMD_ERROR;
- }
-
- return CTRL_CMD_REPLY;
-}
-
-static int verify_net_rf_lock(struct ctrl_cmd *cmd, const char *value, void *data)
-{
- int locked = atoi(cmd->value);
-
- if ((locked != 0) && (locked != 1))
- return 1;
-
- return 0;
-}
-CTRL_CMD_DEFINE(net_rf_lock, "rf_locked");
-
-static int get_net_bts_num(struct ctrl_cmd *cmd, void *data)
-{
- struct gsm_network *net = cmd->node;
-
- cmd->reply = talloc_asprintf(cmd, "%u", net->num_bts);
- return CTRL_CMD_REPLY;
-}
-CTRL_CMD_DEFINE_RO(net_bts_num, "number-of-bts");
-
-/* TRX related commands below here */
-CTRL_HELPER_GET_INT(trx_max_power, struct gsm_bts_trx, max_power_red);
-static int verify_trx_max_power(struct ctrl_cmd *cmd, const char *value, void *_data)
-{
- int tmp = atoi(value);
-
- if (tmp < 0 || tmp > 22) {
- cmd->reply = "Value must be between 0 and 22";
- return -1;
- }
-
- if (tmp & 1) {
- cmd->reply = "Value must be even";
- return -1;
- }
-
- return 0;
-}
-CTRL_CMD_DEFINE_RANGE(trx_arfcn, "arfcn", struct gsm_bts_trx, arfcn, 0, 1023);
-
-static int set_trx_max_power(struct ctrl_cmd *cmd, void *_data)
-{
- struct gsm_bts_trx *trx = cmd->node;
- int old_power;
-
- /* remember the old value, set the new one */
- old_power = trx->max_power_red;
- trx->max_power_red = atoi(cmd->value);
-
- /* Maybe update the value */
- if (old_power != trx->max_power_red) {
- LOGP(DCTRL, LOGL_NOTICE,
- "%s updating max_pwr_red(%d)\n",
- gsm_trx_name(trx), trx->max_power_red);
- abis_nm_update_max_power_red(trx);
- }
-
- return get_trx_max_power(cmd, _data);
-}
-CTRL_CMD_DEFINE(trx_max_power, "max-power-reduction");
-
-int bsc_base_ctrl_cmds_install(void)
-{
- int rc = 0;
- rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_mnc);
- rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_mcc);
- rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_short_name);
- rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_long_name);
- rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_apply_config);
- rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_mcc_mnc_apply);
- rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_rf_lock);
- rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_bts_num);
-
- rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_lac);
- rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_ci);
- rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_apply_config);
- rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_si);
- rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_chan_load);
- rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_oml_conn);
- rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_gprs_mode);
- rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_rf_state);
-
- rc |= ctrl_cmd_install(CTRL_NODE_TRX, &cmd_trx_max_power);
- rc |= ctrl_cmd_install(CTRL_NODE_TRX, &cmd_trx_arfcn);
-
- return rc;
-}
diff --git a/openbsc/src/libbsc/bsc_ctrl_lookup.c b/openbsc/src/libbsc/bsc_ctrl_lookup.c
deleted file mode 100644
index a8a8cf526..000000000
--- a/openbsc/src/libbsc/bsc_ctrl_lookup.c
+++ /dev/null
@@ -1,107 +0,0 @@
-/* SNMP-like status interface. Look-up of BTS/TRX
- *
- * (C) 2010-2011 by Daniel Willmann <daniel@totalueberwachung.de>
- * (C) 2010-2011 by On-Waves
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- */
-
-#include <errno.h>
-
-#include <osmocom/vty/command.h>
-#include <osmocom/ctrl/control_if.h>
-#include <openbsc/debug.h>
-#include <openbsc/gsm_data.h>
-
-extern vector ctrl_node_vec;
-
-/*! \brief control interface lookup function for bsc/bts gsm_data
- * \param[in] data Private data passed to controlif_setup()
- * \param[in] vline Vector of the line holding the command string
- * \param[out] node_type type (CTRL_NODE_) that was determined
- * \param[out] node_data private dta of node that was determined
- * \param i Current index into vline, up to which it is parsed
- */
-static int bsc_ctrl_node_lookup(void *data, vector vline, int *node_type,
- void **node_data, int *i)
-{
- struct gsm_network *net = data;
- struct gsm_bts *bts = NULL;
- struct gsm_bts_trx *trx = NULL;
- struct gsm_bts_trx_ts *ts = NULL;
- char *token = vector_slot(vline, *i);
- long num;
-
- /* TODO: We need to make sure that the following chars are digits
- * and/or use strtol to check if number conversion was successful
- * Right now something like net.bts_stats will not work */
- if (!strcmp(token, "bts")) {
- if (*node_type != CTRL_NODE_ROOT || !net)
- goto err_missing;
- (*i)++;
- if (!ctrl_parse_get_num(vline, *i, &num))
- goto err_index;
-
- bts = gsm_bts_num(net, num);
- if (!bts)
- goto err_missing;
- *node_data = bts;
- *node_type = CTRL_NODE_BTS;
- } else if (!strcmp(token, "trx")) {
- if (*node_type != CTRL_NODE_BTS || !*node_data)
- goto err_missing;
- bts = *node_data;
- (*i)++;
- if (!ctrl_parse_get_num(vline, *i, &num))
- goto err_index;
-
- trx = gsm_bts_trx_num(bts, num);
- if (!trx)
- goto err_missing;
- *node_data = trx;
- *node_type = CTRL_NODE_TRX;
- } else if (!strcmp(token, "ts")) {
- if (*node_type != CTRL_NODE_TRX || !*node_data)
- goto err_missing;
- trx = *node_data;
- (*i)++;
- if (!ctrl_parse_get_num(vline, *i, &num))
- goto err_index;
-
- if ((num >= 0) && (num < TRX_NR_TS))
- ts = &trx->ts[num];
- if (!ts)
- goto err_missing;
- *node_data = ts;
- *node_type = CTRL_NODE_TS;
- } else
- return 0;
-
- return 1;
-err_missing:
- return -ENODEV;
-err_index:
- return -ERANGE;
-}
-
-struct ctrl_handle *bsc_controlif_setup(struct gsm_network *net,
- const char *bind_addr, uint16_t port)
-{
- return ctrl_interface_setup_dynip(net, bind_addr, port,
- bsc_ctrl_node_lookup);
-}
diff --git a/openbsc/src/libbsc/bsc_dyn_ts.c b/openbsc/src/libbsc/bsc_dyn_ts.c
deleted file mode 100644
index e5422fc5c..000000000
--- a/openbsc/src/libbsc/bsc_dyn_ts.c
+++ /dev/null
@@ -1,77 +0,0 @@
-/* Dynamic PDCH initialisation implementation shared across NM and RSL */
-
-/* (C) 2016 by sysmocom s.f.m.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/abis_rsl.h>
-
-void tchf_pdch_ts_init(struct gsm_bts_trx_ts *ts)
-{
- int rc;
-
- if (ts->trx->bts->gprs.mode == BTS_GPRS_NONE) {
- LOGP(DRSL, LOGL_NOTICE, "%s: GPRS mode is 'none':"
- " not activating PDCH.\n",
- gsm_ts_and_pchan_name(ts));
- return;
- }
-
- LOGP(DRSL, LOGL_DEBUG, "%s: trying to PDCH ACT\n",
- gsm_ts_and_pchan_name(ts));
-
- rc = rsl_ipacc_pdch_activate(ts, 1);
- if (rc != 0)
- LOGP(DRSL, LOGL_ERROR, "%s %s: PDCH ACT failed\n",
- gsm_ts_name(ts), gsm_pchan_name(ts->pchan));
-}
-
-void tchf_tchh_pdch_ts_init(struct gsm_bts_trx_ts *ts)
-{
- if (ts->trx->bts->gprs.mode == BTS_GPRS_NONE) {
- LOGP(DRSL, LOGL_NOTICE, "%s: GPRS mode is 'none':"
- " not activating PDCH.\n",
- gsm_ts_and_pchan_name(ts));
- return;
- }
-
- dyn_ts_switchover_start(ts, GSM_PCHAN_PDCH);
-}
-
-void dyn_ts_init(struct gsm_bts_trx_ts *ts)
-{
- /* Clear all TCH/F_PDCH flags */
- ts->flags &= ~(TS_F_PDCH_PENDING_MASK | TS_F_PDCH_ACTIVE);
-
- /* Clear TCH/F_TCH/H_PDCH state */
- ts->dyn.pchan_is = ts->dyn.pchan_want = GSM_PCHAN_NONE;
- ts->dyn.pending_chan_activ = NULL;
-
- switch (ts->pchan) {
- case GSM_PCHAN_TCH_F_PDCH:
- tchf_pdch_ts_init(ts);
- break;
- case GSM_PCHAN_TCH_F_TCH_H_PDCH:
- tchf_tchh_pdch_ts_init(ts);
- break;
- default:
- break;
- }
-}
diff --git a/openbsc/src/libbsc/bsc_init.c b/openbsc/src/libbsc/bsc_init.c
deleted file mode 100644
index ec87a7bc7..000000000
--- a/openbsc/src/libbsc/bsc_init.c
+++ /dev/null
@@ -1,552 +0,0 @@
-/* A hackish minimal BSC (+MSC +HLR) implementation */
-
-/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org>
- * (C) 2009 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 <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>
-#include <openbsc/misdn.h>
-#include <osmocom/vty/telnet_interface.h>
-#include <osmocom/vty/ports.h>
-#include <openbsc/system_information.h>
-#include <openbsc/paging.h>
-#include <openbsc/signal.h>
-#include <openbsc/chan_alloc.h>
-#include <osmocom/core/talloc.h>
-#include <openbsc/ipaccess.h>
-#include <osmocom/gsm/sysinfo.h>
-#include <openbsc/e1_config.h>
-#include <openbsc/common_bsc.h>
-#include <openbsc/pcu_if.h>
-#include <limits.h>
-
-/* global pointer to the gsm network data structure */
-extern struct gsm_network *bsc_gsmnet;
-
-/* Callback function for NACK on the OML NM */
-static int oml_msg_nack(struct nm_nack_signal_data *nack)
-{
- if (nack->mt == NM_MT_GET_ATTR_NACK) {
- LOGP(DNM, LOGL_ERROR, "BTS%u does not support Get Attributes "
- "OML message.\n", nack->bts->nr);
- return 0;
- }
-
- if (nack->mt == NM_MT_SET_BTS_ATTR_NACK)
- LOGP(DNM, LOGL_ERROR, "Failed to set BTS attributes. That is fatal. "
- "Was the bts type and frequency properly specified?\n");
- else
- LOGP(DNM, LOGL_ERROR, "Got %s NACK going to drop the OML links.\n",
- abis_nm_nack_name(nack->mt));
-
- if (!nack->bts) {
- LOGP(DNM, LOGL_ERROR, "Unknown bts. Can not drop it.\n");
- return 0;
- }
-
- if (is_ipaccess_bts(nack->bts))
- ipaccess_drop_oml(nack->bts);
-
- return 0;
-}
-
-/* Callback function to be called every time we receive a signal from NM */
-static int nm_sig_cb(unsigned int subsys, unsigned int signal,
- void *handler_data, void *signal_data)
-{
- struct nm_nack_signal_data *nack;
-
- switch (signal) {
- case S_NM_NACK:
- nack = signal_data;
- return oml_msg_nack(nack);
- default:
- break;
- }
- return 0;
-}
-
-int bsc_shutdown_net(struct gsm_network *net)
-{
- struct gsm_bts *bts;
-
- llist_for_each_entry(bts, &net->bts_list, list) {
- LOGP(DNM, LOGL_NOTICE, "shutting down OML for BTS %u\n", bts->nr);
- osmo_signal_dispatch(SS_L_GLOBAL, S_GLOBAL_BTS_CLOSE_OM, bts);
- }
-
- return 0;
-}
-
-static int rsl_si(struct gsm_bts_trx *trx, enum osmo_sysinfo_type i, int si_len)
-{
- struct gsm_bts *bts = trx->bts;
- int rc, j;
-
- DEBUGP(DRR, "SI%s: %s\n", get_value_string(osmo_sitype_strs, i),
- osmo_hexdump(GSM_BTS_SI(bts, i), GSM_MACBLOCK_LEN));
-
- switch (i) {
- case SYSINFO_TYPE_5:
- case SYSINFO_TYPE_5bis:
- case SYSINFO_TYPE_5ter:
- case SYSINFO_TYPE_6:
- rc = rsl_sacch_filling(trx, osmo_sitype2rsl(i),
- GSM_BTS_SI(bts, i), si_len);
- break;
- case SYSINFO_TYPE_2quater:
- for (j = 0; j <= bts->si2q_count; j++)
- rc = rsl_bcch_info(trx, i, (const uint8_t *)GSM_BTS_SI2Q(bts, j), GSM_MACBLOCK_LEN);
- break;
- default:
- rc = rsl_bcch_info(trx, i, GSM_BTS_SI(bts, i), si_len);
- break;
- }
-
- return rc;
-}
-
-/* set all system information types for a TRX */
-int gsm_bts_trx_set_system_infos(struct gsm_bts_trx *trx)
-{
- int i, rc;
- struct gsm_bts *bts = trx->bts;
- uint8_t gen_si[_MAX_SYSINFO_TYPE], n_si = 0, n;
- int si_len[_MAX_SYSINFO_TYPE];
-
- bts->si_common.cell_sel_par.ms_txpwr_max_ccch =
- ms_pwr_ctl_lvl(bts->band, bts->ms_max_power);
- bts->si_common.cell_sel_par.neci = bts->network->neci;
-
- /* Zero, forget the state of the SIs */
- bts->si_valid = 0;
-
- /* First, we determine which of the SI messages we actually need */
-
- if (trx == bts->c0) {
- /* 1...4 are always present on a C0 TRX */
- gen_si[n_si++] = SYSINFO_TYPE_1;
- gen_si[n_si++] = SYSINFO_TYPE_2;
- gen_si[n_si++] = SYSINFO_TYPE_2bis;
- gen_si[n_si++] = SYSINFO_TYPE_2ter;
- gen_si[n_si++] = SYSINFO_TYPE_2quater;
- gen_si[n_si++] = SYSINFO_TYPE_3;
- gen_si[n_si++] = SYSINFO_TYPE_4;
-
- /* 13 is always present on a C0 TRX of a GPRS BTS */
- if (bts->gprs.mode != BTS_GPRS_NONE)
- gen_si[n_si++] = SYSINFO_TYPE_13;
- }
-
- /* 5 and 6 are always present on every TRX */
- gen_si[n_si++] = SYSINFO_TYPE_5;
- gen_si[n_si++] = SYSINFO_TYPE_5bis;
- gen_si[n_si++] = SYSINFO_TYPE_5ter;
- gen_si[n_si++] = SYSINFO_TYPE_6;
-
- /* Second, we generate the selected SI via RSL */
-
- for (n = 0; n < n_si; n++) {
- i = gen_si[n];
- /* Only generate SI if this SI is not in "static" (user-defined) mode */
- if (!(bts->si_mode_static & (1 << i))) {
- /* Set SI as being valid. gsm_generate_si() might unset
- * it, if SI is not required. */
- bts->si_valid |= (1 << i);
- rc = gsm_generate_si(bts, i);
- if (rc < 0)
- goto err_out;
- si_len[i] = rc;
- } else {
- if (i == SYSINFO_TYPE_5 || i == SYSINFO_TYPE_5bis
- || i == SYSINFO_TYPE_5ter)
- si_len[i] = 18;
- else if (i == SYSINFO_TYPE_6)
- si_len[i] = 11;
- else
- si_len[i] = 23;
- }
- }
-
- /* Third, we send the selected SI via RSL */
-
- for (n = 0; n < n_si; n++) {
- i = gen_si[n];
- if (!GSM_BTS_HAS_SI(bts, i))
- continue;
- rc = rsl_si(trx, i, si_len[i]);
- if (rc < 0)
- return rc;
- }
-
- /* Make sure the PCU is aware (in case anything GPRS related has
- * changed in SI */
- pcu_info_update(bts);
-
- return 0;
-err_out:
- LOGP(DRR, LOGL_ERROR, "Cannot generate SI%s for BTS %u: error <%s>, "
- "most likely a problem with neighbor cell list generation\n",
- get_value_string(osmo_sitype_strs, i), bts->nr, strerror(-rc));
- return rc;
-}
-
-/* set all system information types for a BTS */
-int gsm_bts_set_system_infos(struct gsm_bts *bts)
-{
- struct gsm_bts_trx *trx;
-
- /* Generate a new ID */
- bts->bcch_change_mark += 1;
- bts->bcch_change_mark %= 0x7;
-
- llist_for_each_entry(trx, &bts->trx_list, list) {
- int rc;
-
- rc = gsm_bts_trx_set_system_infos(trx);
- if (rc != 0)
- return rc;
- }
-
- return 0;
-}
-
-/* Produce a MA as specified in 10.5.2.21 */
-static int generate_ma_for_ts(struct gsm_bts_trx_ts *ts)
-{
- /* we have three bitvecs: the per-timeslot ARFCNs, the cell chan ARFCNs
- * and the MA */
- struct bitvec *cell_chan = &ts->trx->bts->si_common.cell_alloc;
- struct bitvec *ts_arfcn = &ts->hopping.arfcns;
- struct bitvec *ma = &ts->hopping.ma;
- unsigned int num_cell_arfcns, bitnum, n_chan;
- int i;
-
- /* re-set the MA to all-zero */
- ma->cur_bit = 0;
- ts->hopping.ma_len = 0;
- memset(ma->data, 0, ma->data_len);
-
- if (!ts->hopping.enabled)
- return 0;
-
- /* count the number of ARFCNs in the cell channel allocation */
- num_cell_arfcns = 0;
- for (i = 0; i < 1024; i++) {
- if (bitvec_get_bit_pos(cell_chan, i))
- num_cell_arfcns++;
- }
-
- /* pad it to octet-aligned number of bits */
- ts->hopping.ma_len = num_cell_arfcns / 8;
- if (num_cell_arfcns % 8)
- ts->hopping.ma_len++;
-
- n_chan = 0;
- for (i = 0; i < 1024; i++) {
- if (!bitvec_get_bit_pos(cell_chan, i))
- continue;
- /* set the corresponding bit in the MA */
- bitnum = (ts->hopping.ma_len * 8) - 1 - n_chan;
- if (bitvec_get_bit_pos(ts_arfcn, i))
- bitvec_set_bit_pos(ma, bitnum, 1);
- else
- bitvec_set_bit_pos(ma, bitnum, 0);
- n_chan++;
- }
-
- /* ARFCN 0 is special: It is coded last in the bitmask */
- if (bitvec_get_bit_pos(cell_chan, 0)) {
- n_chan++;
- /* set the corresponding bit in the MA */
- bitnum = (ts->hopping.ma_len * 8) - 1 - n_chan;
- if (bitvec_get_bit_pos(ts_arfcn, 0))
- bitvec_set_bit_pos(ma, bitnum, 1);
- else
- bitvec_set_bit_pos(ma, bitnum, 0);
- }
-
- return 0;
-}
-
-static void bootstrap_rsl(struct gsm_bts_trx *trx)
-{
- unsigned int i;
-
- LOGP(DRSL, LOGL_NOTICE, "bootstrapping RSL for BTS/TRX (%u/%u) "
- "on ARFCN %u using MCC=%u MNC=%u LAC=%u CID=%u BSIC=%u\n",
- trx->bts->nr, trx->nr, trx->arfcn, bsc_gsmnet->country_code,
- bsc_gsmnet->network_code, trx->bts->location_area_code,
- trx->bts->cell_identity, trx->bts->bsic);
-
- if (trx->bts->type == GSM_BTS_TYPE_NOKIA_SITE) {
- rsl_nokia_si_begin(trx);
- }
-
- gsm_bts_trx_set_system_infos(trx);
-
- if (trx->bts->type == GSM_BTS_TYPE_NOKIA_SITE) {
- /* channel unspecific, power reduction in 2 dB steps */
- rsl_bs_power_control(trx, 0xFF, trx->max_power_red / 2);
- rsl_nokia_si_end(trx);
- }
-
- for (i = 0; i < ARRAY_SIZE(trx->ts); i++)
- generate_ma_for_ts(&trx->ts[i]);
-}
-
-/* Callback function to be called every time we receive a signal from INPUT */
-static int inp_sig_cb(unsigned int subsys, unsigned int signal,
- void *handler_data, void *signal_data)
-{
- struct input_signal_data *isd = signal_data;
- struct gsm_bts_trx *trx = isd->trx;
- int ts_no, lchan_no;
- /* N. B: we rely on attribute order when parsing response in abis_nm_rx_get_attr_resp() */
- const uint8_t bts_attr[] = { NM_ATT_MANUF_ID, NM_ATT_SW_CONFIG, };
- const uint8_t trx_attr[] = { NM_ATT_MANUF_STATE, NM_ATT_SW_CONFIG, };
-
- /* we should not request more attributes than we're ready to handle */
- OSMO_ASSERT(sizeof(bts_attr) < MAX_BTS_ATTR);
- OSMO_ASSERT(sizeof(trx_attr) < MAX_BTS_ATTR);
-
- if (subsys != SS_L_INPUT)
- return -EINVAL;
-
- LOGP(DLMI, LOGL_DEBUG, "%s(): Input signal '%s' received\n", __func__,
- get_value_string(e1inp_signal_names, signal));
- switch (signal) {
- case S_L_INP_TEI_UP:
- if (isd->link_type == E1INP_SIGN_OML) {
- /* TODO: this is required for the Nokia BTS, hopping is configured
- during OML, other MA is not set. */
- struct gsm_bts_trx *cur_trx;
- /* was static in system_information.c */
- extern int generate_cell_chan_list(uint8_t *chan_list, struct gsm_bts *bts);
- uint8_t ca[20];
- /* has to be called before generate_ma_for_ts to
- set bts->si_common.cell_alloc */
- generate_cell_chan_list(ca, trx->bts);
-
- /* Request generic BTS-level attributes */
- abis_nm_get_attr(trx->bts, NM_OC_BTS, trx->bts->nr, trx->nr, 0xFF, bts_attr, sizeof(bts_attr));
-
- llist_for_each_entry(cur_trx, &trx->bts->trx_list, list) {
- int i;
- /* Request TRX-level attributes */
- abis_nm_get_attr(cur_trx->bts, NM_OC_BASEB_TRANSC, cur_trx->bts->nr, cur_trx->nr, 0xFF,
- trx_attr, sizeof(trx_attr));
- for (i = 0; i < ARRAY_SIZE(cur_trx->ts); i++)
- generate_ma_for_ts(&cur_trx->ts[i]);
- }
- }
- if (isd->link_type == E1INP_SIGN_RSL)
- bootstrap_rsl(trx);
- break;
- case S_L_INP_TEI_DN:
- LOGP(DLMI, LOGL_ERROR, "Lost some E1 TEI link: %d %p\n", isd->link_type, trx);
-
- if (isd->link_type == E1INP_SIGN_OML)
- rate_ctr_inc(&trx->bts->network->bsc_ctrs->ctr[BSC_CTR_BTS_OML_FAIL]);
- else if (isd->link_type == E1INP_SIGN_RSL)
- rate_ctr_inc(&trx->bts->network->bsc_ctrs->ctr[BSC_CTR_BTS_RSL_FAIL]);
-
- /*
- * free all allocated channels. change the nm_state so the
- * trx and trx_ts becomes unusable and chan_alloc.c can not
- * allocate from it.
- */
- for (ts_no = 0; ts_no < ARRAY_SIZE(trx->ts); ++ts_no) {
- struct gsm_bts_trx_ts *ts = &trx->ts[ts_no];
-
- for (lchan_no = 0; lchan_no < ARRAY_SIZE(ts->lchan); ++lchan_no) {
- if (ts->lchan[lchan_no].state != LCHAN_S_NONE)
- lchan_free(&ts->lchan[lchan_no]);
- lchan_reset(&ts->lchan[lchan_no]);
- }
- }
-
- gsm_bts_mo_reset(trx->bts);
-
- abis_nm_clear_queue(trx->bts);
- break;
- default:
- break;
- }
-
- return 0;
-}
-
-static int bootstrap_bts(struct gsm_bts *bts)
-{
- int i, n;
-
- if (!bts->model)
- return -EFAULT;
-
- if (bts->model->start && !bts->model->started) {
- int ret = bts->model->start(bts->network);
- if (ret < 0)
- return ret;
-
- bts->model->started = true;
- }
-
- /* FIXME: What about secondary TRX of a BTS? What about a BTS that has TRX
- * in different bands? Why is 'band' a parameter of the BTS and not of the TRX? */
- switch (bts->band) {
- case GSM_BAND_1800:
- if (bts->c0->arfcn < 512 || bts->c0->arfcn > 885) {
- LOGP(DNM, LOGL_ERROR, "GSM1800 channel must be between 512-885.\n");
- return -EINVAL;
- }
- break;
- case GSM_BAND_1900:
- if (bts->c0->arfcn < 512 || bts->c0->arfcn > 810) {
- LOGP(DNM, LOGL_ERROR, "GSM1900 channel must be between 512-810.\n");
- return -EINVAL;
- }
- break;
- case GSM_BAND_900:
- if ((bts->c0->arfcn > 124 && bts->c0->arfcn < 955) ||
- bts->c0->arfcn > 1023) {
- LOGP(DNM, LOGL_ERROR, "GSM900 channel must be between 0-124, 955-1023.\n");
- return -EINVAL;
- }
- break;
- case GSM_BAND_850:
- if (bts->c0->arfcn < 128 || bts->c0->arfcn > 251) {
- LOGP(DNM, LOGL_ERROR, "GSM850 channel must be between 128-251.\n");
- return -EINVAL;
- }
- break;
- default:
- LOGP(DNM, LOGL_ERROR, "Unsupported frequency band.\n");
- return -EINVAL;
- }
-
- if (bts->network->auth_policy == GSM_AUTH_POLICY_ACCEPT_ALL &&
- !bts->si_common.rach_control.cell_bar)
- LOGP(DNM, LOGL_ERROR, "\nWARNING: You are running an 'accept-all' "
- "network on a BTS that is not barred. This "
- "configuration is likely to interfere with production "
- "GSM networks and should only be used in a RF "
- "shielded environment such as a faraday cage!\n\n");
-
- /* Control Channel Description is set from vty/config */
-
- /* T3212 is set from vty/config */
-
- /* Set ccch config by looking at ts config */
- for (n=0, i=0; i<8; i++)
- n += bts->c0->ts[i].pchan == GSM_PCHAN_CCCH ? 1 : 0;
-
- switch (n) {
- case 0:
- bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_1_C;
- /* Limit reserved block to 2 on combined channel according to
- 3GPP TS 44.018 Table 10.5.2.11.1 */
- if (bts->si_common.chan_desc.bs_ag_blks_res > 2) {
- LOGP(DNM, LOGL_NOTICE, "CCCH is combined with SDCCHs, "
- "reducing BS-AG-BLKS-RES value %d -> 2\n",
- bts->si_common.chan_desc.bs_ag_blks_res);
- bts->si_common.chan_desc.bs_ag_blks_res = 2;
- }
- break;
- case 1:
- bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_1_NC;
- break;
- case 2:
- bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_2_NC;
- break;
- case 3:
- bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_3_NC;
- break;
- case 4:
- bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_4_NC;
- break;
- default:
- LOGP(DNM, LOGL_ERROR, "Unsupported CCCH timeslot configuration\n");
- return -EINVAL;
- }
-
- bts->si_common.cell_options.pwrc = 0; /* PWRC not set */
-
- bts->si_common.cell_sel_par.acs = 0;
-
- bts->si_common.ncc_permitted = 0xff;
-
- /* Initialize the BTS state */
- gsm_bts_mo_reset(bts);
-
- return 0;
-}
-
-int bsc_network_alloc(mncc_recv_cb_t mncc_recv)
-{
- /* initialize our data structures */
- bsc_gsmnet = bsc_network_init(tall_bsc_ctx, 1, 1, mncc_recv);
- if (!bsc_gsmnet)
- return -ENOMEM;
-
- bsc_gsmnet->name_long = talloc_strdup(bsc_gsmnet, "OpenBSC");
- bsc_gsmnet->name_short = talloc_strdup(bsc_gsmnet, "OpenBSC");
-
- return 0;
-}
-
-int bsc_network_configure(const char *config_file)
-{
- struct gsm_bts *bts;
- int rc;
-
- rc = vty_read_config_file(config_file, NULL);
- if (rc < 0) {
- LOGP(DNM, LOGL_FATAL, "Failed to parse the config file: '%s'\n", config_file);
- return rc;
- }
-
- /* start telnet after reading config for vty_get_bind_addr() */
- rc = telnet_init_dynif(tall_bsc_ctx, bsc_gsmnet, vty_get_bind_addr(),
- OSMO_VTY_PORT_NITB_BSC);
- if (rc < 0)
- return rc;
-
- osmo_signal_register_handler(SS_NM, nm_sig_cb, NULL);
- osmo_signal_register_handler(SS_L_INPUT, inp_sig_cb, NULL);
-
- llist_for_each_entry(bts, &bsc_gsmnet->bts_list, list) {
- rc = bootstrap_bts(bts);
- if (rc < 0) {
- LOGP(DNM, LOGL_FATAL, "Error bootstrapping BTS\n");
- return rc;
- }
- rc = e1_reconfig_bts(bts);
- if (rc < 0) {
- LOGP(DNM, LOGL_FATAL, "Error enabling E1 input driver\n");
- return rc;
- }
- }
-
- return 0;
-}
diff --git a/openbsc/src/libbsc/bsc_msc.c b/openbsc/src/libbsc/bsc_msc.c
deleted file mode 100644
index 82a572dbe..000000000
--- a/openbsc/src/libbsc/bsc_msc.c
+++ /dev/null
@@ -1,320 +0,0 @@
-/* Routines to talk to the MSC using the IPA Protocol */
-/*
- * (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
- * (C) 2010 by On-Waves
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <openbsc/bsc_msc.h>
-#include <openbsc/debug.h>
-#include <osmocom/abis/ipaccess.h>
-
-#include <osmocom/core/write_queue.h>
-#include <osmocom/core/talloc.h>
-
-#include <osmocom/gsm/tlv.h>
-
-#include <arpa/inet.h>
-#include <sys/socket.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-
-static void connection_loss(struct bsc_msc_connection *con)
-{
- struct osmo_fd *fd;
-
- fd = &con->write_queue.bfd;
-
- if (con->pending_msg) {
- LOGP(DMSC, LOGL_ERROR,
- "MSC(%s) dropping incomplete message.\n", con->name);
- msgb_free(con->pending_msg);
- con->pending_msg = NULL;
- }
-
- close(fd->fd);
- fd->fd = -1;
- fd->cb = osmo_wqueue_bfd_cb;
- fd->when = 0;
-
- con->is_connected = 0;
- con->first_contact = 0;
- con->connection_loss(con);
-}
-
-static void msc_con_timeout(void *_con)
-{
- struct bsc_msc_connection *con = _con;
-
- LOGP(DMSC, LOGL_ERROR,
- "MSC(%s) Connection timeout.\n", con->name);
- bsc_msc_lost(con);
-}
-
-/* called in the case of a non blocking connect */
-static int msc_connection_connect(struct osmo_fd *fd, unsigned int what)
-{
- int rc;
- int val;
- struct bsc_msc_connection *con;
- struct osmo_wqueue *queue;
-
- socklen_t len = sizeof(val);
-
- queue = container_of(fd, struct osmo_wqueue, bfd);
- con = container_of(queue, struct bsc_msc_connection, write_queue);
-
- if ((what & BSC_FD_WRITE) == 0) {
- LOGP(DMSC, LOGL_ERROR,
- "MSC(%s) Callback but not writable.\n", con->name);
- return -1;
- }
-
- /* From here on we will either be connected or reconnect */
- osmo_timer_del(&con->timeout_timer);
-
- /* check the socket state */
- rc = getsockopt(fd->fd, SOL_SOCKET, SO_ERROR, &val, &len);
- if (rc != 0) {
- LOGP(DMSC, LOGL_ERROR,
- "getsockopt for the MSC(%s) socket failed.\n", con->name);
- goto error;
- }
- if (val != 0) {
- LOGP(DMSC, LOGL_ERROR,
- "Not connected to the MSC(%s): %d\n",
- con->name, val);
- goto error;
- }
-
-
- /* go to full operation */
- fd->cb = osmo_wqueue_bfd_cb;
- fd->when = BSC_FD_READ | BSC_FD_EXCEPT;
-
- con->is_connected = 1;
- LOGP(DMSC, LOGL_NOTICE,
- "(Re)Connected to the MSC(%s).\n", con->name);
- if (con->connected)
- con->connected(con);
- return 0;
-
-error:
- osmo_fd_unregister(fd);
- connection_loss(con);
- return -1;
-}
-static void setnonblocking(struct osmo_fd *fd)
-{
- int flags;
-
- flags = fcntl(fd->fd, F_GETFL);
- if (flags < 0) {
- perror("fcntl get failed");
- close(fd->fd);
- fd->fd = -1;
- return;
- }
-
- flags |= O_NONBLOCK;
- flags = fcntl(fd->fd, F_SETFL, flags);
- if (flags < 0) {
- perror("fcntl get failed");
- close(fd->fd);
- fd->fd = -1;
- return;
- }
-}
-
-int bsc_msc_connect(struct bsc_msc_connection *con)
-{
- struct bsc_msc_dest *dest;
- struct osmo_fd *fd;
- struct sockaddr_in sin;
- int on = 1, ret;
-
- if (llist_empty(con->dests)) {
- LOGP(DMSC, LOGL_ERROR,
- "No MSC(%s) connections configured.\n",
- con->name);
- connection_loss(con);
- return -1;
- }
-
- /* TODO: Why are we not using the libosmocore soecket
- * abstraction, or libosmo-netif? */
-
- /* move to the next connection */
- dest = (struct bsc_msc_dest *) con->dests->next;
- llist_del(&dest->list);
- llist_add_tail(&dest->list, con->dests);
-
- LOGP(DMSC, LOGL_NOTICE,
- "Attempting to connect MSC(%s) at %s:%d\n",
- con->name, dest->ip, dest->port);
-
- con->is_connected = 0;
-
- msgb_free(con->pending_msg);
- con->pending_msg = NULL;
-
- fd = &con->write_queue.bfd;
- fd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- fd->priv_nr = 1;
-
- if (fd->fd < 0) {
- perror("Creating TCP socket failed");
- return fd->fd;
- }
-
- /* make it non blocking */
- setnonblocking(fd);
-
- /* set the socket priority */
- ret = setsockopt(fd->fd, IPPROTO_IP, IP_TOS,
- &dest->dscp, sizeof(dest->dscp));
- if (ret != 0)
- LOGP(DMSC, LOGL_ERROR,
- "Failed to set DSCP to %d on MSC(%s). %s\n",
- dest->dscp, con->name, strerror(errno));
-
- memset(&sin, 0, sizeof(sin));
- sin.sin_family = AF_INET;
- sin.sin_port = htons(dest->port);
- inet_aton(dest->ip, &sin.sin_addr);
-
- ret = setsockopt(fd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
- if (ret != 0)
- LOGP(DMSC, LOGL_ERROR,
- "Failed to set SO_REUSEADDR socket option\n");
- ret = connect(fd->fd, (struct sockaddr *) &sin, sizeof(sin));
-
- if (ret == -1 && errno == EINPROGRESS) {
- LOGP(DMSC, LOGL_ERROR,
- "MSC(%s) Connection in progress\n", con->name);
- fd->when = BSC_FD_WRITE;
- fd->cb = msc_connection_connect;
- osmo_timer_setup(&con->timeout_timer, msc_con_timeout, con);
- osmo_timer_schedule(&con->timeout_timer, 20, 0);
- } else if (ret < 0) {
- perror("Connection failed");
- connection_loss(con);
- return ret;
- } else {
- fd->when = BSC_FD_READ | BSC_FD_EXCEPT;
- fd->cb = osmo_wqueue_bfd_cb;
- con->is_connected = 1;
- if (con->connected)
- con->connected(con);
- }
-
- ret = osmo_fd_register(fd);
- if (ret < 0) {
- perror("Registering the fd failed");
- close(fd->fd);
- return ret;
- }
-
- return ret;
-}
-
-struct bsc_msc_connection *bsc_msc_create(void *ctx, struct llist_head *dests)
-{
- struct bsc_msc_connection *con;
-
- con = talloc_zero(NULL, struct bsc_msc_connection);
- if (!con) {
- LOGP(DMSC, LOGL_FATAL, "Failed to create the MSC connection.\n");
- return NULL;
- }
-
- con->dests = dests;
- con->write_queue.bfd.fd = -1;
- con->name = "";
- osmo_wqueue_init(&con->write_queue, 100);
- return con;
-}
-
-void bsc_msc_lost(struct bsc_msc_connection *con)
-{
- osmo_wqueue_clear(&con->write_queue);
- osmo_timer_del(&con->timeout_timer);
- osmo_timer_del(&con->reconnect_timer);
-
- if (con->write_queue.bfd.fd >= 0)
- osmo_fd_unregister(&con->write_queue.bfd);
- connection_loss(con);
-}
-
-static void reconnect_msc(void *_msc)
-{
- struct bsc_msc_connection *con = _msc;
-
- LOGP(DMSC, LOGL_NOTICE,
- "Attempting to reconnect to the MSC(%s).\n", con->name);
- bsc_msc_connect(con);
-}
-
-void bsc_msc_schedule_connect(struct bsc_msc_connection *con)
-{
- LOGP(DMSC, LOGL_NOTICE,
- "Attempting to reconnect to the MSC(%s)\n", con->name);
- osmo_timer_setup(&con->reconnect_timer, reconnect_msc, con);
- osmo_timer_schedule(&con->reconnect_timer, 5, 0);
-}
-
-struct msgb *bsc_msc_id_get_resp(int fixed, const char *token, const uint8_t *res, int len)
-{
- struct msgb *msg;
-
- if (!token) {
- LOGP(DMSC, LOGL_ERROR, "No token specified.\n");
- return NULL;
- }
-
- msg = msgb_alloc_headroom(4096, 128, "id resp");
- if (!msg) {
- LOGP(DMSC, LOGL_ERROR, "Failed to create the message.\n");
- return NULL;
- }
-
- /*
- * The situation is bizarre. The encoding doesn't follow the
- * TLV structure. It is more like a LV and old versions had
- * it wrong but we want new versions to old servers so we
- * introduce the quirk here.
- */
- msg->l2h = msgb_v_put(msg, IPAC_MSGT_ID_RESP);
- if (fixed) {
- msgb_put_u8(msg, 0);
- msgb_put_u8(msg, strlen(token) + 2);
- msgb_tv_fixed_put(msg, IPAC_IDTAG_UNITNAME, strlen(token) + 1, (uint8_t *) token);
- if (len > 0) {
- msgb_put_u8(msg, 0);
- msgb_put_u8(msg, len + 1);
- msgb_tv_fixed_put(msg, 0x24, len, res);
- }
- } else {
- msgb_l16tv_put(msg, strlen(token) + 1,
- IPAC_IDTAG_UNITNAME, (uint8_t *) token);
- }
-
- return msg;
-}
diff --git a/openbsc/src/libbsc/bsc_rf_ctrl.c b/openbsc/src/libbsc/bsc_rf_ctrl.c
deleted file mode 100644
index b7b6fc819..000000000
--- a/openbsc/src/libbsc/bsc_rf_ctrl.c
+++ /dev/null
@@ -1,534 +0,0 @@
-/* RF Ctl handling socket */
-
-/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
- * (C) 2010-2014 by Holger Hans Peter Freyther <zecke@selfish.org>
- * (C) 2010-2014 by On-Waves
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <openbsc/osmo_bsc_rf.h>
-#include <openbsc/debug.h>
-#include <openbsc/gsm_data.h>
-#include <openbsc/signal.h>
-#include <openbsc/bsc_msc_data.h>
-#include <openbsc/ipaccess.h>
-
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/utils.h>
-#include <osmocom/gsm/protocol/gsm_12_21.h>
-
-#include <sys/socket.h>
-#include <sys/un.h>
-
-#include <errno.h>
-#include <unistd.h>
-
-#define RF_CMD_QUERY '?'
-#define RF_CMD_OFF '0'
-#define RF_CMD_ON '1'
-#define RF_CMD_D_OFF 'd'
-#define RF_CMD_ON_G 'g'
-
-static const struct value_string opstate_names[] = {
- { OSMO_BSC_RF_OPSTATE_INOPERATIONAL, "inoperational" },
- { OSMO_BSC_RF_OPSTATE_OPERATIONAL, "operational" },
- { 0, NULL }
-};
-
-static const struct value_string adminstate_names[] = {
- { OSMO_BSC_RF_ADMINSTATE_UNLOCKED, "unlocked" },
- { OSMO_BSC_RF_ADMINSTATE_LOCKED, "locked" },
- { 0, NULL }
-};
-
-static const struct value_string policy_names[] = {
- { OSMO_BSC_RF_POLICY_OFF, "off" },
- { OSMO_BSC_RF_POLICY_ON, "on" },
- { OSMO_BSC_RF_POLICY_GRACE, "grace" },
- { OSMO_BSC_RF_POLICY_UNKNOWN, "unknown" },
- { 0, NULL }
-};
-
-const char *osmo_bsc_rf_get_opstate_name(enum osmo_bsc_rf_opstate opstate)
-{
- return get_value_string(opstate_names, opstate);
-}
-
-const char *osmo_bsc_rf_get_adminstate_name(enum osmo_bsc_rf_adminstate adminstate)
-{
- return get_value_string(adminstate_names, adminstate);
-}
-
-const char *osmo_bsc_rf_get_policy_name(enum osmo_bsc_rf_policy policy)
-{
- return get_value_string(policy_names, policy);
-}
-
-enum osmo_bsc_rf_opstate osmo_bsc_rf_get_opstate_by_bts(struct gsm_bts *bts)
-{
- struct gsm_bts_trx *trx;
-
- llist_for_each_entry(trx, &bts->trx_list, list) {
- if (trx->mo.nm_state.operational == NM_OPSTATE_ENABLED)
- return OSMO_BSC_RF_OPSTATE_OPERATIONAL;
- }
-
- /* No trx were active, so this bts is disabled */
- return OSMO_BSC_RF_OPSTATE_INOPERATIONAL;
-}
-
-enum osmo_bsc_rf_adminstate osmo_bsc_rf_get_adminstate_by_bts(struct gsm_bts *bts)
-{
- struct gsm_bts_trx *trx;
-
- llist_for_each_entry(trx, &bts->trx_list, list) {
- if (trx->mo.nm_state.administrative == NM_STATE_UNLOCKED)
- return OSMO_BSC_RF_ADMINSTATE_UNLOCKED;
- }
-
- /* All trx administrative states were locked */
- return OSMO_BSC_RF_ADMINSTATE_LOCKED;
-}
-
-enum osmo_bsc_rf_policy osmo_bsc_rf_get_policy_by_bts(struct gsm_bts *bts)
-{
- struct osmo_bsc_data *bsc_data = bts->network->bsc_data;
-
- if (!bsc_data)
- return OSMO_BSC_RF_POLICY_UNKNOWN;
-
- switch (bsc_data->rf_ctrl->policy) {
- case S_RF_ON:
- return OSMO_BSC_RF_POLICY_ON;
- case S_RF_OFF:
- return OSMO_BSC_RF_POLICY_OFF;
- case S_RF_GRACE:
- return OSMO_BSC_RF_POLICY_GRACE;
- default:
- return OSMO_BSC_RF_POLICY_UNKNOWN;
- }
-}
-
-static int lock_each_trx(struct gsm_network *net, int lock)
-{
- struct gsm_bts *bts;
-
- llist_for_each_entry(bts, &net->bts_list, list) {
- struct gsm_bts_trx *trx;
-
- /* Exclude the BTS from the global lock */
- if (bts->excl_from_rf_lock) {
- LOGP(DLINP, LOGL_DEBUG,
- "Excluding BTS(%d) from trx lock.\n", bts->nr);
- continue;
- }
-
- llist_for_each_entry(trx, &bts->trx_list, list) {
- gsm_trx_lock_rf(trx, lock);
- }
- }
-
- return 0;
-}
-
-static void send_resp(struct osmo_bsc_rf_conn *conn, char send)
-{
- struct msgb *msg;
-
- msg = msgb_alloc(10, "RF Query");
- if (!msg) {
- LOGP(DLINP, LOGL_ERROR, "Failed to allocate response msg.\n");
- return;
- }
-
- msg->l2h = msgb_put(msg, 1);
- msg->l2h[0] = send;
-
- if (osmo_wqueue_enqueue(&conn->queue, msg) != 0) {
- LOGP(DLINP, LOGL_ERROR, "Failed to enqueue the answer.\n");
- msgb_free(msg);
- return;
- }
-
- return;
-}
-
-
-/*
- * Send a
- * 'g' when we are in grace mode
- * '1' when one TRX is online,
- * '0' otherwise
- */
-static void handle_query(struct osmo_bsc_rf_conn *conn)
-{
- struct gsm_bts *bts;
- char send = RF_CMD_OFF;
-
- if (conn->rf->policy == S_RF_GRACE)
- return send_resp(conn, RF_CMD_ON_G);
-
- llist_for_each_entry(bts, &conn->rf->gsm_network->bts_list, list) {
- struct gsm_bts_trx *trx;
-
- /* Exclude the BTS from the global lock */
- if (bts->excl_from_rf_lock) {
- LOGP(DLINP, LOGL_DEBUG,
- "Excluding BTS(%d) from query.\n", bts->nr);
- continue;
- }
- llist_for_each_entry(trx, &bts->trx_list, list) {
- if (trx->mo.nm_state.availability == NM_AVSTATE_OK &&
- trx->mo.nm_state.operational != NM_OPSTATE_DISABLED) {
- send = RF_CMD_ON;
- break;
- }
- }
- }
-
- send_resp(conn, send);
-}
-
-static void rf_check_cb(void *_data)
-{
- struct gsm_bts *bts;
- struct osmo_bsc_rf *rf = _data;
-
- llist_for_each_entry(bts, &rf->gsm_network->bts_list, list) {
- struct gsm_bts_trx *trx;
-
- /* don't bother to check a booting or missing BTS */
- if (!bts->oml_link || !is_ipaccess_bts(bts))
- continue;
-
- /* Exclude the BTS from the global lock */
- if (bts->excl_from_rf_lock) {
- LOGP(DLINP, LOGL_DEBUG,
- "Excluding BTS(%d) from query.\n", bts->nr);
- continue;
- }
-
- llist_for_each_entry(trx, &bts->trx_list, list) {
- if (trx->mo.nm_state.availability != NM_AVSTATE_OK ||
- trx->mo.nm_state.operational != NM_OPSTATE_ENABLED ||
- trx->mo.nm_state.administrative != NM_STATE_UNLOCKED) {
- LOGP(DNM, LOGL_ERROR, "RF activation failed. Starting again.\n");
- ipaccess_drop_oml(bts);
- break;
- }
- }
- }
-}
-
-static void send_signal(struct osmo_bsc_rf *rf, int val)
-{
- struct rf_signal_data sig;
- sig.net = rf->gsm_network;
-
- rf->policy = val;
- osmo_signal_dispatch(SS_RF, val, &sig);
-}
-
-static int switch_rf_off(struct osmo_bsc_rf *rf)
-{
- lock_each_trx(rf->gsm_network, 1);
- send_signal(rf, S_RF_OFF);
-
- return 0;
-}
-
-static void grace_timeout(void *_data)
-{
- struct osmo_bsc_rf *rf = (struct osmo_bsc_rf *) _data;
-
- LOGP(DLINP, LOGL_NOTICE, "Grace timeout. Going to disable all BTS/TRX.\n");
- switch_rf_off(rf);
-}
-
-static int enter_grace(struct osmo_bsc_rf *rf)
-{
- if (osmo_timer_pending(&rf->grace_timeout)) {
- LOGP(DLINP, LOGL_NOTICE, "RF Grace timer is pending. Not restarting.\n");
- return 0;
- }
-
- osmo_timer_setup(&rf->grace_timeout, grace_timeout, rf);
- osmo_timer_schedule(&rf->grace_timeout, rf->gsm_network->bsc_data->mid_call_timeout, 0);
- LOGP(DLINP, LOGL_NOTICE, "Going to switch RF off in %d seconds.\n",
- rf->gsm_network->bsc_data->mid_call_timeout);
-
- send_signal(rf, S_RF_GRACE);
- return 0;
-}
-
-static void rf_delay_cmd_cb(void *data)
-{
- struct osmo_bsc_rf *rf = data;
-
- switch (rf->last_request) {
- case RF_CMD_D_OFF:
- rf->last_state_command = "RF Direct Off";
- osmo_timer_del(&rf->rf_check);
- osmo_timer_del(&rf->grace_timeout);
- switch_rf_off(rf);
- break;
- case RF_CMD_ON:
- rf->last_state_command = "RF Direct On";
- osmo_timer_del(&rf->grace_timeout);
- lock_each_trx(rf->gsm_network, 0);
- send_signal(rf, S_RF_ON);
- osmo_timer_schedule(&rf->rf_check, 3, 0);
- break;
- case RF_CMD_OFF:
- rf->last_state_command = "RF Scheduled Off";
- osmo_timer_del(&rf->rf_check);
- enter_grace(rf);
- break;
- }
-}
-
-static int rf_read_cmd(struct osmo_fd *fd)
-{
- struct osmo_bsc_rf_conn *conn = fd->data;
- char buf[1];
- int rc;
-
- rc = read(fd->fd, buf, sizeof(buf));
- if (rc != sizeof(buf)) {
- LOGP(DLINP, LOGL_ERROR, "Short read %d/%s\n", errno, strerror(errno));
- osmo_fd_unregister(fd);
- close(fd->fd);
- osmo_wqueue_clear(&conn->queue);
- talloc_free(conn);
- return -1;
- }
-
- switch (buf[0]) {
- case RF_CMD_QUERY:
- handle_query(conn);
- break;
- case RF_CMD_D_OFF:
- case RF_CMD_ON:
- case RF_CMD_OFF:
- osmo_bsc_rf_schedule_lock(conn->rf, buf[0]);
- break;
- default:
- conn->rf->last_state_command = "Unknown command";
- LOGP(DLINP, LOGL_ERROR, "Unknown command %d\n", buf[0]);
- break;
- }
-
- return 0;
-}
-
-static int rf_write_cmd(struct osmo_fd *fd, struct msgb *msg)
-{
- int rc;
-
- rc = write(fd->fd, msg->data, msg->len);
- if (rc != msg->len) {
- LOGP(DLINP, LOGL_ERROR, "Short write %d/%s\n", errno, strerror(errno));
- return -1;
- }
-
- return 0;
-}
-
-static int rf_ctrl_accept(struct osmo_fd *bfd, unsigned int what)
-{
- struct osmo_bsc_rf_conn *conn;
- struct osmo_bsc_rf *rf = bfd->data;
- struct sockaddr_un addr;
- socklen_t len = sizeof(addr);
- int fd;
-
- fd = accept(bfd->fd, (struct sockaddr *) &addr, &len);
- if (fd < 0) {
- LOGP(DLINP, LOGL_ERROR, "Failed to accept. errno: %d/%s\n",
- errno, strerror(errno));
- return -1;
- }
-
- conn = talloc_zero(rf, struct osmo_bsc_rf_conn);
- if (!conn) {
- LOGP(DLINP, LOGL_ERROR, "Failed to allocate mem.\n");
- close(fd);
- return -1;
- }
-
- osmo_wqueue_init(&conn->queue, 10);
- conn->queue.bfd.data = conn;
- conn->queue.bfd.fd = fd;
- conn->queue.bfd.when = BSC_FD_READ | BSC_FD_WRITE;
- conn->queue.read_cb = rf_read_cmd;
- conn->queue.write_cb = rf_write_cmd;
- conn->rf = rf;
-
- if (osmo_fd_register(&conn->queue.bfd) != 0) {
- close(fd);
- talloc_free(conn);
- return -1;
- }
-
- return 0;
-}
-
-static void rf_auto_off_cb(void *_timer)
-{
- struct osmo_bsc_rf *rf = _timer;
-
- LOGP(DLINP, LOGL_NOTICE,
- "Going to switch off RF due lack of a MSC connection.\n");
- osmo_bsc_rf_schedule_lock(rf, RF_CMD_D_OFF);
-}
-
-static int msc_signal_handler(unsigned int subsys, unsigned int signal,
- void *handler_data, void *signal_data)
-{
- struct gsm_network *net;
- struct msc_signal_data *msc;
- struct osmo_bsc_rf *rf;
-
- /* check if we want to handle this signal */
- if (subsys != SS_MSC)
- return 0;
-
- net = handler_data;
- msc = signal_data;
-
- /* check if we have the needed information */
- if (!net->bsc_data)
- return 0;
- if (msc->data->type != MSC_CON_TYPE_NORMAL)
- return 0;
-
- rf = net->bsc_data->rf_ctrl;
- switch (signal) {
- case S_MSC_LOST:
- if (net->bsc_data->auto_off_timeout < 0)
- return 0;
- if (osmo_timer_pending(&rf->auto_off_timer))
- return 0;
- osmo_timer_schedule(&rf->auto_off_timer,
- net->bsc_data->auto_off_timeout, 0);
- break;
- case S_MSC_CONNECTED:
- osmo_timer_del(&rf->auto_off_timer);
- break;
- }
-
- return 0;
-}
-
-static int rf_create_socket(struct osmo_bsc_rf *rf, const char *path)
-{
- unsigned int namelen;
- struct sockaddr_un local;
- struct osmo_fd *bfd;
- int rc;
-
- bfd = &rf->listen;
- bfd->fd = socket(AF_UNIX, SOCK_STREAM, 0);
- if (bfd->fd < 0) {
- LOGP(DLINP, LOGL_ERROR, "Can not create socket. %d/%s\n",
- errno, strerror(errno));
- return -1;
- }
-
- local.sun_family = AF_UNIX;
- osmo_strlcpy(local.sun_path, path, sizeof(local.sun_path));
- unlink(local.sun_path);
-
- /* we use the same magic that X11 uses in Xtranssock.c for
- * calculating the proper length of the sockaddr */
-#if defined(BSD44SOCKETS) || defined(__UNIXWARE__)
- local.sun_len = strlen(local.sun_path);
-#endif
-#if defined(BSD44SOCKETS) || defined(SUN_LEN)
- namelen = SUN_LEN(&local);
-#else
- namelen = strlen(local.sun_path) +
- offsetof(struct sockaddr_un, sun_path);
-#endif
-
- rc = bind(bfd->fd, (struct sockaddr *) &local, namelen);
- if (rc != 0) {
- LOGP(DLINP, LOGL_ERROR, "Failed to bind '%s' errno: %d/%s\n",
- local.sun_path, errno, strerror(errno));
- close(bfd->fd);
- return -1;
- }
-
- if (listen(bfd->fd, 0) != 0) {
- LOGP(DLINP, LOGL_ERROR, "Failed to listen: %d/%s\n", errno, strerror(errno));
- close(bfd->fd);
- return -1;
- }
-
- bfd->when = BSC_FD_READ;
- bfd->cb = rf_ctrl_accept;
- bfd->data = rf;
-
- if (osmo_fd_register(bfd) != 0) {
- LOGP(DLINP, LOGL_ERROR, "Failed to register bfd.\n");
- close(bfd->fd);
- return -1;
- }
-
- return 0;
-}
-
-struct osmo_bsc_rf *osmo_bsc_rf_create(const char *path, struct gsm_network *net)
-{
- struct osmo_bsc_rf *rf;
-
- rf = talloc_zero(NULL, struct osmo_bsc_rf);
- if (!rf) {
- LOGP(DLINP, LOGL_ERROR, "Failed to create osmo_bsc_rf.\n");
- return NULL;
- }
-
- if (path && rf_create_socket(rf, path) != 0) {
- talloc_free(rf);
- return NULL;
- }
-
- rf->gsm_network = net;
- rf->policy = S_RF_ON;
- rf->last_state_command = "";
- rf->last_rf_lock_ctrl_command = talloc_strdup(rf, "");
-
- /* check the rf state */
- osmo_timer_setup(&rf->rf_check, rf_check_cb, rf);
-
- /* delay cmd handling */
- osmo_timer_setup(&rf->delay_cmd, rf_delay_cmd_cb, rf);
-
- osmo_timer_setup(&rf->auto_off_timer, rf_auto_off_cb, rf);
-
- /* listen to RF signals */
- osmo_signal_register_handler(SS_MSC, msc_signal_handler, net);
-
- return rf;
-}
-
-void osmo_bsc_rf_schedule_lock(struct osmo_bsc_rf *rf, char cmd)
-{
- rf->last_request = cmd;
- if (!osmo_timer_pending(&rf->delay_cmd))
- osmo_timer_schedule(&rf->delay_cmd, 1, 0);
-}
diff --git a/openbsc/src/libbsc/bsc_rll.c b/openbsc/src/libbsc/bsc_rll.c
deleted file mode 100644
index bb488da15..000000000
--- a/openbsc/src/libbsc/bsc_rll.c
+++ /dev/null
@@ -1,139 +0,0 @@
-/* GSM BSC Radio Link Layer API
- * 3GPP TS 08.58 version 8.6.0 Release 1999 / ETSI TS 100 596 V8.6.0 */
-
-/* (C) 2009 by Harald Welte <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 <openbsc/debug.h>
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/timer.h>
-#include <osmocom/core/linuxlist.h>
-#include <openbsc/bsc_rll.h>
-#include <openbsc/gsm_data.h>
-#include <openbsc/chan_alloc.h>
-#include <openbsc/abis_rsl.h>
-#include <openbsc/signal.h>
-
-struct bsc_rll_req {
- struct llist_head list;
- struct osmo_timer_list timer;
-
- struct gsm_lchan *lchan;
- uint8_t link_id;
-
- void (*cb)(struct gsm_lchan *lchan, uint8_t link_id,
- void *data, enum bsc_rllr_ind);
- void *data;
-};
-
-/* we only compare C1, C2 and SAPI */
-#define LINKID_MASK 0xC7
-
-static LLIST_HEAD(bsc_rll_reqs);
-
-static void complete_rllr(struct bsc_rll_req *rllr, enum bsc_rllr_ind type)
-{
- llist_del(&rllr->list);
- rllr->cb(rllr->lchan, rllr->link_id, rllr->data, type);
- talloc_free(rllr);
-}
-
-static void timer_cb(void *_rllr)
-{
- struct bsc_rll_req *rllr = _rllr;
-
- complete_rllr(rllr, BSC_RLLR_IND_TIMEOUT);
-}
-
-/* establish a RLL connection with given SAPI / priority */
-int rll_establish(struct gsm_lchan *lchan, uint8_t sapi,
- void (*cb)(struct gsm_lchan *, uint8_t, void *,
- enum bsc_rllr_ind),
- void *data)
-{
- struct bsc_rll_req *rllr = talloc_zero(tall_bsc_ctx, struct bsc_rll_req);
- uint8_t link_id;
- if (!rllr)
- return -ENOMEM;
-
- link_id = sapi;
-
- /* If we are a TCH and not in signalling mode, we need to
- * indicate that the new RLL connection is to be made on the SACCH */
- if ((lchan->type == GSM_LCHAN_TCH_F ||
- lchan->type == GSM_LCHAN_TCH_H) && sapi != 0)
- link_id |= 0x40;
-
- rllr->lchan = lchan;
- rllr->link_id = link_id;
- rllr->cb = cb;
- rllr->data = data;
-
- llist_add(&rllr->list, &bsc_rll_reqs);
-
- osmo_timer_setup(&rllr->timer, timer_cb, rllr);
- osmo_timer_schedule(&rllr->timer, 7, 0);
-
- /* send the RSL RLL ESTablish REQuest */
- return rsl_establish_request(rllr->lchan, rllr->link_id);
-}
-
-/* Called from RSL code in case we have received an indication regarding
- * any RLL link */
-void rll_indication(struct gsm_lchan *lchan, uint8_t link_id, uint8_t type)
-{
- struct bsc_rll_req *rllr, *rllr2;
-
- llist_for_each_entry_safe(rllr, rllr2, &bsc_rll_reqs, list) {
- if (rllr->lchan == lchan &&
- (rllr->link_id & LINKID_MASK) == (link_id & LINKID_MASK)) {
- osmo_timer_del(&rllr->timer);
- complete_rllr(rllr, type);
- return;
- }
- }
-}
-
-static int rll_lchan_signal(unsigned int subsys, unsigned int signal,
- void *handler_data, void *signal_data)
-{
- struct challoc_signal_data *challoc;
- struct bsc_rll_req *rllr, *rllr2;
-
- if (subsys != SS_CHALLOC || signal != S_CHALLOC_FREED)
- return 0;
-
- challoc = (struct challoc_signal_data *) signal_data;
-
- llist_for_each_entry_safe(rllr, rllr2, &bsc_rll_reqs, list) {
- if (rllr->lchan == challoc->lchan) {
- osmo_timer_del(&rllr->timer);
- complete_rllr(rllr, BSC_RLLR_IND_ERR_IND);
- }
- }
-
- return 0;
-}
-
-static __attribute__((constructor)) void on_dso_load_rll(void)
-{
- osmo_signal_register_handler(SS_CHALLOC, rll_lchan_signal, NULL);
-}
diff --git a/openbsc/src/libbsc/bsc_subscriber.c b/openbsc/src/libbsc/bsc_subscriber.c
deleted file mode 100644
index 73e61e801..000000000
--- a/openbsc/src/libbsc/bsc_subscriber.c
+++ /dev/null
@@ -1,168 +0,0 @@
-/* GSM subscriber details for use in BSC land */
-
-/*
- * (C) 2016 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 <talloc.h>
-#include <string.h>
-#include <limits.h>
-
-#include <osmocom/gsm/gsm48.h>
-#include <osmocom/core/logging.h>
-
-#include <openbsc/bsc_subscriber.h>
-#include <openbsc/debug.h>
-
-static struct bsc_subscr *bsc_subscr_alloc(struct llist_head *list)
-{
- struct bsc_subscr *bsub;
-
- bsub = talloc_zero(list, struct bsc_subscr);
- if (!bsub)
- return NULL;
-
- llist_add_tail(&bsub->entry, list);
- bsub->use_count = 1;
-
- return bsub;
-}
-
-struct bsc_subscr *bsc_subscr_find_by_imsi(struct llist_head *list,
- const char *imsi)
-{
- struct bsc_subscr *bsub;
-
- if (!imsi || !*imsi)
- return NULL;
-
- llist_for_each_entry(bsub, list, entry) {
- if (!strcmp(bsub->imsi, imsi))
- return bsc_subscr_get(bsub);
- }
- return NULL;
-}
-
-struct bsc_subscr *bsc_subscr_find_by_tmsi(struct llist_head *list,
- uint32_t tmsi)
-{
- struct bsc_subscr *bsub;
-
- if (tmsi == GSM_RESERVED_TMSI)
- return NULL;
-
- llist_for_each_entry(bsub, list, entry) {
- if (bsub->tmsi == tmsi)
- return bsc_subscr_get(bsub);
- }
- return NULL;
-}
-
-void bsc_subscr_set_imsi(struct bsc_subscr *bsub, const char *imsi)
-{
- if (!bsub)
- return;
- osmo_strlcpy(bsub->imsi, imsi, sizeof(bsub->imsi));
-}
-
-struct bsc_subscr *bsc_subscr_find_or_create_by_imsi(struct llist_head *list,
- const char *imsi)
-{
- struct bsc_subscr *bsub;
- bsub = bsc_subscr_find_by_imsi(list, imsi);
- if (bsub)
- return bsub;
- bsub = bsc_subscr_alloc(list);
- bsc_subscr_set_imsi(bsub, imsi);
- return bsub;
-}
-
-struct bsc_subscr *bsc_subscr_find_or_create_by_tmsi(struct llist_head *list,
- uint32_t tmsi)
-{
- struct bsc_subscr *bsub;
- bsub = bsc_subscr_find_by_tmsi(list, tmsi);
- if (bsub)
- return bsub;
- bsub = bsc_subscr_alloc(list);
- bsub->tmsi = tmsi;
- return bsub;
-}
-
-const char *bsc_subscr_name(struct bsc_subscr *bsub)
-{
- static char buf[32];
- if (!bsub)
- return "unknown";
- if (bsub->imsi[0])
- snprintf(buf, sizeof(buf), "IMSI:%s", bsub->imsi);
- else
- snprintf(buf, sizeof(buf), "TMSI:0x%08x", bsub->tmsi);
- return buf;
-}
-
-static void bsc_subscr_free(struct bsc_subscr *bsub)
-{
- llist_del(&bsub->entry);
- talloc_free(bsub);
-}
-
-struct bsc_subscr *_bsc_subscr_get(struct bsc_subscr *bsub,
- const char *file, int line)
-{
- OSMO_ASSERT(bsub->use_count < INT_MAX);
- bsub->use_count++;
- LOGPSRC(DREF, LOGL_DEBUG, file, line,
- "BSC subscr %s usage increases to: %d\n",
- bsc_subscr_name(bsub), bsub->use_count);
- return bsub;
-}
-
-struct bsc_subscr *_bsc_subscr_put(struct bsc_subscr *bsub,
- const char *file, int line)
-{
- bsub->use_count--;
- LOGPSRC(DREF, bsub->use_count >= 0? LOGL_DEBUG : LOGL_ERROR,
- file, line,
- "BSC subscr %s usage decreases to: %d\n",
- bsc_subscr_name(bsub), bsub->use_count);
- if (bsub->use_count <= 0)
- bsc_subscr_free(bsub);
- return NULL;
-}
-
-void log_set_filter_bsc_subscr(struct log_target *target,
- struct bsc_subscr *bsc_subscr)
-{
- struct bsc_subscr **fsub = (void*)&target->filter_data[LOG_FLT_BSC_SUBSCR];
-
- /* free the old data */
- if (*fsub) {
- bsc_subscr_put(*fsub);
- *fsub = NULL;
- }
-
- if (bsc_subscr) {
- target->filter_map |= (1 << LOG_FLT_BSC_SUBSCR);
- *fsub = bsc_subscr_get(bsc_subscr);
- } else
- target->filter_map &= ~(1 << LOG_FLT_BSC_SUBSCR);
-}
diff --git a/openbsc/src/libbsc/bsc_vty.c b/openbsc/src/libbsc/bsc_vty.c
deleted file mode 100644
index 9fc289503..000000000
--- a/openbsc/src/libbsc/bsc_vty.c
+++ /dev/null
@@ -1,4304 +0,0 @@
-/* OpenBSC interface to quagga VTY */
-/* (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 <stdlib.h>
-#include <stdbool.h>
-#include <unistd.h>
-
-#include <osmocom/vty/command.h>
-#include <osmocom/vty/buffer.h>
-#include <osmocom/vty/vty.h>
-#include <osmocom/vty/logging.h>
-#include <osmocom/vty/stats.h>
-#include <osmocom/vty/telnet_interface.h>
-#include <osmocom/vty/misc.h>
-#include <osmocom/gsm/protocol/gsm_04_08.h>
-#include <osmocom/gsm/gsm0502.h>
-
-#include <arpa/inet.h>
-
-#include <osmocom/core/linuxlist.h>
-#include <openbsc/gsm_data.h>
-#include <osmocom/abis/e1_input.h>
-#include <openbsc/abis_nm.h>
-#include <openbsc/abis_om2000.h>
-#include <osmocom/core/utils.h>
-#include <osmocom/gsm/gsm_utils.h>
-#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>
-#include <openbsc/debug.h>
-#include <openbsc/paging.h>
-#include <openbsc/ipaccess.h>
-#include <openbsc/abis_rsl.h>
-#include <openbsc/bsc_msc_data.h>
-#include <openbsc/osmo_bsc_rf.h>
-#include <openbsc/pcu_if.h>
-
-#include <openbsc/common_cs.h>
-
-#include <inttypes.h>
-
-#include "../../bscconfig.h"
-
-
-#define LCHAN_NR_STR "Logical Channel Number\n"
-
-
-/* FIXME: this should go to some common file */
-static const struct value_string gprs_ns_timer_strs[] = {
- { 0, "tns-block" },
- { 1, "tns-block-retries" },
- { 2, "tns-reset" },
- { 3, "tns-reset-retries" },
- { 4, "tns-test" },
- { 5, "tns-alive" },
- { 6, "tns-alive-retries" },
- { 0, NULL }
-};
-
-static const struct value_string gprs_bssgp_cfg_strs[] = {
- { 0, "blocking-timer" },
- { 1, "blocking-retries" },
- { 2, "unblocking-retries" },
- { 3, "reset-timer" },
- { 4, "reset-retries" },
- { 5, "suspend-timer" },
- { 6, "suspend-retries" },
- { 7, "resume-timer" },
- { 8, "resume-retries" },
- { 9, "capability-update-timer" },
- { 10, "capability-update-retries" },
- { 0, NULL }
-};
-
-static const struct value_string bts_neigh_mode_strs[] = {
- { NL_MODE_AUTOMATIC, "automatic" },
- { NL_MODE_MANUAL, "manual" },
- { NL_MODE_MANUAL_SI5SEP, "manual-si5" },
- { 0, NULL }
-};
-
-const struct value_string bts_loc_fix_names[] = {
- { BTS_LOC_FIX_INVALID, "invalid" },
- { BTS_LOC_FIX_2D, "fix2d" },
- { BTS_LOC_FIX_3D, "fix3d" },
- { 0, NULL }
-};
-
-struct cmd_node bts_node = {
- BTS_NODE,
- "%s(config-net-bts)# ",
- 1,
-};
-
-struct cmd_node trx_node = {
- TRX_NODE,
- "%s(config-net-bts-trx)# ",
- 1,
-};
-
-struct cmd_node ts_node = {
- TS_NODE,
- "%s(config-net-bts-trx-ts)# ",
- 1,
-};
-
-static int dummy_config_write(struct vty *v)
-{
- return CMD_SUCCESS;
-}
-
-static void net_dump_nmstate(struct vty *vty, struct gsm_nm_state *nms)
-{
- vty_out(vty,"Oper '%s', Admin '%s', Avail '%s'%s",
- abis_nm_opstate_name(nms->operational),
- get_value_string(abis_nm_adm_state_names, nms->administrative),
- abis_nm_avail_name(nms->availability), VTY_NEWLINE);
-}
-
-static void dump_pchan_load_vty(struct vty *vty, char *prefix,
- const struct pchan_load *pl)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(pl->pchan); i++) {
- const struct load_counter *lc = &pl->pchan[i];
- unsigned int percent;
-
- if (lc->total == 0)
- continue;
-
- percent = (lc->used * 100) / lc->total;
-
- vty_out(vty, "%s%20s: %3u%% (%u/%u)%s", prefix,
- gsm_pchan_name(i), percent, lc->used, lc->total,
- VTY_NEWLINE);
- }
-}
-
-static void net_dump_vty(struct vty *vty, struct gsm_network *net)
-{
- struct pchan_load pl;
-
- vty_out(vty, "BSC is on Country Code %u, Network Code %u "
- "and has %u BTS%s", net->country_code, net->network_code,
- net->num_bts, VTY_NEWLINE);
- vty_out(vty, " Long network name: '%s'%s",
- net->name_long, VTY_NEWLINE);
- vty_out(vty, " Short network name: '%s'%s",
- net->name_short, VTY_NEWLINE);
- vty_out(vty, " Authentication policy: %s",
- gsm_auth_policy_name(net->auth_policy));
- if (net->authorized_reg_str)
- vty_out(vty, ", authorized regexp: %s", net->authorized_reg_str);
- vty_out(vty, "%s", VTY_NEWLINE);
- vty_out(vty, " Auto create subscriber: %s%s",
- net->auto_create_subscr ? "yes" : "no", VTY_NEWLINE);
- vty_out(vty, " Auto assign extension: %s%s",
- net->auto_assign_exten ? "yes" : "no", VTY_NEWLINE);
- vty_out(vty, " Location updating reject cause: %u%s",
- net->reject_cause, VTY_NEWLINE);
- vty_out(vty, " Encryption: A5/%u%s", net->a5_encryption,
- VTY_NEWLINE);
- vty_out(vty, " NECI (TCH/H): %u%s", net->neci,
- VTY_NEWLINE);
- vty_out(vty, " Use TCH for Paging any: %d%s", net->pag_any_tch,
- VTY_NEWLINE);
- vty_out(vty, " RRLP Mode: %s%s", rrlp_mode_name(net->rrlp.mode),
- VTY_NEWLINE);
- vty_out(vty, " MM Info: %s%s", net->send_mm_info ? "On" : "Off",
- VTY_NEWLINE);
- vty_out(vty, " Handover: %s%s", net->handover.active ? "On" : "Off",
- VTY_NEWLINE);
- network_chan_load(&pl, net);
- vty_out(vty, " Current Channel Load:%s", VTY_NEWLINE);
- dump_pchan_load_vty(vty, " ", &pl);
-
- /* show rf */
- if (net->bsc_data)
- vty_out(vty, " Last RF Command: %s%s",
- net->bsc_data->rf_ctrl->last_state_command,
- VTY_NEWLINE);
- if (net->bsc_data)
- vty_out(vty, " Last RF Lock Command: %s%s",
- net->bsc_data->rf_ctrl->last_rf_lock_ctrl_command,
- VTY_NEWLINE);
-}
-
-DEFUN(bsc_show_net, bsc_show_net_cmd, "show network",
- SHOW_STR "Display information about a GSM NETWORK\n")
-{
- struct gsm_network *net = gsmnet_from_vty(vty);
- net_dump_vty(vty, net);
-
- return CMD_SUCCESS;
-}
-
-static void e1isl_dump_vty(struct vty *vty, struct e1inp_sign_link *e1l)
-{
- struct e1inp_line *line;
-
- if (!e1l) {
- vty_out(vty, " None%s", VTY_NEWLINE);
- return;
- }
-
- line = e1l->ts->line;
-
- vty_out(vty, " E1 Line %u, Type %s: Timeslot %u, Mode %s%s",
- line->num, line->driver->name, e1l->ts->num,
- e1inp_signtype_name(e1l->type), VTY_NEWLINE);
- vty_out(vty, " E1 TEI %u, SAPI %u%s",
- e1l->tei, e1l->sapi, VTY_NEWLINE);
-}
-
-static void bts_dump_vty(struct vty *vty, struct gsm_bts *bts)
-{
- struct pchan_load pl;
-
- vty_out(vty, "BTS %u is of %s type in band %s, has CI %u LAC %u, "
- "BSIC %u (NCC=%u, BCC=%u) and %u TRX%s",
- bts->nr, btstype2str(bts->type), gsm_band_name(bts->band),
- bts->cell_identity,
- bts->location_area_code, bts->bsic,
- bts->bsic >> 3, bts->bsic & 7,
- bts->num_trx, VTY_NEWLINE);
- vty_out(vty, "Description: %s%s",
- bts->description ? bts->description : "(null)", VTY_NEWLINE);
- if (strnlen(bts->pcu_version, MAX_VERSION_LENGTH))
- vty_out(vty, "PCU version %s connected%s", bts->pcu_version,
- VTY_NEWLINE);
- vty_out(vty, "MS Max power: %u dBm%s", bts->ms_max_power, VTY_NEWLINE);
- vty_out(vty, "Minimum Rx Level for Access: %i dBm%s",
- rxlev2dbm(bts->si_common.cell_sel_par.rxlev_acc_min),
- VTY_NEWLINE);
- vty_out(vty, "Cell Reselection Hysteresis: %u dBm%s",
- bts->si_common.cell_sel_par.cell_resel_hyst*2, VTY_NEWLINE);
- vty_out(vty, "RACH TX-Integer: %u%s", bts->si_common.rach_control.tx_integer,
- VTY_NEWLINE);
- vty_out(vty, "RACH Max transmissions: %u%s",
- rach_max_trans_raw2val(bts->si_common.rach_control.max_trans),
- VTY_NEWLINE);
- if (bts->si_common.rach_control.cell_bar)
- vty_out(vty, " CELL IS BARRED%s", VTY_NEWLINE);
- if (bts->dtxu != GSM48_DTX_SHALL_NOT_BE_USED)
- vty_out(vty, "Uplink DTX: %s%s",
- (bts->dtxu != GSM48_DTX_SHALL_BE_USED) ?
- "enabled" : "forced", VTY_NEWLINE);
- else
- vty_out(vty, "Uplink DTX: not enabled%s", VTY_NEWLINE);
- vty_out(vty, "Downlink DTX: %senabled%s", bts->dtxd ? "" : "not ",
- VTY_NEWLINE);
- vty_out(vty, "Channel Description Attachment: %s%s",
- (bts->si_common.chan_desc.att) ? "yes" : "no", VTY_NEWLINE);
- vty_out(vty, "Channel Description BS-PA-MFRMS: %u%s",
- bts->si_common.chan_desc.bs_pa_mfrms + 2, VTY_NEWLINE);
- vty_out(vty, "Channel Description BS-AG_BLKS-RES: %u%s",
- bts->si_common.chan_desc.bs_ag_blks_res, VTY_NEWLINE);
- vty_out(vty, "System Information present: 0x%08x, static: 0x%08x%s",
- bts->si_valid, bts->si_mode_static, VTY_NEWLINE);
- vty_out(vty, "Early Classmark Sending: %s%s",
- bts->early_classmark_allowed ? "allowed" : "forbidden",
- VTY_NEWLINE);
- if (bts->pcu_sock_path)
- vty_out(vty, "PCU Socket Path: %s%s", bts->pcu_sock_path, VTY_NEWLINE);
- if (is_ipaccess_bts(bts))
- vty_out(vty, " Unit ID: %u/%u/0, OML Stream ID 0x%02x%s",
- bts->ip_access.site_id, bts->ip_access.bts_id,
- bts->oml_tei, VTY_NEWLINE);
- else if (bts->type == GSM_BTS_TYPE_NOKIA_SITE)
- vty_out(vty, " Skip Reset: %d%s",
- bts->nokia.skip_reset, VTY_NEWLINE);
- vty_out(vty, " NM State: ");
- net_dump_nmstate(vty, &bts->mo.nm_state);
- vty_out(vty, " Site Mgr NM State: ");
- net_dump_nmstate(vty, &bts->site_mgr.mo.nm_state);
- vty_out(vty, " GPRS NSE: ");
- net_dump_nmstate(vty, &bts->gprs.nse.mo.nm_state);
- vty_out(vty, " GPRS CELL: ");
- net_dump_nmstate(vty, &bts->gprs.cell.mo.nm_state);
- vty_out(vty, " GPRS NSVC0: ");
- net_dump_nmstate(vty, &bts->gprs.nsvc[0].mo.nm_state);
- vty_out(vty, " GPRS NSVC1: ");
- net_dump_nmstate(vty, &bts->gprs.nsvc[1].mo.nm_state);
- vty_out(vty, " Paging: %u pending requests, %u free slots%s",
- paging_pending_requests_nr(bts),
- bts->paging.available_slots, VTY_NEWLINE);
- if (is_ipaccess_bts(bts)) {
- vty_out(vty, " OML Link state: %s.%s",
- bts->oml_link ? "connected" : "disconnected", VTY_NEWLINE);
- } else {
- vty_out(vty, " E1 Signalling Link:%s", VTY_NEWLINE);
- e1isl_dump_vty(vty, bts->oml_link);
- }
-
- /* FIXME: chan_desc */
- memset(&pl, 0, sizeof(pl));
- bts_chan_load(&pl, bts);
- vty_out(vty, " Current Channel Load:%s", VTY_NEWLINE);
- dump_pchan_load_vty(vty, " ", &pl);
-}
-
-DEFUN(show_bts, show_bts_cmd, "show bts [<0-255>]",
- SHOW_STR "Display information about a BTS\n"
- "BTS number")
-{
- struct gsm_network *net = gsmnet_from_vty(vty);
- int bts_nr;
-
- if (argc != 0) {
- /* use the BTS number that the user has specified */
- bts_nr = atoi(argv[0]);
- if (bts_nr >= net->num_bts) {
- vty_out(vty, "%% can't find BTS '%s'%s", argv[0],
- VTY_NEWLINE);
- return CMD_WARNING;
- }
- bts_dump_vty(vty, gsm_bts_num(net, bts_nr));
- return CMD_SUCCESS;
- }
- /* print all BTS's */
- for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++)
- bts_dump_vty(vty, gsm_bts_num(net, bts_nr));
-
- return CMD_SUCCESS;
-}
-
-/* utility functions */
-static void parse_e1_link(struct gsm_e1_subslot *e1_link, const char *line,
- const char *ts, const char *ss)
-{
- e1_link->e1_nr = atoi(line);
- e1_link->e1_ts = atoi(ts);
- if (!strcmp(ss, "full"))
- e1_link->e1_ts_ss = 255;
- else
- e1_link->e1_ts_ss = atoi(ss);
-}
-
-static void config_write_e1_link(struct vty *vty, struct gsm_e1_subslot *e1_link,
- const char *prefix)
-{
- if (!e1_link->e1_ts)
- return;
-
- if (e1_link->e1_ts_ss == 255)
- vty_out(vty, "%se1 line %u timeslot %u sub-slot full%s",
- prefix, e1_link->e1_nr, e1_link->e1_ts, VTY_NEWLINE);
- else
- vty_out(vty, "%se1 line %u timeslot %u sub-slot %u%s",
- prefix, e1_link->e1_nr, e1_link->e1_ts,
- e1_link->e1_ts_ss, VTY_NEWLINE);
-}
-
-
-static void config_write_ts_single(struct vty *vty, struct gsm_bts_trx_ts *ts)
-{
- vty_out(vty, " timeslot %u%s", ts->nr, VTY_NEWLINE);
- if (ts->tsc != -1)
- vty_out(vty, " training_sequence_code %u%s", ts->tsc, VTY_NEWLINE);
- if (ts->pchan != GSM_PCHAN_NONE)
- vty_out(vty, " phys_chan_config %s%s",
- gsm_pchan_name(ts->pchan), VTY_NEWLINE);
- vty_out(vty, " hopping enabled %u%s",
- ts->hopping.enabled, VTY_NEWLINE);
- if (ts->hopping.enabled) {
- unsigned int i;
- vty_out(vty, " hopping sequence-number %u%s",
- ts->hopping.hsn, VTY_NEWLINE);
- vty_out(vty, " hopping maio %u%s",
- ts->hopping.maio, VTY_NEWLINE);
- for (i = 0; i < ts->hopping.arfcns.data_len*8; i++) {
- if (!bitvec_get_bit_pos(&ts->hopping.arfcns, i))
- continue;
- vty_out(vty, " hopping arfcn add %u%s",
- i, VTY_NEWLINE);
- }
- }
- config_write_e1_link(vty, &ts->e1_link, " ");
-
- if (ts->trx->bts->model->config_write_ts)
- ts->trx->bts->model->config_write_ts(vty, ts);
-}
-
-static void config_write_trx_single(struct vty *vty, struct gsm_bts_trx *trx)
-{
- int i;
-
- vty_out(vty, " trx %u%s", trx->nr, VTY_NEWLINE);
- if (trx->description)
- vty_out(vty, " description %s%s", trx->description,
- VTY_NEWLINE);
- vty_out(vty, " rf_locked %u%s",
- trx->mo.nm_state.administrative == NM_STATE_LOCKED ? 1 : 0,
- VTY_NEWLINE);
- vty_out(vty, " arfcn %u%s", trx->arfcn, VTY_NEWLINE);
- vty_out(vty, " nominal power %u%s", trx->nominal_power, VTY_NEWLINE);
- vty_out(vty, " max_power_red %u%s", trx->max_power_red, VTY_NEWLINE);
- config_write_e1_link(vty, &trx->rsl_e1_link, " rsl ");
- vty_out(vty, " rsl e1 tei %u%s", trx->rsl_tei, VTY_NEWLINE);
-
- if (trx->bts->model->config_write_trx)
- trx->bts->model->config_write_trx(vty, trx);
-
- for (i = 0; i < TRX_NR_TS; i++)
- config_write_ts_single(vty, &trx->ts[i]);
-}
-
-static void config_write_bts_gprs(struct vty *vty, struct gsm_bts *bts)
-{
- unsigned int i;
- vty_out(vty, " gprs mode %s%s", bts_gprs_mode_name(bts->gprs.mode),
- VTY_NEWLINE);
- if (bts->gprs.mode == BTS_GPRS_NONE)
- return;
-
- vty_out(vty, " gprs 11bit_rach_support_for_egprs %u%s",
- bts->gprs.supports_egprs_11bit_rach, VTY_NEWLINE);
-
- vty_out(vty, " gprs routing area %u%s", bts->gprs.rac,
- VTY_NEWLINE);
- vty_out(vty, " gprs network-control-order nc%u%s",
- bts->gprs.net_ctrl_ord, VTY_NEWLINE);
- if (!bts->gprs.ctrl_ack_type_use_block)
- vty_out(vty, " gprs control-ack-type-rach%s", VTY_NEWLINE);
- vty_out(vty, " gprs cell bvci %u%s", bts->gprs.cell.bvci,
- VTY_NEWLINE);
- for (i = 0; i < ARRAY_SIZE(bts->gprs.cell.timer); i++)
- vty_out(vty, " gprs cell timer %s %u%s",
- get_value_string(gprs_bssgp_cfg_strs, i),
- bts->gprs.cell.timer[i], VTY_NEWLINE);
- vty_out(vty, " gprs nsei %u%s", bts->gprs.nse.nsei,
- VTY_NEWLINE);
- for (i = 0; i < ARRAY_SIZE(bts->gprs.nse.timer); i++)
- vty_out(vty, " gprs ns timer %s %u%s",
- get_value_string(gprs_ns_timer_strs, i),
- bts->gprs.nse.timer[i], VTY_NEWLINE);
- for (i = 0; i < ARRAY_SIZE(bts->gprs.nsvc); i++) {
- struct gsm_bts_gprs_nsvc *nsvc =
- &bts->gprs.nsvc[i];
- struct in_addr ia;
-
- ia.s_addr = htonl(nsvc->remote_ip);
- vty_out(vty, " gprs nsvc %u nsvci %u%s", i,
- nsvc->nsvci, VTY_NEWLINE);
- vty_out(vty, " gprs nsvc %u local udp port %u%s", i,
- nsvc->local_port, VTY_NEWLINE);
- vty_out(vty, " gprs nsvc %u remote udp port %u%s", i,
- nsvc->remote_port, VTY_NEWLINE);
- vty_out(vty, " gprs nsvc %u remote ip %s%s", i,
- inet_ntoa(ia), VTY_NEWLINE);
- }
-}
-
-/* Write the model data if there is one */
-static void config_write_bts_model(struct vty *vty, struct gsm_bts *bts)
-{
- struct gsm_bts_trx *trx;
-
- if (!bts->model)
- return;
-
- if (bts->model->config_write_bts)
- bts->model->config_write_bts(vty, bts);
-
- llist_for_each_entry(trx, &bts->trx_list, list)
- config_write_trx_single(vty, trx);
-}
-
-static void write_amr_modes(struct vty *vty, const char *prefix,
- const char *name, struct amr_mode *modes, int num)
-{
- int i;
-
- vty_out(vty, " %s threshold %s", prefix, name);
- for (i = 0; i < num - 1; i++)
- vty_out(vty, " %d", modes[i].threshold);
- vty_out(vty, "%s", VTY_NEWLINE);
- vty_out(vty, " %s hysteresis %s", prefix, name);
- for (i = 0; i < num - 1; i++)
- vty_out(vty, " %d", modes[i].hysteresis);
- vty_out(vty, "%s", VTY_NEWLINE);
-}
-
-static void config_write_bts_amr(struct vty *vty, struct gsm_bts *bts,
- struct amr_multirate_conf *mr, int full)
-{
- struct gsm48_multi_rate_conf *mr_conf;
- const char *prefix = (full) ? "amr tch-f" : "amr tch-h";
- int i, num;
-
- if (!(mr->gsm48_ie[1]))
- return;
-
- mr_conf = (struct gsm48_multi_rate_conf *) mr->gsm48_ie;
-
- num = 0;
- vty_out(vty, " %s modes", prefix);
- for (i = 0; i < ((full) ? 8 : 6); i++) {
- if ((mr->gsm48_ie[1] & (1 << i))) {
- vty_out(vty, " %d", i);
- num++;
- }
- }
- vty_out(vty, "%s", VTY_NEWLINE);
- if (num > 4)
- num = 4;
- if (num > 1) {
- write_amr_modes(vty, prefix, "ms", mr->ms_mode, num);
- write_amr_modes(vty, prefix, "bts", mr->bts_mode, num);
- }
- vty_out(vty, " %s start-mode ", prefix);
- if (mr_conf->icmi) {
- num = 0;
- for (i = 0; i < ((full) ? 8 : 6) && num < 4; i++) {
- if ((mr->gsm48_ie[1] & (1 << i)))
- num++;
- if (mr_conf->smod == num - 1) {
- vty_out(vty, "%d%s", num, VTY_NEWLINE);
- break;
- }
- }
- } else
- vty_out(vty, "auto%s", VTY_NEWLINE);
-}
-
-static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts)
-{
- int i;
- uint8_t tmp;
-
- vty_out(vty, " bts %u%s", bts->nr, VTY_NEWLINE);
- vty_out(vty, " type %s%s", btstype2str(bts->type), VTY_NEWLINE);
- if (bts->description)
- vty_out(vty, " description %s%s", bts->description, VTY_NEWLINE);
- vty_out(vty, " band %s%s", gsm_band_name(bts->band), VTY_NEWLINE);
- vty_out(vty, " cell_identity %u%s", bts->cell_identity, VTY_NEWLINE);
- vty_out(vty, " location_area_code %u%s", bts->location_area_code,
- VTY_NEWLINE);
- if (bts->dtxu != GSM48_DTX_SHALL_NOT_BE_USED)
- vty_out(vty, " dtx uplink%s%s",
- (bts->dtxu != GSM48_DTX_SHALL_BE_USED) ? "" : " force",
- VTY_NEWLINE);
- if (bts->dtxd)
- vty_out(vty, " dtx downlink%s", VTY_NEWLINE);
- vty_out(vty, " base_station_id_code %u%s", bts->bsic, VTY_NEWLINE);
- vty_out(vty, " ms max power %u%s", bts->ms_max_power, VTY_NEWLINE);
- vty_out(vty, " cell reselection hysteresis %u%s",
- bts->si_common.cell_sel_par.cell_resel_hyst*2, VTY_NEWLINE);
- vty_out(vty, " rxlev access min %u%s",
- bts->si_common.cell_sel_par.rxlev_acc_min, VTY_NEWLINE);
-
- if (bts->si_common.cell_ro_sel_par.present) {
- struct gsm48_si_selection_params *sp;
- sp = &bts->si_common.cell_ro_sel_par;
-
- if (sp->cbq)
- vty_out(vty, " cell bar qualify %u%s",
- sp->cbq, VTY_NEWLINE);
-
- if (sp->cell_resel_off)
- vty_out(vty, " cell reselection offset %u%s",
- sp->cell_resel_off*2, VTY_NEWLINE);
-
- if (sp->temp_offs == 7)
- vty_out(vty, " temporary offset infinite%s",
- VTY_NEWLINE);
- else if (sp->temp_offs)
- vty_out(vty, " temporary offset %u%s",
- sp->temp_offs*10, VTY_NEWLINE);
-
- if (sp->penalty_time == 31)
- vty_out(vty, " penalty time reserved%s",
- VTY_NEWLINE);
- else if (sp->penalty_time)
- vty_out(vty, " penalty time %u%s",
- (sp->penalty_time*20)+20, VTY_NEWLINE);
- }
-
- /* Is periodic LU enabled or disabled? */
- if (bts->si_common.chan_desc.t3212 == 0)
- vty_out(vty, " no periodic location update%s", VTY_NEWLINE);
- else
- vty_out(vty, " periodic location update %u%s",
- bts->si_common.chan_desc.t3212 * 6, VTY_NEWLINE);
-
- if (gsm_bts_get_radio_link_timeout(bts) < 0)
- vty_out(vty, " radio-link-timeout infinite%s", VTY_NEWLINE);
- else
- vty_out(vty, " radio-link-timeout %d%s",
- gsm_bts_get_radio_link_timeout(bts), VTY_NEWLINE);
- vty_out(vty, " channel allocator %s%s",
- bts->chan_alloc_reverse ? "descending" : "ascending",
- VTY_NEWLINE);
- vty_out(vty, " rach tx integer %u%s",
- bts->si_common.rach_control.tx_integer, VTY_NEWLINE);
- vty_out(vty, " rach max transmission %u%s",
- rach_max_trans_raw2val(bts->si_common.rach_control.max_trans),
- VTY_NEWLINE);
-
- vty_out(vty, " channel-descrption attach %u%s",
- bts->si_common.chan_desc.att, VTY_NEWLINE);
- vty_out(vty, " channel-descrption bs-pa-mfrms %u%s",
- bts->si_common.chan_desc.bs_pa_mfrms + 2, VTY_NEWLINE);
- vty_out(vty, " channel-descrption bs-ag-blks-res %u%s",
- bts->si_common.chan_desc.bs_ag_blks_res, VTY_NEWLINE);
-
- if (bts->rach_b_thresh != -1)
- vty_out(vty, " rach nm busy threshold %u%s",
- bts->rach_b_thresh, VTY_NEWLINE);
- if (bts->rach_ldavg_slots != -1)
- vty_out(vty, " rach nm load average %u%s",
- bts->rach_ldavg_slots, VTY_NEWLINE);
- if (bts->si_common.rach_control.cell_bar)
- vty_out(vty, " cell barred 1%s", VTY_NEWLINE);
- if ((bts->si_common.rach_control.t2 & 0x4) == 0)
- vty_out(vty, " rach emergency call allowed 1%s", VTY_NEWLINE);
- if ((bts->si_common.rach_control.t3) != 0)
- for (i = 0; i < 8; i++)
- if (bts->si_common.rach_control.t3 & (0x1 << i))
- vty_out(vty, " rach access-control-class %d barred%s", i, VTY_NEWLINE);
- if ((bts->si_common.rach_control.t2 & 0xfb) != 0)
- for (i = 0; i < 8; i++)
- if ((i != 2) && (bts->si_common.rach_control.t2 & (0x1 << i)))
- vty_out(vty, " rach access-control-class %d barred%s", i+8, VTY_NEWLINE);
- for (i = SYSINFO_TYPE_1; i < _MAX_SYSINFO_TYPE; i++) {
- if (bts->si_mode_static & (1 << i)) {
- vty_out(vty, " system-information %s mode static%s",
- get_value_string(osmo_sitype_strs, i), VTY_NEWLINE);
- vty_out(vty, " system-information %s static %s%s",
- get_value_string(osmo_sitype_strs, i),
- osmo_hexdump_nospc(GSM_BTS_SI(bts, i), GSM_MACBLOCK_LEN),
- VTY_NEWLINE);
- }
- }
- vty_out(vty, " early-classmark-sending %s%s",
- bts->early_classmark_allowed ? "allowed" : "forbidden", VTY_NEWLINE);
- switch (bts->type) {
- case GSM_BTS_TYPE_NANOBTS:
- case GSM_BTS_TYPE_OSMOBTS:
- vty_out(vty, " ip.access unit_id %u %u%s",
- bts->ip_access.site_id, bts->ip_access.bts_id, VTY_NEWLINE);
- if (bts->ip_access.rsl_ip) {
- struct in_addr ia;
- ia.s_addr = htonl(bts->ip_access.rsl_ip);
- vty_out(vty, " ip.access rsl-ip %s%s", inet_ntoa(ia),
- VTY_NEWLINE);
- }
- vty_out(vty, " oml ip.access stream_id %u line %u%s",
- bts->oml_tei, bts->oml_e1_link.e1_nr, VTY_NEWLINE);
- break;
- case GSM_BTS_TYPE_NOKIA_SITE:
- vty_out(vty, " nokia_site skip-reset %d%s", bts->nokia.skip_reset, VTY_NEWLINE);
- vty_out(vty, " nokia_site no-local-rel-conf %d%s",
- bts->nokia.no_loc_rel_cnf, VTY_NEWLINE);
- vty_out(vty, " nokia_site bts-reset-timer %d%s", bts->nokia.bts_reset_timer_cnf, VTY_NEWLINE);
- /* fall through: Nokia requires "oml e1" parameters also */
- default:
- config_write_e1_link(vty, &bts->oml_e1_link, " oml ");
- vty_out(vty, " oml e1 tei %u%s", bts->oml_tei, VTY_NEWLINE);
- break;
- }
-
- /* if we have a limit, write it */
- if (bts->paging.free_chans_need >= 0)
- vty_out(vty, " paging free %d%s", bts->paging.free_chans_need, VTY_NEWLINE);
-
- vty_out(vty, " neighbor-list mode %s%s",
- get_value_string(bts_neigh_mode_strs, bts->neigh_list_manual_mode), VTY_NEWLINE);
- if (bts->neigh_list_manual_mode != NL_MODE_AUTOMATIC) {
- for (i = 0; i < 1024; i++) {
- if (bitvec_get_bit_pos(&bts->si_common.neigh_list, i))
- vty_out(vty, " neighbor-list add arfcn %u%s",
- i, VTY_NEWLINE);
- }
- }
- if (bts->neigh_list_manual_mode == NL_MODE_MANUAL_SI5SEP) {
- for (i = 0; i < 1024; i++) {
- if (bitvec_get_bit_pos(&bts->si_common.si5_neigh_list, i))
- vty_out(vty, " si5 neighbor-list add arfcn %u%s",
- i, VTY_NEWLINE);
- }
- }
-
- for (i = 0; i < MAX_EARFCN_LIST; i++) {
- struct osmo_earfcn_si2q *e = &bts->si_common.si2quater_neigh_list;
- if (e->arfcn[i] != OSMO_EARFCN_INVALID) {
- vty_out(vty, " si2quater neighbor-list add earfcn %u "
- "thresh-hi %u", e->arfcn[i], e->thresh_hi);
-
- vty_out(vty, " thresh-lo %u",
- e->thresh_lo_valid ? e->thresh_lo : 32);
-
- vty_out(vty, " prio %u",
- e->prio_valid ? e->prio : 8);
-
- vty_out(vty, " qrxlv %u",
- e->qrxlm_valid ? e->qrxlm : 32);
-
- tmp = e->meas_bw[i];
- vty_out(vty, " meas %u",
- (tmp != OSMO_EARFCN_MEAS_INVALID) ? tmp : 8);
-
- vty_out(vty, "%s", VTY_NEWLINE);
- }
- }
-
- for (i = 0; i < bts->si_common.uarfcn_length; i++) {
- vty_out(vty, " si2quater neighbor-list add uarfcn %u %u %u%s",
- bts->si_common.data.uarfcn_list[i],
- bts->si_common.data.scramble_list[i] & ~(1 << 9),
- (bts->si_common.data.scramble_list[i] >> 9) & 1,
- VTY_NEWLINE);
- }
-
- vty_out(vty, " codec-support fr");
- if (bts->codec.hr)
- vty_out(vty, " hr");
- if (bts->codec.efr)
- vty_out(vty, " efr");
- if (bts->codec.amr)
- vty_out(vty, " amr");
- vty_out(vty, "%s", VTY_NEWLINE);
-
- config_write_bts_amr(vty, bts, &bts->mr_full, 1);
- config_write_bts_amr(vty, bts, &bts->mr_half, 0);
-
- config_write_bts_gprs(vty, bts);
-
- if (bts->excl_from_rf_lock)
- vty_out(vty, " rf-lock-exclude%s", VTY_NEWLINE);
-
- vty_out(vty, " %sforce-combined-si%s",
- bts->force_combined_si ? "" : "no ", VTY_NEWLINE);
-
- for (i = 0; i < ARRAY_SIZE(bts->depends_on); ++i) {
- int j;
-
- if (bts->depends_on[i] == 0)
- continue;
-
- for (j = 0; j < sizeof(bts->depends_on[i]) * 8; ++j) {
- int bts_nr;
-
- if ((bts->depends_on[i] & (1<<j)) == 0)
- continue;
-
- bts_nr = (i * sizeof(bts->depends_on[i]) * 8) + j;
- vty_out(vty, " depends-on-bts %d%s", bts_nr, VTY_NEWLINE);
- }
- }
- if (bts->pcu_sock_path)
- vty_out(vty, " pcu-socket %s%s", bts->pcu_sock_path, VTY_NEWLINE);
-
- config_write_bts_model(vty, bts);
-}
-
-static int config_write_bts(struct vty *v)
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(v);
- struct gsm_bts *bts;
-
- llist_for_each_entry(bts, &gsmnet->bts_list, list)
- config_write_bts_single(v, bts);
-
- return CMD_SUCCESS;
-}
-
-static int config_write_net(struct vty *vty)
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
-
- vty_out(vty, "network%s", VTY_NEWLINE);
- vty_out(vty, " network country code %u%s", gsmnet->country_code, VTY_NEWLINE);
- vty_out(vty, " mobile network code %u%s", gsmnet->network_code, VTY_NEWLINE);
- vty_out(vty, " short name %s%s", gsmnet->name_short, VTY_NEWLINE);
- vty_out(vty, " long name %s%s", gsmnet->name_long, VTY_NEWLINE);
- vty_out(vty, " auth policy %s%s", gsm_auth_policy_name(gsmnet->auth_policy), VTY_NEWLINE);
- if (gsmnet->authorized_reg_str)
- vty_out(vty, " authorized-regexp %s%s", gsmnet->authorized_reg_str, VTY_NEWLINE);
- vty_out(vty, " location updating reject cause %u%s",
- gsmnet->reject_cause, VTY_NEWLINE);
- vty_out(vty, " encryption a5 %u%s", gsmnet->a5_encryption, VTY_NEWLINE);
- vty_out(vty, " neci %u%s", gsmnet->neci, VTY_NEWLINE);
- vty_out(vty, " paging any use tch %d%s", gsmnet->pag_any_tch, VTY_NEWLINE);
- vty_out(vty, " rrlp mode %s%s", rrlp_mode_name(gsmnet->rrlp.mode),
- VTY_NEWLINE);
- vty_out(vty, " mm info %u%s", gsmnet->send_mm_info, VTY_NEWLINE);
- vty_out(vty, " handover %u%s", gsmnet->handover.active, VTY_NEWLINE);
- vty_out(vty, " handover window rxlev averaging %u%s",
- gsmnet->handover.win_rxlev_avg, VTY_NEWLINE);
- vty_out(vty, " handover window rxqual averaging %u%s",
- gsmnet->handover.win_rxqual_avg, VTY_NEWLINE);
- vty_out(vty, " handover window rxlev neighbor averaging %u%s",
- gsmnet->handover.win_rxlev_avg_neigh, VTY_NEWLINE);
- vty_out(vty, " handover power budget interval %u%s",
- gsmnet->handover.pwr_interval, VTY_NEWLINE);
- vty_out(vty, " handover power budget hysteresis %u%s",
- gsmnet->handover.pwr_hysteresis, VTY_NEWLINE);
- vty_out(vty, " handover maximum distance %u%s",
- gsmnet->handover.max_distance, VTY_NEWLINE);
- vty_out(vty, " timer t3101 %u%s", gsmnet->T3101, VTY_NEWLINE);
- vty_out(vty, " timer t3103 %u%s", gsmnet->T3103, VTY_NEWLINE);
- vty_out(vty, " timer t3105 %u%s", gsmnet->T3105, VTY_NEWLINE);
- vty_out(vty, " timer t3107 %u%s", gsmnet->T3107, VTY_NEWLINE);
- vty_out(vty, " timer t3109 %u%s", gsmnet->T3109, VTY_NEWLINE);
- vty_out(vty, " timer t3111 %u%s", gsmnet->T3111, VTY_NEWLINE);
- vty_out(vty, " timer t3113 %u%s", gsmnet->T3113, VTY_NEWLINE);
- vty_out(vty, " timer t3115 %u%s", gsmnet->T3115, VTY_NEWLINE);
- vty_out(vty, " timer t3117 %u%s", gsmnet->T3117, VTY_NEWLINE);
- vty_out(vty, " timer t3119 %u%s", gsmnet->T3119, VTY_NEWLINE);
- vty_out(vty, " timer t3122 %u%s", gsmnet->T3122, VTY_NEWLINE);
- vty_out(vty, " timer t3141 %u%s", gsmnet->T3141, VTY_NEWLINE);
- vty_out(vty, " dyn_ts_allow_tch_f %d%s",
- gsmnet->dyn_ts_allow_tch_f ? 1 : 0, VTY_NEWLINE);
- vty_out(vty, " subscriber-keep-in-ram %d%s",
- gsmnet->subscr_group->keep_subscr, 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);
- }
-
- return CMD_SUCCESS;
-}
-
-static void trx_dump_vty(struct vty *vty, struct gsm_bts_trx *trx)
-{
- vty_out(vty, "TRX %u of BTS %u is on ARFCN %u%s",
- trx->nr, trx->bts->nr, trx->arfcn, VTY_NEWLINE);
- vty_out(vty, "Description: %s%s",
- trx->description ? trx->description : "(null)", VTY_NEWLINE);
- vty_out(vty, " RF Nominal Power: %d dBm, reduced by %u dB, "
- "resulting BS power: %d dBm%s",
- trx->nominal_power, trx->max_power_red,
- trx->nominal_power - trx->max_power_red, VTY_NEWLINE);
- vty_out(vty, " NM State: ");
- net_dump_nmstate(vty, &trx->mo.nm_state);
- vty_out(vty, " Baseband Transceiver NM State: ");
- net_dump_nmstate(vty, &trx->bb_transc.mo.nm_state);
- if (is_ipaccess_bts(trx->bts)) {
- vty_out(vty, " ip.access stream ID: 0x%02x%s",
- trx->rsl_tei, VTY_NEWLINE);
- } else {
- vty_out(vty, " E1 Signalling Link:%s", VTY_NEWLINE);
- e1isl_dump_vty(vty, trx->rsl_link);
- }
-}
-
-DEFUN(show_trx,
- show_trx_cmd,
- "show trx [<0-255>] [<0-255>]",
- SHOW_STR "Display information about a TRX\n"
- "BTS Number\n"
- "TRX Number\n")
-{
- struct gsm_network *net = gsmnet_from_vty(vty);
- struct gsm_bts *bts = NULL;
- struct gsm_bts_trx *trx;
- int bts_nr, trx_nr;
-
- if (argc >= 1) {
- /* use the BTS number that the user has specified */
- bts_nr = atoi(argv[0]);
- if (bts_nr >= net->num_bts) {
- vty_out(vty, "%% can't find BTS '%s'%s", argv[0],
- VTY_NEWLINE);
- return CMD_WARNING;
- }
- bts = gsm_bts_num(net, bts_nr);
- }
- if (argc >= 2) {
- trx_nr = atoi(argv[1]);
- if (trx_nr >= bts->num_trx) {
- vty_out(vty, "%% can't find TRX '%s'%s", argv[1],
- VTY_NEWLINE);
- return CMD_WARNING;
- }
- trx = gsm_bts_trx_num(bts, trx_nr);
- trx_dump_vty(vty, trx);
- return CMD_SUCCESS;
- }
- if (bts) {
- /* print all TRX in this BTS */
- for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) {
- trx = gsm_bts_trx_num(bts, trx_nr);
- trx_dump_vty(vty, trx);
- }
- return CMD_SUCCESS;
- }
-
- for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) {
- bts = gsm_bts_num(net, bts_nr);
- for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) {
- trx = gsm_bts_trx_num(bts, trx_nr);
- trx_dump_vty(vty, trx);
- }
- }
-
- return CMD_SUCCESS;
-}
-
-
-static void ts_dump_vty(struct vty *vty, struct gsm_bts_trx_ts *ts)
-{
- vty_out(vty, "BTS %u, TRX %u, Timeslot %u, phys cfg %s, TSC %u",
- ts->trx->bts->nr, ts->trx->nr, ts->nr,
- gsm_pchan_name(ts->pchan), gsm_ts_tsc(ts));
- if (ts->pchan == GSM_PCHAN_TCH_F_PDCH)
- vty_out(vty, " (%s mode)",
- ts->flags & TS_F_PDCH_ACTIVE ? "PDCH" : "TCH/F");
- vty_out(vty, "%s", VTY_NEWLINE);
- vty_out(vty, " NM State: ");
- net_dump_nmstate(vty, &ts->mo.nm_state);
- if (!is_ipaccess_bts(ts->trx->bts))
- vty_out(vty, " E1 Line %u, Timeslot %u, Subslot %u%s",
- ts->e1_link.e1_nr, ts->e1_link.e1_ts,
- ts->e1_link.e1_ts_ss, VTY_NEWLINE);
-}
-
-DEFUN(show_ts,
- show_ts_cmd,
- "show timeslot [<0-255>] [<0-255>] [<0-7>]",
- SHOW_STR "Display information about a TS\n"
- "BTS Number\n" "TRX Number\n" "Timeslot Number\n")
-{
- struct gsm_network *net = gsmnet_from_vty(vty);
- struct gsm_bts *bts = NULL;
- struct gsm_bts_trx *trx = NULL;
- struct gsm_bts_trx_ts *ts = NULL;
- int bts_nr, trx_nr, ts_nr;
-
- if (argc >= 1) {
- /* use the BTS number that the user has specified */
- bts_nr = atoi(argv[0]);
- if (bts_nr >= net->num_bts) {
- vty_out(vty, "%% can't find BTS '%s'%s", argv[0],
- VTY_NEWLINE);
- return CMD_WARNING;
- }
- bts = gsm_bts_num(net, bts_nr);
- }
- if (argc >= 2) {
- trx_nr = atoi(argv[1]);
- if (trx_nr >= bts->num_trx) {
- vty_out(vty, "%% can't find TRX '%s'%s", argv[1],
- VTY_NEWLINE);
- return CMD_WARNING;
- }
- trx = gsm_bts_trx_num(bts, trx_nr);
- }
- if (argc >= 3) {
- ts_nr = atoi(argv[2]);
- if (ts_nr >= TRX_NR_TS) {
- vty_out(vty, "%% can't find TS '%s'%s", argv[2],
- VTY_NEWLINE);
- return CMD_WARNING;
- }
- /* Fully Specified: print and exit */
- ts = &trx->ts[ts_nr];
- ts_dump_vty(vty, ts);
- return CMD_SUCCESS;
- }
-
- if (bts && trx) {
- /* Iterate over all TS in this TRX */
- for (ts_nr = 0; ts_nr < TRX_NR_TS; ts_nr++) {
- ts = &trx->ts[ts_nr];
- ts_dump_vty(vty, ts);
- }
- } else if (bts) {
- /* Iterate over all TRX in this BTS, TS in each TRX */
- for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) {
- trx = gsm_bts_trx_num(bts, trx_nr);
- for (ts_nr = 0; ts_nr < TRX_NR_TS; ts_nr++) {
- ts = &trx->ts[ts_nr];
- ts_dump_vty(vty, ts);
- }
- }
- } else {
- /* Iterate over all BTS, TRX in each BTS, TS in each TRX */
- for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) {
- bts = gsm_bts_num(net, bts_nr);
- for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) {
- trx = gsm_bts_trx_num(bts, trx_nr);
- for (ts_nr = 0; ts_nr < TRX_NR_TS; ts_nr++) {
- ts = &trx->ts[ts_nr];
- ts_dump_vty(vty, ts);
- }
- }
- }
- }
-
- return CMD_SUCCESS;
-}
-
-static void subscr_dump_vty(struct vty *vty, struct gsm_subscriber *subscr)
-{
- vty_out(vty, " ID: %llu, Authorized: %d%s", subscr->id,
- subscr->authorized, VTY_NEWLINE);
- if (strlen(subscr->name))
- vty_out(vty, " Name: '%s'%s", subscr->name, VTY_NEWLINE);
- if (strlen(subscr->extension))
- vty_out(vty, " Extension: %s%s", subscr->extension,
- VTY_NEWLINE);
- vty_out(vty, " IMSI: %s%s", subscr->imsi, VTY_NEWLINE);
- if (subscr->tmsi != GSM_RESERVED_TMSI)
- vty_out(vty, " TMSI: %08X%s", subscr->tmsi,
- VTY_NEWLINE);
-
- vty_out(vty, " Use count: %u%s", subscr->use_count, VTY_NEWLINE);
-}
-
-static void bsc_subscr_dump_vty(struct vty *vty, struct bsc_subscr *bsub)
-{
- if (strlen(bsub->imsi))
- vty_out(vty, " IMSI: %s%s", bsub->imsi, VTY_NEWLINE);
- if (bsub->tmsi != GSM_RESERVED_TMSI)
- vty_out(vty, " TMSI: 0x%08x%s", bsub->tmsi,
- VTY_NEWLINE);
- vty_out(vty, " Use count: %d%s", bsub->use_count, VTY_NEWLINE);
-}
-
-static void meas_rep_dump_uni_vty(struct vty *vty,
- struct gsm_meas_rep_unidir *mru,
- const char *prefix,
- const char *dir)
-{
- vty_out(vty, "%s RXL-FULL-%s: %4d dBm, RXL-SUB-%s: %4d dBm ",
- prefix, dir, rxlev2dbm(mru->full.rx_lev),
- dir, rxlev2dbm(mru->sub.rx_lev));
- vty_out(vty, "RXQ-FULL-%s: %d, RXQ-SUB-%s: %d%s",
- dir, mru->full.rx_qual, dir, mru->sub.rx_qual,
- VTY_NEWLINE);
-}
-
-static void meas_rep_dump_vty(struct vty *vty, struct gsm_meas_rep *mr,
- const char *prefix)
-{
- vty_out(vty, "%sMeasurement Report:%s", prefix, VTY_NEWLINE);
- vty_out(vty, "%s Flags: %s%s%s%s%s", prefix,
- mr->flags & MEAS_REP_F_UL_DTX ? "DTXu " : "",
- mr->flags & MEAS_REP_F_DL_DTX ? "DTXd " : "",
- mr->flags & MEAS_REP_F_FPC ? "FPC " : "",
- mr->flags & MEAS_REP_F_DL_VALID ? " " : "DLinval ",
- VTY_NEWLINE);
- if (mr->flags & MEAS_REP_F_MS_TO)
- vty_out(vty, "%s MS Timing Offset: %d%s", prefix, mr->ms_timing_offset, VTY_NEWLINE);
- if (mr->flags & MEAS_REP_F_MS_L1)
- vty_out(vty, "%s L1 MS Power: %u dBm, Timing Advance: %u%s",
- prefix, mr->ms_l1.pwr, mr->ms_l1.ta, VTY_NEWLINE);
- if (mr->flags & MEAS_REP_F_DL_VALID)
- meas_rep_dump_uni_vty(vty, &mr->dl, prefix, "dl");
- meas_rep_dump_uni_vty(vty, &mr->ul, prefix, "ul");
-}
-
-/* FIXME: move this to libosmogsm */
-static const struct value_string gsm48_cmode_names[] = {
- { GSM48_CMODE_SIGN, "signalling" },
- { GSM48_CMODE_SPEECH_V1, "FR or HR" },
- { GSM48_CMODE_SPEECH_EFR, "EFR" },
- { GSM48_CMODE_SPEECH_AMR, "AMR" },
- { GSM48_CMODE_DATA_14k5, "CSD(14k5)" },
- { GSM48_CMODE_DATA_12k0, "CSD(12k0)" },
- { GSM48_CMODE_DATA_6k0, "CSD(6k0)" },
- { GSM48_CMODE_DATA_3k6, "CSD(3k6)" },
- { 0, NULL }
-};
-
-/* call vty_out() to print a string like " as TCH/H" for dynamic timeslots.
- * Don't do anything if the ts is not dynamic. */
-static void vty_out_dyn_ts_status(struct vty *vty, struct gsm_bts_trx_ts *ts)
-{
- switch (ts->pchan) {
- case GSM_PCHAN_TCH_F_TCH_H_PDCH:
- if (ts->dyn.pchan_is == ts->dyn.pchan_want)
- vty_out(vty, " as %s",
- gsm_pchan_name(ts->dyn.pchan_is));
- else
- vty_out(vty, " switching %s -> %s",
- gsm_pchan_name(ts->dyn.pchan_is),
- gsm_pchan_name(ts->dyn.pchan_want));
- break;
- case GSM_PCHAN_TCH_F_PDCH:
- if ((ts->flags & TS_F_PDCH_PENDING_MASK) == 0)
- vty_out(vty, " as %s",
- (ts->flags & TS_F_PDCH_ACTIVE)? "PDCH"
- : "TCH/F");
- else
- vty_out(vty, " switching %s -> %s",
- (ts->flags & TS_F_PDCH_ACTIVE)? "PDCH"
- : "TCH/F",
- (ts->flags & TS_F_PDCH_ACT_PENDING)? "PDCH"
- : "TCH/F");
- break;
- default:
- /* no dyn ts */
- break;
- }
-}
-
-static void lchan_dump_full_vty(struct vty *vty, struct gsm_lchan *lchan)
-{
- int idx;
-
- vty_out(vty, "BTS %u, TRX %u, Timeslot %u, Lchan %u: Type %s%s",
- lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr,
- lchan->nr, gsm_lchant_name(lchan->type), VTY_NEWLINE);
- /* show dyn TS details, if applicable */
- switch (lchan->ts->pchan) {
- case GSM_PCHAN_TCH_F_TCH_H_PDCH:
- vty_out(vty, " Osmocom Dyn TS:");
- vty_out_dyn_ts_status(vty, lchan->ts);
- vty_out(vty, VTY_NEWLINE);
- break;
- case GSM_PCHAN_TCH_F_PDCH:
- vty_out(vty, " IPACC Dyn PDCH TS:");
- vty_out_dyn_ts_status(vty, lchan->ts);
- vty_out(vty, VTY_NEWLINE);
- break;
- default:
- /* no dyn ts */
- break;
- }
- vty_out(vty, " Connection: %u, State: %s%s%s%s",
- lchan->conn ? 1: 0,
- gsm_lchans_name(lchan->state),
- lchan->state == LCHAN_S_BROKEN ? " Error reason: " : "",
- lchan->state == LCHAN_S_BROKEN ? lchan->broken_reason : "",
- VTY_NEWLINE);
- vty_out(vty, " BS Power: %u dBm, MS Power: %u dBm%s",
- lchan->ts->trx->nominal_power - lchan->ts->trx->max_power_red
- - lchan->bs_power*2,
- ms_pwr_dbm(lchan->ts->trx->bts->band, lchan->ms_power),
- VTY_NEWLINE);
- vty_out(vty, " Channel Mode / Codec: %s%s",
- get_value_string(gsm48_cmode_names, lchan->tch_mode),
- VTY_NEWLINE);
- if (lchan->conn && lchan->conn->subscr) {
- vty_out(vty, " Subscriber:%s", VTY_NEWLINE);
- subscr_dump_vty(vty, lchan->conn->subscr);
- } else
- vty_out(vty, " No Subscriber%s", VTY_NEWLINE);
- if (is_ipaccess_bts(lchan->ts->trx->bts)) {
- struct in_addr ia;
- ia.s_addr = htonl(lchan->abis_ip.bound_ip);
- vty_out(vty, " Bound IP: %s Port %u RTP_TYPE2=%u CONN_ID=%u%s",
- inet_ntoa(ia), lchan->abis_ip.bound_port,
- lchan->abis_ip.rtp_payload2, lchan->abis_ip.conn_id,
- VTY_NEWLINE);
- }
-
- /* we want to report the last measurement report */
- idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep),
- lchan->meas_rep_idx, 1);
- meas_rep_dump_vty(vty, &lchan->meas_rep[idx], " ");
-}
-
-static void lchan_dump_short_vty(struct vty *vty, struct gsm_lchan *lchan)
-{
- struct gsm_meas_rep *mr;
- int idx;
-
- /* we want to report the last measurement report */
- idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep),
- lchan->meas_rep_idx, 1);
- mr = &lchan->meas_rep[idx];
-
- vty_out(vty, "BTS %u, TRX %u, Timeslot %u %s",
- lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr,
- gsm_pchan_name(lchan->ts->pchan));
- vty_out_dyn_ts_status(vty, lchan->ts);
- vty_out(vty, ", Lchan %u, Type %s, State %s - "
- "L1 MS Power: %u dBm RXL-FULL-dl: %4d dBm RXL-FULL-ul: %4d dBm%s",
- lchan->nr,
- gsm_lchant_name(lchan->type), gsm_lchans_name(lchan->state),
- mr->ms_l1.pwr,
- rxlev2dbm(mr->dl.full.rx_lev),
- rxlev2dbm(mr->ul.full.rx_lev),
- VTY_NEWLINE);
-}
-
-
-static int dump_lchan_trx_ts(struct gsm_bts_trx_ts *ts, struct vty *vty,
- void (*dump_cb)(struct vty *, struct gsm_lchan *))
-{
- int lchan_nr;
- for (lchan_nr = 0; lchan_nr < TS_MAX_LCHAN; lchan_nr++) {
- struct gsm_lchan *lchan = &ts->lchan[lchan_nr];
- if ((lchan->type == GSM_LCHAN_NONE) && (lchan->state == LCHAN_S_NONE))
- continue;
- dump_cb(vty, lchan);
- }
-
- return CMD_SUCCESS;
-}
-
-static int dump_lchan_trx(struct gsm_bts_trx *trx, struct vty *vty,
- void (*dump_cb)(struct vty *, struct gsm_lchan *))
-{
- int ts_nr;
-
- for (ts_nr = 0; ts_nr < TRX_NR_TS; ts_nr++) {
- struct gsm_bts_trx_ts *ts = &trx->ts[ts_nr];
- dump_lchan_trx_ts(ts, vty, dump_cb);
- }
-
- return CMD_SUCCESS;
-}
-
-static int dump_lchan_bts(struct gsm_bts *bts, struct vty *vty,
- void (*dump_cb)(struct vty *, struct gsm_lchan *))
-{
- int trx_nr;
-
- for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) {
- struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, trx_nr);
- dump_lchan_trx(trx, vty, dump_cb);
- }
-
- return CMD_SUCCESS;
-}
-
-static int lchan_summary(struct vty *vty, int argc, const char **argv,
- void (*dump_cb)(struct vty *, struct gsm_lchan *))
-{
- struct gsm_network *net = gsmnet_from_vty(vty);
- struct gsm_bts *bts;
- struct gsm_bts_trx *trx;
- struct gsm_bts_trx_ts *ts;
- struct gsm_lchan *lchan;
- int bts_nr, trx_nr, ts_nr, lchan_nr;
-
- if (argc >= 1) {
- /* use the BTS number that the user has specified */
- bts_nr = atoi(argv[0]);
- if (bts_nr >= net->num_bts) {
- vty_out(vty, "%% can't find BTS %s%s", argv[0],
- VTY_NEWLINE);
- return CMD_WARNING;
- }
- bts = gsm_bts_num(net, bts_nr);
-
- if (argc == 1)
- return dump_lchan_bts(bts, vty, dump_cb);
- }
- if (argc >= 2) {
- trx_nr = atoi(argv[1]);
- if (trx_nr >= bts->num_trx) {
- vty_out(vty, "%% can't find TRX %s%s", argv[1],
- VTY_NEWLINE);
- return CMD_WARNING;
- }
- trx = gsm_bts_trx_num(bts, trx_nr);
-
- if (argc == 2)
- return dump_lchan_trx(trx, vty, dump_cb);
- }
- if (argc >= 3) {
- ts_nr = atoi(argv[2]);
- if (ts_nr >= TRX_NR_TS) {
- vty_out(vty, "%% can't find TS %s%s", argv[2],
- VTY_NEWLINE);
- return CMD_WARNING;
- }
- ts = &trx->ts[ts_nr];
-
- if (argc == 3)
- return dump_lchan_trx_ts(ts, vty, dump_cb);
- }
- if (argc >= 4) {
- lchan_nr = atoi(argv[3]);
- if (lchan_nr >= TS_MAX_LCHAN) {
- vty_out(vty, "%% can't find LCHAN %s%s", argv[3],
- VTY_NEWLINE);
- return CMD_WARNING;
- }
- lchan = &ts->lchan[lchan_nr];
- dump_cb(vty, lchan);
- return CMD_SUCCESS;
- }
-
-
- for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) {
- bts = gsm_bts_num(net, bts_nr);
- dump_lchan_bts(bts, vty, dump_cb);
- }
-
- return CMD_SUCCESS;
-}
-
-
-DEFUN(show_lchan,
- show_lchan_cmd,
- "show lchan [<0-255>] [<0-255>] [<0-7>] [lchan_nr]",
- SHOW_STR "Display information about a logical channel\n"
- "BTS Number\n" "TRX Number\n" "Timeslot Number\n"
- LCHAN_NR_STR)
-
-{
- return lchan_summary(vty, argc, argv, lchan_dump_full_vty);
-}
-
-DEFUN(show_lchan_summary,
- show_lchan_summary_cmd,
- "show lchan summary [<0-255>] [<0-255>] [<0-7>] [lchan_nr]",
- SHOW_STR "Display information about a logical channel\n"
- "Short summary\n"
- "BTS Number\n" "TRX Number\n" "Timeslot Number\n"
- LCHAN_NR_STR)
-{
- return lchan_summary(vty, argc, argv, lchan_dump_short_vty);
-}
-
-static void paging_dump_vty(struct vty *vty, struct gsm_paging_request *pag)
-{
- vty_out(vty, "Paging on BTS %u%s", pag->bts->nr, VTY_NEWLINE);
- bsc_subscr_dump_vty(vty, pag->bsub);
-}
-
-static void bts_paging_dump_vty(struct vty *vty, struct gsm_bts *bts)
-{
- struct gsm_paging_request *pag;
-
- if (!bts->paging.bts)
- return;
-
- llist_for_each_entry(pag, &bts->paging.pending_requests, entry)
- paging_dump_vty(vty, pag);
-}
-
-DEFUN(show_paging,
- show_paging_cmd,
- "show paging [<0-255>]",
- SHOW_STR "Display information about paging reuqests of a BTS\n"
- "BTS Number\n")
-{
- struct gsm_network *net = gsmnet_from_vty(vty);
- struct gsm_bts *bts;
- int bts_nr;
-
- if (argc >= 1) {
- /* use the BTS number that the user has specified */
- bts_nr = atoi(argv[0]);
- if (bts_nr >= net->num_bts) {
- vty_out(vty, "%% can't find BTS %s%s", argv[0],
- VTY_NEWLINE);
- return CMD_WARNING;
- }
- bts = gsm_bts_num(net, bts_nr);
- bts_paging_dump_vty(vty, bts);
-
- return CMD_SUCCESS;
- }
- for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) {
- bts = gsm_bts_num(net, bts_nr);
- bts_paging_dump_vty(vty, bts);
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN(show_paging_group,
- show_paging_group_cmd,
- "show paging-group <0-255> IMSI",
- SHOW_STR "Display the paging group\n"
- "BTS Number\n" "IMSI\n")
-{
- struct gsm_network *net = gsmnet_from_vty(vty);
- struct gsm_bts *bts;
- unsigned int page_group;
- int bts_nr = atoi(argv[0]);
-
- if (bts_nr >= net->num_bts) {
- vty_out(vty, "%% can't find BTS %s%s", argv[0], VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- bts = gsm_bts_num(net, bts_nr);
- if (!bts) {
- vty_out(vty, "%% can't find BTS %s%s", argv[0], VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- page_group = gsm0502_calc_paging_group(&bts->si_common.chan_desc,
- str_to_imsi(argv[1]));
- vty_out(vty, "%%Paging group for IMSI %" PRIu64 " on BTS #%d is %u%s",
- str_to_imsi(argv[1]), bts->nr,
- page_group, VTY_NEWLINE);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_neci,
- cfg_net_neci_cmd,
- "neci (0|1)",
- "New Establish Cause Indication\n"
- "Don't set the NECI bit\n" "Set the NECI bit\n")
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
-
- gsmnet->neci = atoi(argv[0]);
- gsm_net_update_ctype(gsmnet);
- return CMD_SUCCESS;
-}
-
-#define HANDOVER_STR "Handover Options\n"
-
-DEFUN(cfg_net_handover, cfg_net_handover_cmd,
- "handover (0|1)",
- HANDOVER_STR
- "Don't perform in-call handover\n"
- "Perform in-call handover\n")
-{
- int enable = atoi(argv[0]);
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
-
- if (enable && ipacc_rtp_direct) {
- vty_out(vty, "%% Cannot enable handover unless RTP Proxy mode "
- "is enabled by using the -P command line option%s",
- VTY_NEWLINE);
- return CMD_WARNING;
- }
- gsmnet->handover.active = enable;
-
- return CMD_SUCCESS;
-}
-
-#define HO_WIN_STR HANDOVER_STR "Measurement Window\n"
-#define HO_WIN_RXLEV_STR HO_WIN_STR "Received Level Averaging\n"
-#define HO_WIN_RXQUAL_STR HO_WIN_STR "Received Quality Averaging\n"
-#define HO_PBUDGET_STR HANDOVER_STR "Power Budget\n"
-#define HO_AVG_COUNT_STR "Amount to use for Averaging\n"
-
-DEFUN(cfg_net_ho_win_rxlev_avg, cfg_net_ho_win_rxlev_avg_cmd,
- "handover window rxlev averaging <1-10>",
- HO_WIN_RXLEV_STR
- "How many RxLev measurements are used for averaging\n"
- HO_AVG_COUNT_STR)
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- gsmnet->handover.win_rxlev_avg = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_ho_win_rxqual_avg, cfg_net_ho_win_rxqual_avg_cmd,
- "handover window rxqual averaging <1-10>",
- HO_WIN_RXQUAL_STR
- "How many RxQual measurements are used for averaging\n"
- HO_AVG_COUNT_STR)
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- gsmnet->handover.win_rxqual_avg = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_ho_win_rxlev_neigh_avg, cfg_net_ho_win_rxlev_avg_neigh_cmd,
- "handover window rxlev neighbor averaging <1-10>",
- HO_WIN_RXLEV_STR "Neighbor\n"
- "How many RxQual measurements are used for averaging\n"
- HO_AVG_COUNT_STR)
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- gsmnet->handover.win_rxlev_avg_neigh = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_ho_pwr_interval, cfg_net_ho_pwr_interval_cmd,
- "handover power budget interval <1-99>",
- HO_PBUDGET_STR
- "How often to check if we have a better cell (SACCH frames)\n"
- "Interval\n" "Number\n")
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- gsmnet->handover.pwr_interval = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_ho_pwr_hysteresis, cfg_net_ho_pwr_hysteresis_cmd,
- "handover power budget hysteresis <0-999>",
- HO_PBUDGET_STR
- "How many dB does a neighbor to be stronger to become a HO candidate\n"
- "Hysteresis\n" "Number\n")
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- gsmnet->handover.pwr_hysteresis = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_ho_max_distance, cfg_net_ho_max_distance_cmd,
- "handover maximum distance <0-9999>",
- HANDOVER_STR
- "How big is the maximum timing advance before HO is forced\n"
- "Distance\n" "Number\n")
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- gsmnet->handover.max_distance = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_pag_any_tch,
- cfg_net_pag_any_tch_cmd,
- "paging any use tch (0|1)",
- "Assign a TCH when receiving a Paging Any request\n"
- "Any Channel\n" "Use\n" "TCH\n"
- "Do not use TCH for Paging Request Any\n"
- "Do use TCH for Paging Request Any\n")
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- gsmnet->pag_any_tch = atoi(argv[0]);
- gsm_net_update_ctype(gsmnet);
- return CMD_SUCCESS;
-}
-
-#define DECLARE_TIMER(number, doc) \
- DEFUN(cfg_net_T##number, \
- cfg_net_T##number##_cmd, \
- "timer t" #number " <0-65535>", \
- "Configure GSM Timers\n" \
- doc "Timer Value in seconds\n") \
-{ \
- struct gsm_network *gsmnet = gsmnet_from_vty(vty); \
- int value = atoi(argv[0]); \
- \
- if (value < 0 || value > 65535) { \
- vty_out(vty, "Timer value %s out of range.%s", \
- argv[0], VTY_NEWLINE); \
- return CMD_WARNING; \
- } \
- \
- gsmnet->T##number = value; \
- return CMD_SUCCESS; \
-}
-
-DECLARE_TIMER(3101, "Set the timeout value for IMMEDIATE ASSIGNMENT.\n")
-DECLARE_TIMER(3103, "Set the timeout value for HANDOVER.\n")
-DECLARE_TIMER(3105, "Set the timer for repetition of PHYSICAL INFORMATION.\n")
-DECLARE_TIMER(3107, "Currently not used.\n")
-DECLARE_TIMER(3109, "Set the RSL SACCH deactivation timeout.\n")
-DECLARE_TIMER(3111, "Set the RSL timeout to wait before releasing the RF Channel.\n")
-DECLARE_TIMER(3113, "Set the time to try paging a subscriber.\n")
-DECLARE_TIMER(3115, "Currently not used.\n")
-DECLARE_TIMER(3117, "Currently not used.\n")
-DECLARE_TIMER(3119, "Currently not used.\n")
-DECLARE_TIMER(3122, "Waiting time (seconds) after IMM ASS REJECT\n")
-DECLARE_TIMER(3141, "Currently not used.\n")
-
-DEFUN_DEPRECATED(cfg_net_dtx,
- cfg_net_dtx_cmd,
- "dtx-used (0|1)",
- ".HIDDEN\n""Obsolete\n""Obsolete\n")
-{
- vty_out(vty, "%% 'dtx-used' is now deprecated: use dtx * "
- "configuration options of BTS instead%s", VTY_NEWLINE);
- return CMD_SUCCESS;
-}
-
-/* per-BTS configuration */
-DEFUN(cfg_bts,
- cfg_bts_cmd,
- "bts <0-255>",
- "Select a BTS to configure\n"
- "BTS Number\n")
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- int bts_nr = atoi(argv[0]);
- struct gsm_bts *bts;
-
- if (bts_nr > gsmnet->num_bts) {
- vty_out(vty, "%% The next unused BTS number is %u%s",
- gsmnet->num_bts, VTY_NEWLINE);
- return CMD_WARNING;
- } else if (bts_nr == gsmnet->num_bts) {
- /* allocate a new one */
- bts = gsm_bts_alloc_register(gsmnet, GSM_BTS_TYPE_UNKNOWN,
- HARDCODED_BSIC);
- } else
- bts = gsm_bts_num(gsmnet, bts_nr);
-
- if (!bts) {
- vty_out(vty, "%% Unable to allocate BTS %u%s",
- gsmnet->num_bts, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- vty->index = bts;
- vty->index_sub = &bts->description;
- vty->node = BTS_NODE;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_type,
- cfg_bts_type_cmd,
- "type TYPE", /* dynamically created */
- "Set the BTS type\n" "Type\n")
-{
- struct gsm_bts *bts = vty->index;
- int rc;
-
- rc = gsm_set_bts_type(bts, str2btstype(argv[0]));
- if (rc < 0)
- return CMD_WARNING;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_band,
- cfg_bts_band_cmd,
- "band BAND",
- "Set the frequency band of this BTS\n" "Frequency band\n")
-{
- struct gsm_bts *bts = vty->index;
- int band = gsm_band_parse(argv[0]);
-
- if (band < 0) {
- vty_out(vty, "%% BAND %d is not a valid GSM band%s",
- band, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- bts->band = band;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_dtxu, cfg_bts_dtxu_cmd, "dtx uplink [force]",
- "Configure discontinuous transmission\n"
- "Enable Uplink DTX for this BTS\n"
- "MS 'shall' use DTXu instead of 'may' use (might not be supported by "
- "older phones).\n")
-{
- struct gsm_bts *bts = vty->index;
-
- bts->dtxu = (argc > 0) ? GSM48_DTX_SHALL_BE_USED : GSM48_DTX_MAY_BE_USED;
- if (!is_ipaccess_bts(bts))
- vty_out(vty, "%% DTX enabled on non-IP BTS: this configuration "
- "neither supported nor tested!%s", VTY_NEWLINE);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_no_dtxu, cfg_bts_no_dtxu_cmd, "no dtx uplink",
- NO_STR
- "Configure discontinuous transmission\n"
- "Disable Uplink DTX for this BTS\n")
-{
- struct gsm_bts *bts = vty->index;
-
- bts->dtxu = GSM48_DTX_SHALL_NOT_BE_USED;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_dtxd, cfg_bts_dtxd_cmd, "dtx downlink",
- "Configure discontinuous transmission\n"
- "Enable Downlink DTX for this BTS\n")
-{
- struct gsm_bts *bts = vty->index;
-
- bts->dtxd = true;
- if (!is_ipaccess_bts(bts))
- vty_out(vty, "%% DTX enabled on non-IP BTS: this configuration "
- "neither supported nor tested!%s", VTY_NEWLINE);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_no_dtxd, cfg_bts_no_dtxd_cmd, "no dtx downlink",
- NO_STR
- "Configure discontinuous transmission\n"
- "Disable Downlink DTX for this BTS\n")
-{
- struct gsm_bts *bts = vty->index;
-
- bts->dtxd = false;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_ci,
- cfg_bts_ci_cmd,
- "cell_identity <0-65535>",
- "Set the Cell identity of this BTS\n" "Cell Identity\n")
-{
- struct gsm_bts *bts = vty->index;
- int ci = atoi(argv[0]);
-
- if (ci < 0 || ci > 0xffff) {
- vty_out(vty, "%% CI %d is not in the valid range (0-65535)%s",
- ci, VTY_NEWLINE);
- return CMD_WARNING;
- }
- bts->cell_identity = ci;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_lac,
- cfg_bts_lac_cmd,
- "location_area_code <0-65535>",
- "Set the Location Area Code (LAC) of this BTS\n" "LAC\n")
-{
- struct gsm_bts *bts = vty->index;
- int lac = atoi(argv[0]);
-
- if (lac < 0 || lac > 0xffff) {
- vty_out(vty, "%% LAC %d is not in the valid range (0-65535)%s",
- lac, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- if (lac == GSM_LAC_RESERVED_DETACHED || lac == GSM_LAC_RESERVED_ALL_BTS) {
- vty_out(vty, "%% LAC %d is reserved by GSM 04.08%s",
- lac, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- bts->location_area_code = lac;
-
- return CMD_SUCCESS;
-}
-
-
-/* compatibility wrapper for old config files */
-DEFUN_HIDDEN(cfg_bts_tsc,
- cfg_bts_tsc_cmd,
- "training_sequence_code <0-7>",
- "Set the Training Sequence Code (TSC) of this BTS\n" "TSC\n")
-{
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_bsic,
- cfg_bts_bsic_cmd,
- "base_station_id_code <0-63>",
- "Set the Base Station Identity Code (BSIC) of this BTS\n"
- "BSIC of this BTS\n")
-{
- struct gsm_bts *bts = vty->index;
- int bsic = atoi(argv[0]);
-
- if (bsic < 0 || bsic > 0x3f) {
- vty_out(vty, "%% BSIC %d is not in the valid range (0-255)%s",
- bsic, VTY_NEWLINE);
- return CMD_WARNING;
- }
- bts->bsic = bsic;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_unit_id,
- cfg_bts_unit_id_cmd,
- "ip.access unit_id <0-65534> <0-255>",
- "Abis/IP specific options\n"
- "Set the IPA BTS Unit ID\n"
- "Unit ID (Site)\n"
- "Unit ID (BTS)\n")
-{
- struct gsm_bts *bts = vty->index;
- int site_id = atoi(argv[0]);
- int bts_id = atoi(argv[1]);
-
- if (!is_ipaccess_bts(bts)) {
- vty_out(vty, "%% BTS is not of ip.access type%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- bts->ip_access.site_id = site_id;
- bts->ip_access.bts_id = bts_id;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_rsl_ip,
- cfg_bts_rsl_ip_cmd,
- "ip.access rsl-ip A.B.C.D",
- "Abis/IP specific options\n"
- "Set the IPA RSL IP Address of the BSC\n"
- "Destination IP address for RSL connection\n")
-{
- struct gsm_bts *bts = vty->index;
- struct in_addr ia;
-
- if (!is_ipaccess_bts(bts)) {
- vty_out(vty, "%% BTS is not of ip.access type%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- inet_aton(argv[0], &ia);
- bts->ip_access.rsl_ip = ntohl(ia.s_addr);
-
- return CMD_SUCCESS;
-}
-
-#define NOKIA_STR "Nokia *Site related commands\n"
-
-DEFUN(cfg_bts_nokia_site_skip_reset,
- cfg_bts_nokia_site_skip_reset_cmd,
- "nokia_site skip-reset (0|1)",
- NOKIA_STR
- "Skip the reset step during bootstrap process of this BTS\n"
- "Do NOT skip the reset\n" "Skip the reset\n")
-{
- struct gsm_bts *bts = vty->index;
-
- if (bts->type != GSM_BTS_TYPE_NOKIA_SITE) {
- vty_out(vty, "%% BTS is not of Nokia *Site type%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- bts->nokia.skip_reset = atoi(argv[0]);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_nokia_site_no_loc_rel_cnf,
- cfg_bts_nokia_site_no_loc_rel_cnf_cmd,
- "nokia_site no-local-rel-conf (0|1)",
- NOKIA_STR
- "Do not wait for RELease CONFirm message when releasing channel locally\n"
- "Wait for RELease CONFirm\n" "Do not wait for RELease CONFirm\n")
-{
- struct gsm_bts *bts = vty->index;
-
- if (!is_nokia_bts(bts)) {
- vty_out(vty, "%% BTS is not of Nokia *Site type%s",
- VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- bts->nokia.no_loc_rel_cnf = atoi(argv[0]);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_nokia_site_bts_reset_timer_cnf,
- cfg_bts_nokia_site_bts_reset_timer_cnf_cmd,
- "nokia_site bts-reset-timer <15-100>",
- NOKIA_STR
- "The amount of time (in sec.) between BTS_RESET is sent,\n"
- "and the BTS is being bootstrapped.\n")
-{
- struct gsm_bts *bts = vty->index;
-
- if (!is_nokia_bts(bts)) {
- vty_out(vty, "%% BTS is not of Nokia *Site type%s",
- VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- bts->nokia.bts_reset_timer_cnf = atoi(argv[0]);
-
- return CMD_SUCCESS;
-}
-#define OML_STR "Organization & Maintenance Link\n"
-#define IPA_STR "A-bis/IP Specific Options\n"
-
-DEFUN(cfg_bts_stream_id,
- cfg_bts_stream_id_cmd,
- "oml ip.access stream_id <0-255> line E1_LINE",
- OML_STR IPA_STR
- "Set the ip.access Stream ID of the OML link of this BTS\n"
- "Stream Identifier\n" "Virtual E1 Line Number\n" "Virtual E1 Line Number\n")
-{
- struct gsm_bts *bts = vty->index;
- int stream_id = atoi(argv[0]), linenr = atoi(argv[1]);
-
- if (!is_ipaccess_bts(bts)) {
- vty_out(vty, "%% BTS is not of ip.access type%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- bts->oml_tei = stream_id;
- /* This is used by e1inp_bind_ops callback for each BTS model. */
- bts->oml_e1_link.e1_nr = linenr;
-
- return CMD_SUCCESS;
-}
-
-#define OML_E1_STR OML_STR "OML E1/T1 Configuration\n"
-
-DEFUN(cfg_bts_oml_e1,
- cfg_bts_oml_e1_cmd,
- "oml e1 line E1_LINE timeslot <1-31> sub-slot (0|1|2|3|full)",
- OML_E1_STR
- "E1/T1 line number to be used for OML\n"
- "E1/T1 line number to be used for OML\n"
- "E1/T1 timeslot to be used for OML\n"
- "E1/T1 timeslot to be used for OML\n"
- "E1/T1 sub-slot to be used for OML\n"
- "Use E1/T1 sub-slot 0\n"
- "Use E1/T1 sub-slot 1\n"
- "Use E1/T1 sub-slot 2\n"
- "Use E1/T1 sub-slot 3\n"
- "Use full E1 slot 3\n"
- )
-{
- struct gsm_bts *bts = vty->index;
-
- parse_e1_link(&bts->oml_e1_link, argv[0], argv[1], argv[2]);
-
- return CMD_SUCCESS;
-}
-
-
-DEFUN(cfg_bts_oml_e1_tei,
- cfg_bts_oml_e1_tei_cmd,
- "oml e1 tei <0-63>",
- OML_E1_STR
- "Set the TEI to be used for OML\n"
- "TEI Number\n")
-{
- struct gsm_bts *bts = vty->index;
-
- bts->oml_tei = atoi(argv[0]);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_challoc, cfg_bts_challoc_cmd,
- "channel allocator (ascending|descending)",
- "Channnel Allocator\n" "Channel Allocator\n"
- "Allocate Timeslots and Transceivers in ascending order\n"
- "Allocate Timeslots and Transceivers in descending order\n")
-{
- struct gsm_bts *bts = vty->index;
-
- if (!strcmp(argv[0], "ascending"))
- bts->chan_alloc_reverse = 0;
- else
- bts->chan_alloc_reverse = 1;
-
- return CMD_SUCCESS;
-}
-
-#define RACH_STR "Random Access Control Channel\n"
-
-DEFUN(cfg_bts_rach_tx_integer,
- cfg_bts_rach_tx_integer_cmd,
- "rach tx integer <0-15>",
- RACH_STR
- "Set the raw tx integer value in RACH Control parameters IE\n"
- "Set the raw tx integer value in RACH Control parameters IE\n"
- "Raw tx integer value in RACH Control parameters IE\n")
-{
- struct gsm_bts *bts = vty->index;
- bts->si_common.rach_control.tx_integer = atoi(argv[0]) & 0xf;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_rach_max_trans,
- cfg_bts_rach_max_trans_cmd,
- "rach max transmission (1|2|4|7)",
- RACH_STR
- "Set the maximum number of RACH burst transmissions\n"
- "Set the maximum number of RACH burst transmissions\n"
- "Maximum number of 1 RACH burst transmissions\n"
- "Maximum number of 2 RACH burst transmissions\n"
- "Maximum number of 4 RACH burst transmissions\n"
- "Maximum number of 7 RACH burst transmissions\n")
-{
- struct gsm_bts *bts = vty->index;
- bts->si_common.rach_control.max_trans = rach_max_trans_val2raw(atoi(argv[0]));
- return CMD_SUCCESS;
-}
-
-#define CD_STR "Channel Description\n"
-
-DEFUN(cfg_bts_chan_desc_att,
- cfg_bts_chan_desc_att_cmd,
- "channel-descrption attach (0|1)",
- CD_STR
- "Set if attachment is required\n"
- "Attachment is NOT required\n"
- "Attachment is required (standard)\n")
-{
- struct gsm_bts *bts = vty->index;
- bts->si_common.chan_desc.att = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_chan_desc_bs_pa_mfrms,
- cfg_bts_chan_desc_bs_pa_mfrms_cmd,
- "channel-descrption bs-pa-mfrms <2-9>",
- CD_STR
- "Set number of multiframe periods for paging groups\n"
- "Number of multiframe periods for paging groups\n")
-{
- struct gsm_bts *bts = vty->index;
- int bs_pa_mfrms = atoi(argv[0]);
-
- bts->si_common.chan_desc.bs_pa_mfrms = bs_pa_mfrms - 2;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_chan_desc_bs_ag_blks_res,
- cfg_bts_chan_desc_bs_ag_blks_res_cmd,
- "channel-descrption bs-ag-blks-res <0-7>",
- CD_STR
- "Set number of blocks reserved for access grant\n"
- "Number of blocks reserved for access grant\n")
-{
- struct gsm_bts *bts = vty->index;
- int bs_ag_blks_res = atoi(argv[0]);
-
- bts->si_common.chan_desc.bs_ag_blks_res = bs_ag_blks_res;
- return CMD_SUCCESS;
-}
-
-#define NM_STR "Network Management\n"
-
-DEFUN(cfg_bts_rach_nm_b_thresh,
- cfg_bts_rach_nm_b_thresh_cmd,
- "rach nm busy threshold <0-255>",
- RACH_STR NM_STR
- "Set the NM Busy Threshold\n"
- "Set the NM Busy Threshold\n"
- "NM Busy Threshold in dB")
-{
- struct gsm_bts *bts = vty->index;
- bts->rach_b_thresh = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_rach_nm_ldavg,
- cfg_bts_rach_nm_ldavg_cmd,
- "rach nm load average <0-65535>",
- RACH_STR NM_STR
- "Set the NM Loadaverage Slots value\n"
- "Set the NM Loadaverage Slots value\n"
- "NM Loadaverage Slots value\n")
-{
- struct gsm_bts *bts = vty->index;
- bts->rach_ldavg_slots = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_cell_barred, cfg_bts_cell_barred_cmd,
- "cell barred (0|1)",
- "Should this cell be barred from access?\n"
- "Should this cell be barred from access?\n"
- "Cell should NOT be barred\n"
- "Cell should be barred\n")
-
-{
- struct gsm_bts *bts = vty->index;
-
- bts->si_common.rach_control.cell_bar = atoi(argv[0]);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_rach_ec_allowed, cfg_bts_rach_ec_allowed_cmd,
- "rach emergency call allowed (0|1)",
- RACH_STR
- "Should this cell allow emergency calls?\n"
- "Should this cell allow emergency calls?\n"
- "Should this cell allow emergency calls?\n"
- "Do NOT allow emergency calls\n"
- "Allow emergency calls\n")
-{
- struct gsm_bts *bts = vty->index;
-
- if (atoi(argv[0]) == 0)
- bts->si_common.rach_control.t2 |= 0x4;
- else
- bts->si_common.rach_control.t2 &= ~0x4;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_rach_ac_class, cfg_bts_rach_ac_class_cmd,
- "rach access-control-class (0|1|2|3|4|5|6|7|8|9|11|12|13|14|15) (barred|allowed)",
- RACH_STR
- "Set access control class\n"
- "Access control class 0\n"
- "Access control class 1\n"
- "Access control class 2\n"
- "Access control class 3\n"
- "Access control class 4\n"
- "Access control class 5\n"
- "Access control class 6\n"
- "Access control class 7\n"
- "Access control class 8\n"
- "Access control class 9\n"
- "Access control class 11 for PLMN use\n"
- "Access control class 12 for security services\n"
- "Access control class 13 for public utilities (e.g. water/gas suppliers)\n"
- "Access control class 14 for emergency services\n"
- "Access control class 15 for PLMN staff\n"
- "barred to use access control class\n"
- "allowed to use access control class\n")
-{
- struct gsm_bts *bts = vty->index;
-
- uint8_t control_class;
- uint8_t allowed = 0;
-
- if (strcmp(argv[1], "allowed") == 0)
- allowed = 1;
-
- control_class = atoi(argv[0]);
- if (control_class < 8)
- if (allowed)
- bts->si_common.rach_control.t3 &= ~(0x1 << control_class);
- else
- bts->si_common.rach_control.t3 |= (0x1 << control_class);
- else
- if (allowed)
- bts->si_common.rach_control.t2 &= ~(0x1 << (control_class - 8));
- else
- bts->si_common.rach_control.t2 |= (0x1 << (control_class - 8));
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_ms_max_power, cfg_bts_ms_max_power_cmd,
- "ms max power <0-40>",
- "MS Options\n"
- "Maximum transmit power of the MS\n"
- "Maximum transmit power of the MS\n"
- "Maximum transmit power of the MS in dBm")
-{
- struct gsm_bts *bts = vty->index;
-
- bts->ms_max_power = atoi(argv[0]);
-
- return CMD_SUCCESS;
-}
-
-#define CELL_STR "Cell Parameters\n"
-
-DEFUN(cfg_bts_cell_resel_hyst, cfg_bts_cell_resel_hyst_cmd,
- "cell reselection hysteresis <0-14>",
- CELL_STR "Cell re-selection parameters\n"
- "Cell Re-Selection Hysteresis in dB\n"
- "Cell Re-Selection Hysteresis in dB")
-{
- struct gsm_bts *bts = vty->index;
-
- bts->si_common.cell_sel_par.cell_resel_hyst = atoi(argv[0])/2;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_rxlev_acc_min, cfg_bts_rxlev_acc_min_cmd,
- "rxlev access min <0-63>",
- "Minimum RxLev needed for cell access\n"
- "Minimum RxLev needed for cell access\n"
- "Minimum RxLev needed for cell access\n"
- "Minimum RxLev needed for cell access (better than -110dBm)")
-{
- struct gsm_bts *bts = vty->index;
-
- bts->si_common.cell_sel_par.rxlev_acc_min = atoi(argv[0]);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_cell_bar_qualify, cfg_bts_cell_bar_qualify_cmd,
- "cell bar qualify (0|1)",
- CELL_STR "Cell Bar Qualify\n" "Cell Bar Qualify\n"
- "Set CBQ to 0\n" "Set CBQ to 1\n")
-{
- struct gsm_bts *bts = vty->index;
-
- bts->si_common.cell_ro_sel_par.present = 1;
- bts->si_common.cell_ro_sel_par.cbq = atoi(argv[0]);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_cell_resel_ofs, cfg_bts_cell_resel_ofs_cmd,
- "cell reselection offset <0-126>",
- CELL_STR "Cell Re-Selection Parameters\n"
- "Cell Re-Selection Offset (CRO) in dB\n"
- "Cell Re-Selection Offset (CRO) in dB\n"
- )
-{
- struct gsm_bts *bts = vty->index;
-
- bts->si_common.cell_ro_sel_par.present = 1;
- bts->si_common.cell_ro_sel_par.cell_resel_off = atoi(argv[0])/2;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_temp_ofs, cfg_bts_temp_ofs_cmd,
- "temporary offset <0-60>",
- "Cell selection temporary negative offset\n"
- "Cell selection temporary negative offset\n"
- "Cell selection temporary negative offset in dB")
-{
- struct gsm_bts *bts = vty->index;
-
- bts->si_common.cell_ro_sel_par.present = 1;
- bts->si_common.cell_ro_sel_par.temp_offs = atoi(argv[0])/10;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_temp_ofs_inf, cfg_bts_temp_ofs_inf_cmd,
- "temporary offset infinite",
- "Cell selection temporary negative offset\n"
- "Cell selection temporary negative offset\n"
- "Sets cell selection temporary negative offset to infinity")
-{
- struct gsm_bts *bts = vty->index;
-
- bts->si_common.cell_ro_sel_par.present = 1;
- bts->si_common.cell_ro_sel_par.temp_offs = 7;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_penalty_time, cfg_bts_penalty_time_cmd,
- "penalty time <20-620>",
- "Cell selection penalty time\n"
- "Cell selection penalty time\n"
- "Cell selection penalty time in seconds (by 20s increments)\n")
-{
- struct gsm_bts *bts = vty->index;
-
- bts->si_common.cell_ro_sel_par.present = 1;
- bts->si_common.cell_ro_sel_par.penalty_time = (atoi(argv[0])-20)/20;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_penalty_time_rsvd, cfg_bts_penalty_time_rsvd_cmd,
- "penalty time reserved",
- "Cell selection penalty time\n"
- "Cell selection penalty time\n"
- "Set cell selection penalty time to reserved value 31, "
- "(indicate that CELL_RESELECT_OFFSET is subtracted from C2 "
- "and TEMPORARY_OFFSET is ignored)")
-{
- struct gsm_bts *bts = vty->index;
-
- bts->si_common.cell_ro_sel_par.present = 1;
- bts->si_common.cell_ro_sel_par.penalty_time = 31;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_per_loc_upd, cfg_bts_per_loc_upd_cmd,
- "periodic location update <6-1530>",
- "Periodic Location Updating Interval\n"
- "Periodic Location Updating Interval\n"
- "Periodic Location Updating Interval\n"
- "Periodic Location Updating Interval in Minutes\n")
-{
- struct gsm_bts *bts = vty->index;
-
- bts->si_common.chan_desc.t3212 = atoi(argv[0]) / 6;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_no_per_loc_upd, cfg_bts_no_per_loc_upd_cmd,
- "no periodic location update",
- NO_STR
- "Periodic Location Updating Interval\n"
- "Periodic Location Updating Interval\n"
- "Periodic Location Updating Interval\n")
-{
- struct gsm_bts *bts = vty->index;
-
- bts->si_common.chan_desc.t3212 = 0;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_radio_link_timeout, cfg_bts_radio_link_timeout_cmd,
- "radio-link-timeout <4-64>",
- "Radio link timeout criterion (BTS side)\n"
- "Radio link timeout value (lost SACCH block)\n")
-{
- struct gsm_bts *bts = vty->index;
-
- gsm_bts_set_radio_link_timeout(bts, atoi(argv[0]));
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_radio_link_timeout_inf, cfg_bts_radio_link_timeout_inf_cmd,
- "radio-link-timeout infinite",
- "Radio link timeout criterion (BTS side)\n"
- "Infinite Radio link timeout value (use only for BTS RF testing)\n")
-{
- struct gsm_bts *bts = vty->index;
-
- if (bts->type != GSM_BTS_TYPE_OSMOBTS) {
- vty_out(vty, "%% infinite radio link timeout not supported by this BTS%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- vty_out(vty, "%% INFINITE RADIO LINK TIMEOUT, USE ONLY FOR BTS RF TESTING%s", VTY_NEWLINE);
- gsm_bts_set_radio_link_timeout(bts, -1);
-
- return CMD_SUCCESS;
-}
-
-#define GPRS_TEXT "GPRS Packet Network\n"
-
-DEFUN(cfg_bts_prs_bvci, cfg_bts_gprs_bvci_cmd,
- "gprs cell bvci <2-65535>",
- GPRS_TEXT
- "GPRS Cell Settings\n"
- "GPRS BSSGP VC Identifier\n"
- "GPRS BSSGP VC Identifier")
-{
- struct gsm_bts *bts = vty->index;
-
- if (bts->gprs.mode == BTS_GPRS_NONE) {
- vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- bts->gprs.cell.bvci = atoi(argv[0]);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_gprs_nsei, cfg_bts_gprs_nsei_cmd,
- "gprs nsei <0-65535>",
- GPRS_TEXT
- "GPRS NS Entity Identifier\n"
- "GPRS NS Entity Identifier")
-{
- struct gsm_bts *bts = vty->index;
-
- if (bts->gprs.mode == BTS_GPRS_NONE) {
- vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- bts->gprs.nse.nsei = atoi(argv[0]);
-
- return CMD_SUCCESS;
-}
-
-#define NSVC_TEXT "Network Service Virtual Connection (NS-VC)\n" \
- "NSVC Logical Number\n"
-
-DEFUN(cfg_bts_gprs_nsvci, cfg_bts_gprs_nsvci_cmd,
- "gprs nsvc <0-1> nsvci <0-65535>",
- GPRS_TEXT NSVC_TEXT
- "NS Virtual Connection Identifier\n"
- "GPRS NS VC Identifier")
-{
- struct gsm_bts *bts = vty->index;
- int idx = atoi(argv[0]);
-
- if (bts->gprs.mode == BTS_GPRS_NONE) {
- vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- bts->gprs.nsvc[idx].nsvci = atoi(argv[1]);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_gprs_nsvc_lport, cfg_bts_gprs_nsvc_lport_cmd,
- "gprs nsvc <0-1> local udp port <0-65535>",
- GPRS_TEXT NSVC_TEXT
- "GPRS NS Local UDP Port\n"
- "GPRS NS Local UDP Port\n"
- "GPRS NS Local UDP Port\n"
- "GPRS NS Local UDP Port Number\n")
-{
- struct gsm_bts *bts = vty->index;
- int idx = atoi(argv[0]);
-
- if (bts->gprs.mode == BTS_GPRS_NONE) {
- vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- bts->gprs.nsvc[idx].local_port = atoi(argv[1]);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_gprs_nsvc_rport, cfg_bts_gprs_nsvc_rport_cmd,
- "gprs nsvc <0-1> remote udp port <0-65535>",
- GPRS_TEXT NSVC_TEXT
- "GPRS NS Remote UDP Port\n"
- "GPRS NS Remote UDP Port\n"
- "GPRS NS Remote UDP Port\n"
- "GPRS NS Remote UDP Port Number\n")
-{
- struct gsm_bts *bts = vty->index;
- int idx = atoi(argv[0]);
-
- if (bts->gprs.mode == BTS_GPRS_NONE) {
- vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- bts->gprs.nsvc[idx].remote_port = atoi(argv[1]);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_gprs_nsvc_rip, cfg_bts_gprs_nsvc_rip_cmd,
- "gprs nsvc <0-1> remote ip A.B.C.D",
- GPRS_TEXT NSVC_TEXT
- "GPRS NS Remote IP Address\n"
- "GPRS NS Remote IP Address\n"
- "GPRS NS Remote IP Address\n")
-{
- struct gsm_bts *bts = vty->index;
- int idx = atoi(argv[0]);
- struct in_addr ia;
-
- if (bts->gprs.mode == BTS_GPRS_NONE) {
- vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- inet_aton(argv[1], &ia);
- bts->gprs.nsvc[idx].remote_ip = ntohl(ia.s_addr);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_pag_free, cfg_bts_pag_free_cmd,
- "paging free <-1-1024>",
- "Paging options\n"
- "Only page when having a certain amount of free slots\n"
- "amount of required free paging slots. -1 to disable\n")
-{
- struct gsm_bts *bts = vty->index;
-
- bts->paging.free_chans_need = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_gprs_ns_timer, cfg_bts_gprs_ns_timer_cmd,
- "gprs ns timer " NS_TIMERS " <0-255>",
- GPRS_TEXT "Network Service\n"
- "Network Service Timer\n"
- NS_TIMERS_HELP "Timer Value\n")
-{
- struct gsm_bts *bts = vty->index;
- int idx = get_string_value(gprs_ns_timer_strs, argv[0]);
- int val = atoi(argv[1]);
-
- if (bts->gprs.mode == BTS_GPRS_NONE) {
- vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- if (idx < 0 || idx >= ARRAY_SIZE(bts->gprs.nse.timer))
- return CMD_WARNING;
-
- bts->gprs.nse.timer[idx] = val;
-
- return CMD_SUCCESS;
-}
-
-#define BSSGP_TIMERS "(blocking-timer|blocking-retries|unblocking-retries|reset-timer|reset-retries|suspend-timer|suspend-retries|resume-timer|resume-retries|capability-update-timer|capability-update-retries)"
-#define BSSGP_TIMERS_HELP \
- "Tbvc-block timeout\n" \
- "Tbvc-block retries\n" \
- "Tbvc-unblock retries\n" \
- "Tbvcc-reset timeout\n" \
- "Tbvc-reset retries\n" \
- "Tbvc-suspend timeout\n" \
- "Tbvc-suspend retries\n" \
- "Tbvc-resume timeout\n" \
- "Tbvc-resume retries\n" \
- "Tbvc-capa-update timeout\n" \
- "Tbvc-capa-update retries\n"
-
-DEFUN(cfg_bts_gprs_cell_timer, cfg_bts_gprs_cell_timer_cmd,
- "gprs cell timer " BSSGP_TIMERS " <0-255>",
- GPRS_TEXT "Cell / BSSGP\n"
- "Cell/BSSGP Timer\n"
- BSSGP_TIMERS_HELP "Timer Value\n")
-{
- struct gsm_bts *bts = vty->index;
- int idx = get_string_value(gprs_bssgp_cfg_strs, argv[0]);
- int val = atoi(argv[1]);
-
- if (bts->gprs.mode == BTS_GPRS_NONE) {
- vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- if (idx < 0 || idx >= ARRAY_SIZE(bts->gprs.cell.timer))
- return CMD_WARNING;
-
- bts->gprs.cell.timer[idx] = val;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_gprs_rac, cfg_bts_gprs_rac_cmd,
- "gprs routing area <0-255>",
- GPRS_TEXT
- "GPRS Routing Area Code\n"
- "GPRS Routing Area Code\n"
- "GPRS Routing Area Code\n")
-{
- struct gsm_bts *bts = vty->index;
-
- if (bts->gprs.mode == BTS_GPRS_NONE) {
- vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- bts->gprs.rac = atoi(argv[0]);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_gprs_ctrl_ack, cfg_bts_gprs_ctrl_ack_cmd,
- "gprs control-ack-type-rach", GPRS_TEXT
- "Set GPRS Control Ack Type for PACKET CONTROL ACKNOWLEDGMENT message to "
- "four access bursts format instead of default RLC/MAC control block\n")
-{
- struct gsm_bts *bts = vty->index;
-
- if (bts->gprs.mode == BTS_GPRS_NONE) {
- vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- bts->gprs.ctrl_ack_type_use_block = false;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_no_bts_gprs_ctrl_ack, cfg_no_bts_gprs_ctrl_ack_cmd,
- "no gprs control-ack-type-rach", NO_STR GPRS_TEXT
- "Set GPRS Control Ack Type for PACKET CONTROL ACKNOWLEDGMENT message to "
- "four access bursts format instead of default RLC/MAC control block\n")
-{
- struct gsm_bts *bts = vty->index;
-
- if (bts->gprs.mode == BTS_GPRS_NONE) {
- vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- bts->gprs.ctrl_ack_type_use_block = true;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_gprs_net_ctrl_ord, cfg_bts_gprs_net_ctrl_ord_cmd,
- "gprs network-control-order (nc0|nc1|nc2)",
- GPRS_TEXT
- "GPRS Network Control Order\n"
- "MS controlled cell re-selection, no measurement reporting\n"
- "MS controlled cell re-selection, MS sends measurement reports\n"
- "Network controlled cell re-selection, MS sends measurement reports\n")
-{
- struct gsm_bts *bts = vty->index;
-
- if (bts->gprs.mode == BTS_GPRS_NONE) {
- vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- bts->gprs.net_ctrl_ord = atoi(argv[0] + 2);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_gprs_mode, cfg_bts_gprs_mode_cmd,
- "gprs mode (none|gprs|egprs)",
- GPRS_TEXT
- "GPRS Mode for this BTS\n"
- "GPRS Disabled on this BTS\n"
- "GPRS Enabled on this BTS\n"
- "EGPRS (EDGE) Enabled on this BTS\n")
-{
- struct gsm_bts *bts = vty->index;
- enum bts_gprs_mode mode = bts_gprs_mode_parse(argv[0], NULL);
-
- if (!bts_gprs_mode_is_compat(bts, mode)) {
- vty_out(vty, "This BTS type does not support %s%s", argv[0],
- VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- bts->gprs.mode = mode;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_gprs_11bit_rach_support_for_egprs,
- cfg_bts_gprs_11bit_rach_support_for_egprs_cmd,
- "gprs 11bit_rach_support_for_egprs (0|1)",
- GPRS_TEXT "11 bit RACH options\n"
- "Disable 11 bit RACH for EGPRS\n"
- "Enable 11 bit RACH for EGPRS")
-{
- struct gsm_bts *bts = vty->index;
-
- bts->gprs.supports_egprs_11bit_rach = atoi(argv[0]);
-
- if (bts->gprs.supports_egprs_11bit_rach > 1) {
- vty_out(vty, "Error in RACH type%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- if ((bts->gprs.mode == BTS_GPRS_NONE) &&
- (bts->gprs.supports_egprs_11bit_rach == 1)) {
- vty_out(vty, "Error:gprs mode is none and 11bit rach is"
- " enabled%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- return CMD_SUCCESS;
-}
-
-#define SI_TEXT "System Information Messages\n"
-#define SI_TYPE_TEXT "(1|2|3|4|5|6|7|8|9|10|13|16|17|18|19|20|2bis|2ter|2quater|5bis|5ter)"
-#define SI_TYPE_HELP "System Information Type 1\n" \
- "System Information Type 2\n" \
- "System Information Type 3\n" \
- "System Information Type 4\n" \
- "System Information Type 5\n" \
- "System Information Type 6\n" \
- "System Information Type 7\n" \
- "System Information Type 8\n" \
- "System Information Type 9\n" \
- "System Information Type 10\n" \
- "System Information Type 13\n" \
- "System Information Type 16\n" \
- "System Information Type 17\n" \
- "System Information Type 18\n" \
- "System Information Type 19\n" \
- "System Information Type 20\n" \
- "System Information Type 2bis\n" \
- "System Information Type 2ter\n" \
- "System Information Type 2quater\n" \
- "System Information Type 5bis\n" \
- "System Information Type 5ter\n"
-
-DEFUN(cfg_bts_si_mode, cfg_bts_si_mode_cmd,
- "system-information " SI_TYPE_TEXT " mode (static|computed)",
- SI_TEXT SI_TYPE_HELP
- "System Information Mode\n"
- "Static user-specified\n"
- "Dynamic, BSC-computed\n")
-{
- struct gsm_bts *bts = vty->index;
- int type;
-
- type = get_string_value(osmo_sitype_strs, argv[0]);
- if (type < 0) {
- vty_out(vty, "Error SI Type%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- if (!strcmp(argv[1], "static"))
- bts->si_mode_static |= (1 << type);
- else
- bts->si_mode_static &= ~(1 << type);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_si_static, cfg_bts_si_static_cmd,
- "system-information " SI_TYPE_TEXT " static HEXSTRING",
- SI_TEXT SI_TYPE_HELP
- "Static System Information filling\n"
- "Static user-specified SI content in HEX notation\n")
-{
- struct gsm_bts *bts = vty->index;
- int rc, type;
-
- type = get_string_value(osmo_sitype_strs, argv[0]);
- if (type < 0) {
- vty_out(vty, "Error SI Type%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- if (!(bts->si_mode_static & (1 << type))) {
- vty_out(vty, "SI Type %s is not configured in static mode%s",
- get_value_string(osmo_sitype_strs, type), VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- /* Fill buffer with padding pattern */
- memset(GSM_BTS_SI(bts, type), 0x2b, GSM_MACBLOCK_LEN);
-
- /* Parse the user-specified SI in hex format, [partially] overwriting padding */
- rc = osmo_hexparse(argv[1], GSM_BTS_SI(bts, type), GSM_MACBLOCK_LEN);
- if (rc < 0 || rc > GSM_MACBLOCK_LEN) {
- vty_out(vty, "Error parsing HEXSTRING%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- /* Mark this SI as present */
- bts->si_valid |= (1 << type);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_early_cm, cfg_bts_early_cm_cmd,
- "early-classmark-sending (allowed|forbidden)",
- "Early Classmark Sending\n"
- "Early Classmark Sending is allowed\n"
- "Early Classmark Sending is forbidden\n")
-{
- struct gsm_bts *bts = vty->index;
-
- if (!strcmp(argv[0], "allowed"))
- bts->early_classmark_allowed = true;
- else
- bts->early_classmark_allowed = false;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_neigh_mode, cfg_bts_neigh_mode_cmd,
- "neighbor-list mode (automatic|manual|manual-si5)",
- "Neighbor List\n" "Mode of Neighbor List generation\n"
- "Automatically from all BTS in this OpenBSC\n" "Manual\n"
- "Manual with different lists for SI2 and SI5\n")
-{
- struct gsm_bts *bts = vty->index;
- int mode = get_string_value(bts_neigh_mode_strs, argv[0]);
-
- switch (mode) {
- case NL_MODE_MANUAL_SI5SEP:
- case NL_MODE_MANUAL:
- /* make sure we clear the current list when switching to
- * manual mode */
- if (bts->neigh_list_manual_mode == 0)
- memset(&bts->si_common.data.neigh_list, 0,
- sizeof(bts->si_common.data.neigh_list));
- break;
- default:
- break;
- }
-
- bts->neigh_list_manual_mode = mode;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_neigh, cfg_bts_neigh_cmd,
- "neighbor-list (add|del) arfcn <0-1023>",
- "Neighbor List\n" "Add to manual neighbor list\n"
- "Delete from manual neighbor list\n" "ARFCN of neighbor\n"
- "ARFCN of neighbor\n")
-{
- struct gsm_bts *bts = vty->index;
- struct bitvec *bv = &bts->si_common.neigh_list;
- uint16_t arfcn = atoi(argv[1]);
-
- if (!bts->neigh_list_manual_mode) {
- vty_out(vty, "%% Cannot configure neighbor list in "
- "automatic mode%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- if (!strcmp(argv[0], "add"))
- bitvec_set_bit_pos(bv, arfcn, 1);
- else
- bitvec_set_bit_pos(bv, arfcn, 0);
-
- return CMD_SUCCESS;
-}
-
-/* help text should be kept in sync with EARFCN_*_INVALID defines */
-DEFUN(cfg_bts_si2quater_neigh_add, cfg_bts_si2quater_neigh_add_cmd,
- "si2quater neighbor-list add earfcn <0-65535> thresh-hi <0-31> "
- "thresh-lo <0-32> prio <0-8> qrxlv <0-32> meas <0-8>",
- "SI2quater Neighbor List\n" "SI2quater Neighbor List\n"
- "Add to manual SI2quater neighbor list\n"
- "EARFCN of neighbor\n" "EARFCN of neighbor\n"
- "threshold high bits\n" "threshold high bits\n"
- "threshold low bits\n" "threshold low bits (32 means NA)\n"
- "priority\n" "priority (8 means NA)\n"
- "QRXLEVMIN\n" "QRXLEVMIN (32 means NA)\n"
- "measurement bandwidth\n" "measurement bandwidth (8 means NA)\n")
-{
- struct gsm_bts *bts = vty->index;
- struct osmo_earfcn_si2q *e = &bts->si_common.si2quater_neigh_list;
- uint16_t arfcn = atoi(argv[0]);
- uint8_t thresh_hi = atoi(argv[1]), thresh_lo = atoi(argv[2]),
- prio = atoi(argv[3]), qrx = atoi(argv[4]), meas = atoi(argv[5]);
- int r = bts_earfcn_add(bts, arfcn, thresh_hi, thresh_lo, prio, qrx, meas);
-
- switch (r) {
- case 1:
- vty_out(vty, "Warning: multiple threshold-high are not supported, overriding with %u%s",
- thresh_hi, VTY_NEWLINE);
- break;
- case EARFCN_THRESH_LOW_INVALID:
- vty_out(vty, "Warning: multiple threshold-low are not supported, overriding with %u%s",
- thresh_lo, VTY_NEWLINE);
- break;
- case EARFCN_QRXLV_INVALID + 1:
- vty_out(vty, "Warning: multiple QRXLEVMIN are not supported, overriding with %u%s",
- qrx, VTY_NEWLINE);
- break;
- case EARFCN_PRIO_INVALID:
- vty_out(vty, "Warning: multiple priorities are not supported, overriding with %u%s",
- prio, VTY_NEWLINE);
- break;
- default:
- if (r < 0) {
- vty_out(vty, "Unable to add ARFCN %u: %s%s", arfcn, strerror(-r), VTY_NEWLINE);
- return CMD_WARNING;
- }
- }
-
- if (si2q_num(bts) <= SI2Q_MAX_NUM)
- return CMD_SUCCESS;
-
- vty_out(vty, "Warning: not enough space in SI2quater (%u/%u used) for a given EARFCN %u%s",
- bts->si2q_count, SI2Q_MAX_NUM, arfcn, VTY_NEWLINE);
- osmo_earfcn_del(e, arfcn);
-
- return CMD_WARNING;
-}
-
-DEFUN(cfg_bts_si2quater_neigh_del, cfg_bts_si2quater_neigh_del_cmd,
- "si2quater neighbor-list del earfcn <0-65535>",
- "SI2quater Neighbor List\n"
- "SI2quater Neighbor List\n"
- "Delete from SI2quater manual neighbor list\n"
- "EARFCN of neighbor\n"
- "EARFCN\n")
-{
- struct gsm_bts *bts = vty->index;
- struct osmo_earfcn_si2q *e = &bts->si_common.si2quater_neigh_list;
- uint16_t arfcn = atoi(argv[0]);
- int r = osmo_earfcn_del(e, arfcn);
- if (r < 0) {
- vty_out(vty, "Unable to delete arfcn %u: %s%s", arfcn,
- strerror(-r), VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_si2quater_uarfcn_add, cfg_bts_si2quater_uarfcn_add_cmd,
- "si2quater neighbor-list add uarfcn <0-16383> <0-511> <0-1>",
- "SI2quater Neighbor List\n"
- "SI2quater Neighbor List\n" "Add to manual SI2quater neighbor list\n"
- "UARFCN of neighbor\n" "UARFCN of neighbor\n" "scrambling code\n"
- "diversity bit\n")
-{
- struct gsm_bts *bts = vty->index;
- uint16_t arfcn = atoi(argv[0]), scramble = atoi(argv[1]);
-
- switch(bts_uarfcn_add(bts, arfcn, scramble, atoi(argv[2]))) {
- case -ENOMEM:
- vty_out(vty, "Unable to add UARFCN: max number of UARFCNs (%u) reached%s", MAX_EARFCN_LIST, VTY_NEWLINE);
- return CMD_WARNING;
- case -ENOSPC:
- vty_out(vty, "Warning: not enough space in SI2quater for a given UARFCN (%u, %u)%s",
- arfcn, scramble, VTY_NEWLINE);
- return CMD_WARNING;
- case -EADDRINUSE:
- vty_out(vty, "Unable to add UARFCN: (%u, %u) is already added%s", arfcn, scramble, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_si2quater_uarfcn_del, cfg_bts_si2quater_uarfcn_del_cmd,
- "si2quater neighbor-list del uarfcn <0-16383> <0-511>",
- "SI2quater Neighbor List\n"
- "SI2quater Neighbor List\n"
- "Delete from SI2quater manual neighbor list\n"
- "UARFCN of neighbor\n"
- "UARFCN\n"
- "scrambling code\n")
-{
- struct gsm_bts *bts = vty->index;
-
- if (bts_uarfcn_del(bts, atoi(argv[0]), atoi(argv[1])) < 0) {
- vty_out(vty, "Unable to delete uarfcn: pair not found%s",
- VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_si5_neigh, cfg_bts_si5_neigh_cmd,
- "si5 neighbor-list (add|del) arfcn <0-1023>",
- "SI5 Neighbor List\n"
- "SI5 Neighbor List\n" "Add to manual SI5 neighbor list\n"
- "Delete from SI5 manual neighbor list\n" "ARFCN of neighbor\n"
- "ARFCN of neighbor\n")
-{
- struct gsm_bts *bts = vty->index;
- struct bitvec *bv = &bts->si_common.si5_neigh_list;
- uint16_t arfcn = atoi(argv[1]);
-
- if (!bts->neigh_list_manual_mode) {
- vty_out(vty, "%% Cannot configure neighbor list in "
- "automatic mode%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- if (!strcmp(argv[0], "add"))
- bitvec_set_bit_pos(bv, arfcn, 1);
- else
- bitvec_set_bit_pos(bv, arfcn, 0);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_pcu_sock, cfg_bts_pcu_sock_cmd,
- "pcu-socket PATH",
- "PCU Socket Path for using OsmoPCU co-located with BSC (legacy BTS)\n"
- "Path in the file system for the unix-domain PCU socket\n")
-{
- struct gsm_bts *bts = vty->index;
- int rc;
-
- osmo_talloc_replace_string(bts, &bts->pcu_sock_path, argv[0]);
- pcu_sock_exit(bts);
- rc = pcu_sock_init(bts->pcu_sock_path, bts);
- if (rc < 0) {
- vty_out(vty, "%% Error creating PCU socket `%s' for BTS %u%s",
- bts->pcu_sock_path, bts->nr, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- return CMD_SUCCESS;
-}
-
-#define EXCL_RFLOCK_STR "Exclude this BTS from the global RF Lock\n"
-
-DEFUN(cfg_bts_excl_rf_lock,
- cfg_bts_excl_rf_lock_cmd,
- "rf-lock-exclude",
- EXCL_RFLOCK_STR)
-{
- struct gsm_bts *bts = vty->index;
- bts->excl_from_rf_lock = 1;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_no_excl_rf_lock,
- cfg_bts_no_excl_rf_lock_cmd,
- "no rf-lock-exclude",
- NO_STR EXCL_RFLOCK_STR)
-{
- struct gsm_bts *bts = vty->index;
- bts->excl_from_rf_lock = 0;
- return CMD_SUCCESS;
-}
-
-#define FORCE_COMB_SI_STR "Force the generation of a single SI (no ter/bis)\n"
-
-DEFUN(cfg_bts_force_comb_si,
- cfg_bts_force_comb_si_cmd,
- "force-combined-si",
- FORCE_COMB_SI_STR)
-{
- struct gsm_bts *bts = vty->index;
- bts->force_combined_si = 1;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_no_force_comb_si,
- cfg_bts_no_force_comb_si_cmd,
- "no force-combined-si",
- NO_STR FORCE_COMB_SI_STR)
-{
- struct gsm_bts *bts = vty->index;
- bts->force_combined_si = 0;
- return CMD_SUCCESS;
-}
-
-static void _get_codec_from_arg(struct vty *vty, int argc, const char *argv[])
-{
- struct gsm_bts *bts = vty->index;
- struct bts_codec_conf *codec = &bts->codec;
- int i;
-
- codec->hr = 0;
- codec->efr = 0;
- codec->amr = 0;
- for (i = 0; i < argc; i++) {
- if (!strcmp(argv[i], "hr"))
- codec->hr = 1;
- if (!strcmp(argv[i], "efr"))
- codec->efr = 1;
- if (!strcmp(argv[i], "amr"))
- codec->amr = 1;
- }
-}
-
-#define CODEC_PAR_STR " (hr|efr|amr)"
-#define CODEC_HELP_STR "Half Rate\n" \
- "Enhanced Full Rate\nAdaptive Multirate\n"
-
-DEFUN(cfg_bts_codec0, cfg_bts_codec0_cmd,
- "codec-support fr",
- "Codec Support settings\nFullrate\n")
-{
- _get_codec_from_arg(vty, 0, argv);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_codec1, cfg_bts_codec1_cmd,
- "codec-support fr" CODEC_PAR_STR,
- "Codec Support settings\nFullrate\n"
- CODEC_HELP_STR)
-{
- _get_codec_from_arg(vty, 1, argv);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_codec2, cfg_bts_codec2_cmd,
- "codec-support fr" CODEC_PAR_STR CODEC_PAR_STR,
- "Codec Support settings\nFullrate\n"
- CODEC_HELP_STR CODEC_HELP_STR)
-{
- _get_codec_from_arg(vty, 2, argv);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_codec3, cfg_bts_codec3_cmd,
- "codec-support fr" CODEC_PAR_STR CODEC_PAR_STR CODEC_PAR_STR,
- "Codec Support settings\nFullrate\n"
- CODEC_HELP_STR CODEC_HELP_STR CODEC_HELP_STR)
-{
- _get_codec_from_arg(vty, 3, argv);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_codec4, cfg_bts_codec4_cmd,
- "codec-support fr" CODEC_PAR_STR CODEC_PAR_STR CODEC_PAR_STR CODEC_PAR_STR,
- "Codec Support settings\nFullrate\n"
- CODEC_HELP_STR CODEC_HELP_STR CODEC_HELP_STR CODEC_HELP_STR)
-{
- _get_codec_from_arg(vty, 4, argv);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_depends_on, cfg_bts_depends_on_cmd,
- "depends-on-bts <0-255>",
- "This BTS can only be started if another one is up\n" "BTS Number\n")
-{
- struct gsm_bts *bts = vty->index;
- struct gsm_bts *other_bts;
- int dep = atoi(argv[0]);
-
-
- if (!is_ipaccess_bts(bts)) {
- vty_out(vty, "This feature is only available for IP systems.%s",
- VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- other_bts = gsm_bts_num(bts->network, dep);
- if (!other_bts || !is_ipaccess_bts(other_bts)) {
- vty_out(vty, "This feature is only available for IP systems.%s",
- VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- if (dep >= bts->nr) {
- vty_out(vty, "%%Need to depend on an already declared unit.%s",
- VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- bts_depend_mark(bts, dep);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_no_depends_on, cfg_bts_no_depends_on_cmd,
- "depeneds-on-bts <0-255>",
- NO_STR "This BTS can only be started if another one is up\n"
- "BTS Number\n")
-{
- struct gsm_bts *bts = vty->index;
- int dep = atoi(argv[0]);
-
- bts_depend_clear(bts, dep);
- return CMD_SUCCESS;
-}
-
-#define AMR_TEXT "Adaptive Multi Rate settings\n"
-#define AMR_MODE_TEXT "Codec modes to use with AMR codec\n"
-#define AMR_START_TEXT "Initial codec to use with AMR\n" \
- "Automatically\nFirst codec\nSecond codec\nThird codec\nFourth codec\n"
-#define AMR_TH_TEXT "AMR threshold between codecs\nMS side\nBTS side\n"
-#define AMR_HY_TEXT "AMR hysteresis between codecs\nMS side\nBTS side\n"
-
-static void get_amr_from_arg(struct vty *vty, int argc, const char *argv[], int full)
-{
- struct gsm_bts *bts = vty->index;
- struct amr_multirate_conf *mr = (full) ? &bts->mr_full: &bts->mr_half;
- struct gsm48_multi_rate_conf *mr_conf =
- (struct gsm48_multi_rate_conf *) mr->gsm48_ie;
- int i;
-
- mr->gsm48_ie[1] = 0;
- for (i = 0; i < argc; i++)
- mr->gsm48_ie[1] |= 1 << atoi(argv[i]);
- mr_conf->icmi = 0;
-}
-
-static void get_amr_th_from_arg(struct vty *vty, int argc, const char *argv[], int full)
-{
- struct gsm_bts *bts = vty->index;
- struct amr_multirate_conf *mr = (full) ? &bts->mr_full: &bts->mr_half;
- struct amr_mode *modes;
- int i;
-
- modes = argv[0][0]=='m' ? mr->ms_mode : mr->bts_mode;
- for (i = 0; i < argc - 1; i++)
- modes[i].threshold = atoi(argv[i + 1]);
-}
-
-static void get_amr_hy_from_arg(struct vty *vty, int argc, const char *argv[], int full)
-{
- struct gsm_bts *bts = vty->index;
- struct amr_multirate_conf *mr = (full) ? &bts->mr_full: &bts->mr_half;
- struct amr_mode *modes;
- int i;
-
- modes = argv[0][0]=='m' ? mr->ms_mode : mr->bts_mode;
- for (i = 0; i < argc - 1; i++)
- modes[i].hysteresis = atoi(argv[i + 1]);
-}
-
-static void get_amr_start_from_arg(struct vty *vty, const char *argv[], int full)
-{
- struct gsm_bts *bts = vty->index;
- struct amr_multirate_conf *mr = (full) ? &bts->mr_full: &bts->mr_half;
- struct gsm48_multi_rate_conf *mr_conf =
- (struct gsm48_multi_rate_conf *) mr->gsm48_ie;
- int num = 0, i;
-
- for (i = 0; i < ((full) ? 8 : 6); i++) {
- if ((mr->gsm48_ie[1] & (1 << i))) {
- num++;
- }
- }
-
- if (argv[0][0] == 'a' || num == 0)
- mr_conf->icmi = 0;
- else {
- mr_conf->icmi = 1;
- if (num < atoi(argv[0]))
- mr_conf->smod = num - 1;
- else
- mr_conf->smod = atoi(argv[0]) - 1;
- }
-}
-
-#define AMR_TCHF_PAR_STR " (0|1|2|3|4|5|6|7)"
-#define AMR_TCHF_HELP_STR "4,75k\n5,15k\n5,90k\n6,70k\n7,40k\n7,95k\n" \
- "10,2k\n12,2k\n"
-
-#define AMR_TCHH_PAR_STR " (0|1|2|3|4|5)"
-#define AMR_TCHH_HELP_STR "4,75k\n5,15k\n5,90k\n6,70k\n7,40k\n7,95k\n"
-
-#define AMR_TH_HELP_STR "Threshold between codec 1 and 2\n"
-#define AMR_HY_HELP_STR "Hysteresis between codec 1 and 2\n"
-
-DEFUN(cfg_bts_amr_fr_modes1, cfg_bts_amr_fr_modes1_cmd,
- "amr tch-f modes" AMR_TCHF_PAR_STR,
- AMR_TEXT "Full Rate\n" AMR_MODE_TEXT
- AMR_TCHF_HELP_STR)
-{
- get_amr_from_arg(vty, 1, argv, 1);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_amr_fr_modes2, cfg_bts_amr_fr_modes2_cmd,
- "amr tch-f modes" AMR_TCHF_PAR_STR AMR_TCHF_PAR_STR,
- AMR_TEXT "Full Rate\n" AMR_MODE_TEXT
- AMR_TCHF_HELP_STR AMR_TCHF_HELP_STR)
-{
- get_amr_from_arg(vty, 2, argv, 1);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_amr_fr_modes3, cfg_bts_amr_fr_modes3_cmd,
- "amr tch-f modes" AMR_TCHF_PAR_STR AMR_TCHF_PAR_STR AMR_TCHF_PAR_STR,
- AMR_TEXT "Full Rate\n" AMR_MODE_TEXT
- AMR_TCHF_HELP_STR AMR_TCHF_HELP_STR AMR_TCHF_HELP_STR)
-{
- get_amr_from_arg(vty, 3, argv, 1);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_amr_fr_modes4, cfg_bts_amr_fr_modes4_cmd,
- "amr tch-f modes" AMR_TCHF_PAR_STR AMR_TCHF_PAR_STR AMR_TCHF_PAR_STR AMR_TCHF_PAR_STR,
- AMR_TEXT "Full Rate\n" AMR_MODE_TEXT
- AMR_TCHF_HELP_STR AMR_TCHF_HELP_STR AMR_TCHF_HELP_STR AMR_TCHF_HELP_STR)
-{
- get_amr_from_arg(vty, 4, argv, 1);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_amr_fr_start_mode, cfg_bts_amr_fr_start_mode_cmd,
- "amr tch-f start-mode (auto|1|2|3|4)",
- AMR_TEXT "Full Rate\n" AMR_START_TEXT)
-{
- get_amr_start_from_arg(vty, argv, 1);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_amr_fr_thres1, cfg_bts_amr_fr_thres1_cmd,
- "amr tch-f threshold (ms|bts) <0-63>",
- AMR_TEXT "Full Rate\n" AMR_TH_TEXT
- AMR_TH_HELP_STR)
-{
- get_amr_th_from_arg(vty, 2, argv, 1);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_amr_fr_thres2, cfg_bts_amr_fr_thres2_cmd,
- "amr tch-f threshold (ms|bts) <0-63> <0-63>",
- AMR_TEXT "Full Rate\n" AMR_TH_TEXT
- AMR_TH_HELP_STR AMR_TH_HELP_STR)
-{
- get_amr_th_from_arg(vty, 3, argv, 1);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_amr_fr_thres3, cfg_bts_amr_fr_thres3_cmd,
- "amr tch-f threshold (ms|bts) <0-63> <0-63> <0-63>",
- AMR_TEXT "Full Rate\n" AMR_TH_TEXT
- AMR_TH_HELP_STR AMR_TH_HELP_STR AMR_TH_HELP_STR)
-{
- get_amr_th_from_arg(vty, 4, argv, 1);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_amr_fr_hyst1, cfg_bts_amr_fr_hyst1_cmd,
- "amr tch-f hysteresis (ms|bts) <0-15>",
- AMR_TEXT "Full Rate\n" AMR_HY_TEXT
- AMR_HY_HELP_STR)
-{
- get_amr_hy_from_arg(vty, 2, argv, 1);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_amr_fr_hyst2, cfg_bts_amr_fr_hyst2_cmd,
- "amr tch-f hysteresis (ms|bts) <0-15> <0-15>",
- AMR_TEXT "Full Rate\n" AMR_HY_TEXT
- AMR_HY_HELP_STR AMR_HY_HELP_STR)
-{
- get_amr_hy_from_arg(vty, 3, argv, 1);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_amr_fr_hyst3, cfg_bts_amr_fr_hyst3_cmd,
- "amr tch-f hysteresis (ms|bts) <0-15> <0-15> <0-15>",
- AMR_TEXT "Full Rate\n" AMR_HY_TEXT
- AMR_HY_HELP_STR AMR_HY_HELP_STR AMR_HY_HELP_STR)
-{
- get_amr_hy_from_arg(vty, 4, argv, 1);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_amr_hr_modes1, cfg_bts_amr_hr_modes1_cmd,
- "amr tch-h modes" AMR_TCHH_PAR_STR,
- AMR_TEXT "Half Rate\n" AMR_MODE_TEXT
- AMR_TCHH_HELP_STR)
-{
- get_amr_from_arg(vty, 1, argv, 0);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_amr_hr_modes2, cfg_bts_amr_hr_modes2_cmd,
- "amr tch-h modes" AMR_TCHH_PAR_STR AMR_TCHH_PAR_STR,
- AMR_TEXT "Half Rate\n" AMR_MODE_TEXT
- AMR_TCHH_HELP_STR AMR_TCHH_HELP_STR)
-{
- get_amr_from_arg(vty, 2, argv, 0);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_amr_hr_modes3, cfg_bts_amr_hr_modes3_cmd,
- "amr tch-h modes" AMR_TCHH_PAR_STR AMR_TCHH_PAR_STR AMR_TCHH_PAR_STR,
- AMR_TEXT "Half Rate\n" AMR_MODE_TEXT
- AMR_TCHH_HELP_STR AMR_TCHH_HELP_STR AMR_TCHH_HELP_STR)
-{
- get_amr_from_arg(vty, 3, argv, 0);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_amr_hr_modes4, cfg_bts_amr_hr_modes4_cmd,
- "amr tch-h modes" AMR_TCHH_PAR_STR AMR_TCHH_PAR_STR AMR_TCHH_PAR_STR AMR_TCHH_PAR_STR,
- AMR_TEXT "Half Rate\n" AMR_MODE_TEXT
- AMR_TCHH_HELP_STR AMR_TCHH_HELP_STR AMR_TCHH_HELP_STR AMR_TCHH_HELP_STR)
-{
- get_amr_from_arg(vty, 4, argv, 0);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_amr_hr_start_mode, cfg_bts_amr_hr_start_mode_cmd,
- "amr tch-h start-mode (auto|1|2|3|4)",
- AMR_TEXT "Half Rate\n" AMR_START_TEXT)
-{
- get_amr_start_from_arg(vty, argv, 0);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_amr_hr_thres1, cfg_bts_amr_hr_thres1_cmd,
- "amr tch-h threshold (ms|bts) <0-63>",
- AMR_TEXT "Half Rate\n" AMR_TH_TEXT
- AMR_TH_HELP_STR)
-{
- get_amr_th_from_arg(vty, 2, argv, 0);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_amr_hr_thres2, cfg_bts_amr_hr_thres2_cmd,
- "amr tch-h threshold (ms|bts) <0-63> <0-63>",
- AMR_TEXT "Half Rate\n" AMR_TH_TEXT
- AMR_TH_HELP_STR AMR_TH_HELP_STR)
-{
- get_amr_th_from_arg(vty, 3, argv, 0);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_amr_hr_thres3, cfg_bts_amr_hr_thres3_cmd,
- "amr tch-h threshold (ms|bts) <0-63> <0-63> <0-63>",
- AMR_TEXT "Half Rate\n" AMR_TH_TEXT
- AMR_TH_HELP_STR AMR_TH_HELP_STR AMR_TH_HELP_STR)
-{
- get_amr_th_from_arg(vty, 4, argv, 0);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_amr_hr_hyst1, cfg_bts_amr_hr_hyst1_cmd,
- "amr tch-h hysteresis (ms|bts) <0-15>",
- AMR_TEXT "Half Rate\n" AMR_HY_TEXT
- AMR_HY_HELP_STR)
-{
- get_amr_hy_from_arg(vty, 2, argv, 0);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_amr_hr_hyst2, cfg_bts_amr_hr_hyst2_cmd,
- "amr tch-h hysteresis (ms|bts) <0-15> <0-15>",
- AMR_TEXT "Half Rate\n" AMR_HY_TEXT
- AMR_HY_HELP_STR AMR_HY_HELP_STR)
-{
- get_amr_hy_from_arg(vty, 3, argv, 0);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bts_amr_hr_hyst3, cfg_bts_amr_hr_hyst3_cmd,
- "amr tch-h hysteresis (ms|bts) <0-15> <0-15> <0-15>",
- AMR_TEXT "Half Rate\n" AMR_HY_TEXT
- AMR_HY_HELP_STR AMR_HY_HELP_STR AMR_HY_HELP_STR)
-{
- get_amr_hy_from_arg(vty, 4, argv, 0);
- return CMD_SUCCESS;
-}
-
-#define TRX_TEXT "Radio Transceiver\n"
-
-/* per TRX configuration */
-DEFUN(cfg_trx,
- cfg_trx_cmd,
- "trx <0-255>",
- TRX_TEXT
- "Select a TRX to configure")
-{
- int trx_nr = atoi(argv[0]);
- struct gsm_bts *bts = vty->index;
- struct gsm_bts_trx *trx;
-
- if (trx_nr > bts->num_trx) {
- vty_out(vty, "%% The next unused TRX number in this BTS is %u%s",
- bts->num_trx, VTY_NEWLINE);
- return CMD_WARNING;
- } else if (trx_nr == bts->num_trx) {
- /* we need to allocate a new one */
- trx = gsm_bts_trx_alloc(bts);
- } else
- trx = gsm_bts_trx_num(bts, trx_nr);
-
- if (!trx)
- return CMD_WARNING;
-
- vty->index = trx;
- vty->index_sub = &trx->description;
- vty->node = TRX_NODE;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_trx_arfcn,
- cfg_trx_arfcn_cmd,
- "arfcn <0-1023>",
- "Set the ARFCN for this TRX\n"
- "Absolute Radio Frequency Channel Number\n")
-{
- int arfcn = atoi(argv[0]);
- struct gsm_bts_trx *trx = vty->index;
-
- /* FIXME: check if this ARFCN is supported by this TRX */
-
- trx->arfcn = arfcn;
-
- /* FIXME: patch ARFCN into SYSTEM INFORMATION */
- /* FIXME: use OML layer to update the ARFCN */
- /* FIXME: use RSL layer to update SYSTEM INFORMATION */
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_trx_nominal_power,
- cfg_trx_nominal_power_cmd,
- "nominal power <0-100>",
- "Nominal TRX RF Power in dBm\n"
- "Nominal TRX RF Power in dBm\n"
- "Nominal TRX RF Power in dBm\n")
-{
- struct gsm_bts_trx *trx = vty->index;
-
- trx->nominal_power = atoi(argv[0]);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_trx_max_power_red,
- cfg_trx_max_power_red_cmd,
- "max_power_red <0-100>",
- "Reduction of maximum BS RF Power (relative to nominal power)\n"
- "Reduction of maximum BS RF Power in dB\n")
-{
- int maxpwr_r = atoi(argv[0]);
- struct gsm_bts_trx *trx = vty->index;
- int upper_limit = 24; /* default 12.21 max power red. */
-
- /* FIXME: check if our BTS type supports more than 12 */
- if (maxpwr_r < 0 || maxpwr_r > upper_limit) {
- vty_out(vty, "%% Power %d dB is not in the valid range%s",
- maxpwr_r, VTY_NEWLINE);
- return CMD_WARNING;
- }
- if (maxpwr_r & 1) {
- vty_out(vty, "%% Power %d dB is not an even value%s",
- maxpwr_r, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- trx->max_power_red = maxpwr_r;
-
- /* FIXME: make sure we update this using OML */
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_trx_rsl_e1,
- cfg_trx_rsl_e1_cmd,
- "rsl e1 line E1_LINE timeslot <1-31> sub-slot (0|1|2|3|full)",
- "RSL Parameters\n"
- "E1/T1 interface to be used for RSL\n"
- "E1/T1 interface to be used for RSL\n"
- "E1/T1 Line Number to be used for RSL\n"
- "E1/T1 Timeslot to be used for RSL\n"
- "E1/T1 Timeslot to be used for RSL\n"
- "E1/T1 Sub-slot to be used for RSL\n"
- "E1/T1 Sub-slot 0 is to be used for RSL\n"
- "E1/T1 Sub-slot 1 is to be used for RSL\n"
- "E1/T1 Sub-slot 2 is to be used for RSL\n"
- "E1/T1 Sub-slot 3 is to be used for RSL\n"
- "E1/T1 full timeslot is to be used for RSL\n")
-{
- struct gsm_bts_trx *trx = vty->index;
-
- parse_e1_link(&trx->rsl_e1_link, argv[0], argv[1], argv[2]);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_trx_rsl_e1_tei,
- cfg_trx_rsl_e1_tei_cmd,
- "rsl e1 tei <0-63>",
- "RSL Parameters\n"
- "Set the TEI to be used for RSL\n"
- "Set the TEI to be used for RSL\n"
- "TEI to be used for RSL\n")
-{
- struct gsm_bts_trx *trx = vty->index;
-
- trx->rsl_tei = atoi(argv[0]);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_trx_rf_locked,
- cfg_trx_rf_locked_cmd,
- "rf_locked (0|1)",
- "Set or unset the RF Locking (Turn off RF of the TRX)\n"
- "TRX is NOT RF locked (active)\n"
- "TRX is RF locked (turned off)\n")
-{
- int locked = atoi(argv[0]);
- struct gsm_bts_trx *trx = vty->index;
-
- gsm_trx_lock_rf(trx, locked);
- return CMD_SUCCESS;
-}
-
-/* per TS configuration */
-DEFUN(cfg_ts,
- cfg_ts_cmd,
- "timeslot <0-7>",
- "Select a Timeslot to configure\n"
- "Timeslot number\n")
-{
- int ts_nr = atoi(argv[0]);
- struct gsm_bts_trx *trx = vty->index;
- struct gsm_bts_trx_ts *ts;
-
- if (ts_nr >= TRX_NR_TS) {
- vty_out(vty, "%% A GSM TRX only has %u Timeslots per TRX%s",
- TRX_NR_TS, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- ts = &trx->ts[ts_nr];
-
- vty->index = ts;
- vty->node = TS_NODE;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_ts_pchan,
- cfg_ts_pchan_cmd,
- "phys_chan_config PCHAN", /* dynamically generated! */
- "Physical Channel configuration (TCH/SDCCH/...)\n" "Physical Channel\n")
-{
- struct gsm_bts_trx_ts *ts = vty->index;
- int pchanc;
-
- pchanc = gsm_pchan_parse(argv[0]);
- if (pchanc < 0)
- return CMD_WARNING;
-
- ts->pchan = pchanc;
-
- return CMD_SUCCESS;
-}
-
-/* used for backwards compatibility with old config files that still
- * have uppercase pchan type names */
-DEFUN_HIDDEN(cfg_ts_pchan_compat,
- cfg_ts_pchan_compat_cmd,
- "phys_chan_config PCHAN",
- "Physical Channel configuration (TCH/SDCCH/...)\n" "Physical Channel\n")
-{
- struct gsm_bts_trx_ts *ts = vty->index;
- int pchanc;
-
- pchanc = gsm_pchan_parse(argv[0]);
- if (pchanc < 0)
- return CMD_WARNING;
-
- ts->pchan = pchanc;
-
- return CMD_SUCCESS;
-}
-
-
-
-DEFUN(cfg_ts_tsc,
- cfg_ts_tsc_cmd,
- "training_sequence_code <0-7>",
- "Training Sequence Code of the Timeslot\n" "TSC\n")
-{
- struct gsm_bts_trx_ts *ts = vty->index;
-
- if (!gsm_btsmodel_has_feature(ts->trx->bts->model, BTS_FEAT_MULTI_TSC)) {
- vty_out(vty, "%% This BTS does not support a TSC != BCC, "
- "falling back to BCC%s", VTY_NEWLINE);
- ts->tsc = -1;
- return CMD_WARNING;
- }
-
- ts->tsc = atoi(argv[0]);
-
- return CMD_SUCCESS;
-}
-
-#define HOPPING_STR "Configure frequency hopping\n"
-
-DEFUN(cfg_ts_hopping,
- cfg_ts_hopping_cmd,
- "hopping enabled (0|1)",
- HOPPING_STR "Enable or disable frequency hopping\n"
- "Disable frequency hopping\n" "Enable frequency hopping\n")
-{
- struct gsm_bts_trx_ts *ts = vty->index;
- int enabled = atoi(argv[0]);
-
- if (enabled && !gsm_btsmodel_has_feature(ts->trx->bts->model, BTS_FEAT_HOPPING)) {
- vty_out(vty, "BTS model does not support hopping%s",
- VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- ts->hopping.enabled = enabled;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_ts_hsn,
- cfg_ts_hsn_cmd,
- "hopping sequence-number <0-63>",
- HOPPING_STR
- "Which hopping sequence to use for this channel\n"
- "Hopping Sequence Number (HSN)\n")
-{
- struct gsm_bts_trx_ts *ts = vty->index;
-
- ts->hopping.hsn = atoi(argv[0]);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_ts_maio,
- cfg_ts_maio_cmd,
- "hopping maio <0-63>",
- HOPPING_STR
- "Which hopping MAIO to use for this channel\n"
- "Mobile Allocation Index Offset (MAIO)\n")
-{
- struct gsm_bts_trx_ts *ts = vty->index;
-
- ts->hopping.maio = atoi(argv[0]);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_ts_arfcn_add,
- cfg_ts_arfcn_add_cmd,
- "hopping arfcn add <0-1023>",
- HOPPING_STR "Configure hopping ARFCN list\n"
- "Add an entry to the hopping ARFCN list\n" "ARFCN\n")
-{
- struct gsm_bts_trx_ts *ts = vty->index;
- int arfcn = atoi(argv[0]);
-
- bitvec_set_bit_pos(&ts->hopping.arfcns, arfcn, 1);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_ts_arfcn_del,
- cfg_ts_arfcn_del_cmd,
- "hopping arfcn del <0-1023>",
- HOPPING_STR "Configure hopping ARFCN list\n"
- "Delete an entry to the hopping ARFCN list\n" "ARFCN\n")
-{
- struct gsm_bts_trx_ts *ts = vty->index;
- int arfcn = atoi(argv[0]);
-
- bitvec_set_bit_pos(&ts->hopping.arfcns, arfcn, 0);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_ts_e1_subslot,
- cfg_ts_e1_subslot_cmd,
- "e1 line E1_LINE timeslot <1-31> sub-slot (0|1|2|3|full)",
- "E1/T1 channel connected to this on-air timeslot\n"
- "E1/T1 channel connected to this on-air timeslot\n"
- "E1/T1 line connected to this on-air timeslot\n"
- "E1/T1 timeslot connected to this on-air timeslot\n"
- "E1/T1 timeslot connected to this on-air timeslot\n"
- "E1/T1 sub-slot connected to this on-air timeslot\n"
- "E1/T1 sub-slot 0 connected to this on-air timeslot\n"
- "E1/T1 sub-slot 1 connected to this on-air timeslot\n"
- "E1/T1 sub-slot 2 connected to this on-air timeslot\n"
- "E1/T1 sub-slot 3 connected to this on-air timeslot\n"
- "Full E1/T1 timeslot connected to this on-air timeslot\n")
-{
- struct gsm_bts_trx_ts *ts = vty->index;
-
- parse_e1_link(&ts->e1_link, argv[0], argv[1], argv[2]);
-
- return CMD_SUCCESS;
-}
-
-void openbsc_vty_print_statistics(struct vty *vty, struct gsm_network *net)
-{
- vty_out(vty, "Channel Requests : %lu total, %lu no channel%s",
- net->bsc_ctrs->ctr[BSC_CTR_CHREQ_TOTAL].current,
- net->bsc_ctrs->ctr[BSC_CTR_CHREQ_NO_CHANNEL].current,
- VTY_NEWLINE);
- vty_out(vty, "Channel Failures : %lu rf_failures, %lu rll failures%s",
- net->bsc_ctrs->ctr[BSC_CTR_CHAN_RF_FAIL].current,
- net->bsc_ctrs->ctr[BSC_CTR_CHAN_RLL_ERR].current,
- VTY_NEWLINE);
- vty_out(vty, "Paging : %lu attempted, %lu complete, %lu expired%s",
- net->bsc_ctrs->ctr[BSC_CTR_PAGING_ATTEMPTED].current,
- net->bsc_ctrs->ctr[BSC_CTR_PAGING_COMPLETED].current,
- net->bsc_ctrs->ctr[BSC_CTR_PAGING_EXPIRED].current,
- VTY_NEWLINE);
- vty_out(vty, "BTS failures : %lu OML, %lu RSL%s",
- net->bsc_ctrs->ctr[BSC_CTR_BTS_OML_FAIL].current,
- net->bsc_ctrs->ctr[BSC_CTR_BTS_RSL_FAIL].current,
- VTY_NEWLINE);
-}
-
-DEFUN(drop_bts,
- drop_bts_cmd,
- "drop bts connection <0-65535> (oml|rsl)",
- "Debug/Simulation command to drop Abis/IP BTS\n"
- "Debug/Simulation command to drop Abis/IP BTS\n"
- "Debug/Simulation command to drop Abis/IP BTS\n"
- "BTS NR\n" "Drop OML Connection\n" "Drop RSL Connection\n")
-{
- struct gsm_network *gsmnet;
- struct gsm_bts_trx *trx;
- struct gsm_bts *bts;
- unsigned int bts_nr;
-
- gsmnet = gsmnet_from_vty(vty);
-
- bts_nr = atoi(argv[0]);
- if (bts_nr >= gsmnet->num_bts) {
- vty_out(vty, "BTS number must be between 0 and %d. It was %d.%s",
- gsmnet->num_bts, bts_nr, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- bts = gsm_bts_num(gsmnet, bts_nr);
- if (!bts) {
- vty_out(vty, "BTS Nr. %d could not be found.%s", bts_nr, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- if (!is_ipaccess_bts(bts)) {
- vty_out(vty, "This command only works for ipaccess.%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
-
- /* close all connections */
- if (strcmp(argv[1], "oml") == 0) {
- ipaccess_drop_oml(bts);
- } else if (strcmp(argv[1], "rsl") == 0) {
- /* close all rsl connections */
- llist_for_each_entry(trx, &bts->trx_list, list) {
- ipaccess_drop_rsl(trx);
- }
- } else {
- vty_out(vty, "Argument must be 'oml# or 'rsl'.%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN(restart_bts, restart_bts_cmd,
- "restart-bts <0-65535>",
- "Restart ip.access nanoBTS through OML\n"
- "BTS Number\n")
-{
- struct gsm_network *gsmnet;
- struct gsm_bts_trx *trx;
- struct gsm_bts *bts;
- unsigned int bts_nr;
-
- gsmnet = gsmnet_from_vty(vty);
-
- bts_nr = atoi(argv[0]);
- if (bts_nr >= gsmnet->num_bts) {
- vty_out(vty, "BTS number must be between 0 and %d. It was %d.%s",
- gsmnet->num_bts, bts_nr, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- bts = gsm_bts_num(gsmnet, bts_nr);
- if (!bts) {
- vty_out(vty, "BTS Nr. %d could not be found.%s", bts_nr, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- if (!is_ipaccess_bts(bts) || is_sysmobts_v2(bts)) {
- vty_out(vty, "This command only works for ipaccess nanoBTS.%s",
- VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- /* go from last TRX to c0 */
- llist_for_each_entry_reverse(trx, &bts->trx_list, list)
- abis_nm_ipaccess_restart(trx);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(smscb_cmd, smscb_cmd_cmd,
- "bts <0-255> smscb-command <1-4> HEXSTRING",
- "BTS related commands\n" "BTS Number\n"
- "SMS Cell Broadcast\n" "Last Valid Block\n"
- "Hex Encoded SMSCB message (up to 88 octets)\n")
-{
- struct gsm_bts *bts;
- int bts_nr = atoi(argv[0]);
- int last_block = atoi(argv[1]);
- struct rsl_ie_cb_cmd_type cb_cmd;
- uint8_t buf[88];
- int rc;
-
- bts = gsm_bts_num(gsmnet_from_vty(vty), bts_nr);
- if (!bts) {
- vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE);
- return CMD_WARNING;
- }
- rc = osmo_hexparse(argv[2], buf, sizeof(buf));
- if (rc < 0 || rc > sizeof(buf)) {
- vty_out(vty, "Error parsing HEXSTRING%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- cb_cmd.spare = 0;
- cb_cmd.def_bcast = 0;
- cb_cmd.command = RSL_CB_CMD_TYPE_NORMAL;
-
- switch (last_block) {
- case 1:
- cb_cmd.last_block = RSL_CB_CMD_LASTBLOCK_1;
- break;
- case 2:
- cb_cmd.last_block = RSL_CB_CMD_LASTBLOCK_2;
- break;
- case 3:
- cb_cmd.last_block = RSL_CB_CMD_LASTBLOCK_3;
- break;
- case 4:
- cb_cmd.last_block = RSL_CB_CMD_LASTBLOCK_4;
- break;
- }
-
- rsl_sms_cb_command(bts, RSL_CHAN_SDCCH4_ACCH, cb_cmd, buf, rc);
-
- return CMD_SUCCESS;
-}
-
-/* resolve a gsm_bts_trx_ts basd on the given numeric identifiers */
-static struct gsm_bts_trx_ts *vty_get_ts(struct vty *vty, const char *bts_str, const char *trx_str,
- const char *ts_str)
-{
- int bts_nr = atoi(bts_str);
- int trx_nr = atoi(trx_str);
- int ts_nr = atoi(ts_str);
- struct gsm_bts *bts;
- struct gsm_bts_trx *trx;
- struct gsm_bts_trx_ts *ts;
-
- bts = gsm_bts_num(gsmnet_from_vty(vty), bts_nr);
- if (!bts) {
- vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE);
- return NULL;
- }
-
- trx = gsm_bts_trx_num(bts, trx_nr);
- if (!trx) {
- vty_out(vty, "%% No such TRX (%d)%s", trx_nr, VTY_NEWLINE);
- return NULL;
- }
-
- ts = &trx->ts[ts_nr];
-
- return ts;
-}
-
-DEFUN(pdch_act, pdch_act_cmd,
- "bts <0-255> trx <0-255> timeslot <0-7> pdch (activate|deactivate)",
- "BTS related commands\n" "BTS Number\n" "Transceiver\n" "Transceiver Number\n"
- "TRX Timeslot\n" "Timeslot Number\n" "Packet Data Channel\n"
- "Activate Dynamic PDCH/TCH (-> PDCH mode)\n"
- "Deactivate Dynamic PDCH/TCH (-> TCH mode)\n")
-{
- struct gsm_bts_trx_ts *ts;
- int activate;
-
- ts = vty_get_ts(vty, argv[0], argv[1], argv[2]);
- if (!ts)
- return CMD_WARNING;
-
- if (!is_ipaccess_bts(ts->trx->bts)) {
- vty_out(vty, "%% This command only works for ipaccess BTS%s",
- VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- if (ts->pchan != GSM_PCHAN_TCH_F_PDCH) {
- vty_out(vty, "%% Timeslot %u is not in dynamic TCH_F/PDCH "
- "mode%s", ts->nr, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- if (!strcmp(argv[3], "activate"))
- activate = 1;
- else
- activate = 0;
-
- rsl_ipacc_pdch_activate(ts, activate);
-
- return CMD_SUCCESS;
-
-}
-
-/* determine the logical channel type based on the physical channel type */
-static int lchan_type_by_pchan(enum gsm_phys_chan_config pchan)
-{
- switch (pchan) {
- case GSM_PCHAN_TCH_F:
- return GSM_LCHAN_TCH_F;
- case GSM_PCHAN_TCH_H:
- return GSM_LCHAN_TCH_H;
- case GSM_PCHAN_SDCCH8_SACCH8C:
- case GSM_PCHAN_SDCCH8_SACCH8C_CBCH:
- case GSM_PCHAN_CCCH_SDCCH4:
- case GSM_PCHAN_CCCH_SDCCH4_CBCH:
- return GSM_LCHAN_SDCCH;
- default:
- return -1;
- }
-}
-
-/* configure the lchan for a single AMR mode (as specified) */
-static int lchan_set_single_amr_mode(struct gsm_lchan *lchan, uint8_t amr_mode)
-{
- struct amr_multirate_conf mr;
- struct gsm48_multi_rate_conf *mr_conf;
- mr_conf = (struct gsm48_multi_rate_conf *) &mr.gsm48_ie;
-
- if (amr_mode > 7)
- return -1;
-
- memset(&mr, 0, sizeof(mr));
- mr_conf->ver = 1;
- /* bit-mask of supported modes, only one bit is set. Reflects
- * Figure 10.5.2.47a where there are no thershold and only a
- * single mode */
- mr.gsm48_ie[1] = 1 << amr_mode;
-
- mr.ms_mode[0].mode = amr_mode;
- mr.bts_mode[0].mode = amr_mode;
-
- /* encode this configuration into the lchan for both uplink and
- * downlink direction */
- gsm48_multirate_config(lchan->mr_ms_lv, &mr, mr.ms_mode);
- gsm48_multirate_config(lchan->mr_bts_lv, &mr, mr.bts_mode);
-
- return 0;
-}
-
-/* Debug/Measurement command to activate a given logical channel
- * manually in a given mode/codec. This is useful for receiver
- * performance testing (FER/RBER/...) */
-DEFUN(lchan_act, lchan_act_cmd,
- "bts <0-255> trx <0-255> timeslot <0-7> sub-slot <0-7> (activate|deactivate) (hr|fr|efr|amr) [<0-7>]",
- "BTS related commands\n" "BTS Number\n" "Transceiver\n" "Transceiver Number\n"
- "TRX Timeslot\n" "Timeslot Number\n" "Sub-Slot Number\n" "Sub-Slot Number\n"
- "Manual Channel Activation (e.g. for BER test)\n"
- "Manual Channel Deactivation (e.g. for BER test)\n"
- "Half-Rate v1\n" "Full-Rate\n" "Enhanced Full Rate\n" "Adaptive Multi-Rate\n" "AMR Mode\n")
-{
- struct gsm_bts_trx_ts *ts;
- struct gsm_lchan *lchan;
- int ss_nr = atoi(argv[3]);
- const char *act_str = argv[4];
- const char *codec_str = argv[5];
- int activate;
-
- ts = vty_get_ts(vty, argv[0], argv[1], argv[2]);
- if (!ts)
- return CMD_WARNING;
-
- lchan = &ts->lchan[ss_nr];
-
- if (!strcmp(act_str, "activate"))
- activate = 1;
- else
- activate = 0;
-
- if (ss_nr >= ts_subslots(ts)) {
- vty_out(vty, "%% subslot %d >= permitted %d for physical channel %s%s",
- ss_nr, ts_subslots(ts), gsm_pchan_name(ts->pchan), VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- if (activate) {
- int lchan_t;
- if (lchan->state != LCHAN_S_NONE) {
- vty_out(vty, "%% Cannot activate: Channel busy!%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
- lchan_t = lchan_type_by_pchan(ts->pchan);
- if (lchan_t < 0)
- return CMD_WARNING;
- /* configure the lchan */
- lchan->type = lchan_t;
- lchan->rsl_cmode = RSL_CMOD_SPD_SPEECH;
- if (!strcmp(codec_str, "hr") || !strcmp(codec_str, "fr"))
- lchan->tch_mode = GSM48_CMODE_SPEECH_V1;
- else if (!strcmp(codec_str, "efr"))
- lchan->tch_mode = GSM48_CMODE_SPEECH_EFR;
- else if (!strcmp(codec_str, "amr")) {
- int amr_mode;
- if (argc < 7) {
- vty_out(vty, "%% AMR requires specification of AMR mode%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
- amr_mode = atoi(argv[6]);
- lchan->tch_mode = GSM48_CMODE_SPEECH_AMR;
- lchan_set_single_amr_mode(lchan, amr_mode);
- }
- vty_out(vty, "%% activating lchan %s%s", gsm_lchan_name(lchan), VTY_NEWLINE);
- rsl_chan_activate_lchan(lchan, RSL_ACT_TYPE_INITIAL, 0);
- rsl_ipacc_crcx(lchan);
- } else {
- rsl_direct_rf_release(lchan);
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN(lchan_mdcx, lchan_mdcx_cmd,
- "bts <0-255> trx <0-255> timeslot <0-7> sub-slot <0-7> mdcx A.B.C.D <0-65535>",
- "BTS related commands\n" "BTS Number\n" "Transceiver\n" "Transceiver Number\n"
- "TRX Timeslot\n" "Timeslot Number\n" "Sub-Slot\n" "Sub-Slot Number\n"
- "Modify RTP Connection\n" "MGW IP Address\n" "MGW UDP Port\n")
-{
- struct gsm_bts_trx_ts *ts;
- struct gsm_lchan *lchan;
- int ss_nr = atoi(argv[3]);
- int port = atoi(argv[5]);
- struct in_addr ia;
- inet_aton(argv[4], &ia);
-
- ts = vty_get_ts(vty, argv[0], argv[1], argv[2]);
- if (!ts)
- return CMD_WARNING;
-
- lchan = &ts->lchan[ss_nr];
-
- if (ss_nr >= ts_subslots(ts)) {
- vty_out(vty, "%% subslot %d >= permitted %d for physical channel %s%s",
- ss_nr, ts_subslots(ts), gsm_pchan_name(ts->pchan), VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- vty_out(vty, "%% connecting RTP of %s to %s:%u%s", gsm_lchan_name(lchan),
- inet_ntoa(ia), port, VTY_NEWLINE);
- rsl_ipacc_mdcx(lchan, ntohl(ia.s_addr), port, 0);
- return CMD_SUCCESS;
-}
-extern int bsc_vty_init_extra(void);
-
-int bsc_vty_init(struct gsm_network *network)
-{
- cfg_ts_pchan_cmd.string =
- vty_cmd_string_from_valstr(tall_bsc_ctx,
- gsm_pchant_names,
- "phys_chan_config (", "|", ")",
- VTY_DO_LOWER);
- cfg_ts_pchan_cmd.doc =
- vty_cmd_string_from_valstr(tall_bsc_ctx,
- gsm_pchant_descs,
- "Physical Channel Combination\n",
- "\n", "", 0);
-
- cfg_bts_type_cmd.string =
- vty_cmd_string_from_valstr(tall_bsc_ctx,
- bts_type_names,
- "type (", "|", ")",
- VTY_DO_LOWER);
- cfg_bts_type_cmd.doc =
- vty_cmd_string_from_valstr(tall_bsc_ctx,
- bts_type_descs,
- "BTS Vendor/Type\n",
- "\n", "", 0);
-
- common_cs_vty_init(network, config_write_net);
-
- install_element_ve(&bsc_show_net_cmd);
- install_element_ve(&show_bts_cmd);
- install_element_ve(&show_trx_cmd);
- install_element_ve(&show_ts_cmd);
- install_element_ve(&show_lchan_cmd);
- install_element_ve(&show_lchan_summary_cmd);
-
- install_element_ve(&show_paging_cmd);
- install_element_ve(&show_paging_group_cmd);
-
- logging_vty_add_cmds(NULL);
- osmo_stats_vty_add_cmds();
-
- install_element(GSMNET_NODE, &cfg_net_neci_cmd);
- install_element(GSMNET_NODE, &cfg_net_handover_cmd);
- install_element(GSMNET_NODE, &cfg_net_ho_win_rxlev_avg_cmd);
- install_element(GSMNET_NODE, &cfg_net_ho_win_rxqual_avg_cmd);
- install_element(GSMNET_NODE, &cfg_net_ho_win_rxlev_avg_neigh_cmd);
- install_element(GSMNET_NODE, &cfg_net_ho_pwr_interval_cmd);
- install_element(GSMNET_NODE, &cfg_net_ho_pwr_hysteresis_cmd);
- install_element(GSMNET_NODE, &cfg_net_ho_max_distance_cmd);
- install_element(GSMNET_NODE, &cfg_net_T3101_cmd);
- install_element(GSMNET_NODE, &cfg_net_T3103_cmd);
- install_element(GSMNET_NODE, &cfg_net_T3105_cmd);
- install_element(GSMNET_NODE, &cfg_net_T3107_cmd);
- install_element(GSMNET_NODE, &cfg_net_T3109_cmd);
- install_element(GSMNET_NODE, &cfg_net_T3111_cmd);
- install_element(GSMNET_NODE, &cfg_net_T3113_cmd);
- install_element(GSMNET_NODE, &cfg_net_T3115_cmd);
- install_element(GSMNET_NODE, &cfg_net_T3117_cmd);
- install_element(GSMNET_NODE, &cfg_net_T3119_cmd);
- install_element(GSMNET_NODE, &cfg_net_T3122_cmd);
- install_element(GSMNET_NODE, &cfg_net_T3141_cmd);
- install_element(GSMNET_NODE, &cfg_net_dtx_cmd);
- install_element(GSMNET_NODE, &cfg_net_pag_any_tch_cmd);
-
- install_element(GSMNET_NODE, &cfg_bts_cmd);
- install_node(&bts_node, config_write_bts);
- vty_install_default(BTS_NODE);
- install_element(BTS_NODE, &cfg_bts_type_cmd);
- install_element(BTS_NODE, &cfg_description_cmd);
- install_element(BTS_NODE, &cfg_no_description_cmd);
- install_element(BTS_NODE, &cfg_bts_band_cmd);
- install_element(BTS_NODE, &cfg_bts_ci_cmd);
- install_element(BTS_NODE, &cfg_bts_dtxu_cmd);
- install_element(BTS_NODE, &cfg_bts_dtxd_cmd);
- install_element(BTS_NODE, &cfg_bts_no_dtxu_cmd);
- install_element(BTS_NODE, &cfg_bts_no_dtxd_cmd);
- install_element(BTS_NODE, &cfg_bts_lac_cmd);
- install_element(BTS_NODE, &cfg_bts_tsc_cmd);
- install_element(BTS_NODE, &cfg_bts_bsic_cmd);
- install_element(BTS_NODE, &cfg_bts_unit_id_cmd);
- install_element(BTS_NODE, &cfg_bts_rsl_ip_cmd);
- install_element(BTS_NODE, &cfg_bts_nokia_site_skip_reset_cmd);
- install_element(BTS_NODE, &cfg_bts_nokia_site_no_loc_rel_cnf_cmd);
- install_element(BTS_NODE, &cfg_bts_nokia_site_bts_reset_timer_cnf_cmd);
- install_element(BTS_NODE, &cfg_bts_stream_id_cmd);
- install_element(BTS_NODE, &cfg_bts_oml_e1_cmd);
- install_element(BTS_NODE, &cfg_bts_oml_e1_tei_cmd);
- install_element(BTS_NODE, &cfg_bts_challoc_cmd);
- install_element(BTS_NODE, &cfg_bts_rach_tx_integer_cmd);
- install_element(BTS_NODE, &cfg_bts_rach_max_trans_cmd);
- install_element(BTS_NODE, &cfg_bts_chan_desc_att_cmd);
- install_element(BTS_NODE, &cfg_bts_chan_desc_bs_pa_mfrms_cmd);
- install_element(BTS_NODE, &cfg_bts_chan_desc_bs_ag_blks_res_cmd);
- install_element(BTS_NODE, &cfg_bts_rach_nm_b_thresh_cmd);
- install_element(BTS_NODE, &cfg_bts_rach_nm_ldavg_cmd);
- install_element(BTS_NODE, &cfg_bts_cell_barred_cmd);
- install_element(BTS_NODE, &cfg_bts_rach_ec_allowed_cmd);
- install_element(BTS_NODE, &cfg_bts_rach_ac_class_cmd);
- install_element(BTS_NODE, &cfg_bts_ms_max_power_cmd);
- install_element(BTS_NODE, &cfg_bts_per_loc_upd_cmd);
- install_element(BTS_NODE, &cfg_bts_no_per_loc_upd_cmd);
- install_element(BTS_NODE, &cfg_bts_cell_resel_hyst_cmd);
- install_element(BTS_NODE, &cfg_bts_rxlev_acc_min_cmd);
- install_element(BTS_NODE, &cfg_bts_cell_bar_qualify_cmd);
- install_element(BTS_NODE, &cfg_bts_cell_resel_ofs_cmd);
- install_element(BTS_NODE, &cfg_bts_temp_ofs_cmd);
- install_element(BTS_NODE, &cfg_bts_temp_ofs_inf_cmd);
- install_element(BTS_NODE, &cfg_bts_penalty_time_cmd);
- install_element(BTS_NODE, &cfg_bts_penalty_time_rsvd_cmd);
- install_element(BTS_NODE, &cfg_bts_radio_link_timeout_cmd);
- install_element(BTS_NODE, &cfg_bts_radio_link_timeout_inf_cmd);
- install_element(BTS_NODE, &cfg_bts_gprs_mode_cmd);
- install_element(BTS_NODE, &cfg_bts_gprs_11bit_rach_support_for_egprs_cmd);
- install_element(BTS_NODE, &cfg_bts_gprs_ns_timer_cmd);
- install_element(BTS_NODE, &cfg_bts_gprs_rac_cmd);
- install_element(BTS_NODE, &cfg_bts_gprs_net_ctrl_ord_cmd);
- install_element(BTS_NODE, &cfg_bts_gprs_ctrl_ack_cmd);
- install_element(BTS_NODE, &cfg_no_bts_gprs_ctrl_ack_cmd);
- install_element(BTS_NODE, &cfg_bts_gprs_bvci_cmd);
- install_element(BTS_NODE, &cfg_bts_gprs_cell_timer_cmd);
- install_element(BTS_NODE, &cfg_bts_gprs_nsei_cmd);
- install_element(BTS_NODE, &cfg_bts_gprs_nsvci_cmd);
- install_element(BTS_NODE, &cfg_bts_gprs_nsvc_lport_cmd);
- install_element(BTS_NODE, &cfg_bts_gprs_nsvc_rport_cmd);
- install_element(BTS_NODE, &cfg_bts_gprs_nsvc_rip_cmd);
- install_element(BTS_NODE, &cfg_bts_pag_free_cmd);
- install_element(BTS_NODE, &cfg_bts_si_mode_cmd);
- install_element(BTS_NODE, &cfg_bts_si_static_cmd);
- install_element(BTS_NODE, &cfg_bts_early_cm_cmd);
- install_element(BTS_NODE, &cfg_bts_neigh_mode_cmd);
- install_element(BTS_NODE, &cfg_bts_neigh_cmd);
- install_element(BTS_NODE, &cfg_bts_si5_neigh_cmd);
- install_element(BTS_NODE, &cfg_bts_si2quater_neigh_add_cmd);
- install_element(BTS_NODE, &cfg_bts_si2quater_neigh_del_cmd);
- install_element(BTS_NODE, &cfg_bts_si2quater_uarfcn_add_cmd);
- install_element(BTS_NODE, &cfg_bts_si2quater_uarfcn_del_cmd);
- install_element(BTS_NODE, &cfg_bts_excl_rf_lock_cmd);
- install_element(BTS_NODE, &cfg_bts_no_excl_rf_lock_cmd);
- install_element(BTS_NODE, &cfg_bts_force_comb_si_cmd);
- install_element(BTS_NODE, &cfg_bts_no_force_comb_si_cmd);
- install_element(BTS_NODE, &cfg_bts_codec0_cmd);
- install_element(BTS_NODE, &cfg_bts_codec1_cmd);
- install_element(BTS_NODE, &cfg_bts_codec2_cmd);
- install_element(BTS_NODE, &cfg_bts_codec3_cmd);
- install_element(BTS_NODE, &cfg_bts_codec4_cmd);
- install_element(BTS_NODE, &cfg_bts_depends_on_cmd);
- install_element(BTS_NODE, &cfg_bts_no_depends_on_cmd);
- install_element(BTS_NODE, &cfg_bts_amr_fr_modes1_cmd);
- install_element(BTS_NODE, &cfg_bts_amr_fr_modes2_cmd);
- install_element(BTS_NODE, &cfg_bts_amr_fr_modes3_cmd);
- install_element(BTS_NODE, &cfg_bts_amr_fr_modes4_cmd);
- install_element(BTS_NODE, &cfg_bts_amr_fr_thres1_cmd);
- install_element(BTS_NODE, &cfg_bts_amr_fr_thres2_cmd);
- install_element(BTS_NODE, &cfg_bts_amr_fr_thres3_cmd);
- install_element(BTS_NODE, &cfg_bts_amr_fr_hyst1_cmd);
- install_element(BTS_NODE, &cfg_bts_amr_fr_hyst2_cmd);
- install_element(BTS_NODE, &cfg_bts_amr_fr_hyst3_cmd);
- install_element(BTS_NODE, &cfg_bts_amr_fr_start_mode_cmd);
- install_element(BTS_NODE, &cfg_bts_amr_hr_modes1_cmd);
- install_element(BTS_NODE, &cfg_bts_amr_hr_modes2_cmd);
- install_element(BTS_NODE, &cfg_bts_amr_hr_modes3_cmd);
- install_element(BTS_NODE, &cfg_bts_amr_hr_modes4_cmd);
- install_element(BTS_NODE, &cfg_bts_amr_hr_thres1_cmd);
- install_element(BTS_NODE, &cfg_bts_amr_hr_thres2_cmd);
- install_element(BTS_NODE, &cfg_bts_amr_hr_thres3_cmd);
- install_element(BTS_NODE, &cfg_bts_amr_hr_hyst1_cmd);
- install_element(BTS_NODE, &cfg_bts_amr_hr_hyst2_cmd);
- install_element(BTS_NODE, &cfg_bts_amr_hr_hyst3_cmd);
- install_element(BTS_NODE, &cfg_bts_amr_hr_start_mode_cmd);
- install_element(BTS_NODE, &cfg_bts_pcu_sock_cmd);
-
- install_element(BTS_NODE, &cfg_trx_cmd);
- install_node(&trx_node, dummy_config_write);
- vty_install_default(TRX_NODE);
- install_element(TRX_NODE, &cfg_trx_arfcn_cmd);
- install_element(TRX_NODE, &cfg_description_cmd);
- install_element(TRX_NODE, &cfg_no_description_cmd);
- install_element(TRX_NODE, &cfg_trx_nominal_power_cmd);
- install_element(TRX_NODE, &cfg_trx_max_power_red_cmd);
- install_element(TRX_NODE, &cfg_trx_rsl_e1_cmd);
- install_element(TRX_NODE, &cfg_trx_rsl_e1_tei_cmd);
- install_element(TRX_NODE, &cfg_trx_rf_locked_cmd);
-
- install_element(TRX_NODE, &cfg_ts_cmd);
- install_node(&ts_node, dummy_config_write);
- vty_install_default(TS_NODE);
- install_element(TS_NODE, &cfg_ts_pchan_cmd);
- install_element(TS_NODE, &cfg_ts_pchan_compat_cmd);
- install_element(TS_NODE, &cfg_ts_tsc_cmd);
- install_element(TS_NODE, &cfg_ts_hopping_cmd);
- install_element(TS_NODE, &cfg_ts_hsn_cmd);
- install_element(TS_NODE, &cfg_ts_maio_cmd);
- install_element(TS_NODE, &cfg_ts_arfcn_add_cmd);
- install_element(TS_NODE, &cfg_ts_arfcn_del_cmd);
- install_element(TS_NODE, &cfg_ts_e1_subslot_cmd);
-
- install_element(ENABLE_NODE, &drop_bts_cmd);
- install_element(ENABLE_NODE, &restart_bts_cmd);
- install_element(ENABLE_NODE, &pdch_act_cmd);
- install_element(ENABLE_NODE, &lchan_act_cmd);
- install_element(ENABLE_NODE, &lchan_mdcx_cmd);
- install_element(ENABLE_NODE, &smscb_cmd_cmd);
-
- abis_nm_vty_init();
- abis_om2k_vty_init();
- e1inp_vty_init();
- osmo_fsm_vty_add_cmds();
-
- bsc_vty_init_extra();
-
- return 0;
-}
diff --git a/openbsc/src/libbsc/bts_ericsson_rbs2000.c b/openbsc/src/libbsc/bts_ericsson_rbs2000.c
deleted file mode 100644
index 99da4e75f..000000000
--- a/openbsc/src/libbsc/bts_ericsson_rbs2000.c
+++ /dev/null
@@ -1,204 +0,0 @@
-/* Ericsson RBS-2xxx specific code */
-
-/* (C) 2011 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/tlv.h>
-
-#include <openbsc/debug.h>
-#include <openbsc/gsm_data.h>
-#include <openbsc/abis_om2000.h>
-#include <openbsc/abis_nm.h>
-#include <osmocom/abis/e1_input.h>
-#include <openbsc/signal.h>
-
-#include <osmocom/abis/lapd.h>
-
-static void bootstrap_om_bts(struct gsm_bts *bts)
-{
- LOGP(DNM, LOGL_NOTICE, "bootstrapping OML for BTS %u\n", bts->nr);
-
- /* FIXME: this is global init, not bootstrapping */
- abis_om2k_bts_init(bts);
- abis_om2k_trx_init(bts->c0);
-
- /* TODO: Should we wait for a Failure report? */
- om2k_bts_fsm_start(bts);
-}
-
-static void bootstrap_om_trx(struct gsm_bts_trx *trx)
-{
- LOGP(DNM, LOGL_NOTICE, "bootstrapping OML for TRX %u/%u\n",
- trx->bts->nr, trx->nr);
- /* FIXME */
-}
-
-static int shutdown_om(struct gsm_bts *bts)
-{
- /* FIXME */
- return 0;
-}
-
-
-/* Tell LAPD to start start the SAP (send SABM requests) for all signalling
- * timeslots in this line */
-static void start_sabm_in_line(struct e1inp_line *line, int start)
-{
- struct e1inp_sign_link *link;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(line->ts); i++) {
- struct e1inp_ts *ts = &line->ts[i];
-
- if (ts->type != E1INP_TS_TYPE_SIGN)
- continue;
-
- llist_for_each_entry(link, &ts->sign.sign_links, list) {
- if (!ts->lapd)
- continue;
- lapd_instance_set_profile(ts->lapd,
- &lapd_profile_abis_ericsson);
-
- if (start)
- lapd_sap_start(ts->lapd, link->tei, link->sapi);
- else
- lapd_sap_stop(ts->lapd, link->tei, link->sapi);
- }
- }
-}
-
-/* Callback function to be called every time we receive a signal from INPUT */
-static int gbl_sig_cb(unsigned int subsys, unsigned int signal,
- void *handler_data, void *signal_data)
-{
- struct gsm_bts *bts;
-
- if (subsys != SS_L_GLOBAL)
- return 0;
-
- switch (signal) {
- case S_GLOBAL_BTS_CLOSE_OM:
- bts = signal_data;
- if (bts->type == GSM_BTS_TYPE_RBS2000)
- shutdown_om(signal_data);
- break;
- }
-
- return 0;
-}
-
-/* Callback function to be called every time we receive a signal from INPUT */
-static int inp_sig_cb(unsigned int subsys, unsigned int signal,
- void *handler_data, void *signal_data)
-{
- struct input_signal_data *isd = signal_data;
- struct e1inp_ts *e1i_ts;
-
- if (subsys != SS_L_INPUT)
- return 0;
-
- LOGP(DNM, LOGL_DEBUG, "%s(): Input signal '%s' received\n", __func__,
- get_value_string(e1inp_signal_names, signal));
- switch (signal) {
- case S_L_INP_TEI_UP:
- switch (isd->link_type) {
- case E1INP_SIGN_OML:
- if (isd->trx->bts->type != GSM_BTS_TYPE_RBS2000)
- break;
- if (isd->tei == isd->trx->bts->oml_tei)
- bootstrap_om_bts(isd->trx->bts);
- else
- bootstrap_om_trx(isd->trx);
- break;
- }
- break;
- case S_L_INP_TEI_DN:
- if (isd->trx->bts->type != GSM_BTS_TYPE_RBS2000)
- break;
- LOGP(DNM, LOGL_NOTICE, "Line-%u TS-%u TEI-%u SAPI-%u: Link "
- "Lost for Ericsson RBS2000. Re-starting DL Establishment\n",
- isd->line->num, isd->ts_nr, isd->tei, isd->sapi);
- /* Some datalink for a given TEI/SAPI went down, try to re-start it */
- e1i_ts = &isd->line->ts[isd->ts_nr-1];
- OSMO_ASSERT(e1i_ts->type == E1INP_TS_TYPE_SIGN);
- lapd_sap_start(e1i_ts->lapd, isd->tei, isd->sapi);
- break;
- case S_L_INP_LINE_INIT:
- case S_L_INP_LINE_NOALARM:
- if (strcasecmp(isd->line->driver->name, "DAHDI")
- && strcasecmp(isd->line->driver->name, "MISDN_LAPD")
- && strcasecmp(isd->line->driver->name, "UNIXSOCKET"))
- break;
- start_sabm_in_line(isd->line, 1);
- break;
- case S_L_INP_LINE_ALARM:
- if (strcasecmp(isd->line->driver->name, "DAHDI")
- && strcasecmp(isd->line->driver->name, "MISDN_LAPD")
- && strcasecmp(isd->line->driver->name, "UNIXSOCKET"))
- break;
- start_sabm_in_line(isd->line, 0);
- break;
- }
-
- return 0;
-}
-
-static void config_write_bts(struct vty *vty, struct gsm_bts *bts)
-{
- abis_om2k_config_write_bts(vty, bts);
-}
-
-static int bts_model_rbs2k_start(struct gsm_network *net);
-
-static void bts_model_rbs2k_e1line_bind_ops(struct e1inp_line *line)
-{
- e1inp_line_bind_ops(line, &bts_isdn_e1inp_line_ops);
-}
-
-static struct gsm_bts_model model_rbs2k = {
- .type = GSM_BTS_TYPE_RBS2000,
- .name = "rbs2000",
- .start = bts_model_rbs2k_start,
- .oml_rcvmsg = &abis_om2k_rcvmsg,
- .config_write_bts = &config_write_bts,
- .e1line_bind_ops = &bts_model_rbs2k_e1line_bind_ops,
-};
-
-static int bts_model_rbs2k_start(struct gsm_network *net)
-{
- model_rbs2k.features.data = &model_rbs2k._features_data[0];
- model_rbs2k.features.data_len = sizeof(model_rbs2k._features_data);
-
- gsm_btsmodel_set_feature(&model_rbs2k, BTS_FEAT_GPRS);
- gsm_btsmodel_set_feature(&model_rbs2k, BTS_FEAT_EGPRS);
- gsm_btsmodel_set_feature(&model_rbs2k, BTS_FEAT_HOPPING);
- gsm_btsmodel_set_feature(&model_rbs2k, BTS_FEAT_HSCSD);
- gsm_btsmodel_set_feature(&model_rbs2k, BTS_FEAT_MULTI_TSC);
-
- osmo_signal_register_handler(SS_L_INPUT, inp_sig_cb, NULL);
- osmo_signal_register_handler(SS_L_GLOBAL, gbl_sig_cb, NULL);
-
- return 0;
-}
-
-int bts_model_rbs2k_init(void)
-{
- return gsm_bts_model_register(&model_rbs2k);
-}
diff --git a/openbsc/src/libbsc/bts_init.c b/openbsc/src/libbsc/bts_init.c
deleted file mode 100644
index d6b152a79..000000000
--- a/openbsc/src/libbsc/bts_init.c
+++ /dev/null
@@ -1,30 +0,0 @@
-/* (C) 2011 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/bss.h>
-
-int bts_init(void)
-{
- bts_model_bs11_init();
- bts_model_rbs2k_init();
- bts_model_nanobts_init();
- bts_model_nokia_site_init();
- bts_model_sysmobts_init();
- /* Your new BTS here. */
- return 0;
-}
diff --git a/openbsc/src/libbsc/bts_ipaccess_nanobts.c b/openbsc/src/libbsc/bts_ipaccess_nanobts.c
deleted file mode 100644
index a1bde778f..000000000
--- a/openbsc/src/libbsc/bts_ipaccess_nanobts.c
+++ /dev/null
@@ -1,520 +0,0 @@
-/* ip.access nanoBTS specific code */
-
-/* (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 <arpa/inet.h>
-
-#include <osmocom/gsm/tlv.h>
-
-#include <openbsc/gsm_data.h>
-#include <openbsc/signal.h>
-#include <openbsc/abis_nm.h>
-#include <osmocom/abis/e1_input.h>
-#include <osmocom/gsm/tlv.h>
-#include <osmocom/core/msgb.h>
-#include <osmocom/core/talloc.h>
-#include <openbsc/gsm_data.h>
-#include <openbsc/abis_nm.h>
-#include <openbsc/abis_rsl.h>
-#include <openbsc/debug.h>
-#include <osmocom/abis/subchan_demux.h>
-#include <osmocom/gsm/ipa.h>
-#include <osmocom/abis/ipaccess.h>
-#include <osmocom/core/logging.h>
-#include <openbsc/ipaccess.h>
-#include <openbsc/bts_ipaccess_nanobts_omlattr.h>
-
-extern struct gsm_network *bsc_gsmnet;
-
-static int bts_model_nanobts_start(struct gsm_network *net);
-static void bts_model_nanobts_e1line_bind_ops(struct e1inp_line *line);
-
-struct gsm_bts_model bts_model_nanobts = {
- .type = GSM_BTS_TYPE_NANOBTS,
- .name = "nanobts",
- .start = bts_model_nanobts_start,
- .oml_rcvmsg = &abis_nm_rcvmsg,
- .e1line_bind_ops = bts_model_nanobts_e1line_bind_ops,
- .nm_att_tlvdef = {
- .def = {
- /* ip.access specifics */
- [NM_ATT_IPACC_DST_IP] = { TLV_TYPE_FIXED, 4 },
- [NM_ATT_IPACC_DST_IP_PORT] = { TLV_TYPE_FIXED, 2 },
- [NM_ATT_IPACC_STREAM_ID] = { TLV_TYPE_TV, },
- [NM_ATT_IPACC_SEC_OML_CFG] = { TLV_TYPE_FIXED, 6 },
- [NM_ATT_IPACC_IP_IF_CFG] = { TLV_TYPE_FIXED, 8 },
- [NM_ATT_IPACC_IP_GW_CFG] = { TLV_TYPE_FIXED, 12 },
- [NM_ATT_IPACC_IN_SERV_TIME] = { TLV_TYPE_FIXED, 4 },
- [NM_ATT_IPACC_LOCATION] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_PAGING_CFG] = { TLV_TYPE_FIXED, 2 },
- [NM_ATT_IPACC_UNIT_ID] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_UNIT_NAME] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_SNMP_CFG] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_PRIM_OML_CFG_LIST] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_NV_FLAGS] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_FREQ_CTRL] = { TLV_TYPE_FIXED, 2 },
- [NM_ATT_IPACC_PRIM_OML_FB_TOUT] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_CUR_SW_CFG] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_TIMING_BUS] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_CGI] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_RAC] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_OBJ_VERSION] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_GPRS_PAGING_CFG]= { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_NSEI] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_BVCI] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_NSVCI] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_NS_CFG] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_BSSGP_CFG] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_NS_LINK_CFG] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_RLC_CFG] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_ALM_THRESH_LIST]= { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_MONIT_VAL_LIST] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_TIB_CONTROL] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_SUPP_FEATURES] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_CODING_SCHEMES] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_RLC_CFG_2] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_HEARTB_TOUT] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_UPTIME] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_RLC_CFG_3] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_SSL_CFG] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_SEC_POSSIBLE] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_IML_SSL_STATE] = { TLV_TYPE_TL16V },
- [NM_ATT_IPACC_REVOC_DATE] = { TLV_TYPE_TL16V },
- },
- },
-};
-
-
-/* Callback function to be called whenever we get a GSM 12.21 state change event */
-static int nm_statechg_event(int evt, struct nm_statechg_signal_data *nsd)
-{
- uint8_t obj_class = nsd->obj_class;
- void *obj = nsd->obj;
- struct gsm_nm_state *new_state = nsd->new_state;
-
- struct gsm_bts *bts;
- struct gsm_bts_trx *trx;
- struct gsm_bts_trx_ts *ts;
- struct gsm_bts_gprs_nsvc *nsvc;
-
- struct msgb *msgb;
-
- if (!is_ipaccess_bts(nsd->bts))
- return 0;
-
- /* This event-driven BTS setup is currently only required on nanoBTS */
-
- /* S_NM_STATECHG_ADM is called after we call chg_adm_state() and would create
- * endless loop */
- if (evt != S_NM_STATECHG_OPER)
- return 0;
-
- switch (obj_class) {
- case NM_OC_SITE_MANAGER:
- bts = container_of(obj, struct gsm_bts, site_mgr);
- if ((new_state->operational == NM_OPSTATE_ENABLED &&
- new_state->availability == NM_AVSTATE_OK) ||
- (new_state->operational == NM_OPSTATE_DISABLED &&
- new_state->availability == NM_AVSTATE_OFF_LINE))
- abis_nm_opstart(bts, obj_class, 0xff, 0xff, 0xff);
- break;
- case NM_OC_BTS:
- bts = obj;
- if (new_state->availability == NM_AVSTATE_DEPENDENCY) {
- msgb = nanobts_attr_bts_get(bts);
- abis_nm_set_bts_attr(bts, msgb->data, msgb->len);
- msgb_free(msgb);
- abis_nm_chg_adm_state(bts, obj_class,
- bts->bts_nr, 0xff, 0xff,
- NM_STATE_UNLOCKED);
- abis_nm_opstart(bts, obj_class,
- bts->bts_nr, 0xff, 0xff);
- }
- break;
- case NM_OC_CHANNEL:
- ts = obj;
- trx = ts->trx;
- if (new_state->operational == NM_OPSTATE_DISABLED &&
- new_state->availability == NM_AVSTATE_DEPENDENCY) {
- enum abis_nm_chan_comb ccomb =
- abis_nm_chcomb4pchan(ts->pchan);
- if (abis_nm_set_channel_attr(ts, ccomb) == -EINVAL) {
- ipaccess_drop_oml(trx->bts);
- return -1;
- }
- abis_nm_chg_adm_state(trx->bts, obj_class,
- trx->bts->bts_nr, trx->nr, ts->nr,
- NM_STATE_UNLOCKED);
- abis_nm_opstart(trx->bts, obj_class,
- trx->bts->bts_nr, trx->nr, ts->nr);
- }
- if (new_state->operational == NM_OPSTATE_ENABLED
- && new_state->availability == NM_AVSTATE_OK)
- dyn_ts_init(ts);
- break;
- case NM_OC_RADIO_CARRIER:
- trx = obj;
- if (new_state->operational == NM_OPSTATE_DISABLED &&
- new_state->availability == NM_AVSTATE_OK)
- abis_nm_opstart(trx->bts, obj_class, trx->bts->bts_nr,
- trx->nr, 0xff);
- break;
- case NM_OC_GPRS_NSE:
- bts = container_of(obj, struct gsm_bts, gprs.nse);
- if (bts->gprs.mode == BTS_GPRS_NONE)
- break;
- if (new_state->availability == NM_AVSTATE_DEPENDENCY) {
- msgb = nanobts_attr_nse_get(bts);
- abis_nm_ipaccess_set_attr(bts, obj_class, bts->bts_nr,
- 0xff, 0xff, msgb->data,
- msgb->len);
- msgb_free(msgb);
- abis_nm_opstart(bts, obj_class, bts->bts_nr,
- 0xff, 0xff);
- }
- break;
- case NM_OC_GPRS_CELL:
- bts = container_of(obj, struct gsm_bts, gprs.cell);
- if (bts->gprs.mode == BTS_GPRS_NONE)
- break;
- if (new_state->availability == NM_AVSTATE_DEPENDENCY) {
- msgb = nanobts_attr_cell_get(bts);
- abis_nm_ipaccess_set_attr(bts, obj_class, bts->bts_nr,
- 0, 0xff, msgb->data,
- msgb->len);
- msgb_free(msgb);
- abis_nm_opstart(bts, obj_class, bts->bts_nr,
- 0, 0xff);
- abis_nm_chg_adm_state(bts, obj_class, bts->bts_nr,
- 0, 0xff, NM_STATE_UNLOCKED);
- abis_nm_chg_adm_state(bts, NM_OC_GPRS_NSE, bts->bts_nr,
- 0xff, 0xff, NM_STATE_UNLOCKED);
- }
- break;
- case NM_OC_GPRS_NSVC:
- nsvc = obj;
- bts = nsvc->bts;
- if (bts->gprs.mode == BTS_GPRS_NONE)
- break;
- /* We skip NSVC1 since we only use NSVC0 */
- if (nsvc->id == 1)
- break;
- if ((new_state->availability == NM_AVSTATE_OFF_LINE) ||
- (new_state->availability == NM_AVSTATE_DEPENDENCY)) {
- msgb = nanobts_attr_nscv_get(bts);
- abis_nm_ipaccess_set_attr(bts, obj_class, bts->bts_nr,
- nsvc->id, 0xff,
- msgb->data, msgb->len);
- msgb_free(msgb);
- abis_nm_opstart(bts, obj_class, bts->bts_nr,
- nsvc->id, 0xff);
- abis_nm_chg_adm_state(bts, obj_class, bts->bts_nr,
- nsvc->id, 0xff,
- NM_STATE_UNLOCKED);
- }
- default:
- break;
- }
- return 0;
-}
-
-/* Callback function to be called every time we receive a 12.21 SW activated report */
-static int sw_activ_rep(struct msgb *mb)
-{
- struct abis_om_fom_hdr *foh = msgb_l3(mb);
- struct e1inp_sign_link *sign_link = mb->dst;
- struct gsm_bts *bts = sign_link->trx->bts;
- struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, foh->obj_inst.trx_nr);
-
- if (!trx)
- return -EINVAL;
-
- if (!is_ipaccess_bts(trx->bts))
- return 0;
-
- switch (foh->obj_class) {
- case NM_OC_BASEB_TRANSC:
- abis_nm_chg_adm_state(trx->bts, foh->obj_class,
- trx->bts->bts_nr, trx->nr, 0xff,
- NM_STATE_UNLOCKED);
- abis_nm_opstart(trx->bts, foh->obj_class,
- trx->bts->bts_nr, trx->nr, 0xff);
- /* TRX software is active, tell it to initiate RSL Link */
- abis_nm_ipaccess_rsl_connect(trx, trx->bts->ip_access.rsl_ip,
- 3003, trx->rsl_tei);
- break;
- case NM_OC_RADIO_CARRIER: {
- /*
- * Locking the radio carrier will make it go
- * offline again and we would come here. The
- * framework should determine that there was
- * no change and avoid recursion.
- *
- * This code is here to make sure that on start
- * a TRX remains locked.
- */
- int rc_state = trx->mo.nm_state.administrative;
- /* Patch ARFCN into radio attribute */
- struct msgb *msgb = nanobts_attr_radio_get(trx->bts, trx);
- abis_nm_set_radio_attr(trx, msgb->data, msgb->len);
- msgb_free(msgb);
- abis_nm_chg_adm_state(trx->bts, foh->obj_class,
- trx->bts->bts_nr, trx->nr, 0xff,
- rc_state);
- abis_nm_opstart(trx->bts, foh->obj_class, trx->bts->bts_nr,
- trx->nr, 0xff);
- break;
- }
- }
- return 0;
-}
-
-/* Callback function to be called every time we receive a signal from NM */
-static int bts_ipa_nm_sig_cb(unsigned int subsys, unsigned int signal,
- void *handler_data, void *signal_data)
-{
- if (subsys != SS_NM)
- return 0;
-
- switch (signal) {
- case S_NM_SW_ACTIV_REP:
- return sw_activ_rep(signal_data);
- case S_NM_STATECHG_OPER:
- case S_NM_STATECHG_ADM:
- return nm_statechg_event(signal, signal_data);
- default:
- break;
- }
- return 0;
-}
-
-static int bts_model_nanobts_start(struct gsm_network *net)
-{
- osmo_signal_unregister_handler(SS_NM, bts_ipa_nm_sig_cb, NULL);
- osmo_signal_register_handler(SS_NM, bts_ipa_nm_sig_cb, NULL);
- return 0;
-}
-
-int bts_model_nanobts_init(void)
-{
- bts_model_nanobts.features.data = &bts_model_nanobts._features_data[0];
- bts_model_nanobts.features.data_len =
- sizeof(bts_model_nanobts._features_data);
-
- gsm_btsmodel_set_feature(&bts_model_nanobts, BTS_FEAT_GPRS);
- gsm_btsmodel_set_feature(&bts_model_nanobts, BTS_FEAT_EGPRS);
- gsm_btsmodel_set_feature(&bts_model_nanobts, BTS_FEAT_MULTI_TSC);
-
- return gsm_bts_model_register(&bts_model_nanobts);
-}
-
-#define OML_UP 0x0001
-#define RSL_UP 0x0002
-
-static struct gsm_bts *
-find_bts_by_unitid(struct gsm_network *net, uint16_t site_id, uint16_t bts_id)
-{
- struct gsm_bts *bts;
-
- llist_for_each_entry(bts, &net->bts_list, list) {
- if (!is_ipaccess_bts(bts))
- continue;
-
- if (bts->ip_access.site_id == site_id &&
- bts->ip_access.bts_id == bts_id)
- return bts;
- }
- return NULL;
-}
-
-/* These are exported because they are used by the VTY interface. */
-void ipaccess_drop_rsl(struct gsm_bts_trx *trx)
-{
- if (!trx->rsl_link)
- return;
-
- e1inp_sign_link_destroy(trx->rsl_link);
- trx->rsl_link = NULL;
-}
-
-void ipaccess_drop_oml(struct gsm_bts *bts)
-{
- struct gsm_bts *rdep_bts;
- struct gsm_bts_trx *trx;
-
- if (!bts->oml_link)
- return;
-
- e1inp_sign_link_destroy(bts->oml_link);
- bts->oml_link = NULL;
-
- /* we have issues reconnecting RSL, drop everything. */
- llist_for_each_entry(trx, &bts->trx_list, list)
- ipaccess_drop_rsl(trx);
-
- bts->ip_access.flags = 0;
-
- /*
- * Go through the list and see if we are the depndency of a BTS
- * and then drop the BTS. This can lead to some recursion but it
- * should be fine in userspace.
- * The oml_link is serving as recursion anchor for us and
- * it is set to NULL some lines above.
- */
- llist_for_each_entry(rdep_bts, &bts->network->bts_list, list) {
- if (!bts_depend_is_depedency(rdep_bts, bts))
- continue;
- LOGP(DLINP, LOGL_NOTICE, "Dropping BTS(%u) due BTS(%u).\n",
- rdep_bts->nr, bts->nr);
- ipaccess_drop_oml(rdep_bts);
- }
-}
-
-/* This function is called once the OML/RSL link becomes up. */
-static struct e1inp_sign_link *
-ipaccess_sign_link_up(void *unit_data, struct e1inp_line *line,
- enum e1inp_sign_type type)
-{
- struct gsm_bts *bts;
- struct ipaccess_unit *dev = unit_data;
- struct e1inp_sign_link *sign_link = NULL;
-
- bts = find_bts_by_unitid(bsc_gsmnet, dev->site_id, dev->bts_id);
- if (!bts) {
- LOGP(DLINP, LOGL_ERROR, "Unable to find BTS configuration for "
- " %u/%u/%u, disconnecting\n", dev->site_id,
- dev->bts_id, dev->trx_id);
- return NULL;
- }
- DEBUGP(DLINP, "Identified BTS %u/%u/%u\n",
- dev->site_id, dev->bts_id, dev->trx_id);
-
- switch(type) {
- case E1INP_SIGN_OML:
- /* remove old OML signal link for this BTS. */
- ipaccess_drop_oml(bts);
-
- if (!bts_depend_check(bts)) {
- LOGP(DLINP, LOGL_NOTICE,
- "Dependency not full-filled for %u/%u/%u\n",
- dev->site_id, dev->bts_id, dev->trx_id);
- return NULL;
- }
-
- /* create new OML link. */
- sign_link = bts->oml_link =
- e1inp_sign_link_create(&line->ts[E1INP_SIGN_OML - 1],
- E1INP_SIGN_OML, bts->c0,
- bts->oml_tei, 0);
- break;
- case E1INP_SIGN_RSL: {
- struct e1inp_ts *ts;
- struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, dev->trx_id);
-
- /* no OML link set yet? give up. */
- if (!bts->oml_link || !trx)
- return NULL;
-
- /* remove old RSL link for this TRX. */
- ipaccess_drop_rsl(trx);
-
- /* set new RSL link for this TRX. */
- line = bts->oml_link->ts->line;
- ts = &line->ts[E1INP_SIGN_RSL + dev->trx_id - 1];
- e1inp_ts_config_sign(ts, line);
- sign_link = trx->rsl_link =
- e1inp_sign_link_create(ts, E1INP_SIGN_RSL,
- trx, trx->rsl_tei, 0);
- trx->rsl_link->ts->sign.delay = 0;
- break;
- }
- default:
- break;
- }
- return sign_link;
-}
-
-static void ipaccess_sign_link_down(struct e1inp_line *line)
-{
- /* No matter what link went down, we close both signal links. */
- struct e1inp_ts *ts = &line->ts[E1INP_SIGN_OML-1];
- struct e1inp_sign_link *link;
-
- llist_for_each_entry(link, &ts->sign.sign_links, list) {
- struct gsm_bts *bts = link->trx->bts;
-
- ipaccess_drop_oml(bts);
- /* Yes, we only use the first element of the list. */
- break;
- }
-}
-
-/* This function is called if we receive one OML/RSL message. */
-static int ipaccess_sign_link(struct msgb *msg)
-{
- int ret = 0;
- struct e1inp_sign_link *link = msg->dst;
- struct e1inp_ts *e1i_ts = link->ts;
-
- switch (link->type) {
- case E1INP_SIGN_RSL:
- if (!(link->trx->bts->ip_access.flags &
- (RSL_UP << link->trx->nr))) {
- e1inp_event(e1i_ts, S_L_INP_TEI_UP,
- link->tei, link->sapi);
- link->trx->bts->ip_access.flags |=
- (RSL_UP << link->trx->nr);
- }
- ret = abis_rsl_rcvmsg(msg);
- break;
- case E1INP_SIGN_OML:
- if (!(link->trx->bts->ip_access.flags & OML_UP)) {
- e1inp_event(e1i_ts, S_L_INP_TEI_UP,
- link->tei, link->sapi);
- link->trx->bts->ip_access.flags |= OML_UP;
- }
- ret = abis_nm_rcvmsg(msg);
- break;
- default:
- LOGP(DLINP, LOGL_ERROR, "Unknown signal link type %d\n",
- link->type);
- msgb_free(msg);
- break;
- }
- return ret;
-}
-
-/* not static, ipaccess-config needs it. */
-struct e1inp_line_ops ipaccess_e1inp_line_ops = {
- .cfg = {
- .ipa = {
- .addr = "0.0.0.0",
- .role = E1INP_LINE_R_BSC,
- },
- },
- .sign_link_up = ipaccess_sign_link_up,
- .sign_link_down = ipaccess_sign_link_down,
- .sign_link = ipaccess_sign_link,
-};
-
-static void bts_model_nanobts_e1line_bind_ops(struct e1inp_line *line)
-{
- e1inp_line_bind_ops(line, &ipaccess_e1inp_line_ops);
-}
diff --git a/openbsc/src/libbsc/bts_ipaccess_nanobts_omlattr.c b/openbsc/src/libbsc/bts_ipaccess_nanobts_omlattr.c
deleted file mode 100644
index 473e1caea..000000000
--- a/openbsc/src/libbsc/bts_ipaccess_nanobts_omlattr.c
+++ /dev/null
@@ -1,240 +0,0 @@
-/* ip.access nanoBTS specific code, OML attribute table generator */
-
-/* (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 <arpa/inet.h>
-#include <osmocom/core/msgb.h>
-#include <openbsc/gsm_data.h>
-#include <openbsc/abis_nm.h>
-
-static void patch_16(uint8_t *data, const uint16_t val)
-{
- memcpy(data, &val, sizeof(val));
-}
-
-static void patch_32(uint8_t *data, const uint32_t val)
-{
- memcpy(data, &val, sizeof(val));
-}
-
-struct msgb *nanobts_attr_bts_get(struct gsm_bts *bts)
-{
- struct msgb *msgb;
- uint8_t buf[256];
- int rlt;
- msgb = msgb_alloc(1024, "nanobts_attr_bts");
-
- memcpy(buf, "\x55\x5b\x61\x67\x6d\x73", 6);
- msgb_tv_fixed_put(msgb, NM_ATT_INTERF_BOUND, 6, buf);
-
- /* interference avg. period in numbers of SACCH multifr */
- msgb_tv_put(msgb, NM_ATT_INTAVE_PARAM, 0x06);
-
- rlt = gsm_bts_get_radio_link_timeout(bts);
- if (rlt == -1) {
- /* Osmocom extension: Use infinite radio link timeout */
- buf[0] = 0xFF;
- buf[1] = 0x00;
- } else {
- /* conn fail based on SACCH error rate */
- buf[0] = 0x01;
- buf[1] = rlt;
- }
- msgb_tl16v_put(msgb, NM_ATT_CONN_FAIL_CRIT, 2, buf);
-
- memcpy(buf, "\x1e\x24\x24\xa8\x34\x21\xa8", 7);
- msgb_tv_fixed_put(msgb, NM_ATT_T200, 7, buf);
-
- msgb_tv_put(msgb, NM_ATT_MAX_TA, 0x3f);
-
- /* seconds */
- memcpy(buf, "\x00\x01\x0a", 3);
- msgb_tv_fixed_put(msgb, NM_ATT_OVERL_PERIOD, 3, buf);
-
- /* percent */
- msgb_tv_put(msgb, NM_ATT_CCCH_L_T, 10);
-
- /* seconds */
- msgb_tv_put(msgb, NM_ATT_CCCH_L_I_P, 1);
-
- /* busy threshold in - dBm */
- buf[0] = 10;
- if (bts->rach_b_thresh != -1)
- buf[0] = bts->rach_b_thresh & 0xff;
- msgb_tv_put(msgb, NM_ATT_RACH_B_THRESH, buf[0]);
-
- /* rach load averaging 1000 slots */
- buf[0] = 0x03;
- buf[1] = 0xe8;
- if (bts->rach_ldavg_slots != -1) {
- buf[0] = (bts->rach_ldavg_slots >> 8) & 0x0f;
- buf[1] = bts->rach_ldavg_slots & 0xff;
- }
- msgb_tv_fixed_put(msgb, NM_ATT_LDAVG_SLOTS, 2, buf);
-
- /* miliseconds */
- msgb_tv_put(msgb, NM_ATT_BTS_AIR_TIMER, 128);
-
- /* 10 retransmissions of physical config */
- msgb_tv_put(msgb, NM_ATT_NY1, 10);
-
- buf[0] = (bts->c0->arfcn >> 8) & 0x0f;
- buf[1] = bts->c0->arfcn & 0xff;
- msgb_tv_fixed_put(msgb, NM_ATT_BCCH_ARFCN, 2, buf);
-
- msgb_tv_put(msgb, NM_ATT_BSIC, bts->bsic);
-
- abis_nm_ipaccess_cgi(buf, bts);
- msgb_tl16v_put(msgb, NM_ATT_IPACC_CGI, 7, buf);
-
- return msgb;
-}
-
-struct msgb *nanobts_attr_nse_get(struct gsm_bts *bts)
-{
- struct msgb *msgb;
- uint8_t buf[256];
- msgb = msgb_alloc(1024, "nanobts_attr_bts");
-
- /* NSEI 925 */
- buf[0] = bts->gprs.nse.nsei >> 8;
- buf[1] = bts->gprs.nse.nsei & 0xff;
- msgb_tl16v_put(msgb, NM_ATT_IPACC_NSEI, 2, buf);
-
- /* all timers in seconds */
- OSMO_ASSERT(ARRAY_SIZE(bts->gprs.nse.timer) < sizeof(buf));
- memcpy(buf, bts->gprs.nse.timer, ARRAY_SIZE(bts->gprs.nse.timer));
- msgb_tl16v_put(msgb, NM_ATT_IPACC_NS_CFG, 7, buf);
-
- /* all timers in seconds */
- buf[0] = 3; /* blockimg timer (T1) */
- buf[1] = 3; /* blocking retries */
- buf[2] = 3; /* unblocking retries */
- buf[3] = 3; /* reset timer (T2) */
- buf[4] = 3; /* reset retries */
- buf[5] = 10; /* suspend timer (T3) in 100ms */
- buf[6] = 3; /* suspend retries */
- buf[7] = 10; /* resume timer (T4) in 100ms */
- buf[8] = 3; /* resume retries */
- buf[9] = 10; /* capability update timer (T5) */
- buf[10] = 3; /* capability update retries */
-
- OSMO_ASSERT(ARRAY_SIZE(bts->gprs.cell.timer) < sizeof(buf));
- memcpy(buf, bts->gprs.cell.timer, ARRAY_SIZE(bts->gprs.cell.timer));
- msgb_tl16v_put(msgb, NM_ATT_IPACC_BSSGP_CFG, 11, buf);
-
- return msgb;
-}
-
-struct msgb *nanobts_attr_cell_get(struct gsm_bts *bts)
-{
- struct msgb *msgb;
- uint8_t buf[256];
- msgb = msgb_alloc(1024, "nanobts_attr_bts");
-
- /* routing area code */
- buf[0] = bts->gprs.rac;
- msgb_tl16v_put(msgb, NM_ATT_IPACC_RAC, 1, buf);
-
- buf[0] = 5; /* repeat time (50ms) */
- buf[1] = 3; /* repeat count */
- msgb_tl16v_put(msgb, NM_ATT_IPACC_GPRS_PAGING_CFG, 2, buf);
-
- /* BVCI 925 */
- buf[0] = bts->gprs.cell.bvci >> 8;
- buf[1] = bts->gprs.cell.bvci & 0xff;
- msgb_tl16v_put(msgb, NM_ATT_IPACC_BVCI, 2, buf);
-
- /* all timers in seconds, unless otherwise stated */
- buf[0] = 20; /* T3142 */
- buf[1] = 5; /* T3169 */
- buf[2] = 5; /* T3191 */
- buf[3] = 160; /* T3193 (units of 10ms) */
- buf[4] = 5; /* T3195 */
- buf[5] = 10; /* N3101 */
- buf[6] = 4; /* N3103 */
- buf[7] = 8; /* N3105 */
- buf[8] = 15; /* RLC CV countdown */
- msgb_tl16v_put(msgb, NM_ATT_IPACC_RLC_CFG, 9, buf);
-
- if (bts->gprs.mode == BTS_GPRS_EGPRS) {
- buf[0] = 0x8f;
- buf[1] = 0xff;
- } else {
- buf[0] = 0x0f;
- buf[1] = 0x00;
- }
- msgb_tl16v_put(msgb, NM_ATT_IPACC_CODING_SCHEMES, 2, buf);
-
- buf[0] = 0; /* T downlink TBF extension (0..500, high byte) */
- buf[1] = 250; /* T downlink TBF extension (0..500, low byte) */
- buf[2] = 0; /* T uplink TBF extension (0..500, high byte) */
- buf[3] = 250; /* T uplink TBF extension (0..500, low byte) */
- buf[4] = 2; /* CS2 */
- msgb_tl16v_put(msgb, NM_ATT_IPACC_RLC_CFG_2, 5, buf);
-
-#if 0
- /* EDGE model only, breaks older models.
- * Should inquire the BTS capabilities */
- buf[0] = 2; /* MCS2 */
- msgb_tl16v_put(msgb, NM_ATT_IPACC_RLC_CFG_3, 1, buf);
-#endif
-
- return msgb;
-}
-
-struct msgb *nanobts_attr_nscv_get(struct gsm_bts *bts)
-{
- struct msgb *msgb;
- uint8_t buf[256];
- msgb = msgb_alloc(1024, "nanobts_attr_bts");
-
- /* 925 */
- buf[0] = bts->gprs.nsvc[0].nsvci >> 8;
- buf[1] = bts->gprs.nsvc[0].nsvci & 0xff;
- msgb_tl16v_put(msgb, NM_ATT_IPACC_NSVCI, 2, buf);
-
- /* remote udp port */
- patch_16(&buf[0], htons(bts->gprs.nsvc[0].remote_port));
- /* remote ip address */
- patch_32(&buf[2], htonl(bts->gprs.nsvc[0].remote_ip));
- /* local udp port */
- patch_16(&buf[6], htons(bts->gprs.nsvc[0].local_port));
- msgb_tl16v_put(msgb, NM_ATT_IPACC_NS_LINK_CFG, 8, buf);
-
- return msgb;
-}
-
-struct msgb *nanobts_attr_radio_get(struct gsm_bts *bts,
- struct gsm_bts_trx *trx)
-{
- struct msgb *msgb;
- uint8_t buf[256];
- msgb = msgb_alloc(1024, "nanobts_attr_bts");
-
- /* number of -2dB reduction steps / Pn */
- msgb_tv_put(msgb, NM_ATT_RF_MAXPOWR_R, trx->max_power_red / 2);
-
- buf[0] = trx->arfcn >> 8;
- buf[1] = trx->arfcn & 0xff;
- msgb_tl16v_put(msgb, NM_ATT_ARFCN_LIST, 2, buf);
-
- return msgb;
-}
diff --git a/openbsc/src/libbsc/bts_nokia_site.c b/openbsc/src/libbsc/bts_nokia_site.c
deleted file mode 100644
index 3ca76c017..000000000
--- a/openbsc/src/libbsc/bts_nokia_site.c
+++ /dev/null
@@ -1,1739 +0,0 @@
-/* Nokia XXXsite family specific code */
-
-/* (C) 2011 by Dieter Spaar <spaar@mirider.augusta.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/>.
- *
- */
-
-/*
- TODO: Attention: There are some static variables used for states during
- configuration. Those variables have to be moved to a BTS specific context,
- otherwise there will most certainly be problems if more than one Nokia BTS
- is used.
-*/
-
-#include <time.h>
-
-#include <osmocom/gsm/tlv.h>
-
-#include <openbsc/debug.h>
-#include <openbsc/gsm_data.h>
-#include <openbsc/abis_nm.h>
-#include <osmocom/abis/e1_input.h>
-#include <openbsc/signal.h>
-
-#include <osmocom/core/timer.h>
-
-#include <osmocom/abis/lapd.h>
-
-/* TODO: put in a separate file ? */
-
-extern int abis_nm_sendmsg(struct gsm_bts *bts, struct msgb *msg);
-/* was static in system_information.c */
-extern int generate_cell_chan_list(uint8_t * chan_list, struct gsm_bts *bts);
-
-static void nokia_abis_nm_queue_send_next(struct gsm_bts *bts);
-static void reset_timer_cb(void *_bts);
-static int abis_nm_reset(struct gsm_bts *bts, uint16_t ref);
-static int dump_elements(uint8_t * data, int len) __attribute__((unused));
-
-static void bootstrap_om_bts(struct gsm_bts *bts)
-{
- LOGP(DNM, LOGL_NOTICE, "bootstrapping OML for BTS %u\n", bts->nr);
-
- if (!bts->nokia.skip_reset) {
- if (!bts->nokia.did_reset)
- abis_nm_reset(bts, 1);
- } else
- bts->nokia.did_reset = 1;
-}
-
-static void bootstrap_om_trx(struct gsm_bts_trx *trx)
-{
- LOGP(DNM, LOGL_NOTICE, "bootstrapping OML for TRX %u/%u\n",
- trx->bts->nr, trx->nr);
-}
-
-static int shutdown_om(struct gsm_bts *bts)
-{
- /* TODO !? */
- return 0;
-}
-
-#define SAPI_OML 62
-#define SAPI_RSL 0
-
-/*
-
- Tell LAPD to start start the SAP (send SABM requests) for all signalling
- timeslots in this line
-
- Attention: this has to be adapted for mISDN
-*/
-
-static void start_sabm_in_line(struct e1inp_line *line, int start, int sapi)
-{
- struct e1inp_sign_link *link;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(line->ts); i++) {
- struct e1inp_ts *ts = &line->ts[i];
-
- if (ts->type != E1INP_TS_TYPE_SIGN)
- continue;
-
- llist_for_each_entry(link, &ts->sign.sign_links, list) {
- if (sapi != -1 && link->sapi != sapi)
- continue;
-
-#if 0 /* debugging */
- printf("sap start/stop (%d): %d tei=%d sapi=%d\n",
- start, i + 1, link->tei, link->sapi);
-#endif
-
- if (start) {
- ts->lapd->profile.t200_sec = 1;
- ts->lapd->profile.t200_usec = 0;
- lapd_sap_start(ts->lapd, link->tei,
- link->sapi);
- } else
- lapd_sap_stop(ts->lapd, link->tei,
- link->sapi);
- }
- }
-}
-
-/* Callback function to be called every time we receive a signal from INPUT */
-static int gbl_sig_cb(unsigned int subsys, unsigned int signal,
- void *handler_data, void *signal_data)
-{
- struct gsm_bts *bts;
-
- if (subsys != SS_L_GLOBAL)
- return 0;
-
- switch (signal) {
- case S_GLOBAL_BTS_CLOSE_OM:
- bts = signal_data;
- if (bts->type == GSM_BTS_TYPE_NOKIA_SITE)
- shutdown_om(signal_data);
- break;
- }
-
- return 0;
-}
-
-/* Callback function to be called every time we receive a signal from INPUT */
-static int inp_sig_cb(unsigned int subsys, unsigned int signal,
- void *handler_data, void *signal_data)
-{
- struct input_signal_data *isd = signal_data;
-
- if (subsys != SS_L_INPUT)
- return 0;
-
- switch (signal) {
- case S_L_INP_LINE_INIT:
- start_sabm_in_line(isd->line, 1, SAPI_OML); /* start only OML */
- break;
- case S_L_INP_TEI_DN:
- break;
- case S_L_INP_TEI_UP:
- switch (isd->link_type) {
- case E1INP_SIGN_OML:
- if (isd->trx->bts->type != GSM_BTS_TYPE_NOKIA_SITE)
- break;
-
- if (isd->tei == isd->trx->bts->oml_tei)
- bootstrap_om_bts(isd->trx->bts);
- else
- bootstrap_om_trx(isd->trx);
- break;
- }
- break;
- case S_L_INP_TEI_UNKNOWN:
- /* We are receiving LAPD frames with one TEI that we do not
- * seem to know, likely that we (the BSC) stopped working
- * and lost our local states. However, the BTS is already
- * configured, we try to take over the RSL links. */
- start_sabm_in_line(isd->line, 1, SAPI_RSL);
- break;
- }
-
- return 0;
-}
-
-static void nm_statechg_evt(unsigned int signal,
- struct nm_statechg_signal_data *nsd)
-{
- if (nsd->bts->type != GSM_BTS_TYPE_NOKIA_SITE)
- return;
-}
-
-static int nm_sig_cb(unsigned int subsys, unsigned int signal,
- void *handler_data, void *signal_data)
-{
- if (subsys != SS_NM)
- return 0;
-
- switch (signal) {
- case S_NM_STATECHG_OPER:
- case S_NM_STATECHG_ADM:
- nm_statechg_evt(signal, signal_data);
- break;
- default:
- break;
- }
-
- return 0;
-}
-
-/* TODO: put in a separate file ? */
-
-static const struct value_string nokia_msgt_name[] = {
- { 0x80, "NOKIA_BTS_CONF_DATA" },
- { 0x81, "NOKIA_BTS_ACK" },
- { 0x82, "NOKIA_BTS_OMU_STARTED" },
- { 0x83, "NOKIA_BTS_START_DOWNLOAD_REQ" },
- { 0x84, "NOKIA_BTS_MF_REQ" },
- { 0x85, "NOKIA_BTS_AF_REQ" },
- { 0x86, "NOKIA_BTS_RESET_REQ" },
- { 0x87, "NOKIA_reserved" },
- { 0x88, "NOKIA_BTS_CONF_REQ" },
- { 0x89, "NOKIA_BTS_TEST_REQ" },
- { 0x8A, "NOKIA_BTS_TEST_REPORT" },
- { 0x8B, "NOKIA_reserved" },
- { 0x8C, "NOKIA_reserved" },
- { 0x8D, "NOKIA_reserved" },
- { 0x8E, "NOKIA_BTS_CONF_COMPL" },
- { 0x8F, "NOKIA_reserved" },
- { 0x90, "NOKIA_BTS_STM_TEST_REQ" },
- { 0x91, "NOKIA_BTS_STM_TEST_REPORT" },
- { 0x92, "NOKIA_BTS_TRANSMISSION_COMMAND" },
- { 0x93, "NOKIA_BTS_TRANSMISSION_ANSWER" },
- { 0x94, "NOKIA_BTS_HW_DB_UPLOAD_REQ" },
- { 0x95, "NOKIA_BTS_START_HW_DB_DOWNLOAD_REQ" },
- { 0x96, "NOKIA_BTS_HW_DB_SAVE_REQ" },
- { 0x97, "NOKIA_BTS_FLASH_ERASURE_REQ" },
- { 0x98, "NOKIA_BTS_HW_DB_DOWNLOAD_REQ" },
- { 0x99, "NOKIA_BTS_PWR_SUPPLY_CONTROL" },
- { 0x9A, "NOKIA_BTS_ATTRIBUTE_REQ" },
- { 0x9B, "NOKIA_BTS_ATTRIBUTE_REPORT" },
- { 0x9C, "NOKIA_BTS_HW_REQ" },
- { 0x9D, "NOKIA_BTS_HW_REPORT" },
- { 0x9E, "NOKIA_BTS_RTE_TEST_REQ" },
- { 0x9F, "NOKIA_BTS_RTE_TEST_REPORT" },
- { 0xA0, "NOKIA_BTS_HW_DB_VERIFICATION_REQ" },
- { 0xA1, "NOKIA_BTS_CLOCK_REQ" },
- { 0xA2, "NOKIA_AC_CIRCUIT_REQ_NACK" },
- { 0xA3, "NOKIA_AC_INTERRUPTED" },
- { 0xA4, "NOKIA_BTS_NEW_TRE_INFO" },
- { 0xA5, "NOKIA_AC_BSC_CIRCUITS_ALLOCATED" },
- { 0xA6, "NOKIA_BTS_TRE_POLL_LIST" },
- { 0xA7, "NOKIA_AC_CIRCUIT_REQ" },
- { 0xA8, "NOKIA_BTS_BLOCK_CTRL_REQ" },
- { 0xA9, "NOKIA_BTS_GSM_TIME_REQ" },
- { 0xAA, "NOKIA_BTS_GSM_TIME" },
- { 0xAB, "NOKIA_BTS_OUTPUT_CONTROL" },
- { 0xAC, "NOKIA_BTS_STATE_CHANGED" },
- { 0xAD, "NOKIA_BTS_SW_SAVE_REQ" },
- { 0xAE, "NOKIA_BTS_ALARM" },
- { 0xAF, "NOKIA_BTS_CHA_ADM_STATE" },
- { 0xB0, "NOKIA_AC_POOL_SIZE_REPORT" },
- { 0xB1, "NOKIA_AC_POOL_SIZE_INQUIRY" },
- { 0xB2, "NOKIA_BTS_COMMISS_TEST_COMPLETED" },
- { 0xB3, "NOKIA_BTS_COMMISS_TEST_REQ" },
- { 0xB4, "NOKIA_BTS_TRANSP_BTS_TO_BSC" },
- { 0xB5, "NOKIA_BTS_TRANSP_BSC_TO_BTS" },
- { 0xB6, "NOKIA_BTS_LCS_COMMAND" },
- { 0xB7, "NOKIA_BTS_LCS_ANSWER" },
- { 0xB8, "NOKIA_BTS_LMU_FN_OFFSET_COMMAND" },
- { 0xB9, "NOKIA_BTS_LMU_FN_OFFSET_ANSWER" },
- { 0, NULL }
-};
-
-static const char *get_msg_type_name_string(uint8_t msg_type)
-{
- return get_value_string(nokia_msgt_name, msg_type);
-}
-
-static const struct value_string nokia_element_name[] = {
- { 0x01, "Ny1" },
- { 0x02, "T3105_F" },
- { 0x03, "Interference band limits" },
- { 0x04, "Interference report timer in secs" },
- { 0x05, "Channel configuration per TS" },
- { 0x06, "BSIC" },
- { 0x07, "RACH report timer in secs" },
- { 0x08, "Hardware database status" },
- { 0x09, "BTS RX level" },
- { 0x0A, "ARFN" },
- { 0x0B, "STM antenna attenuation" },
- { 0x0C, "Cell allocation bitmap" },
- { 0x0D, "Radio definition per TS" },
- { 0x0E, "Frame number" },
- { 0x0F, "Antenna diversity" },
- { 0x10, "T3105_D" },
- { 0x11, "File format" },
- { 0x12, "Last File" },
- { 0x13, "BTS type" },
- { 0x14, "Erasure mode" },
- { 0x15, "Hopping mode" },
- { 0x16, "Floating TRX" },
- { 0x17, "Power supplies" },
- { 0x18, "Reset type" },
- { 0x19, "Averaging period" },
- { 0x1A, "RBER2" },
- { 0x1B, "LAC" },
- { 0x1C, "CI" },
- { 0x1D, "Failure parameters" },
- { 0x1E, "(RF max power reduction)" },
- { 0x1F, "Measured RX_SENS" },
- { 0x20, "Extended cell radius" },
- { 0x21, "reserved" },
- { 0x22, "Success-Failure" },
- { 0x23, "Ack-Nack" },
- { 0x24, "OMU test results" },
- { 0x25, "File identity" },
- { 0x26, "Generation and version code" },
- { 0x27, "SW description" },
- { 0x28, "BCCH LEV" },
- { 0x29, "Test type" },
- { 0x2A, "Subscriber number" },
- { 0x2B, "reserved" },
- { 0x2C, "HSN" },
- { 0x2D, "reserved" },
- { 0x2E, "MS RXLEV" },
- { 0x2F, "MS TXLEV" },
- { 0x30, "RXQUAL" },
- { 0x31, "RX SENS" },
- { 0x32, "Alarm block" },
- { 0x33, "Neighbouring BCCH levels" },
- { 0x34, "STM report type" },
- { 0x35, "MA" },
- { 0x36, "MAIO" },
- { 0x37, "H_FLAG" },
- { 0x38, "TCH_ARFN" },
- { 0x39, "Clock output" },
- { 0x3A, "Transmitted power" },
- { 0x3B, "Clock sync" },
- { 0x3C, "TMS protocol discriminator" },
- { 0x3D, "TMS protocol data" },
- { 0x3E, "FER" },
- { 0x3F, "SWR result" },
- { 0x40, "Object identity" },
- { 0x41, "STM RX Antenna Test" },
- { 0x42, "reserved" },
- { 0x43, "reserved" },
- { 0x44, "Object current state" },
- { 0x45, "reserved" },
- { 0x46, "FU channel configuration" },
- { 0x47, "reserved" },
- { 0x48, "ARFN of a CU" },
- { 0x49, "FU radio definition" },
- { 0x4A, "reserved" },
- { 0x4B, "Severity" },
- { 0x4C, "Diversity selection" },
- { 0x4D, "RX antenna test" },
- { 0x4E, "RX antenna supervision period" },
- { 0x4F, "RX antenna state" },
- { 0x50, "Sector configuration" },
- { 0x51, "Additional info" },
- { 0x52, "SWR parameters" },
- { 0x53, "HW inquiry mode" },
- { 0x54, "reserved" },
- { 0x55, "Availability status" },
- { 0x56, "reserved" },
- { 0x57, "EAC inputs" },
- { 0x58, "EAC outputs" },
- { 0x59, "reserved" },
- { 0x5A, "Position" },
- { 0x5B, "HW unit identity" },
- { 0x5C, "RF test signal attenuation" },
- { 0x5D, "Operational state" },
- { 0x5E, "Logical object identity" },
- { 0x5F, "reserved" },
- { 0x60, "BS_TXPWR_OM" },
- { 0x61, "Loop_Duration" },
- { 0x62, "LNA_Path_Selection" },
- { 0x63, "Serial number" },
- { 0x64, "HW version" },
- { 0x65, "Obj. identity and obj. state" },
- { 0x66, "reserved" },
- { 0x67, "EAC input definition" },
- { 0x68, "EAC id and text" },
- { 0x69, "HW unit status" },
- { 0x6A, "SW release version" },
- { 0x6B, "FW version" },
- { 0x6C, "Bit_Error_Ratio" },
- { 0x6D, "RXLEV_with_Attenuation" },
- { 0x6E, "RXLEV_without_Attenuation" },
- { 0x6F, "reserved" },
- { 0x70, "CU_Results" },
- { 0x71, "reserved" },
- { 0x72, "LNA_Path_Results" },
- { 0x73, "RTE Results" },
- { 0x74, "Real Time" },
- { 0x75, "RX diversity selection" },
- { 0x76, "EAC input config" },
- { 0x77, "Feature support" },
- { 0x78, "File version" },
- { 0x79, "Outputs" },
- { 0x7A, "FU parameters" },
- { 0x7B, "Diagnostic info" },
- { 0x7C, "FU BSIC" },
- { 0x7D, "TRX Configuration" },
- { 0x7E, "Download status" },
- { 0x7F, "RX difference limit" },
- { 0x80, "TRX HW capability" },
- { 0x81, "Common HW config" },
- { 0x82, "Autoconfiguration pool size" },
- { 0x83, "TRE diagnostic info" },
- { 0x84, "TRE object identity" },
- { 0x85, "New TRE Info" },
- { 0x86, "Acknowledgement period" },
- { 0x87, "Synchronization mode" },
- { 0x88, "reserved" },
- { 0x89, "Block Control Data" },
- { 0x8A, "SW load mode" },
- { 0x8B, "Recommended recovery action" },
- { 0x8C, "BSC BCF id" },
- { 0x8D, "Q1 baud rate" },
- { 0x8E, "Allocation status" },
- { 0x8F, "Functional entity number" },
- { 0x90, "Transmission delay" },
- { 0x91, "Loop Duration ms" },
- { 0x92, "Logical channel" },
- { 0x93, "Q1 address" },
- { 0x94, "Alarm detail" },
- { 0x95, "Cabinet type" },
- { 0x96, "HW unit existence" },
- { 0x97, "RF power parameters" },
- { 0x98, "Message scenario" },
- { 0x99, "HW unit max amount" },
- { 0x9A, "Master TRX" },
- { 0x9B, "Transparent data" },
- { 0x9C, "BSC topology info" },
- { 0x9D, "Air i/f modulation" },
- { 0x9E, "LCS Q1 command data" },
- { 0x9F, "Frame number offset" },
- { 0xA0, "Abis TSL" },
- { 0xA1, "Dynamic pool info" },
- { 0xA2, "LCS LLP data" },
- { 0xA3, "LCS Q1 answer data" },
- { 0xA4, "DFCA FU Radio Definition" },
- { 0xA5, "Antenna hopping" },
- { 0xA6, "Field record sequence number" },
- { 0xA7, "Timeslot offslot" },
- { 0xA8, "EPCR capability" },
- { 0xA9, "Connectsite optional element" },
- { 0xAA, "TSC" },
- { 0xAB, "Special TX Power Setting" },
- { 0xAC, "Optional sync settings" },
- { 0xFA, "Abis If parameters" },
- { 0, NULL }
-};
-
-static const char *get_element_name_string(uint16_t element)
-{
- return get_value_string(nokia_element_name, element);
-}
-
-static const struct value_string nokia_bts_types[] = {
- { 0x0a, "MetroSite GSM 900" },
- { 0x0b, "MetroSite GSM 1800" },
- { 0x0c, "MetroSite GSM 1900 (PCS)" },
- { 0x0d, "MetroSite GSM 900 & 1800" },
- { 0x0e, "InSite GSM 900" },
- { 0x0f, "InSite GSM 1800" },
- { 0x10, "InSite GSM 1900" },
- { 0x11, "UltraSite GSM 900" },
- { 0x12, "UltraSite GSM 1800" },
- { 0x13, "UltraSite GSM/US-TDMA 1900" },
- { 0x14, "UltraSite GSM 900 & 1800" },
- { 0x16, "UltraSite GSM/US-TDMA 850" },
- { 0x18, "MetroSite GSM/US-TDMA 850" },
- { 0x19, "UltraSite GSM 800/1900" },
- { 0, NULL }
-};
-
-static const char *get_bts_type_string(uint8_t type)
-{
- return get_value_string(nokia_bts_types, type);
-}
-
-static const struct value_string nokia_severity[] = {
- { 0, "indeterminate" },
- { 1, "critical" },
- { 2, "major" },
- { 3, "minor" },
- { 4, "warning" },
- { 0, NULL }
-};
-
-static const char *get_severity_string(uint8_t severity)
-{
- return get_value_string(nokia_severity, severity);
-}
-
-/* TODO: put in a separate file ? */
-
-/* some message IDs */
-
-#define NOKIA_MSG_CONF_DATA 128
-#define NOKIA_MSG_ACK 129
-#define NOKIA_MSG_OMU_STARTED 130
-#define NOKIA_MSG_START_DOWNLOAD_REQ 131
-#define NOKIA_MSG_MF_REQ 132
-#define NOKIA_MSG_RESET_REQ 134
-#define NOKIA_MSG_CONF_REQ 136
-#define NOKIA_MSG_CONF_COMPLETE 142
-#define NOKIA_MSG_BLOCK_CTRL_REQ 168
-#define NOKIA_MSG_STATE_CHANGED 172
-#define NOKIA_MSG_ALARM 174
-
-/* some element IDs */
-
-#define NOKIA_EI_BTS_TYPE 0x13
-#define NOKIA_EI_ACK 0x23
-#define NOKIA_EI_ADD_INFO 0x51
-#define NOKIA_EI_SEVERITY 0x4B
-#define NOKIA_EI_ALARM_DETAIL 0x94
-
-#define OM_ALLOC_SIZE 1024
-#define OM_HEADROOM_SIZE 128
-
-static uint8_t fu_config_template[] = {
- 0x7F, 0x7A, 0x39,
- /* ID = 0x7A (FU parameters) ## constructed ## */
- /* length = 57 */
- /* [3] */
-
- 0x5F, 0x40, 0x04,
- /* ID = 0x40 (Object identity) */
- /* length = 4 */
- /* [6] */
- 0x00, 0x07, 0x01, 0xFF,
-
- 0x41, 0x02,
- /* ID = 0x01 (Ny1) */
- /* length = 2 */
- /* [12] */
- 0x00, 0x05,
-
- 0x42, 0x02,
- /* ID = 0x02 (T3105_F) */
- /* length = 2 */
- /* [16] */
- 0x00, 0x28, /* FIXME: use net->T3105 */
-
- 0x50, 0x02,
- /* ID = 0x10 (T3105_D) */
- /* length = 2 */
- /* [20] */
- 0x00, 0x28, /* FIXME: use net->T3105 */
-
- 0x43, 0x05,
- /* ID = 0x03 (Interference band limits) */
- /* length = 5 */
- /* [24] */
- 0x0F, 0x1B, 0x27, 0x33, 0x3F,
-
- 0x44, 0x02,
- /* ID = 0x04 (Interference report timer in secs) */
- /* length = 2 */
- /* [31] */
- 0x00, 0x10,
-
- 0x47, 0x01,
- /* ID = 0x07 (RACH report timer in secs) */
- /* length = 1 */
- /* [35] */
- 0x1E,
-
- 0x4C, 0x10,
- /* ID = 0x0C (Cell allocation bitmap) ####### */
- /* length = 16 */
- /* [38] */
- 0x8F, 0xB1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-
- 0x59, 0x01,
- /* ID = 0x19 (Averaging period) */
- /* length = 1 */
- /* [56] */
- 0x01,
-
- 0x5E, 0x01,
- /* ID = 0x1E ((RF max power reduction)) */
- /* length = 1 */
- /* [59] */
- 0x00,
-
- 0x7F, 0x46, 0x11,
- /* ID = 0x46 (FU channel configuration) ## constructed ## */
- /* length = 17 */
- /* [63] */
-
- 0x5F, 0x40, 0x04,
- /* ID = 0x40 (Object identity) */
- /* length = 4 */
- /* [66] */
- 0x00, 0x07, 0x01, 0xFF,
-
- 0x45, 0x08,
- /* ID = 0x05 (Channel configuration per TS) */
- /* length = 8 */
- /* [72] */
- 0x01, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
-
- 0x7F, 0x65, 0x0B,
- /* ID = 0x65 (Obj. identity and obj. state) ## constructed ## */
- /* length = 11 */
- /* [83] */
-
- 0x5F, 0x40, 0x04,
- /* ID = 0x40 (Object identity) */
- /* length = 4 */
- /* [86] */
- 0x00, 0x04, 0x01, 0xFF,
-
- 0x5F, 0x44, 0x01,
- /* ID = 0x44 (Object current state) */
- /* length = 1 */
- /* [93] */
- 0x03,
-
- 0x7F, 0x7C, 0x0A,
- /* ID = 0x7C (FU BSIC) ## constructed ## */
- /* length = 10 */
- /* [97] */
-
- 0x5F, 0x40, 0x04,
- /* ID = 0x40 (Object identity) */
- /* length = 4 */
- /* [100] */
- 0x00, 0x07, 0x01, 0xFF,
-
- 0x46, 0x01,
- /* ID = 0x06 (BSIC) */
- /* length = 1 */
- /* [106] */
- 0x00,
-
- 0x7F, 0x48, 0x0B,
- /* ID = 0x48 (ARFN of a CU) ## constructed ## */
- /* length = 11 */
- /* [110] */
-
- 0x5F, 0x40, 0x04,
- /* ID = 0x40 (Object identity) */
- /* length = 4 */
- /* [113] */
- 0x00, 0x08, 0x01, 0xFF,
-
- 0x4A, 0x02,
- /* ID = 0x0A (ARFN) ####### */
- /* length = 2 */
- /* [119] */
- 0x03, 0x62,
-
- 0x7F, 0x49, 0x59,
- /* ID = 0x49 (FU radio definition) ## constructed ## */
- /* length = 89 */
- /* [124] */
-
- 0x5F, 0x40, 0x04,
- /* ID = 0x40 (Object identity) */
- /* length = 4 */
- /* [127] */
- 0x00, 0x07, 0x01, 0xFF,
-
- 0x4D, 0x50,
- /* ID = 0x0D (Radio definition per TS) ####### */
- /* length = 80 */
- /* [133] */
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MA */
- 0x03, 0x62, /* HSN, MAIO or ARFCN if no hopping */
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x03, 0x62,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x03, 0x62,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x03, 0x62,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x03, 0x62,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x03, 0x62,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x03, 0x62,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x03, 0x62,
-};
-
-/* TODO: put in a separate file ? */
-
-/*
- build the configuration for each TRX
-*/
-
-static int make_fu_config(struct gsm_bts_trx *trx, uint8_t id,
- uint8_t * fu_config, int *hopping)
-{
- int i;
-
- *hopping = 0;
-
- memcpy(fu_config, fu_config_template, sizeof(fu_config_template));
-
- /* set ID */
-
- fu_config[6 + 2] = id;
- fu_config[66 + 2] = id;
- fu_config[86 + 2] = id;
- fu_config[100 + 2] = id;
- fu_config[113 + 2] = id;
- fu_config[127 + 2] = id;
-
- /* set ARFCN */
-
- uint16_t arfcn = trx->arfcn;
-
- fu_config[119] = arfcn >> 8;
- fu_config[119 + 1] = arfcn & 0xFF;
-
- for (i = 0; i < ARRAY_SIZE(trx->ts); i++) {
- struct gsm_bts_trx_ts *ts = &trx->ts[i];
-
- if (ts->hopping.enabled) {
- /* reverse order */
- int j;
- for (j = 0; j < ts->hopping.ma_len; j++)
- fu_config[133 + (i * 10) + (7 - j)] =
- ts->hopping.ma_data[j];
- fu_config[133 + 8 + (i * 10)] = ts->hopping.hsn;
- fu_config[133 + 8 + 1 + (i * 10)] = ts->hopping.maio;
- *hopping = 1;
- } else {
- fu_config[133 + 8 + (i * 10)] = arfcn >> 8;
- fu_config[133 + 8 + 1 + (i * 10)] = arfcn & 0xFF;
- }
- }
-
- /* set BSIC */
-
- /*
- Attention: all TRX except the first one seem to get the TSC
- from the CHANNEL ACTIVATION command (in CHANNEL IDENTIFICATION,
- GSM 04.08 CHANNEL DESCRIPTION).
- There was a bug in rsl_chan_activate_lchan() setting this parameter.
- */
-
- uint8_t bsic = trx->bts->bsic;
-
- fu_config[106] = bsic;
-
- /* set CA */
-
- if (generate_cell_chan_list(&fu_config[38], trx->bts) != 0) {
- fprintf(stderr, "generate_cell_chan_list failed\n");
- return 0;
- }
-
- /* set channel configuration */
-
- for (i = 0; i < ARRAY_SIZE(trx->ts); i++) {
- struct gsm_bts_trx_ts *ts = &trx->ts[i];
- uint8_t chan_config;
-
- /*
- 0 = FCCH + SCH + BCCH + CCCH
- 1 = FCCH + SCH + BCCH + CCCH + SDCCH/4 + SACCH/4
- 2 = BCCH + CCCH (This combination is not used in any BTS)
- 3 = FCCH + SCH + BCCH + CCCH + SDCCH/4 with SDCCH2 used as CBCH
- 4 = SDCCH/8 + SACCH/8
- 5 = SDCCH/8 with SDCCH2 used as CBCH
- 6 = TCH/F + FACCH/F + SACCH/F
- 7 = E-RACH (Talk family)
- 9 = Dual rate (capability for TCH/F and TCH/H)
- 10 = reserved for BTS internal use
- 11 = PBCCH + PCCCH + PDTCH + PACCH + PTCCH (can be used in GPRS release 2).
- 0xFF = spare TS
- */
-
- if (ts->pchan == GSM_PCHAN_NONE)
- chan_config = 0xFF;
- else if (ts->pchan == GSM_PCHAN_CCCH)
- chan_config = 0;
- else if (ts->pchan == GSM_PCHAN_CCCH_SDCCH4)
- chan_config = 1;
- else if (ts->pchan == GSM_PCHAN_TCH_F)
- chan_config = 6; /* 9 should work too */
- else if (ts->pchan == GSM_PCHAN_TCH_H)
- chan_config = 9;
- else if (ts->pchan == GSM_PCHAN_SDCCH8_SACCH8C)
- chan_config = 4;
- else if (ts->pchan == GSM_PCHAN_PDCH)
- chan_config = 11;
- else {
- fprintf(stderr,
- "unsupported channel config %d for timeslot %d\n",
- ts->pchan, i);
- return 0;
- }
-
- fu_config[72 + i] = chan_config;
- }
- return sizeof(fu_config_template);
-}
-
-/* TODO: put in a separate file ? */
-
-static uint8_t bts_config_1[] = {
- 0x4E, 0x02,
- /* ID = 0x0E (Frame number) */
- /* length = 2 */
- /* [2] */
- 0xFF, 0xFF,
-
- 0x5F, 0x4E, 0x02,
- /* ID = 0x4E (RX antenna supervision period) */
- /* length = 2 */
- /* [7] */
- 0xFF, 0xFF,
-
- 0x5F, 0x50, 0x02,
- /* ID = 0x50 (Sector configuration) */
- /* length = 2 */
- /* [12] */
- 0x01, 0x01,
-};
-
-static uint8_t bts_config_2[] = {
- 0x55, 0x02,
- /* ID = 0x15 (Hopping mode) */
- /* length = 2 */
- /* [2] */
- 0x01, 0x00,
-
- 0x5F, 0x75, 0x02,
- /* ID = 0x75 (RX diversity selection) */
- /* length = 2 */
- /* [7] */
- 0x01, 0x01,
-};
-
-static uint8_t bts_config_3[] = {
- 0x5F, 0x20, 0x02,
- /* ID = 0x20 (Extended cell radius) */
- /* length = 2 */
- /* [3] */
- 0x01, 0x00,
-};
-
-static uint8_t bts_config_4[] = {
- 0x5F, 0x74, 0x09,
- /* ID = 0x74 (Real Time) */
- /* length = 9 */
- /* [3] year-high, year-low, month, day, hour, minute, second, msec-high, msec-low */
- 0x07, 0xDB, 0x06, 0x02, 0x0B, 0x20, 0x0C, 0x00,
- 0x00,
-
- 0x5F, 0x76, 0x03,
- /* ID = 0x76 (EAC input config) */
- /* length = 3 */
- /* [15] */
- 0x01, 0x01, 0x00,
-
- 0x5F, 0x76, 0x03,
- /* ID = 0x76 (EAC input config) */
- /* length = 3 */
- /* [21] */
- 0x02, 0x01, 0x00,
-
- 0x5F, 0x76, 0x03,
- /* ID = 0x76 (EAC input config) */
- /* length = 3 */
- /* [27] */
- 0x03, 0x01, 0x00,
-
- 0x5F, 0x76, 0x03,
- /* ID = 0x76 (EAC input config) */
- /* length = 3 */
- /* [33] */
- 0x04, 0x01, 0x00,
-
- 0x5F, 0x76, 0x03,
- /* ID = 0x76 (EAC input config) */
- /* length = 3 */
- /* [39] */
- 0x05, 0x01, 0x00,
-
- 0x5F, 0x76, 0x03,
- /* ID = 0x76 (EAC input config) */
- /* length = 3 */
- /* [45] */
- 0x06, 0x01, 0x00,
-
- 0x5F, 0x76, 0x03,
- /* ID = 0x76 (EAC input config) */
- /* length = 3 */
- /* [51] */
- 0x07, 0x01, 0x00,
-
- 0x5F, 0x76, 0x03,
- /* ID = 0x76 (EAC input config) */
- /* length = 3 */
- /* [57] */
- 0x08, 0x01, 0x00,
-
- 0x5F, 0x76, 0x03,
- /* ID = 0x76 (EAC input config) */
- /* length = 3 */
- /* [63] */
- 0x09, 0x01, 0x00,
-
- 0x5F, 0x76, 0x03,
- /* ID = 0x76 (EAC input config) */
- /* length = 3 */
- /* [69] */
- 0x0A, 0x01, 0x00,
-};
-
-static uint8_t bts_config_insite[] = {
- 0x4E, 0x02,
- /* ID = 0x0E (Frame number) */
- /* length = 2 */
- /* [2] */
- 0xFF, 0xFF,
-
- 0x5F, 0x4E, 0x02,
- /* ID = 0x4E (RX antenna supervision period) */
- /* length = 2 */
- /* [7] */
- 0xFF, 0xFF,
-
- 0x5F, 0x50, 0x02,
- /* ID = 0x50 (Sector configuration) */
- /* length = 2 */
- /* [12] */
- 0x01, 0x01,
-
- 0x55, 0x02,
- /* ID = 0x15 (Hopping mode) */
- /* length = 2 */
- /* [16] */
- 0x01, 0x00,
-
- 0x5F, 0x20, 0x02,
- /* ID = 0x20 (Extended cell radius) */
- /* length = 2 */
- /* [21] */
- 0x01, 0x00,
-
- 0x5F, 0x74, 0x09,
- /* ID = 0x74 (Real Time) */
- /* length = 9 */
- /* [26] */
- 0x07, 0xDB, 0x07, 0x0A, 0x0F, 0x09, 0x0B, 0x00,
- 0x00,
-};
-
-void set_real_time(uint8_t * real_time)
-{
- time_t t;
- struct tm *tm;
-
- t = time(NULL);
- tm = localtime(&t);
-
- /* year-high, year-low, month, day, hour, minute, second, msec-high, msec-low */
-
- real_time[0] = (1900 + tm->tm_year) >> 8;
- real_time[1] = (1900 + tm->tm_year) & 0xFF;
- real_time[2] = tm->tm_mon + 1;
- real_time[3] = tm->tm_mday;
- real_time[4] = tm->tm_hour;
- real_time[5] = tm->tm_min;
- real_time[6] = tm->tm_sec;
- real_time[7] = 0;
- real_time[8] = 0;
-}
-
-/* TODO: put in a separate file ? */
-
-/*
- build the configuration data
-*/
-
-static int make_bts_config(uint8_t bts_type, int n_trx, uint8_t * fu_config,
- int need_hopping)
-{
- /* is it an InSite BTS ? */
- if (bts_type == 0x0E || bts_type == 0x0F || bts_type == 0x10) { /* TODO */
- if (n_trx != 1) {
- fprintf(stderr, "InSite has only one TRX\n");
- return 0;
- }
- if (need_hopping != 0) {
- fprintf(stderr, "InSite does not support hopping\n");
- return 0;
- }
- memcpy(fu_config, bts_config_insite, sizeof(bts_config_insite));
- set_real_time(&fu_config[26]);
- return sizeof(bts_config_insite);
- }
-
- int len = 0;
- int i;
-
- memcpy(fu_config + len, bts_config_1, sizeof(bts_config_1));
-
- /* set sector configuration */
- fu_config[len + 12 - 1] = 1 + n_trx; /* len */
- for (i = 0; i < n_trx; i++)
- fu_config[len + 12 + 1 + i] = ((i + 1) & 0xFF);
-
- len += (sizeof(bts_config_1) + (n_trx - 1));
-
- memcpy(fu_config + len, bts_config_2, sizeof(bts_config_2));
- /* set hopping mode (Baseband and RF hopping work for the MetroSite) */
- if (need_hopping)
- fu_config[len + 2 + 1] = 1; /* 0: no hopping, 1: Baseband hopping, 2: RF hopping */
- len += sizeof(bts_config_2);
-
- /* set extended cell radius for each TRX */
- for (i = 0; i < n_trx; i++) {
- memcpy(fu_config + len, bts_config_3, sizeof(bts_config_3));
- fu_config[len + 3] = ((i + 1) & 0xFF);
- len += sizeof(bts_config_3);
- }
-
- memcpy(fu_config + len, bts_config_4, sizeof(bts_config_4));
- set_real_time(&fu_config[len + 3]);
- len += sizeof(bts_config_4);
-
- return len;
-}
-
-/* TODO: put in a separate file ? */
-
-static struct msgb *nm_msgb_alloc(void)
-{
- return msgb_alloc_headroom(OM_ALLOC_SIZE, OM_HEADROOM_SIZE, "OML");
-}
-
-/* TODO: put in a separate file ? */
-
-struct abis_om_nokia_hdr {
- uint8_t msg_type;
- uint8_t spare;
- uint16_t reference;
- uint8_t data[0];
-} __attribute__ ((packed));
-
-#define ABIS_OM_NOKIA_HDR_SIZE (sizeof(struct abis_om_hdr) + sizeof(struct abis_om_nokia_hdr))
-
-static int abis_nm_send(struct gsm_bts *bts, uint8_t msg_type, uint16_t ref,
- uint8_t * data, int len_data)
-{
- struct abis_om_hdr *oh;
- struct abis_om_nokia_hdr *noh;
- struct msgb *msg = nm_msgb_alloc();
-
- oh = (struct abis_om_hdr *)msgb_put(msg,
- ABIS_OM_NOKIA_HDR_SIZE + len_data);
-
- oh->mdisc = ABIS_OM_MDISC_FOM;
- oh->placement = ABIS_OM_PLACEMENT_ONLY;
- oh->sequence = 0;
- oh->length = sizeof(struct abis_om_nokia_hdr) + len_data;
-
- noh = (struct abis_om_nokia_hdr *)oh->data;
-
- noh->msg_type = msg_type;
- noh->spare = 0;
- noh->reference = htons(ref);
- memcpy(noh->data, data, len_data);
-
- DEBUGPC(DNM, "Sending %s\n", get_msg_type_name_string(msg_type));
-
- return abis_nm_sendmsg(bts, msg);
-}
-
-/* TODO: put in a separate file ? */
-
-static uint8_t download_req[] = {
- 0x5F, 0x25, 0x0B,
- /* ID = 0x25 (File identity) */
- /* length = 11 */
- /* [3] */
- 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A,
- 0x2A, 0x2A, 0x2A,
-
- 0x5F, 0x78, 0x03,
- /* ID = 0x78 (File version) */
- /* length = 3 */
- /* [17] */
- 0x2A, 0x2A, 0x2A,
-
- 0x5F, 0x81, 0x0A, 0x01,
- /* ID = 0x8A (SW load mode) */
- /* length = 1 */
- /* [24] */
- 0x01,
-
- 0x5F, 0x81, 0x06, 0x01,
- /* ID = 0x86 (Acknowledgement period) */
- /* length = 1 */
- /* [29] */
- 0x01,
-};
-
-static int abis_nm_download_req(struct gsm_bts *bts, uint16_t ref)
-{
- uint8_t *data = download_req;
- int len_data = sizeof(download_req);
-
- return abis_nm_send(bts, NOKIA_MSG_START_DOWNLOAD_REQ, ref, data,
- len_data);
-}
-
-/* TODO: put in a separate file ? */
-
-static uint8_t ack[] = {
- 0x5F, 0x23, 0x01,
- /* ID = 0x23 (Ack-Nack) */
- /* length = 1 */
- /* [3] */
- 0x01,
-};
-
-static int abis_nm_ack(struct gsm_bts *bts, uint16_t ref)
-{
- uint8_t *data = ack;
- int len_data = sizeof(ack);
-
- return abis_nm_send(bts, NOKIA_MSG_ACK, ref, data, len_data);
-}
-
-/* TODO: put in a separate file ? */
-
-static uint8_t reset[] = {
- 0x5F, 0x40, 0x04,
- /* ID = 0x40 (Object identity) */
- /* length = 4 */
- /* [3] */
- 0x00, 0x01, 0xFF, 0xFF,
-};
-
-static int abis_nm_reset(struct gsm_bts *bts, uint16_t ref)
-{
- uint8_t *data = reset;
- int len_data = sizeof(reset);
- LOGP(DLINP, LOGL_INFO, "Nokia BTS reset timer: %d\n", bts->nokia.bts_reset_timer_cnf);
- return abis_nm_send(bts, NOKIA_MSG_RESET_REQ, ref, data, len_data);
-}
-
-/* TODO: put in a separate file ? */
-
-static int abis_nm_send_multi_segments(struct gsm_bts *bts, uint8_t msg_type,
- uint16_t ref, uint8_t * data, int len)
-{
- int len_remain, len_to_send, max_send;
- int seq = 0;
- int ret;
-
- len_remain = len;
-
- while (len_remain) {
- struct abis_om_hdr *oh;
- struct abis_om_nokia_hdr *noh;
- struct msgb *msg = nm_msgb_alloc();
-
- if (seq == 0)
- max_send = 256 - sizeof(struct abis_om_nokia_hdr);
- else
- max_send = 256;
-
- if (len_remain > max_send) {
- len_to_send = max_send;
-
- if (seq == 0) {
- /* first segment */
- oh = (struct abis_om_hdr *)msgb_put(msg,
- ABIS_OM_NOKIA_HDR_SIZE
- +
- len_to_send);
-
- oh->mdisc = ABIS_OM_MDISC_FOM;
- oh->placement = ABIS_OM_PLACEMENT_FIRST; /* first segment of multi-segment message */
- oh->sequence = seq;
- oh->length = 0; /* 256 bytes */
-
- noh = (struct abis_om_nokia_hdr *)oh->data;
-
- noh->msg_type = msg_type;
- noh->spare = 0;
- noh->reference = htons(ref);
- memcpy(noh->data, data, len_to_send);
- } else {
- /* segment in between */
- oh = (struct abis_om_hdr *)msgb_put(msg,
- sizeof
- (struct
- abis_om_hdr)
- +
- len_to_send);
-
- oh->mdisc = ABIS_OM_MDISC_FOM;
- oh->placement = ABIS_OM_PLACEMENT_MIDDLE; /* segment of multi-segment message */
- oh->sequence = seq;
- oh->length = 0; /* 256 bytes */
-
- memcpy(oh->data, data, len_to_send);
- }
- } else {
-
- len_to_send = len_remain;
-
- /* check if message fits in a single segment */
-
- if (seq == 0)
- return abis_nm_send(bts, msg_type, ref, data,
- len_to_send);
-
- /* last segment */
-
- oh = (struct abis_om_hdr *)msgb_put(msg,
- sizeof(struct
- abis_om_hdr)
- + len_to_send);
-
- oh->mdisc = ABIS_OM_MDISC_FOM;
- oh->placement = ABIS_OM_PLACEMENT_LAST; /* last segment of multi-segment message */
- oh->sequence = seq;
- oh->length = len_to_send;
-
- memcpy(oh->data, data, len_to_send);
- }
-
- DEBUGPC(DNM, "Sending multi-segment %d\n", seq);
-
- ret = abis_nm_sendmsg(bts, msg);
- if (ret < 0)
- return ret;
-
- nokia_abis_nm_queue_send_next(bts);
-
- /* next segment */
- len_remain -= len_to_send;
- data += len_to_send;
- seq++;
- }
- return ret;
-}
-
-/* TODO: put in a separate file ? */
-
-static int abis_nm_send_config(struct gsm_bts *bts, uint8_t bts_type)
-{
- struct gsm_bts_trx *trx;
- uint8_t config[2048]; /* TODO: might be too small if lots of TRX are used */
- int len = 0;
- int idx = 0;
- int ret;
- int hopping = 0;
- int need_hopping = 0;
-
- memset(config, 0, sizeof(config));
-
- llist_for_each_entry(trx, &bts->trx_list, list) {
-#if 0 /* debugging */
- printf("TRX\n");
- printf(" arfcn: %d\n", trx->arfcn);
- printf(" bsic: %d\n", trx->bts->bsic);
- uint8_t ca[20];
- memset(ca, 0xFF, sizeof(ca));
- ret = generate_cell_chan_list(ca, trx->bts);
- printf(" ca (%d): %s\n", ret, osmo_hexdump(ca, sizeof(ca)));
- int i;
- for (i = 0; i < ARRAY_SIZE(trx->ts); i++) {
- struct gsm_bts_trx_ts *ts = &trx->ts[i];
-
- printf(" pchan %d: %d\n", i, ts->pchan);
- }
-#endif
- ret = make_fu_config(trx, idx + 1, config + len, &hopping);
- need_hopping |= hopping;
- len += ret;
-
- idx++;
- }
-
- ret = make_bts_config(bts_type, idx, config + len, need_hopping);
- len += ret;
-
-#if 0 /* debugging */
- dump_elements(config, len);
-#endif
-
- return abis_nm_send_multi_segments(bts, NOKIA_MSG_CONF_DATA, 1, config,
- len);
-}
-
-#define GET_NEXT_BYTE if(idx >= len) return 0; \
- ub = data[idx++];
-
-static int find_element(uint8_t * data, int len, uint16_t id, uint8_t * value,
- int max_value)
-{
- uint8_t ub;
- int idx = 0;
- int found = 0;
- int constructed __attribute__((unused));
- uint16_t id_value;
-
- for (;;) {
-
- GET_NEXT_BYTE;
-
- /* encoding bit, construced means that other elements are contained */
- constructed = ((ub & 0x20) ? 1 : 0);
-
- if ((ub & 0x1F) == 0x1F) {
- /* fixed pattern, ID follows */
- GET_NEXT_BYTE; /* ID */
- id_value = ub & 0x7F;
- if (ub & 0x80) {
- /* extension bit */
- GET_NEXT_BYTE; /* ID low part */
- id_value = (id_value << 7) | (ub & 0x7F);
- }
- if (id_value == id)
- found = 1;
- } else {
- id_value = (ub & 0x3F);
- if (id_value == id)
- found = 1;
- }
-
- GET_NEXT_BYTE; /* length */
-
- if (found) {
- /* get data */
- uint8_t n = ub;
- uint8_t i;
- for (i = 0; i < n; i++) {
- GET_NEXT_BYTE;
- if (max_value <= 0)
- return -1; /* buffer too small */
- *value = ub;
- value++;
- max_value--;
- }
- return n; /* length */
- } else {
- /* skip data */
- uint8_t n = ub;
- uint8_t i;
- for (i = 0; i < n; i++) {
- GET_NEXT_BYTE;
- }
- }
- }
- return 0; /* not found */
-}
-
-static int dump_elements(uint8_t * data, int len)
-{
- uint8_t ub;
- int idx = 0;
- int constructed;
- uint16_t id_value;
- static char indent[100] = ""; /* TODO: move static to BTS context */
-
- for (;;) {
-
- GET_NEXT_BYTE;
-
- /* encoding bit, construced means that other elements are contained */
- constructed = ((ub & 0x20) ? 1 : 0);
-
- if ((ub & 0x1F) == 0x1F) {
- /* fixed pattern, ID follows */
- GET_NEXT_BYTE; /* ID */
- id_value = ub & 0x7F;
- if (ub & 0x80) {
- /* extension bit */
- GET_NEXT_BYTE; /* ID low part */
- id_value = (id_value << 7) | (ub & 0x7F);
- }
-
- } else {
- id_value = (ub & 0x3F);
- }
-
- GET_NEXT_BYTE; /* length */
-
- printf("%s--ID = 0x%02X (%s) %s\n", indent, id_value,
- get_element_name_string(id_value),
- constructed ? "** constructed **" : "");
- printf("%s length = %d\n", indent, ub);
- printf("%s %s\n", indent, osmo_hexdump(data + idx, ub));
-
- if (constructed) {
- int indent_len = strlen(indent);
- strcat(indent, " ");
-
- dump_elements(data + idx, ub);
-
- indent[indent_len] = 0;
- }
- /* skip data */
- uint8_t n = ub;
- uint8_t i;
- for (i = 0; i < n; i++) {
- GET_NEXT_BYTE;
- }
- }
- return 0;
-}
-
-/* TODO: put in a separate file ? */
-
-/* taken from abis_nm.c */
-
-static void nokia_abis_nm_queue_send_next(struct gsm_bts *bts)
-{
- int wait = 0;
- struct msgb *msg;
- /* the queue is empty */
- while (!llist_empty(&bts->abis_queue)) {
- msg = msgb_dequeue(&bts->abis_queue);
- wait = OBSC_NM_W_ACK_CB(msg);
- abis_sendmsg(msg);
-
- if (wait)
- break;
- }
-
- bts->abis_nm_pend = wait;
-}
-
-/* TODO: put in a separate file ? */
-
-/* timer for restarting OML after BTS reset */
-
-static void reset_timer_cb(void *_bts)
-{
- struct gsm_bts *bts = _bts;
- struct gsm_e1_subslot *e1_link = &bts->oml_e1_link;
- struct e1inp_line *line;
-
- bts->nokia.wait_reset = 0;
-
- /* OML link */
- line = e1inp_line_find(e1_link->e1_nr);
- if (!line) {
- LOGP(DLINP, LOGL_ERROR, "BTS %u OML link referring to "
- "non-existing E1 line %u\n", bts->nr, e1_link->e1_nr);
- return;
- }
-
- start_sabm_in_line(line, 0, -1); /* stop all first */
- start_sabm_in_line(line, 1, SAPI_OML); /* start only OML */
-}
-
-/* TODO: put in a separate file ? */
-
-/*
- This is how the configuration is done:
- - start OML link
- - reset BTS
- - receive ACK, wait some time and restart OML link
- - receive OMU STARTED message, send START DOWNLOAD REQ
- - receive CNF REQ message, send CONF DATA
- - receive ACK, start RSL link(s)
- ACK some other messages received from the BTS.
-
- Probably its also possible to configure the BTS without a reset, this
- has not been tested yet.
-*/
-
-static int abis_nm_rcvmsg_fom(struct msgb *mb)
-{
- struct e1inp_sign_link *sign_link = (struct e1inp_sign_link *)mb->dst;
- struct gsm_bts *bts = sign_link->trx->bts;
- struct abis_om_hdr *oh = msgb_l2(mb);
- struct abis_om_nokia_hdr *noh = msgb_l3(mb);
- uint8_t mt = noh->msg_type;
- int ret = 0;
- uint16_t ref = ntohs(noh->reference);
- uint8_t info[256];
- uint8_t ack = 0xFF;
- uint8_t severity = 0xFF;
- int str_len;
- int len_data;
-
- if (bts->nokia.wait_reset) {
- LOGP(DNM, LOGL_INFO,
- "Ignore message while waiting for reset\n");
- return ret;
- }
-
- if (oh->length < sizeof(struct abis_om_nokia_hdr)) {
- LOGP(DNM, LOGL_ERROR, "Message too short\n");
- return -EINVAL;
- }
-
- len_data = oh->length - sizeof(struct abis_om_nokia_hdr);
- LOGP(DNM, LOGL_INFO, "(0x%02X) %s\n", mt, get_msg_type_name_string(mt));
-#if 0 /* debugging */
- dump_elements(noh->data, len_data);
-#endif
-
- switch (mt) {
- case NOKIA_MSG_OMU_STARTED:
- if (find_element(noh->data, len_data,
- NOKIA_EI_BTS_TYPE, &bts->nokia.bts_type,
- sizeof(uint8_t)) == sizeof(uint8_t))
- LOGP(DNM, LOGL_INFO, "BTS type = %d (%s)\n",
- bts->nokia.bts_type,
- get_bts_type_string(bts->nokia.bts_type));
- else
- LOGP(DNM, LOGL_ERROR, "BTS type not found\n");
- /* send START_DOWNLOAD_REQ */
- abis_nm_download_req(bts, ref);
- break;
- case NOKIA_MSG_MF_REQ:
- break;
- case NOKIA_MSG_CONF_REQ:
- /* send ACK */
- abis_nm_ack(bts, ref);
- nokia_abis_nm_queue_send_next(bts);
- /* send CONF_DATA */
- abis_nm_send_config(bts, bts->nokia.bts_type);
- bts->nokia.configured = 1;
- break;
- case NOKIA_MSG_ACK:
- if (find_element
- (noh->data, len_data, NOKIA_EI_ACK, &ack,
- sizeof(uint8_t)) == sizeof(uint8_t)) {
- LOGP(DNM, LOGL_INFO, "ACK = %d\n", ack);
- if (ack != 1) {
- LOGP(DNM, LOGL_ERROR, "No ACK received (%d)\n",
- ack);
- /* TODO: properly handle failures (NACK) */
- }
- } else
- LOGP(DNM, LOGL_ERROR, "ACK not found\n");
-
- /* TODO: the assumption for the following is that no NACK was received */
-
- /* ACK for reset message ? */
- if (!bts->nokia.did_reset) {
- bts->nokia.did_reset = 1;
-
- /*
- TODO: For the InSite processing the received data is
- blocked in the driver during reset.
- Otherwise the LAPD module might assert because the InSite
- sends garbage on the E1 line during reset.
- This is done by looking at "wait_reset" in the driver
- (function handle_ts1_read()) and ignoring the received data.
- It seems to be necessary for the MetroSite too.
- */
- bts->nokia.wait_reset = 1;
-
- osmo_timer_setup(&bts->nokia.reset_timer,
- reset_timer_cb, bts);
- osmo_timer_schedule(&bts->nokia.reset_timer, bts->nokia.bts_reset_timer_cnf, 0);
-
- struct gsm_e1_subslot *e1_link = &bts->oml_e1_link;
- struct e1inp_line *line;
- /* OML link */
- line = e1inp_line_find(e1_link->e1_nr);
- if (!line) {
- LOGP(DLINP, LOGL_ERROR,
- "BTS %u OML link referring to "
- "non-existing E1 line %u\n", bts->nr,
- e1_link->e1_nr);
- return -ENOMEM;
- }
-
- start_sabm_in_line(line, 0, -1); /* stop all first */
- }
-
- /* ACK for CONF DATA message ? */
- if (bts->nokia.configured != 0) {
- /* start TRX (RSL link) */
-
- struct gsm_e1_subslot *e1_link =
- &sign_link->trx->rsl_e1_link;
- struct e1inp_line *line;
-
- bts->nokia.configured = 0;
-
- /* RSL Link */
- line = e1inp_line_find(e1_link->e1_nr);
- if (!line) {
- LOGP(DLINP, LOGL_ERROR,
- "TRX (%u/%u) RSL link referring "
- "to non-existing E1 line %u\n",
- sign_link->trx->bts->nr, sign_link->trx->nr,
- e1_link->e1_nr);
- return -ENOMEM;
- }
- /* start TRX */
- start_sabm_in_line(line, 1, SAPI_RSL); /* start only RSL */
- }
- break;
- case NOKIA_MSG_STATE_CHANGED:
- /* send ACK */
- abis_nm_ack(bts, ref);
- break;
- case NOKIA_MSG_CONF_COMPLETE:
- /* send ACK */
- abis_nm_ack(bts, ref);
- break;
- case NOKIA_MSG_BLOCK_CTRL_REQ: /* seems to be send when something goes wrong !? */
- /* send ACK (do we have to send an ACK ?) */
- abis_nm_ack(bts, ref);
- break;
- case NOKIA_MSG_ALARM:
- find_element(noh->data, len_data, NOKIA_EI_SEVERITY, &severity,
- sizeof(severity));
- /* TODO: there might be alarms with both elements set */
- str_len =
- find_element(noh->data, len_data, NOKIA_EI_ADD_INFO, info,
- sizeof(info));
- if (str_len > 0) {
- info[str_len] = 0;
- LOGP(DNM, LOGL_INFO, "ALARM Severity %s (%d) : %s\n",
- get_severity_string(severity), severity, info);
- } else { /* nothing found, try details */
- str_len =
- find_element(noh->data, len_data,
- NOKIA_EI_ALARM_DETAIL, info,
- sizeof(info));
- if (str_len > 0) {
- uint16_t code;
- info[str_len] = 0;
- code = (info[0] << 8) + info[1];
- LOGP(DNM, LOGL_INFO,
- "ALARM Severity %s (%d), code 0x%X : %s\n",
- get_severity_string(severity), severity,
- code, info + 2);
- }
- }
- /* send ACK */
- abis_nm_ack(bts, ref);
- break;
- }
-
- nokia_abis_nm_queue_send_next(bts);
-
- return ret;
-}
-
-/* TODO: put in a separate file ? */
-
-int abis_nokia_rcvmsg(struct msgb *msg)
-{
- struct abis_om_hdr *oh = msgb_l2(msg);
- int rc = 0;
-
- /* Various consistency checks */
- if (oh->placement != ABIS_OM_PLACEMENT_ONLY) {
- LOGP(DNM, LOGL_ERROR, "ABIS OML placement 0x%x not supported\n",
- oh->placement);
- if (oh->placement != ABIS_OM_PLACEMENT_FIRST)
- return -EINVAL;
- }
- if (oh->sequence != 0) {
- LOGP(DNM, LOGL_ERROR, "ABIS OML sequence 0x%x != 0x00\n",
- oh->sequence);
- return -EINVAL;
- }
- msg->l3h = (unsigned char *)oh + sizeof(*oh);
-
- switch (oh->mdisc) {
- case ABIS_OM_MDISC_FOM:
- LOGP(DNM, LOGL_INFO, "ABIS_OM_MDISC_FOM\n");
- rc = abis_nm_rcvmsg_fom(msg);
- break;
- case ABIS_OM_MDISC_MANUF:
- LOGP(DNM, LOGL_INFO, "ABIS_OM_MDISC_MANUF\n");
- break;
- case ABIS_OM_MDISC_MMI:
- case ABIS_OM_MDISC_TRAU:
- LOGP(DNM, LOGL_ERROR,
- "unimplemented ABIS OML message discriminator 0x%x\n",
- oh->mdisc);
- break;
- default:
- LOGP(DNM, LOGL_ERROR,
- "unknown ABIS OML message discriminator 0x%x\n",
- oh->mdisc);
- return -EINVAL;
- }
-
- msgb_free(msg);
- return rc;
-}
-
-static int bts_model_nokia_site_start(struct gsm_network *net);
-
-static void bts_model_nokia_site_e1line_bind_ops(struct e1inp_line *line)
-{
- e1inp_line_bind_ops(line, &bts_isdn_e1inp_line_ops);
-}
-
-static struct gsm_bts_model model_nokia_site = {
- .type = GSM_BTS_TYPE_NOKIA_SITE,
- .name = "nokia_site",
- .start = bts_model_nokia_site_start,
- .oml_rcvmsg = &abis_nokia_rcvmsg,
- .e1line_bind_ops = &bts_model_nokia_site_e1line_bind_ops,
-};
-
-static struct gsm_network *my_net;
-
-static int bts_model_nokia_site_start(struct gsm_network *net)
-{
- model_nokia_site.features.data = &model_nokia_site._features_data[0];
- model_nokia_site.features.data_len =
- sizeof(model_nokia_site._features_data);
-
- gsm_btsmodel_set_feature(&model_nokia_site, BTS_FEAT_HOPPING);
- gsm_btsmodel_set_feature(&model_nokia_site, BTS_FEAT_HSCSD);
- gsm_btsmodel_set_feature(&model_nokia_site, BTS_FEAT_MULTI_TSC);
-
- osmo_signal_register_handler(SS_L_INPUT, inp_sig_cb, NULL);
- osmo_signal_register_handler(SS_L_GLOBAL, gbl_sig_cb, NULL);
- osmo_signal_register_handler(SS_NM, nm_sig_cb, NULL);
-
- my_net = net;
-
- return 0;
-}
-
-int bts_model_nokia_site_init(void)
-{
- return gsm_bts_model_register(&model_nokia_site);
-}
diff --git a/openbsc/src/libbsc/bts_siemens_bs11.c b/openbsc/src/libbsc/bts_siemens_bs11.c
deleted file mode 100644
index c083b1e06..000000000
--- a/openbsc/src/libbsc/bts_siemens_bs11.c
+++ /dev/null
@@ -1,602 +0,0 @@
-/* Siemens BS-11 specific code */
-
-/* (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/tlv.h>
-
-#include <openbsc/debug.h>
-#include <openbsc/gsm_data.h>
-#include <openbsc/abis_nm.h>
-#include <osmocom/abis/e1_input.h>
-#include <openbsc/signal.h>
-
-static int bts_model_bs11_start(struct gsm_network *net);
-
-static void bts_model_bs11_e1line_bind_ops(struct e1inp_line *line)
-{
- e1inp_line_bind_ops(line, &bts_isdn_e1inp_line_ops);
-}
-
-static struct gsm_bts_model model_bs11 = {
- .type = GSM_BTS_TYPE_BS11,
- .name = "bs11",
- .start = bts_model_bs11_start,
- .oml_rcvmsg = &abis_nm_rcvmsg,
- .e1line_bind_ops = bts_model_bs11_e1line_bind_ops,
- .nm_att_tlvdef = {
- .def = {
- [NM_ATT_AVAIL_STATUS] = { TLV_TYPE_TLV },
- /* BS11 specifics */
- [NM_ATT_BS11_ESN_FW_CODE_NO] = { TLV_TYPE_TLV },
- [NM_ATT_BS11_ESN_HW_CODE_NO] = { TLV_TYPE_TLV },
- [NM_ATT_BS11_ESN_PCB_SERIAL] = { TLV_TYPE_TLV },
- [NM_ATT_BS11_BOOT_SW_VERS] = { TLV_TYPE_TLV },
- [0xd5] = { TLV_TYPE_TLV },
- [0xa8] = { TLV_TYPE_TLV },
- [NM_ATT_BS11_PASSWORD] = { TLV_TYPE_TLV },
- [NM_ATT_BS11_TXPWR] = { TLV_TYPE_TLV },
- [NM_ATT_BS11_RSSI_OFFS] = { TLV_TYPE_TLV },
- [NM_ATT_BS11_LINE_CFG] = { TLV_TYPE_TV },
- [NM_ATT_BS11_L1_PROT_TYPE] = { TLV_TYPE_TV },
- [NM_ATT_BS11_BIT_ERR_THESH] = { TLV_TYPE_FIXED, 2 },
- [NM_ATT_BS11_DIVERSITY] = { TLV_TYPE_TLV },
- [NM_ATT_BS11_LMT_LOGON_SESSION]={ TLV_TYPE_TLV },
- [NM_ATT_BS11_LMT_LOGIN_TIME] = { TLV_TYPE_TLV },
- [NM_ATT_BS11_LMT_USER_ACC_LEV] ={ TLV_TYPE_TLV },
- [NM_ATT_BS11_LMT_USER_NAME] = { TLV_TYPE_TLV },
- [NM_ATT_BS11_BTS_STATE] = { TLV_TYPE_TLV },
- [NM_ATT_BS11_E1_STATE] = { TLV_TYPE_TLV },
- [NM_ATT_BS11_PLL_MODE] = { TLV_TYPE_TLV },
- [NM_ATT_BS11_PLL] = { TLV_TYPE_TLV },
- [NM_ATT_BS11_CCLK_ACCURACY] = { TLV_TYPE_TV },
- [NM_ATT_BS11_CCLK_TYPE] = { TLV_TYPE_TV },
- [0x95] = { TLV_TYPE_FIXED, 2 },
- },
- },
-};
-
-/* The following definitions are for OM and NM packets that we cannot yet
- * generate by code but we just pass on */
-
-// BTS Site Manager, SET ATTRIBUTES
-
-/*
- Object Class: BTS Site Manager
- Instance 1: FF
- Instance 2: FF
- Instance 3: FF
-SET ATTRIBUTES
- sAbisExternalTime: 2007/09/08 14:36:11
- omLAPDRelTimer: 30sec
- shortLAPDIntTimer: 5sec
- emergencyTimer1: 10 minutes
- emergencyTimer2: 0 minutes
-*/
-
-unsigned char msg_1[] =
-{
- NM_MT_BS11_SET_ATTR, NM_OC_SITE_MANAGER, 0xFF, 0xFF, 0xFF,
- NM_ATT_BS11_ABIS_EXT_TIME, 0x07,
- 0xD7, 0x09, 0x08, 0x0E, 0x24, 0x0B, 0xCE,
- 0x02,
- 0x00, 0x1E,
- NM_ATT_BS11_SH_LAPD_INT_TIMER,
- 0x01, 0x05,
- 0x42, 0x02, 0x00, 0x0A,
- 0x44, 0x02, 0x00, 0x00
-};
-
-// BTS, SET BTS ATTRIBUTES
-
-/*
- Object Class: BTS
- BTS relat. Number: 0
- Instance 2: FF
- Instance 3: FF
-SET BTS ATTRIBUTES
- bsIdentityCode / BSIC:
- PLMN_colour_code: 7h
- BS_colour_code: 7h
- BTS Air Timer T3105: 4 ,unit 10 ms
- btsIsHopping: FALSE
- periodCCCHLoadIndication: 1sec
- thresholdCCCHLoadIndication: 0%
- cellAllocationNumber: 00h = GSM 900
- enableInterferenceClass: 00h = Disabled
- fACCHQual: 6 (FACCH stealing flags minus 1)
- intaveParameter: 31 SACCH multiframes
- interferenceLevelBoundaries:
- Interference Boundary 1: 0Ah
- Interference Boundary 2: 0Fh
- Interference Boundary 3: 14h
- Interference Boundary 4: 19h
- Interference Boundary 5: 1Eh
- mSTxPwrMax: 11
- GSM range: 2=39dBm, 15=13dBm, stepsize 2 dBm
- DCS1800 range: 0=30dBm, 15=0dBm, stepsize 2 dBm
- PCS1900 range: 0=30dBm, 15=0dBm, stepsize 2 dBm
- 30=33dBm, 31=32dBm
- ny1:
- Maximum number of repetitions for PHYSICAL INFORMATION message (GSM 04.08): 20
- powerOutputThresholds:
- Out Power Fault Threshold: -10 dB
- Red Out Power Threshold: - 6 dB
- Excessive Out Power Threshold: 5 dB
- rACHBusyThreshold: -127 dBm
- rACHLoadAveragingSlots: 250 ,number of RACH burst periods
- rfResourceIndicationPeriod: 125 SACCH multiframes
- T200:
- SDCCH: 044 in 5 ms
- FACCH/Full rate: 031 in 5 ms
- FACCH/Half rate: 041 in 5 ms
- SACCH with TCH SAPI0: 090 in 10 ms
- SACCH with SDCCH: 090 in 10 ms
- SDCCH with SAPI3: 090 in 5 ms
- SACCH with TCH SAPI3: 135 in 10 ms
- tSync: 9000 units of 10 msec
- tTrau: 9000 units of 10 msec
- enableUmLoopTest: 00h = disabled
- enableExcessiveDistance: 00h = Disabled
- excessiveDistance: 64km
- hoppingMode: 00h = baseband hopping
- cellType: 00h = Standard Cell
- BCCH ARFCN / bCCHFrequency: 1
-*/
-
-static unsigned char bs11_attr_bts[] =
-{
- NM_ATT_BSIC, HARDCODED_BSIC,
- NM_ATT_BTS_AIR_TIMER, 0x04,
- NM_ATT_BS11_BTSLS_HOPPING, 0x00,
- NM_ATT_CCCH_L_I_P, 0x01,
- NM_ATT_CCCH_L_T, 0x00,
- NM_ATT_BS11_CELL_ALLOC_NR, NM_BS11_CANR_GSM,
- NM_ATT_BS11_ENA_INTERF_CLASS, 0x01,
- NM_ATT_BS11_FACCH_QUAL, 0x06,
- /* interference avg. period in numbers of SACCH multifr */
- NM_ATT_INTAVE_PARAM, 0x1F,
- NM_ATT_INTERF_BOUND, 0x0A, 0x0F, 0x14, 0x19, 0x1E, 0x7B,
- NM_ATT_CCCH_L_T, 0x23,
- NM_ATT_GSM_TIME, 0x28, 0x00,
- NM_ATT_ADM_STATE, 0x03,
- NM_ATT_RACH_B_THRESH, 0x7F,
- NM_ATT_LDAVG_SLOTS, 0x00, 0xFA,
- NM_ATT_BS11_RF_RES_IND_PER, 0x7D,
- NM_ATT_T200, 0x2C, 0x1F, 0x29, 0x5A, 0x5A, 0x5A, 0x87,
- NM_ATT_BS11_TSYNC, 0x23, 0x28,
- NM_ATT_BS11_TTRAU, 0x23, 0x28,
- NM_ATT_TEST_DUR, 0x01, 0x00,
- NM_ATT_OUTST_ALARM, 0x01, 0x00,
- NM_ATT_BS11_EXCESSIVE_DISTANCE, 0x01, 0x40,
- NM_ATT_BS11_HOPPING_MODE, 0x01, 0x00,
- NM_ATT_BS11_PLL, 0x01, 0x00,
- NM_ATT_BCCH_ARFCN, 0x00, HARDCODED_ARFCN/*0x01*/,
-};
-
-// Handover Recognition, SET ATTRIBUTES
-
-/*
-Illegal Contents GSM Formatted O&M Msg
- Object Class: Handover Recognition
- BTS relat. Number: 0
- Instance 2: FF
- Instance 3: FF
-SET ATTRIBUTES
- enableDelayPowerBudgetHO: 00h = Disabled
- enableDistanceHO: 00h = Disabled
- enableInternalInterCellHandover: 00h = Disabled
- enableInternalIntraCellHandover: 00h = Disabled
- enablePowerBudgetHO: 00h = Disabled
- enableRXLEVHO: 00h = Disabled
- enableRXQUALHO: 00h = Disabled
- hoAveragingDistance: 8 SACCH multiframes
- hoAveragingLev:
- A_LEV_HO: 8 SACCH multiframes
- W_LEV_HO: 1 SACCH multiframes
- hoAveragingPowerBudget: 16 SACCH multiframes
- hoAveragingQual:
- A_QUAL_HO: 8 SACCH multiframes
- W_QUAL_HO: 2 SACCH multiframes
- hoLowerThresholdLevDL: (10 - 110) dBm
- hoLowerThresholdLevUL: (5 - 110) dBm
- hoLowerThresholdQualDL: 06h = 6.4% < BER < 12.8%
- hoLowerThresholdQualUL: 06h = 6.4% < BER < 12.8%
- hoThresholdLevDLintra : (20 - 110) dBm
- hoThresholdLevULintra: (20 - 110) dBm
- hoThresholdMsRangeMax: 20 km
- nCell: 06h
- timerHORequest: 3 ,unit 2 SACCH multiframes
-*/
-
-unsigned char msg_3[] =
-{
- NM_MT_BS11_SET_ATTR, NM_OC_BS11_HANDOVER, 0x00, 0xFF, 0xFF,
- 0xD0, 0x00, /* enableDelayPowerBudgetHO */
- 0x64, 0x00, /* enableDistanceHO */
- 0x67, 0x00, /* enableInternalInterCellHandover */
- 0x68, 0x00, /* enableInternalInterCellHandover */
- 0x6A, 0x00, /* enablePowerBudgetHO */
- 0x6C, 0x00, /* enableRXLEVHO */
- 0x6D, 0x00, /* enableRXQUALHO */
- 0x6F, 0x08, /* hoAveragingDistance */
- 0x70, 0x08, 0x01, /* hoAveragingLev */
- 0x71, 0x10, 0x10, 0x10,
- 0x72, 0x08, 0x02, /* hoAveragingQual */
- 0x73, 0x0A, /* hoLowerThresholdLevDL */
- 0x74, 0x05, /* hoLowerThresholdLevUL */
- 0x75, 0x06, /* hoLowerThresholdQualDL */
- 0x76, 0x06, /* hoLowerThresholdQualUL */
- 0x78, 0x14, /* hoThresholdLevDLintra */
- 0x79, 0x14, /* hoThresholdLevULintra */
- 0x7A, 0x14, /* hoThresholdMsRangeMax */
- 0x7D, 0x06, /* nCell */
- NM_ATT_BS11_TIMER_HO_REQUEST, 0x03,
- 0x20, 0x01, 0x00,
- 0x45, 0x01, 0x00,
- 0x48, 0x01, 0x00,
- 0x5A, 0x01, 0x00,
- 0x5B, 0x01, 0x05,
- 0x5E, 0x01, 0x1A,
- 0x5F, 0x01, 0x20,
- 0x9D, 0x01, 0x00,
- 0x47, 0x01, 0x00,
- 0x5C, 0x01, 0x64,
- 0x5D, 0x01, 0x1E,
- 0x97, 0x01, 0x20,
- 0xF7, 0x01, 0x3C,
-};
-
-// Power Control, SET ATTRIBUTES
-
-/*
- Object Class: Power Control
- BTS relat. Number: 0
- Instance 2: FF
- Instance 3: FF
-SET ATTRIBUTES
- enableMsPowerControl: 00h = Disabled
- enablePowerControlRLFW: 00h = Disabled
- pcAveragingLev:
- A_LEV_PC: 4 SACCH multiframes
- W_LEV_PC: 1 SACCH multiframes
- pcAveragingQual:
- A_QUAL_PC: 4 SACCH multiframes
- W_QUAL_PC: 2 SACCH multiframes
- pcLowerThresholdLevDL: 0Fh
- pcLowerThresholdLevUL: 0Ah
- pcLowerThresholdQualDL: 05h = 3.2% < BER < 6.4%
- pcLowerThresholdQualUL: 05h = 3.2% < BER < 6.4%
- pcRLFThreshold: 0Ch
- pcUpperThresholdLevDL: 14h
- pcUpperThresholdLevUL: 0Fh
- pcUpperThresholdQualDL: 04h = 1.6% < BER < 3.2%
- pcUpperThresholdQualUL: 04h = 1.6% < BER < 3.2%
- powerConfirm: 2 ,unit 2 SACCH multiframes
- powerControlInterval: 2 ,unit 2 SACCH multiframes
- powerIncrStepSize: 02h = 4 dB
- powerRedStepSize: 01h = 2 dB
- radioLinkTimeoutBs: 64 SACCH multiframes
- enableBSPowerControl: 00h = disabled
-*/
-
-unsigned char msg_4[] =
-{
- NM_MT_BS11_SET_ATTR, NM_OC_BS11_PWR_CTRL, 0x00, 0xFF, 0xFF,
- NM_ATT_BS11_ENA_MS_PWR_CTRL, 0x00,
- NM_ATT_BS11_ENA_PWR_CTRL_RLFW, 0x00,
- 0x7E, 0x04, 0x01, /* pcAveragingLev */
- 0x7F, 0x04, 0x02, /* pcAveragingQual */
- 0x80, 0x0F, /* pcLowerThresholdLevDL */
- 0x81, 0x0A, /* pcLowerThresholdLevUL */
- 0x82, 0x05, /* pcLowerThresholdQualDL */
- 0x83, 0x05, /* pcLowerThresholdQualUL */
- 0x84, 0x0C, /* pcRLFThreshold */
- 0x85, 0x14, /* pcUpperThresholdLevDL */
- 0x86, 0x0F, /* pcUpperThresholdLevUL */
- 0x87, 0x04, /* pcUpperThresholdQualDL */
- 0x88, 0x04, /* pcUpperThresholdQualUL */
- 0x89, 0x02, /* powerConfirm */
- 0x8A, 0x02, /* powerConfirmInterval */
- 0x8B, 0x02, /* powerIncrStepSize */
- 0x8C, 0x01, /* powerRedStepSize */
- 0x8D, 0x40, /* radioLinkTimeoutBs */
- 0x65, 0x01, 0x00 // set to 0x01 to enable BSPowerControl
-};
-
-
-// Transceiver, SET TRX ATTRIBUTES (TRX 0)
-
-/*
- Object Class: Transceiver
- BTS relat. Number: 0
- Tranceiver number: 0
- Instance 3: FF
-SET TRX ATTRIBUTES
- aRFCNList (HEX): 0001
- txPwrMaxReduction: 00h = 30dB
- radioMeasGran: 254 SACCH multiframes
- radioMeasRep: 01h = enabled
- memberOfEmergencyConfig: 01h = TRUE
- trxArea: 00h = TRX doesn't belong to a concentric cell
-*/
-
-static unsigned char bs11_attr_radio[] =
-{
- NM_ATT_ARFCN_LIST, 0x01, 0x00, HARDCODED_ARFCN /*0x01*/,
- NM_ATT_RF_MAXPOWR_R, 0x00,
- NM_ATT_BS11_RADIO_MEAS_GRAN, 0x01, 0x05,
- NM_ATT_BS11_RADIO_MEAS_REP, 0x01, 0x01,
- NM_ATT_BS11_EMRG_CFG_MEMBER, 0x01, 0x01,
- NM_ATT_BS11_TRX_AREA, 0x01, 0x00,
-};
-
-/*
- * Patch the various SYSTEM INFORMATION tables to update
- * the LAI
- */
-static void patch_nm_tables(struct gsm_bts *bts)
-{
- uint8_t arfcn_low = bts->c0->arfcn & 0xff;
- uint8_t arfcn_high = (bts->c0->arfcn >> 8) & 0x0f;
-
- /* T3105 attribute in units of 10ms */
- bs11_attr_bts[2] = bts->network->T3105 / 10;
-
- /* patch ARFCN into BTS Attributes */
- bs11_attr_bts[69] &= 0xf0;
- bs11_attr_bts[69] |= arfcn_high;
- bs11_attr_bts[70] = arfcn_low;
-
- /* patch ARFCN into TRX Attributes */
- bs11_attr_radio[2] &= 0xf0;
- bs11_attr_radio[2] |= arfcn_high;
- bs11_attr_radio[3] = arfcn_low;
-
- /* patch the RACH attributes */
- if (bts->rach_b_thresh != -1)
- bs11_attr_bts[33] = bts->rach_b_thresh & 0xff;
-
- if (bts->rach_ldavg_slots != -1) {
- uint8_t avg_high = bts->rach_ldavg_slots & 0xff;
- uint8_t avg_low = (bts->rach_ldavg_slots >> 8) & 0x0f;
-
- bs11_attr_bts[35] = avg_high;
- bs11_attr_bts[36] = avg_low;
- }
-
- /* patch BSIC */
- bs11_attr_bts[1] = bts->bsic;
-
- /* patch the power reduction */
- bs11_attr_radio[5] = bts->c0->max_power_red / 2;
-}
-
-
-static void nm_reconfig_ts(struct gsm_bts_trx_ts *ts)
-{
- enum abis_nm_chan_comb ccomb = abis_nm_chcomb4pchan(ts->pchan);
- struct gsm_e1_subslot *e1l = &ts->e1_link;
-
- abis_nm_set_channel_attr(ts, ccomb);
-
- if (is_ipaccess_bts(ts->trx->bts))
- return;
-
- if (ts_is_tch(ts))
- abis_nm_conn_terr_traf(ts, e1l->e1_nr, e1l->e1_ts,
- e1l->e1_ts_ss);
-}
-
-static void nm_reconfig_trx(struct gsm_bts_trx *trx)
-{
- struct gsm_e1_subslot *e1l = &trx->rsl_e1_link;
- int i;
-
- patch_nm_tables(trx->bts);
-
- switch (trx->bts->type) {
- case GSM_BTS_TYPE_BS11:
- /* FIXME: discover this by fetching an attribute */
-#if 0
- trx->nominal_power = 15; /* 15dBm == 30mW PA configuration */
-#else
- trx->nominal_power = 24; /* 24dBm == 250mW PA configuration */
-#endif
- abis_nm_conn_terr_sign(trx, e1l->e1_nr, e1l->e1_ts,
- e1l->e1_ts_ss);
- abis_nm_establish_tei(trx->bts, trx->nr, e1l->e1_nr,
- e1l->e1_ts, e1l->e1_ts_ss, trx->rsl_tei);
-
- /* Set Radio Attributes */
- if (trx == trx->bts->c0)
- abis_nm_set_radio_attr(trx, bs11_attr_radio,
- sizeof(bs11_attr_radio));
- else {
- uint8_t trx1_attr_radio[sizeof(bs11_attr_radio)];
- uint8_t arfcn_low = trx->arfcn & 0xff;
- uint8_t arfcn_high = (trx->arfcn >> 8) & 0x0f;
- memcpy(trx1_attr_radio, bs11_attr_radio,
- sizeof(trx1_attr_radio));
-
- /* patch ARFCN into TRX Attributes */
- trx1_attr_radio[2] &= 0xf0;
- trx1_attr_radio[2] |= arfcn_high;
- trx1_attr_radio[3] = arfcn_low;
-
- abis_nm_set_radio_attr(trx, trx1_attr_radio,
- sizeof(trx1_attr_radio));
- }
- break;
- case GSM_BTS_TYPE_NANOBTS:
- switch (trx->bts->band) {
- case GSM_BAND_850:
- case GSM_BAND_900:
- trx->nominal_power = 20;
- break;
- case GSM_BAND_1800:
- case GSM_BAND_1900:
- trx->nominal_power = 23;
- break;
- default:
- LOGP(DNM, LOGL_ERROR, "Unsupported nanoBTS GSM band %s\n",
- gsm_band_name(trx->bts->band));
- break;
- }
- break;
- default:
- break;
- }
-
- for (i = 0; i < TRX_NR_TS; i++)
- nm_reconfig_ts(&trx->ts[i]);
-}
-
-static void nm_reconfig_bts(struct gsm_bts *bts)
-{
- struct gsm_bts_trx *trx;
-
- switch (bts->type) {
- case GSM_BTS_TYPE_BS11:
- patch_nm_tables(bts);
- abis_nm_raw_msg(bts, sizeof(msg_1), msg_1); /* set BTS SiteMgr attr*/
- abis_nm_set_bts_attr(bts, bs11_attr_bts, sizeof(bs11_attr_bts));
- abis_nm_raw_msg(bts, sizeof(msg_3), msg_3); /* set BTS handover attr */
- abis_nm_raw_msg(bts, sizeof(msg_4), msg_4); /* set BTS power control attr */
- break;
- default:
- break;
- }
-
- llist_for_each_entry(trx, &bts->trx_list, list)
- nm_reconfig_trx(trx);
-}
-
-
-static void bootstrap_om_bs11(struct gsm_bts *bts)
-{
- LOGP(DNM, LOGL_NOTICE, "bootstrapping OML for BTS %u\n", bts->nr);
-
- /* stop sending event reports */
- abis_nm_event_reports(bts, 0);
-
- /* begin DB transmission */
- abis_nm_bs11_db_transmission(bts, 1);
-
- /* end DB transmission */
- abis_nm_bs11_db_transmission(bts, 0);
-
- /* Reset BTS Site manager resource */
- abis_nm_bs11_reset_resource(bts);
-
- /* begin DB transmission */
- abis_nm_bs11_db_transmission(bts, 1);
-
- /* reconfigure BTS with all TRX and all TS */
- nm_reconfig_bts(bts);
-
- /* end DB transmission */
- abis_nm_bs11_db_transmission(bts, 0);
-
- /* Reset BTS Site manager resource */
- abis_nm_bs11_reset_resource(bts);
-
- /* restart sending event reports */
- abis_nm_event_reports(bts, 1);
-}
-
-static int shutdown_om(struct gsm_bts *bts)
-{
- /* stop sending event reports */
- abis_nm_event_reports(bts, 0);
-
- /* begin DB transmission */
- abis_nm_bs11_db_transmission(bts, 1);
-
- /* end DB transmission */
- abis_nm_bs11_db_transmission(bts, 0);
-
- /* Reset BTS Site manager resource */
- abis_nm_bs11_reset_resource(bts);
-
- return 0;
-}
-
-/* Callback function to be called every time we receive a signal from INPUT */
-static int gbl_sig_cb(unsigned int subsys, unsigned int signal,
- void *handler_data, void *signal_data)
-{
- struct gsm_bts *bts;
-
- if (subsys != SS_L_GLOBAL)
- return 0;
-
- switch (signal) {
- case S_GLOBAL_BTS_CLOSE_OM:
- bts = signal_data;
- if (bts->type == GSM_BTS_TYPE_BS11)
- shutdown_om(signal_data);
- break;
- }
-
- return 0;
-}
-
-/* Callback function to be called every time we receive a signal from INPUT */
-static int inp_sig_cb(unsigned int subsys, unsigned int signal,
- void *handler_data, void *signal_data)
-{
- struct input_signal_data *isd = signal_data;
-
- if (subsys != SS_L_INPUT)
- return 0;
-
- switch (signal) {
- case S_L_INP_TEI_UP:
- switch (isd->link_type) {
- case E1INP_SIGN_OML:
- if (isd->trx->bts->type == GSM_BTS_TYPE_BS11)
- bootstrap_om_bs11(isd->trx->bts);
- break;
- }
- }
-
- return 0;
-}
-
-static int bts_model_bs11_start(struct gsm_network *net)
-{
- model_bs11.features.data = &model_bs11._features_data[0];
- model_bs11.features.data_len = sizeof(model_bs11._features_data);
-
- gsm_btsmodel_set_feature(&model_bs11, BTS_FEAT_HOPPING);
- gsm_btsmodel_set_feature(&model_bs11, BTS_FEAT_HSCSD);
- gsm_btsmodel_set_feature(&model_bs11, BTS_FEAT_MULTI_TSC);
-
- osmo_signal_register_handler(SS_L_INPUT, inp_sig_cb, NULL);
- osmo_signal_register_handler(SS_L_GLOBAL, gbl_sig_cb, NULL);
-
- return 0;
-}
-
-int bts_model_bs11_init(void)
-{
- return gsm_bts_model_register(&model_bs11);
-}
diff --git a/openbsc/src/libbsc/bts_sysmobts.c b/openbsc/src/libbsc/bts_sysmobts.c
deleted file mode 100644
index e4b6cdc78..000000000
--- a/openbsc/src/libbsc/bts_sysmobts.c
+++ /dev/null
@@ -1,60 +0,0 @@
-/* sysmocom sysmoBTS specific code */
-
-/* (C) 2010-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 <arpa/inet.h>
-
-#include <osmocom/gsm/tlv.h>
-
-#include <openbsc/gsm_data.h>
-#include <openbsc/signal.h>
-#include <openbsc/abis_nm.h>
-#include <osmocom/abis/e1_input.h>
-#include <osmocom/gsm/tlv.h>
-#include <osmocom/core/msgb.h>
-#include <osmocom/core/talloc.h>
-#include <openbsc/gsm_data.h>
-#include <openbsc/abis_nm.h>
-#include <openbsc/abis_rsl.h>
-#include <openbsc/debug.h>
-#include <osmocom/abis/subchan_demux.h>
-#include <osmocom/abis/ipaccess.h>
-#include <osmocom/core/logging.h>
-
-extern struct gsm_bts_model bts_model_nanobts;
-
-static struct gsm_bts_model model_sysmobts;
-
-int bts_model_sysmobts_init(void)
-{
- model_sysmobts = bts_model_nanobts;
- model_sysmobts.name = "sysmobts";
- model_sysmobts.type = GSM_BTS_TYPE_OSMOBTS;
-
- model_sysmobts.features.data = &model_sysmobts._features_data[0];
- model_sysmobts.features.data_len =
- sizeof(model_sysmobts._features_data);
- memset(model_sysmobts.features.data, 0, sizeof(model_sysmobts.features.data_len));
-
- gsm_btsmodel_set_feature(&model_sysmobts, BTS_FEAT_GPRS);
- gsm_btsmodel_set_feature(&model_sysmobts, BTS_FEAT_EGPRS);
-
- return gsm_bts_model_register(&model_sysmobts);
-}
diff --git a/openbsc/src/libbsc/bts_unknown.c b/openbsc/src/libbsc/bts_unknown.c
deleted file mode 100644
index f1135294f..000000000
--- a/openbsc/src/libbsc/bts_unknown.c
+++ /dev/null
@@ -1,40 +0,0 @@
-/* Generic BTS - VTY code tries to allocate this BTS before type is known */
-
-/* (C) 2010 by Daniel Willmann <daniel@totalueberwachung.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 <openbsc/gsm_data.h>
-#include <osmocom/gsm/tlv.h>
-#include <openbsc/abis_nm.h>
-
-static struct gsm_bts_model model_unknown = {
- .type = GSM_BTS_TYPE_UNKNOWN,
- .name = "unknown",
- .oml_rcvmsg = &abis_nm_rcvmsg,
- .nm_att_tlvdef = {
- .def = {
- },
- },
-};
-
-int bts_model_unknown_init(void)
-{
- return gsm_bts_model_register(&model_unknown);
-}
diff --git a/openbsc/src/libbsc/chan_alloc.c b/openbsc/src/libbsc/chan_alloc.c
deleted file mode 100644
index 33b79a0b2..000000000
--- a/openbsc/src/libbsc/chan_alloc.c
+++ /dev/null
@@ -1,543 +0,0 @@
-/* GSM Channel allocation routines
- *
- * (C) 2008 by Harald Welte <laforge@gnumonks.org>
- * (C) 2008, 2009 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 <errno.h>
-
-#include <openbsc/gsm_subscriber.h>
-#include <openbsc/chan_alloc.h>
-#include <openbsc/abis_nm.h>
-#include <openbsc/abis_rsl.h>
-#include <openbsc/debug.h>
-#include <openbsc/rtp_proxy.h>
-#include <openbsc/signal.h>
-
-#include <osmocom/core/talloc.h>
-
-static int ts_is_usable(struct gsm_bts_trx_ts *ts)
-{
- /* FIXME: How does this behave for BS-11 ? */
- if (is_ipaccess_bts(ts->trx->bts)) {
- if (!nm_is_running(&ts->mo.nm_state))
- return 0;
- }
-
- /* If a TCH/F_PDCH TS is busy changing, it is already taken or not
- * yet available. */
- if (ts->pchan == GSM_PCHAN_TCH_F_PDCH) {
- if (ts->flags & TS_F_PDCH_PENDING_MASK)
- return 0;
- }
-
- /* If a dynamic channel is busy changing, it is already taken or not
- * yet available. */
- if (ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH) {
- if (ts->dyn.pchan_is != ts->dyn.pchan_want)
- return 0;
- }
-
- return 1;
-}
-
-int trx_is_usable(struct gsm_bts_trx *trx)
-{
- /* FIXME: How does this behave for BS-11 ? */
- if (is_ipaccess_bts(trx->bts)) {
- if (!nm_is_running(&trx->mo.nm_state) ||
- !nm_is_running(&trx->bb_transc.mo.nm_state))
- return 0;
- }
-
- return 1;
-}
-
-static struct gsm_lchan *
-_lc_find_trx(struct gsm_bts_trx *trx, enum gsm_phys_chan_config pchan,
- enum gsm_phys_chan_config dyn_as_pchan)
-{
- struct gsm_bts_trx_ts *ts;
- int j, start, stop, dir, ss;
- int check_subslots;
-
- if (!trx_is_usable(trx))
- return NULL;
-
- if (trx->bts->chan_alloc_reverse) {
- /* check TS 7..0 */
- start = 7;
- stop = -1;
- dir = -1;
- } else {
- /* check TS 0..7 */
- start = 0;
- stop = 8;
- dir = 1;
- }
-
- for (j = start; j != stop; j += dir) {
- ts = &trx->ts[j];
- if (!ts_is_usable(ts))
- continue;
- if (ts->pchan != pchan)
- continue;
-
- /*
- * Allocation for fully dynamic timeslots
- * (does not apply for ip.access style GSM_PCHAN_TCH_F_PDCH)
- *
- * Note the special nature of a dynamic timeslot in PDCH mode:
- * in PDCH mode, typically, lchan->type is GSM_LCHAN_NONE and
- * lchan->state is LCHAN_S_NONE -- an otherwise unused slot
- * becomes PDCH implicitly. In the same sense, this channel
- * allocator will never be asked to find an available PDCH
- * slot; only TCH/F or TCH/H will be requested, and PDCH mode
- * means that it is available for switchover.
- *
- * A dynamic timeslot in PDCH mode may be switched to TCH/F or
- * TCH/H. If a dyn TS is already in TCH/F or TCH/H mode, it
- * means that it is in use and its mode can't be switched.
- *
- * The logic concerning channels for TCH/F is trivial: there is
- * only one channel, so a dynamic TS in TCH/F mode is already
- * taken and not available for allocation. For TCH/H, we need
- * to check whether a dynamic timeslot is already in TCH/H mode
- * and whether one of the two channels is still available.
- */
- switch (pchan) {
- case GSM_PCHAN_TCH_F_TCH_H_PDCH:
- if (ts->dyn.pchan_is != ts->dyn.pchan_want) {
- /* The TS's mode is being switched. Not
- * available anymore/yet. */
- DEBUGP(DRLL, "%s already in switchover\n",
- gsm_ts_and_pchan_name(ts));
- continue;
- }
- if (ts->dyn.pchan_is == GSM_PCHAN_PDCH) {
- /* This slot is available. Still check for
- * error states to be sure; in all cases the
- * first lchan will be used. */
- if (ts->lchan->state != LCHAN_S_NONE
- && ts->lchan->state != LCHAN_S_ACTIVE)
- continue;
- return ts->lchan;
- }
- if (ts->dyn.pchan_is != dyn_as_pchan)
- /* not applicable. */
- continue;
- /* The requested type matches the dynamic timeslot's
- * current mode. A channel may still be available
- * (think TCH/H). */
- check_subslots = ts_subslots(ts);
- break;
-
- case GSM_PCHAN_TCH_F_PDCH:
- /* Available for voice when in PDCH mode */
- if (ts_pchan(ts) != GSM_PCHAN_PDCH)
- continue;
- /* Subslots of a PDCH ts don't need to be checked. */
- return ts->lchan;
-
- default:
- /* Not a dynamic channel, there is only one pchan kind: */
- check_subslots = ts_subslots(ts);
- break;
- }
-
- /* Is a sub-slot still available? */
- for (ss = 0; ss < check_subslots; ss++) {
- struct gsm_lchan *lc = &ts->lchan[ss];
- if (lc->type == GSM_LCHAN_NONE &&
- lc->state == LCHAN_S_NONE)
- return lc;
- }
- }
-
- return NULL;
-}
-
-static struct gsm_lchan *
-_lc_dyn_find_bts(struct gsm_bts *bts, enum gsm_phys_chan_config pchan,
- enum gsm_phys_chan_config dyn_as_pchan)
-{
- struct gsm_bts_trx *trx;
- struct gsm_lchan *lc;
-
- if (bts->chan_alloc_reverse) {
- llist_for_each_entry_reverse(trx, &bts->trx_list, list) {
- lc = _lc_find_trx(trx, pchan, dyn_as_pchan);
- if (lc)
- return lc;
- }
- } else {
- llist_for_each_entry(trx, &bts->trx_list, list) {
- lc = _lc_find_trx(trx, pchan, dyn_as_pchan);
- if (lc)
- return lc;
- }
- }
-
- return NULL;
-}
-
-static struct gsm_lchan *
-_lc_find_bts(struct gsm_bts *bts, enum gsm_phys_chan_config pchan)
-{
- return _lc_dyn_find_bts(bts, pchan, GSM_PCHAN_NONE);
-}
-
-/* Allocate a logical channel.
- *
- * Dynamic channel types: we always prefer a dedicated TS, and only pick +
- * switch a dynamic TS if no pure TS of the requested PCHAN is available.
- *
- * TCH_F/PDCH: if we pick a PDCH ACT style dynamic TS as TCH/F channel, PDCH
- * will be disabled in rsl_chan_activate_lchan(); there is no need to check
- * whether PDCH mode is currently active, here.
- */
-struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type,
- int allow_bigger)
-{
- struct gsm_lchan *lchan = NULL;
- enum gsm_phys_chan_config first, first_cbch, second, second_cbch;
-
- switch (type) {
- case GSM_LCHAN_SDCCH:
- if (bts->chan_alloc_reverse) {
- first = GSM_PCHAN_SDCCH8_SACCH8C;
- first_cbch = GSM_PCHAN_SDCCH8_SACCH8C_CBCH;
- second = GSM_PCHAN_CCCH_SDCCH4;
- second_cbch = GSM_PCHAN_CCCH_SDCCH4_CBCH;
- } else {
- first = GSM_PCHAN_CCCH_SDCCH4;
- first_cbch = GSM_PCHAN_CCCH_SDCCH4_CBCH;
- second = GSM_PCHAN_SDCCH8_SACCH8C;
- second_cbch = GSM_PCHAN_SDCCH8_SACCH8C_CBCH;
- }
-
- lchan = _lc_find_bts(bts, first);
- if (lchan == NULL)
- lchan = _lc_find_bts(bts, first_cbch);
- if (lchan == NULL)
- lchan = _lc_find_bts(bts, second);
- if (lchan == NULL)
- lchan = _lc_find_bts(bts, second_cbch);
-
- /* allow to assign bigger channels */
- if (allow_bigger) {
- if (lchan == NULL) {
- lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_H);
- if (lchan)
- type = GSM_LCHAN_TCH_H;
- }
-
- if (lchan == NULL) {
- lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F);
- if (lchan)
- type = GSM_LCHAN_TCH_F;
- }
-
- /* try dynamic TCH/F_PDCH */
- if (lchan == NULL) {
- lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F_PDCH);
- /* TCH/F_PDCH will be used as TCH/F */
- if (lchan)
- type = GSM_LCHAN_TCH_F;
- }
-
- /* try fully dynamic TCH/F_TCH/H_PDCH */
- if (lchan == NULL) {
- lchan = _lc_dyn_find_bts(bts, GSM_PCHAN_TCH_F_TCH_H_PDCH,
- GSM_PCHAN_TCH_H);
- if (lchan)
- type = GSM_LCHAN_TCH_H;
- }
- /*
- * No need to check fully dynamic channels for TCH/F:
- * if no TCH/H was available, neither will be TCH/F.
- */
- }
- break;
- case GSM_LCHAN_TCH_F:
- lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F);
- /* If we don't have TCH/F available, fall-back to TCH/H */
- if (!lchan) {
- lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_H);
- if (lchan)
- type = GSM_LCHAN_TCH_H;
- }
- /* If we don't have TCH/H either, try dynamic TCH/F_PDCH */
- if (!lchan) {
- lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F_PDCH);
- /* TCH/F_PDCH used as TCH/F -- here, type is already
- * set to GSM_LCHAN_TCH_F, but for clarity's sake... */
- if (lchan)
- type = GSM_LCHAN_TCH_F;
- }
-
- /* Try fully dynamic TCH/F_TCH/H_PDCH as TCH/F... */
- if (!lchan && bts->network->dyn_ts_allow_tch_f) {
- lchan = _lc_dyn_find_bts(bts,
- GSM_PCHAN_TCH_F_TCH_H_PDCH,
- GSM_PCHAN_TCH_F);
- if (lchan)
- type = GSM_LCHAN_TCH_F;
- }
- /* ...and as TCH/H. */
- if (!lchan) {
- lchan = _lc_dyn_find_bts(bts,
- GSM_PCHAN_TCH_F_TCH_H_PDCH,
- GSM_PCHAN_TCH_H);
- if (lchan)
- type = GSM_LCHAN_TCH_H;
- }
- break;
- case GSM_LCHAN_TCH_H:
- lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_H);
- /* If we don't have TCH/H available, fall-back to TCH/F */
- if (!lchan) {
- lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F);
- if (lchan)
- type = GSM_LCHAN_TCH_F;
- }
- /* No dedicated TCH/x available -- try fully dynamic
- * TCH/F_TCH/H_PDCH */
- if (!lchan) {
- lchan = _lc_dyn_find_bts(bts,
- GSM_PCHAN_TCH_F_TCH_H_PDCH,
- GSM_PCHAN_TCH_H);
- if (lchan)
- type = GSM_LCHAN_TCH_H;
- }
- /*
- * No need to check TCH/F_TCH/H_PDCH channels for TCH/F:
- * if no TCH/H was available, neither will be TCH/F.
- */
- /* If we don't have TCH/F either, try dynamic TCH/F_PDCH */
- if (!lchan) {
- lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F_PDCH);
- if (lchan)
- type = GSM_LCHAN_TCH_F;
- }
- break;
- default:
- LOGP(DRLL, LOGL_ERROR, "Unknown gsm_chan_t %u\n", type);
- }
-
- if (lchan) {
- lchan->type = type;
-
- LOGP(DRLL, LOGL_INFO, "%s Allocating lchan=%u as %s\n",
- gsm_ts_and_pchan_name(lchan->ts),
- lchan->nr, gsm_lchant_name(lchan->type));
-
- /* clear sapis */
- memset(lchan->sapis, 0, ARRAY_SIZE(lchan->sapis));
-
- /* clear multi rate config */
- memset(&lchan->mr_ms_lv, 0, sizeof(lchan->mr_ms_lv));
- memset(&lchan->mr_bts_lv, 0, sizeof(lchan->mr_bts_lv));
- lchan->broken_reason = "";
- } else {
- struct challoc_signal_data sig;
-
- LOGP(DRLL, LOGL_ERROR, "Failed to allocate %s channel\n",
- gsm_lchant_name(type));
-
- sig.bts = bts;
- sig.type = type;
- osmo_signal_dispatch(SS_CHALLOC, S_CHALLOC_ALLOC_FAIL, &sig);
- }
-
- return lchan;
-}
-
-/* Free a logical channel */
-void lchan_free(struct gsm_lchan *lchan)
-{
- struct challoc_signal_data sig;
- int i;
-
- sig.type = lchan->type;
- lchan->type = GSM_LCHAN_NONE;
-
-
- if (lchan->conn) {
- struct lchan_signal_data sig;
-
- /* We might kill an active channel... */
- sig.lchan = lchan;
- sig.mr = NULL;
- osmo_signal_dispatch(SS_LCHAN, S_LCHAN_UNEXPECTED_RELEASE, &sig);
- }
-
- if (lchan->abis_ip.rtp_socket) {
- LOGP(DRLL, LOGL_ERROR, "%s RTP Proxy Socket remained open.\n",
- gsm_lchan_name(lchan));
- rtp_socket_free(lchan->abis_ip.rtp_socket);
- lchan->abis_ip.rtp_socket = NULL;
- }
-
- /* stop the timer */
- osmo_timer_del(&lchan->T3101);
-
- /* clear cached measuement reports */
- lchan->meas_rep_idx = 0;
- for (i = 0; i < ARRAY_SIZE(lchan->meas_rep); i++) {
- lchan->meas_rep[i].flags = 0;
- lchan->meas_rep[i].nr = 0;
- }
- for (i = 0; i < ARRAY_SIZE(lchan->neigh_meas); i++)
- lchan->neigh_meas[i].arfcn = 0;
-
- if (lchan->rqd_ref) {
- talloc_free(lchan->rqd_ref);
- lchan->rqd_ref = NULL;
- lchan->rqd_ta = 0;
- }
-
- sig.lchan = lchan;
- sig.bts = lchan->ts->trx->bts;
- osmo_signal_dispatch(SS_CHALLOC, S_CHALLOC_FREED, &sig);
-
- if (lchan->conn) {
- LOGP(DRLL, LOGL_ERROR, "the subscriber connection should be gone.\n");
- lchan->conn = NULL;
- }
-
- /* FIXME: ts_free() the timeslot, if we're the last logical
- * channel using it */
-}
-
-/*
- * There was an error with the TRX and we need to forget
- * any state so that a lchan can be allocated again after
- * the trx is fully usable.
- *
- * This should be called after lchan_free to force a channel
- * be available for allocation again. This means that this
- * method will stop the "delay after error"-timer and set the
- * state to LCHAN_S_NONE.
- */
-void lchan_reset(struct gsm_lchan *lchan)
-{
- osmo_timer_del(&lchan->T3101);
- osmo_timer_del(&lchan->T3109);
- osmo_timer_del(&lchan->T3111);
- osmo_timer_del(&lchan->error_timer);
-
- lchan->type = GSM_LCHAN_NONE;
- lchan->state = LCHAN_S_NONE;
-
- if (lchan->abis_ip.rtp_socket) {
- rtp_socket_free(lchan->abis_ip.rtp_socket);
- lchan->abis_ip.rtp_socket = NULL;
- }
-}
-
-/* Drive the release process of the lchan */
-static void _lchan_handle_release(struct gsm_lchan *lchan,
- int sacch_deact, int mode)
-{
- /* Release all SAPIs on the local end and continue */
- rsl_release_sapis_from(lchan, 1, RSL_REL_LOCAL_END);
-
- /*
- * Shall we send a RR Release, start T3109 and wait for the
- * release indication from the BTS or just take it down (e.g.
- * on assignment requests)
- */
- if (sacch_deact) {
- gsm48_send_rr_release(lchan);
-
- /* Deactivate the SACCH on the BTS side */
- rsl_deact_sacch(lchan);
- rsl_start_t3109(lchan);
- } else if (lchan->sapis[0] == LCHAN_SAPI_UNUSED) {
- rsl_direct_rf_release(lchan);
- } else {
- rsl_release_request(lchan, 0, mode);
- }
-}
-
-/* Consider releasing the channel now */
-int lchan_release(struct gsm_lchan *lchan, int sacch_deact, enum rsl_rel_mode mode)
-{
- DEBUGP(DRLL, "%s starting release sequence\n", gsm_lchan_name(lchan));
- rsl_lchan_set_state(lchan, LCHAN_S_REL_REQ);
-
- lchan->conn = NULL;
- _lchan_handle_release(lchan, sacch_deact, mode);
- return 1;
-}
-
-void bts_chan_load(struct pchan_load *cl, const struct gsm_bts *bts)
-{
- struct gsm_bts_trx *trx;
-
- llist_for_each_entry(trx, &bts->trx_list, list) {
- int i;
-
- /* skip administratively deactivated tranxsceivers */
- if (!nm_is_running(&trx->mo.nm_state) ||
- !nm_is_running(&trx->bb_transc.mo.nm_state))
- continue;
-
- for (i = 0; i < ARRAY_SIZE(trx->ts); i++) {
- struct gsm_bts_trx_ts *ts = &trx->ts[i];
- struct load_counter *pl = &cl->pchan[ts->pchan];
- int j;
- int subslots;
-
- /* skip administratively deactivated timeslots */
- if (!nm_is_running(&ts->mo.nm_state))
- continue;
-
- subslots = ts_subslots(ts);
- for (j = 0; j < subslots; j++) {
- struct gsm_lchan *lchan = &ts->lchan[j];
-
- pl->total++;
-
- switch (lchan->state) {
- case LCHAN_S_NONE:
- break;
- default:
- pl->used++;
- break;
- }
- }
- }
- }
-}
-
-void network_chan_load(struct pchan_load *pl, struct gsm_network *net)
-{
- struct gsm_bts *bts;
-
- memset(pl, 0, sizeof(*pl));
-
- llist_for_each_entry(bts, &net->bts_list, list)
- bts_chan_load(pl, bts);
-}
-
diff --git a/openbsc/src/libbsc/e1_config.c b/openbsc/src/libbsc/e1_config.c
deleted file mode 100644
index d57dec57e..000000000
--- a/openbsc/src/libbsc/e1_config.c
+++ /dev/null
@@ -1,297 +0,0 @@
-/* OpenBSC E1 Input code */
-
-/* (C) 2008-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 <string.h>
-#include <errno.h>
-
-#include <netinet/in.h>
-
-#include <openbsc/gsm_data.h>
-#include <osmocom/abis/e1_input.h>
-#include <osmocom/abis/trau_frame.h>
-#include <openbsc/trau_mux.h>
-#include <openbsc/misdn.h>
-#include <osmocom/abis/ipaccess.h>
-#include <osmocom/core/talloc.h>
-#include <openbsc/debug.h>
-#include <openbsc/abis_rsl.h>
-
-#define SAPI_L2ML 0
-#define SAPI_OML 62
-#define SAPI_RSL 0 /* 63 ? */
-
-/* The e1_reconfig_*() functions below take the configuration present in the
- * bts/trx/ts data structures and ensure the E1 configuration reflects the
- * timeslot/subslot/TEI configuration */
-
-int e1_reconfig_ts(struct gsm_bts_trx_ts *ts)
-{
- struct gsm_e1_subslot *e1_link = &ts->e1_link;
- struct e1inp_line *line;
- struct e1inp_ts *e1_ts;
-
- DEBUGP(DLMI, "e1_reconfig_ts(%u,%u,%u)\n", ts->trx->bts->nr, ts->trx->nr, ts->nr);
-
- if (!e1_link->e1_ts) {
- LOGP(DLINP, LOGL_ERROR, "TS (%u/%u/%u) without E1 timeslot?\n",
- ts->nr, ts->trx->nr, ts->trx->bts->nr);
- return 0;
- }
-
- line = e1inp_line_find(e1_link->e1_nr);
- if (!line) {
- LOGP(DLINP, LOGL_ERROR, "TS (%u/%u/%u) referring to "
- "non-existing E1 line %u\n", ts->nr, ts->trx->nr,
- ts->trx->bts->nr, e1_link->e1_nr);
- return -ENOMEM;
- }
-
- if (ts_is_tch(ts)) {
- e1_ts = &line->ts[e1_link->e1_ts-1];
- e1inp_ts_config_trau(e1_ts, line, subch_cb);
- subch_demux_activate(&e1_ts->trau.demux, e1_link->e1_ts_ss);
- }
-
- return 0;
-}
-
-int e1_reconfig_trx(struct gsm_bts_trx *trx)
-{
- struct gsm_e1_subslot *e1_link = &trx->rsl_e1_link;
- struct e1inp_ts *sign_ts;
- struct e1inp_line *line;
- struct e1inp_sign_link *rsl_link;
- int i;
-
- if (!e1_link->e1_ts) {
- LOGP(DLINP, LOGL_ERROR, "TRX (%u/%u) RSL link without "
- "timeslot?\n", trx->bts->nr, trx->nr);
- return -EINVAL;
- }
-
- /* RSL Link */
- line = e1inp_line_find(e1_link->e1_nr);
- if (!line) {
- LOGP(DLINP, LOGL_ERROR, "TRX (%u/%u) RSL link referring "
- "to non-existing E1 line %u\n", trx->bts->nr,
- trx->nr, e1_link->e1_nr);
- return -ENOMEM;
- }
- sign_ts = &line->ts[e1_link->e1_ts-1];
- e1inp_ts_config_sign(sign_ts, line);
- /* Ericsson RBS have a per-TRX OML link in parallel to RSL */
- if (trx->bts->type == GSM_BTS_TYPE_RBS2000) {
- struct e1inp_sign_link *oml_link;
- oml_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OML, trx,
- trx->rsl_tei, SAPI_OML);
- if (!oml_link) {
- LOGP(DLINP, LOGL_ERROR, "TRX (%u/%u) OML link creation "
- "failed\n", trx->bts->nr, trx->nr);
- return -ENOMEM;
- }
- if (trx->oml_link)
- e1inp_sign_link_destroy(trx->oml_link);
- trx->oml_link = oml_link;
- }
- rsl_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_RSL,
- trx, trx->rsl_tei, SAPI_RSL);
- if (!rsl_link) {
- LOGP(DLINP, LOGL_ERROR, "TRX (%u/%u) RSL link creation "
- "failed\n", trx->bts->nr, trx->nr);
- return -ENOMEM;
- }
- if (trx->rsl_link)
- e1inp_sign_link_destroy(trx->rsl_link);
- trx->rsl_link = rsl_link;
-
- for (i = 0; i < TRX_NR_TS; i++)
- e1_reconfig_ts(&trx->ts[i]);
-
- return 0;
-}
-
-/* this is the generic callback for all ISDN-based BTS. */
-static int bts_isdn_sign_link(struct msgb *msg)
-{
- int ret = -EINVAL;
- struct e1inp_sign_link *link = msg->dst;
- struct gsm_bts *bts;
-
- switch (link->type) {
- case E1INP_SIGN_OML:
- bts = link->trx->bts;
- ret = bts->model->oml_rcvmsg(msg);
- break;
- case E1INP_SIGN_RSL:
- ret = abis_rsl_rcvmsg(msg);
- break;
- default:
- LOGP(DLMI, LOGL_ERROR, "unknown link type %u\n", link->type);
- break;
- }
- return ret;
-}
-
-struct e1inp_line_ops bts_isdn_e1inp_line_ops = {
- .sign_link = bts_isdn_sign_link,
-};
-
-int e1_reconfig_bts(struct gsm_bts *bts)
-{
- struct gsm_e1_subslot *e1_link = &bts->oml_e1_link;
- struct e1inp_ts *sign_ts;
- struct e1inp_line *line;
- struct e1inp_sign_link *oml_link;
- struct gsm_bts_trx *trx;
-
- DEBUGP(DLMI, "e1_reconfig_bts(%u)\n", bts->nr);
-
- line = e1inp_line_find(e1_link->e1_nr);
- if (!line) {
- LOGP(DLINP, LOGL_ERROR, "BTS %u OML link referring to "
- "non-existing E1 line %u\n", bts->nr, e1_link->e1_nr);
- return -ENOMEM;
- }
-
- if (!bts->model->e1line_bind_ops) {
- LOGP(DLINP, LOGL_ERROR, "no callback to bind E1 line operations\n");
- return -EINVAL;
- }
- if (!line->ops)
- bts->model->e1line_bind_ops(line);
-
- /* skip signal link initialization, this is done later for these BTS. */
- if (bts->type == GSM_BTS_TYPE_NANOBTS ||
- bts->type == GSM_BTS_TYPE_OSMOBTS)
- return e1inp_line_update(line);
-
- /* OML link */
- if (!e1_link->e1_ts) {
- LOGP(DLINP, LOGL_ERROR, "BTS %u OML link without timeslot?\n",
- bts->nr);
- return -EINVAL;
- }
-
- sign_ts = &line->ts[e1_link->e1_ts-1];
- e1inp_ts_config_sign(sign_ts, line);
- oml_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OML,
- bts->c0, bts->oml_tei, SAPI_OML);
- if (!oml_link) {
- LOGP(DLINP, LOGL_ERROR, "BTS %u OML link creation failed\n",
- bts->nr);
- return -ENOMEM;
- }
- if (bts->oml_link)
- e1inp_sign_link_destroy(bts->oml_link);
- bts->oml_link = oml_link;
-
- llist_for_each_entry(trx, &bts->trx_list, list)
- e1_reconfig_trx(trx);
-
- /* notify E1 input something has changed */
- return e1inp_line_update(line);
-}
-
-#if 0
-/* do some compiled-in configuration for our BTS/E1 setup */
-int e1_config(struct gsm_bts *bts, int cardnr, int release_l2)
-{
- struct e1inp_line *line;
- struct e1inp_ts *sign_ts;
- struct e1inp_sign_link *oml_link, *rsl_link;
- struct gsm_bts_trx *trx = bts->c0;
- int base_ts;
-
- switch (bts->nr) {
- case 0:
- /* First BTS uses E1 TS 01,02,03,04,05 */
- base_ts = HARDCODED_BTS0_TS - 1;
- break;
- case 1:
- /* Second BTS uses E1 TS 06,07,08,09,10 */
- base_ts = HARDCODED_BTS1_TS - 1;
- break;
- case 2:
- /* Third BTS uses E1 TS 11,12,13,14,15 */
- base_ts = HARDCODED_BTS2_TS - 1;
- default:
- return -EINVAL;
- }
-
- line = talloc_zero(tall_bsc_ctx, struct e1inp_line);
- if (!line)
- return -ENOMEM;
-
- /* create E1 timeslots for signalling and TRAU frames */
- e1inp_ts_config(&line->ts[base_ts+1-1], line, E1INP_TS_TYPE_SIGN);
- e1inp_ts_config(&line->ts[base_ts+2-1], line, E1INP_TS_TYPE_TRAU);
- e1inp_ts_config(&line->ts[base_ts+3-1], line, E1INP_TS_TYPE_TRAU);
-
- /* create signalling links for TS1 */
- sign_ts = &line->ts[base_ts+1-1];
- oml_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OML,
- trx, TEI_OML, SAPI_OML);
- rsl_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_RSL,
- trx, TEI_RSL, SAPI_RSL);
-
- /* create back-links from bts/trx */
- bts->oml_link = oml_link;
- trx->rsl_link = rsl_link;
-
- /* enable subchannel demuxer on TS2 */
- subch_demux_activate(&line->ts[base_ts+2-1].trau.demux, 1);
- subch_demux_activate(&line->ts[base_ts+2-1].trau.demux, 2);
- subch_demux_activate(&line->ts[base_ts+2-1].trau.demux, 3);
-
- /* enable subchannel demuxer on TS3 */
- subch_demux_activate(&line->ts[base_ts+3-1].trau.demux, 0);
- subch_demux_activate(&line->ts[base_ts+3-1].trau.demux, 1);
- subch_demux_activate(&line->ts[base_ts+3-1].trau.demux, 2);
- subch_demux_activate(&line->ts[base_ts+3-1].trau.demux, 3);
-
- trx = gsm_bts_trx_num(bts, 1);
- if (trx) {
- /* create E1 timeslots for TRAU frames of TRX1 */
- e1inp_ts_config(&line->ts[base_ts+4-1], line, E1INP_TS_TYPE_TRAU);
- e1inp_ts_config(&line->ts[base_ts+5-1], line, E1INP_TS_TYPE_TRAU);
-
- /* create RSL signalling link for TRX1 */
- sign_ts = &line->ts[base_ts+1-1];
- rsl_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_RSL,
- trx, TEI_RSL+1, SAPI_RSL);
- /* create back-links from trx */
- trx->rsl_link = rsl_link;
-
- /* enable subchannel demuxer on TS2 */
- subch_demux_activate(&line->ts[base_ts+4-1].trau.demux, 0);
- subch_demux_activate(&line->ts[base_ts+4-1].trau.demux, 1);
- subch_demux_activate(&line->ts[base_ts+4-1].trau.demux, 2);
- subch_demux_activate(&line->ts[base_ts+4-1].trau.demux, 3);
-
- /* enable subchannel demuxer on TS3 */
- subch_demux_activate(&line->ts[base_ts+5-1].trau.demux, 0);
- subch_demux_activate(&line->ts[base_ts+5-1].trau.demux, 1);
- subch_demux_activate(&line->ts[base_ts+5-1].trau.demux, 2);
- subch_demux_activate(&line->ts[base_ts+5-1].trau.demux, 3);
- }
-
- return mi_setup(cardnr, line, release_l2);
-}
-#endif
diff --git a/openbsc/src/libbsc/gsm_04_08_utils.c b/openbsc/src/libbsc/gsm_04_08_utils.c
deleted file mode 100644
index 3447d27cb..000000000
--- a/openbsc/src/libbsc/gsm_04_08_utils.c
+++ /dev/null
@@ -1,687 +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
- * utility functions
- */
-
-/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
- * (C) 2008, 2009 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 <errno.h>
-#include <netinet/in.h>
-
-#include <osmocom/core/msgb.h>
-#include <osmocom/gsm/gsm48.h>
-
-#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>
-
-/* should ip.access BTS use direct RTP streams between each other (1),
- * or should OpenBSC always act as RTP relay/proxy in between (0) ? */
-int ipacc_rtp_direct = 1;
-
-static int gsm48_sendmsg(struct msgb *msg)
-{
- if (msg->lchan)
- msg->dst = msg->lchan->ts->trx->rsl_link;
-
- msg->l3h = msg->data;
- return rsl_data_request(msg, 0);
-}
-
-/* Section 9.1.8 / Table 9.9 */
-struct chreq {
- uint8_t val;
- uint8_t mask;
- enum chreq_type type;
-};
-
-/* If SYSTEM INFORMATION TYPE 4 NECI bit == 1 */
-static const struct chreq chreq_type_neci1[] = {
- { 0xa0, 0xe0, CHREQ_T_EMERG_CALL },
- { 0xc0, 0xe0, CHREQ_T_CALL_REEST_TCH_F },
- { 0x68, 0xfc, CHREQ_T_CALL_REEST_TCH_H },
- { 0x6c, 0xfc, CHREQ_T_CALL_REEST_TCH_H_DBL },
- { 0xe0, 0xe0, CHREQ_T_TCH_F },
- { 0x40, 0xf0, CHREQ_T_VOICE_CALL_TCH_H },
- { 0x50, 0xf0, CHREQ_T_DATA_CALL_TCH_H },
- { 0x00, 0xf0, CHREQ_T_LOCATION_UPD },
- { 0x10, 0xf0, CHREQ_T_SDCCH },
- { 0x80, 0xe0, CHREQ_T_PAG_R_ANY_NECI1 },
- { 0x20, 0xf0, CHREQ_T_PAG_R_TCH_F },
- { 0x30, 0xf0, CHREQ_T_PAG_R_TCH_FH },
- { 0x67, 0xff, CHREQ_T_LMU },
- { 0x60, 0xf9, CHREQ_T_RESERVED_SDCCH },
- { 0x61, 0xfb, CHREQ_T_RESERVED_SDCCH },
- { 0x63, 0xff, CHREQ_T_RESERVED_SDCCH },
- { 0x70, 0xf8, CHREQ_T_PDCH_TWO_PHASE },
- { 0x78, 0xfc, CHREQ_T_PDCH_ONE_PHASE },
- { 0x78, 0xfa, CHREQ_T_PDCH_ONE_PHASE },
- { 0x78, 0xf9, CHREQ_T_PDCH_ONE_PHASE },
- { 0x7f, 0xff, CHREQ_T_RESERVED_IGNORE },
-};
-
-/* If SYSTEM INFORMATION TYPE 4 NECI bit == 0 */
-static const struct chreq chreq_type_neci0[] = {
- { 0xa0, 0xe0, CHREQ_T_EMERG_CALL },
- { 0xc0, 0xe0, CHREQ_T_CALL_REEST_TCH_H },
- { 0xe0, 0xe0, CHREQ_T_TCH_F },
- { 0x50, 0xf0, CHREQ_T_DATA_CALL_TCH_H },
- { 0x00, 0xe0, CHREQ_T_LOCATION_UPD },
- { 0x80, 0xe0, CHREQ_T_PAG_R_ANY_NECI0 },
- { 0x20, 0xf0, CHREQ_T_PAG_R_TCH_F },
- { 0x30, 0xf0, CHREQ_T_PAG_R_TCH_FH },
- { 0x67, 0xff, CHREQ_T_LMU },
- { 0x60, 0xf9, CHREQ_T_RESERVED_SDCCH },
- { 0x61, 0xfb, CHREQ_T_RESERVED_SDCCH },
- { 0x63, 0xff, CHREQ_T_RESERVED_SDCCH },
- { 0x70, 0xf8, CHREQ_T_PDCH_TWO_PHASE },
- { 0x78, 0xfc, CHREQ_T_PDCH_ONE_PHASE },
- { 0x78, 0xfa, CHREQ_T_PDCH_ONE_PHASE },
- { 0x78, 0xf9, CHREQ_T_PDCH_ONE_PHASE },
- { 0x7f, 0xff, CHREQ_T_RESERVED_IGNORE },
-};
-
-static const enum gsm_chan_t ctype_by_chreq[] = {
- [CHREQ_T_EMERG_CALL] = GSM_LCHAN_TCH_F,
- [CHREQ_T_CALL_REEST_TCH_F] = GSM_LCHAN_TCH_F,
- [CHREQ_T_CALL_REEST_TCH_H] = GSM_LCHAN_TCH_H,
- [CHREQ_T_CALL_REEST_TCH_H_DBL] = GSM_LCHAN_TCH_H,
- [CHREQ_T_SDCCH] = GSM_LCHAN_SDCCH,
- [CHREQ_T_TCH_F] = GSM_LCHAN_TCH_F,
- [CHREQ_T_VOICE_CALL_TCH_H] = GSM_LCHAN_TCH_H,
- [CHREQ_T_DATA_CALL_TCH_H] = GSM_LCHAN_TCH_H,
- [CHREQ_T_LOCATION_UPD] = GSM_LCHAN_SDCCH,
- [CHREQ_T_PAG_R_ANY_NECI1] = GSM_LCHAN_SDCCH,
- [CHREQ_T_PAG_R_ANY_NECI0] = GSM_LCHAN_SDCCH,
- [CHREQ_T_PAG_R_TCH_F] = GSM_LCHAN_TCH_F,
- [CHREQ_T_PAG_R_TCH_FH] = GSM_LCHAN_TCH_F,
- [CHREQ_T_LMU] = GSM_LCHAN_SDCCH,
- [CHREQ_T_RESERVED_SDCCH] = GSM_LCHAN_SDCCH,
- [CHREQ_T_PDCH_ONE_PHASE] = GSM_LCHAN_PDTCH,
- [CHREQ_T_PDCH_TWO_PHASE] = GSM_LCHAN_PDTCH,
- [CHREQ_T_RESERVED_IGNORE] = GSM_LCHAN_UNKNOWN,
-};
-
-static const enum gsm_chreq_reason_t reason_by_chreq[] = {
- [CHREQ_T_EMERG_CALL] = GSM_CHREQ_REASON_EMERG,
- [CHREQ_T_CALL_REEST_TCH_F] = GSM_CHREQ_REASON_CALL,
- [CHREQ_T_CALL_REEST_TCH_H] = GSM_CHREQ_REASON_CALL,
- [CHREQ_T_CALL_REEST_TCH_H_DBL] = GSM_CHREQ_REASON_CALL,
- [CHREQ_T_SDCCH] = GSM_CHREQ_REASON_OTHER,
- [CHREQ_T_TCH_F] = GSM_CHREQ_REASON_OTHER,
- [CHREQ_T_VOICE_CALL_TCH_H] = GSM_CHREQ_REASON_CALL,
- [CHREQ_T_DATA_CALL_TCH_H] = GSM_CHREQ_REASON_OTHER,
- [CHREQ_T_LOCATION_UPD] = GSM_CHREQ_REASON_LOCATION_UPD,
- [CHREQ_T_PAG_R_ANY_NECI1] = GSM_CHREQ_REASON_PAG,
- [CHREQ_T_PAG_R_ANY_NECI0] = GSM_CHREQ_REASON_PAG,
- [CHREQ_T_PAG_R_TCH_F] = GSM_CHREQ_REASON_PAG,
- [CHREQ_T_PAG_R_TCH_FH] = GSM_CHREQ_REASON_PAG,
- [CHREQ_T_LMU] = GSM_CHREQ_REASON_OTHER,
- [CHREQ_T_PDCH_ONE_PHASE] = GSM_CHREQ_REASON_PDCH,
- [CHREQ_T_PDCH_TWO_PHASE] = GSM_CHREQ_REASON_PDCH,
- [CHREQ_T_RESERVED_SDCCH] = GSM_CHREQ_REASON_OTHER,
- [CHREQ_T_RESERVED_IGNORE] = GSM_CHREQ_REASON_OTHER,
-};
-
-/* verify that the two tables match */
-osmo_static_assert(sizeof(ctype_by_chreq) ==
- sizeof(((struct gsm_network *) NULL)->ctype_by_chreq), assert_size);
-
-/*
- * Update channel types for request based on policy. E.g. in the
- * case of a TCH/H network/bsc use TCH/H for the emergency calls,
- * for early assignment assign a SDCCH and some other options.
- */
-void gsm_net_update_ctype(struct gsm_network *network)
-{
- /* copy over the data */
- memcpy(network->ctype_by_chreq, ctype_by_chreq, sizeof(ctype_by_chreq));
-
- /*
- * Use TCH/H for emergency calls when this cell allows TCH/H. Maybe it
- * is better to iterate over the BTS/TRX and check if no TCH/F is available
- * and then set it to TCH/H.
- */
- if (network->neci)
- network->ctype_by_chreq[CHREQ_T_EMERG_CALL] = GSM_LCHAN_TCH_H;
-
- if (network->pag_any_tch) {
- if (network->neci) {
- network->ctype_by_chreq[CHREQ_T_PAG_R_ANY_NECI0] = GSM_LCHAN_TCH_H;
- network->ctype_by_chreq[CHREQ_T_PAG_R_ANY_NECI1] = GSM_LCHAN_TCH_H;
- } else {
- network->ctype_by_chreq[CHREQ_T_PAG_R_ANY_NECI0] = GSM_LCHAN_TCH_F;
- network->ctype_by_chreq[CHREQ_T_PAG_R_ANY_NECI1] = GSM_LCHAN_TCH_F;
- }
- }
-}
-
-enum gsm_chan_t get_ctype_by_chreq(struct gsm_network *network, uint8_t ra)
-{
- int i;
- int length;
- const struct chreq *chreq;
-
- if (network->neci) {
- chreq = chreq_type_neci1;
- length = ARRAY_SIZE(chreq_type_neci1);
- } else {
- chreq = chreq_type_neci0;
- length = ARRAY_SIZE(chreq_type_neci0);
- }
-
-
- for (i = 0; i < length; i++) {
- const struct chreq *chr = &chreq[i];
- if ((ra & chr->mask) == chr->val)
- return network->ctype_by_chreq[chr->type];
- }
- LOGP(DRR, LOGL_ERROR, "Unknown CHANNEL REQUEST RQD 0x%02x\n", ra);
- return GSM_LCHAN_SDCCH;
-}
-
-int get_reason_by_chreq(uint8_t ra, int neci)
-{
- int i;
- int length;
- const struct chreq *chreq;
-
- if (neci) {
- chreq = chreq_type_neci1;
- length = ARRAY_SIZE(chreq_type_neci1);
- } else {
- chreq = chreq_type_neci0;
- length = ARRAY_SIZE(chreq_type_neci0);
- }
-
- for (i = 0; i < length; i++) {
- const struct chreq *chr = &chreq[i];
- if ((ra & chr->mask) == chr->val)
- return reason_by_chreq[chr->type];
- }
- LOGP(DRR, LOGL_ERROR, "Unknown CHANNEL REQUEST REASON 0x%02x\n", ra);
- return GSM_CHREQ_REASON_OTHER;
-}
-
-static void mr_config_for_ms(struct gsm_lchan *lchan, struct msgb *msg)
-{
- if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR)
- msgb_tlv_put(msg, GSM48_IE_MUL_RATE_CFG, lchan->mr_ms_lv[0],
- lchan->mr_ms_lv + 1);
-}
-
-/* 7.1.7 and 9.1.7: RR CHANnel RELease */
-int gsm48_send_rr_release(struct gsm_lchan *lchan)
-{
- struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 RR REL");
- struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
- uint8_t *cause;
-
- msg->lchan = lchan;
- gh->proto_discr = GSM48_PDISC_RR;
- gh->msg_type = GSM48_MT_RR_CHAN_REL;
-
- cause = msgb_put(msg, 1);
- cause[0] = GSM48_RR_CAUSE_NORMAL;
-
- DEBUGP(DRR, "Sending Channel Release: Chan: Number: %d Type: %d\n",
- lchan->nr, lchan->type);
-
- /* Send actual release request to MS */
- return gsm48_sendmsg(msg);
-}
-
-int send_siemens_mrpci(struct gsm_lchan *lchan,
- uint8_t *classmark2_lv)
-{
- struct rsl_mrpci mrpci;
-
- if (classmark2_lv[0] < 2)
- return -EINVAL;
-
- mrpci.power_class = classmark2_lv[1] & 0x7;
- mrpci.vgcs_capable = classmark2_lv[2] & (1 << 1);
- mrpci.vbs_capable = classmark2_lv[2] & (1 <<2);
- mrpci.gsm_phase = (classmark2_lv[1]) >> 5 & 0x3;
-
- return rsl_siemens_mrpci(lchan, &mrpci);
-}
-
-int gsm48_extract_mi(uint8_t *classmark2_lv, int length, char *mi_string, uint8_t *mi_type)
-{
- /* Check the size for the classmark */
- if (length < 1 + *classmark2_lv)
- return -1;
-
- uint8_t *mi_lv = classmark2_lv + *classmark2_lv + 1;
- if (length < 2 + *classmark2_lv + mi_lv[0])
- return -2;
-
- *mi_type = mi_lv[1] & GSM_MI_TYPE_MASK;
- return gsm48_mi_to_string(mi_string, GSM48_MI_SIZE, mi_lv+1, *mi_lv);
-}
-
-int gsm48_paging_extract_mi(struct gsm48_pag_resp *resp, int length,
- char *mi_string, uint8_t *mi_type)
-{
- static const uint32_t classmark_offset =
- offsetof(struct gsm48_pag_resp, classmark2);
- uint8_t *classmark2_lv = (uint8_t *) &resp->classmark2;
- return gsm48_extract_mi(classmark2_lv, length - classmark_offset,
- mi_string, mi_type);
-}
-
-int gsm48_handle_paging_resp(struct gsm_subscriber_connection *conn,
- struct msgb *msg, struct bsc_subscr *bsub)
-{
- struct gsm_bts *bts = msg->lchan->ts->trx->bts;
- struct gsm48_hdr *gh = msgb_l3(msg);
- uint8_t *classmark2_lv = gh->data + 1;
-
- if (is_siemens_bts(bts))
- send_siemens_mrpci(msg->lchan, classmark2_lv);
-
- if (!conn->bsub) {
- conn->bsub = bsub;
- } else if (conn->bsub != bsub) {
- LOGP(DRR, LOGL_ERROR,
- "<- Channel already owned by someone else?\n");
- bsc_subscr_put(bsub);
- return -EINVAL;
- } else {
- DEBUGP(DRR, "<- Channel already owned by us\n");
- bsc_subscr_put(bsub);
- bsub = conn->bsub;
- }
-
- rate_ctr_inc(&bts->network->bsc_ctrs->ctr[BSC_CTR_PAGING_COMPLETED]);
-
- /* Stop paging on the bts we received the paging response */
- paging_request_stop(&bts->network->bts_list, conn->bts, bsub, conn,
- msg);
- return 0;
-}
-
-/* Chapter 9.1.9: Ciphering Mode Command */
-int gsm48_send_rr_ciph_mode(struct gsm_lchan *lchan, int want_imeisv)
-{
- struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CIPH");
- struct gsm48_hdr *gh;
- uint8_t ciph_mod_set;
-
- msg->lchan = lchan;
-
- DEBUGP(DRR, "TX CIPHERING MODE CMD\n");
-
- if (lchan->encr.alg_id <= RSL_ENC_ALG_A5(0))
- ciph_mod_set = 0;
- else
- ciph_mod_set = (lchan->encr.alg_id-2)<<1 | 1;
-
- gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1);
- gh->proto_discr = GSM48_PDISC_RR;
- gh->msg_type = GSM48_MT_RR_CIPH_M_CMD;
- gh->data[0] = (want_imeisv & 0x1) << 4 | (ciph_mod_set & 0xf);
-
- return rsl_encryption_cmd(msg);
-}
-
-static void gsm48_cell_desc(struct gsm48_cell_desc *cd,
- const struct gsm_bts *bts)
-{
- cd->ncc = (bts->bsic >> 3 & 0x7);
- cd->bcc = (bts->bsic & 0x7);
- cd->arfcn_hi = bts->c0->arfcn >> 8;
- cd->arfcn_lo = bts->c0->arfcn & 0xff;
-}
-
-void gsm48_lchan2chan_desc(struct gsm48_chan_desc *cd,
- const struct gsm_lchan *lchan)
-{
- uint16_t arfcn = lchan->ts->trx->arfcn & 0x3ff;
-
- cd->chan_nr = gsm_lchan2chan_nr(lchan);
- if (!lchan->ts->hopping.enabled) {
- cd->h0.tsc = gsm_ts_tsc(lchan->ts);
- cd->h0.h = 0;
- cd->h0.arfcn_high = arfcn >> 8;
- cd->h0.arfcn_low = arfcn & 0xff;
- } else {
- cd->h1.tsc = gsm_ts_tsc(lchan->ts);
- cd->h1.h = 1;
- cd->h1.maio_high = lchan->ts->hopping.maio >> 2;
- cd->h1.maio_low = lchan->ts->hopping.maio & 0x03;
- cd->h1.hsn = lchan->ts->hopping.hsn;
- }
-}
-
-/*! \brief Encode a TS 04.08 multirate config LV according to 10.5.2.21aa
- * \param[out] lv caller-allocated buffer of 7 bytes. First octet is IS length
- * \param[in] mr multi-rate configuration to encode
- * \param[in] modes array describing the AMR modes
- * \returns 0 on success */
-int gsm48_multirate_config(uint8_t *lv, const struct amr_multirate_conf *mr, const struct amr_mode *modes)
-{
- int num = 0, i;
-
- for (i = 0; i < 8; i++) {
- if (((mr->gsm48_ie[1] >> i) & 1))
- num++;
- }
- if (num > 4) {
- LOGP(DRR, LOGL_ERROR, "BUG: Using multirate codec with too "
- "many modes in config.\n");
- num = 4;
- }
- if (num < 1) {
- LOGP(DRR, LOGL_ERROR, "BUG: Using multirate codec with no "
- "mode in config.\n");
- num = 1;
- }
-
- lv[0] = (num == 1) ? 2 : (num + 2);
- memcpy(lv + 1, mr->gsm48_ie, 2);
- if (num == 1)
- return 0;
-
- lv[3] = modes[0].threshold & 0x3f;
- lv[4] = modes[0].hysteresis << 4;
- if (num == 2)
- return 0;
- lv[4] |= (modes[1].threshold & 0x3f) >> 2;
- lv[5] = modes[1].threshold << 6;
- lv[5] |= (modes[1].hysteresis & 0x0f) << 2;
- if (num == 3)
- return 0;
- lv[5] |= (modes[2].threshold & 0x3f) >> 4;
- lv[6] = modes[2].threshold << 4;
- lv[6] |= modes[2].hysteresis & 0x0f;
-
- return 0;
-}
-
-#define GSM48_HOCMD_CCHDESC_LEN 16
-
-/* Chapter 9.1.15: Handover Command */
-int gsm48_send_ho_cmd(struct gsm_lchan *old_lchan, struct gsm_lchan *new_lchan,
- uint8_t power_command, uint8_t ho_ref)
-{
- struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 HO CMD");
- struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
- struct gsm48_ho_cmd *ho =
- (struct gsm48_ho_cmd *) msgb_put(msg, sizeof(*ho));
-
- msg->lchan = old_lchan;
- gh->proto_discr = GSM48_PDISC_RR;
- gh->msg_type = GSM48_MT_RR_HANDO_CMD;
-
- /* mandatory bits */
- gsm48_cell_desc(&ho->cell_desc, new_lchan->ts->trx->bts);
- gsm48_lchan2chan_desc(&ho->chan_desc, new_lchan);
- ho->ho_ref = ho_ref;
- ho->power_command = power_command;
-
- if (new_lchan->ts->hopping.enabled) {
- struct gsm_bts *bts = new_lchan->ts->trx->bts;
- struct gsm48_system_information_type_1 *si1;
- uint8_t *cur;
-
- si1 = GSM_BTS_SI(bts, SYSINFO_TYPE_1);
- /* Copy the Cell Chan Desc (ARFCNS in this cell) */
- msgb_put_u8(msg, GSM48_IE_CELL_CH_DESC);
- cur = msgb_put(msg, GSM48_HOCMD_CCHDESC_LEN);
- memcpy(cur, si1->cell_channel_description,
- GSM48_HOCMD_CCHDESC_LEN);
- /* Copy the Mobile Allocation */
- msgb_tlv_put(msg, GSM48_IE_MA_BEFORE,
- new_lchan->ts->hopping.ma_len,
- new_lchan->ts->hopping.ma_data);
- }
- /* FIXME: optional bits for type of synchronization? */
-
- return gsm48_sendmsg(msg);
-}
-
-/* Chapter 9.1.2: Assignment Command */
-int gsm48_send_rr_ass_cmd(struct gsm_lchan *dest_lchan, struct gsm_lchan *lchan, uint8_t power_command)
-{
- struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 ASS CMD");
- struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
- struct gsm48_ass_cmd *ass =
- (struct gsm48_ass_cmd *) msgb_put(msg, sizeof(*ass));
-
- DEBUGP(DRR, "-> ASSIGNMENT COMMAND tch_mode=0x%02x\n", lchan->tch_mode);
-
- msg->lchan = dest_lchan;
- gh->proto_discr = GSM48_PDISC_RR;
- gh->msg_type = GSM48_MT_RR_ASS_CMD;
-
- /*
- * fill the channel information element, this code
- * should probably be shared with rsl_rx_chan_rqd(),
- * gsm48_lchan_modify(). But beware that 10.5.2.5
- * 10.5.2.5.a have slightly different semantic for
- * the chan_desc. But as long as multi-slot configurations
- * are not used we seem to be fine.
- */
- gsm48_lchan2chan_desc(&ass->chan_desc, lchan);
- ass->power_command = power_command;
-
- /* optional: cell channel description */
-
- msgb_tv_put(msg, GSM48_IE_CHANMODE_1, lchan->tch_mode);
-
- /* mobile allocation in case of hopping */
- if (lchan->ts->hopping.enabled) {
- msgb_tlv_put(msg, GSM48_IE_MA_BEFORE, lchan->ts->hopping.ma_len,
- lchan->ts->hopping.ma_data);
- }
-
- /* in case of multi rate we need to attach a config */
- mr_config_for_ms(lchan, msg);
-
- return gsm48_sendmsg(msg);
-}
-
-/* 9.1.5 Channel mode modify: Modify the mode on the MS side */
-int gsm48_lchan_modify(struct gsm_lchan *lchan, uint8_t mode)
-{
- struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CHN MOD");
- struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
- struct gsm48_chan_mode_modify *cmm =
- (struct gsm48_chan_mode_modify *) msgb_put(msg, sizeof(*cmm));
-
- DEBUGP(DRR, "-> CHANNEL MODE MODIFY mode=0x%02x\n", mode);
-
- lchan->tch_mode = mode;
- msg->lchan = lchan;
- gh->proto_discr = GSM48_PDISC_RR;
- gh->msg_type = GSM48_MT_RR_CHAN_MODE_MODIF;
-
- /* fill the channel information element, this code
- * should probably be shared with rsl_rx_chan_rqd() */
- gsm48_lchan2chan_desc(&cmm->chan_desc, lchan);
- cmm->mode = mode;
-
- /* in case of multi rate we need to attach a config */
- mr_config_for_ms(lchan, msg);
-
- return gsm48_sendmsg(msg);
-}
-
-int gsm48_rx_rr_modif_ack(struct msgb *msg)
-{
- int rc;
- struct gsm48_hdr *gh = msgb_l3(msg);
- struct gsm48_chan_mode_modify *mod =
- (struct gsm48_chan_mode_modify *) gh->data;
-
- DEBUGP(DRR, "CHANNEL MODE MODIFY ACK\n");
-
- if (mod->mode != msg->lchan->tch_mode) {
- LOGP(DRR, LOGL_ERROR, "CHANNEL MODE change failed. Wanted: %d Got: %d\n",
- msg->lchan->tch_mode, mod->mode);
- return -1;
- }
-
- /* update the channel type */
- switch (mod->mode) {
- case GSM48_CMODE_SIGN:
- msg->lchan->rsl_cmode = RSL_CMOD_SPD_SIGN;
- break;
- case GSM48_CMODE_SPEECH_V1:
- case GSM48_CMODE_SPEECH_EFR:
- case GSM48_CMODE_SPEECH_AMR:
- msg->lchan->rsl_cmode = RSL_CMOD_SPD_SPEECH;
- break;
- case GSM48_CMODE_DATA_14k5:
- case GSM48_CMODE_DATA_12k0:
- case GSM48_CMODE_DATA_6k0:
- case GSM48_CMODE_DATA_3k6:
- msg->lchan->rsl_cmode = RSL_CMOD_SPD_DATA;
- break;
- }
-
- /* We've successfully modified the MS side of the channel,
- * now go on to modify the BTS side of the channel */
- rc = rsl_chan_mode_modify_req(msg->lchan);
-
- /* FIXME: we not only need to do this after mode modify, but
- * also after channel activation */
- if (is_ipaccess_bts(msg->lchan->ts->trx->bts) && mod->mode != GSM48_CMODE_SIGN)
- rsl_ipacc_crcx(msg->lchan);
- return rc;
-}
-
-int gsm48_parse_meas_rep(struct gsm_meas_rep *rep, struct msgb *msg)
-{
- struct gsm48_hdr *gh = msgb_l3(msg);
- uint8_t *data = gh->data;
- struct gsm_bts *bts = msg->lchan->ts->trx->bts;
- struct bitvec *nbv = &bts->si_common.neigh_list;
- struct gsm_meas_rep_cell *mrc;
-
- if (gh->msg_type != GSM48_MT_RR_MEAS_REP)
- return -EINVAL;
-
- if (data[0] & 0x80)
- rep->flags |= MEAS_REP_F_BA1;
- if (data[0] & 0x40)
- rep->flags |= MEAS_REP_F_UL_DTX;
- if ((data[1] & 0x40) == 0x00)
- rep->flags |= MEAS_REP_F_DL_VALID;
-
- rep->dl.full.rx_lev = data[0] & 0x3f;
- rep->dl.sub.rx_lev = data[1] & 0x3f;
- rep->dl.full.rx_qual = (data[2] >> 4) & 0x7;
- rep->dl.sub.rx_qual = (data[2] >> 1) & 0x7;
-
- rep->num_cell = ((data[3] >> 6) & 0x3) | ((data[2] & 0x01) << 2);
- if (rep->num_cell < 1 || rep->num_cell > 6)
- return 0;
-
- /* an encoding nightmare in perfection */
- mrc = &rep->cell[0];
- mrc->rxlev = data[3] & 0x3f;
- mrc->neigh_idx = data[4] >> 3;
- mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1);
- mrc->bsic = ((data[4] & 0x07) << 3) | (data[5] >> 5);
- if (rep->num_cell < 2)
- return 0;
-
- mrc = &rep->cell[1];
- mrc->rxlev = ((data[5] & 0x1f) << 1) | (data[6] >> 7);
- mrc->neigh_idx = (data[6] >> 2) & 0x1f;
- mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1);
- mrc->bsic = ((data[6] & 0x03) << 4) | (data[7] >> 4);
- if (rep->num_cell < 3)
- return 0;
-
- mrc = &rep->cell[2];
- mrc->rxlev = ((data[7] & 0x0f) << 2) | (data[8] >> 6);
- mrc->neigh_idx = (data[8] >> 1) & 0x1f;
- mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1);
- mrc->bsic = ((data[8] & 0x01) << 5) | (data[9] >> 3);
- if (rep->num_cell < 4)
- return 0;
-
- mrc = &rep->cell[3];
- mrc->rxlev = ((data[9] & 0x07) << 3) | (data[10] >> 5);
- mrc->neigh_idx = data[10] & 0x1f;
- mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1);
- mrc->bsic = data[11] >> 2;
- if (rep->num_cell < 5)
- return 0;
-
- mrc = &rep->cell[4];
- mrc->rxlev = ((data[11] & 0x03) << 4) | (data[12] >> 4);
- mrc->neigh_idx = ((data[12] & 0xf) << 1) | (data[13] >> 7);
- mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1);
- mrc->bsic = (data[13] >> 1) & 0x3f;
- if (rep->num_cell < 6)
- return 0;
-
- mrc = &rep->cell[5];
- mrc->rxlev = ((data[13] & 0x01) << 5) | (data[14] >> 3);
- mrc->neigh_idx = ((data[14] & 0x07) << 2) | (data[15] >> 6);
- mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1);
- mrc->bsic = data[15] & 0x3f;
-
- return 0;
-}
-
-/* 9.2.5 CM service accept */
-int gsm48_tx_mm_serv_ack(struct gsm_subscriber_connection *conn)
-{
- struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 SERV ACK");
- struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
-
- msg->lchan = conn->lchan;
-
- gh->proto_discr = GSM48_PDISC_MM;
- gh->msg_type = GSM48_MT_MM_CM_SERV_ACC;
-
- DEBUGP(DMM, "-> CM SERVICE ACK\n");
-
- return gsm0808_submit_dtap(conn, msg, 0, 0);
-}
-
-/* 9.2.6 CM service reject */
-int gsm48_tx_mm_serv_rej(struct gsm_subscriber_connection *conn,
- enum gsm48_reject_value value)
-{
- struct msgb *msg;
-
- msg = gsm48_create_mm_serv_rej(value);
- if (!msg) {
- LOGP(DMM, LOGL_ERROR, "Failed to allocate CM Service Reject.\n");
- return -1;
- }
-
- DEBUGP(DMM, "-> CM SERVICE Reject cause: %d\n", value);
-
- return gsm0808_submit_dtap(conn, msg, 0, 0);
-}
diff --git a/openbsc/src/libbsc/gsm_04_80_utils.c b/openbsc/src/libbsc/gsm_04_80_utils.c
deleted file mode 100644
index e0db81edf..000000000
--- a/openbsc/src/libbsc/gsm_04_80_utils.c
+++ /dev/null
@@ -1,40 +0,0 @@
-/* OpenBSC utility functions for 3GPP TS 04.80 */
-
-/* (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/gsm/gsm0480.h>
-#include <openbsc/bsc_api.h>
-
-int bsc_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 gsm0808_submit_dtap(conn, msg, 0, 0);
-}
-
-int bsc_send_ussd_release_complete(struct gsm_subscriber_connection *conn)
-{
- struct msgb *msg = gsm0480_create_ussd_release_complete();
- if (!msg)
- return -1;
- return gsm0808_submit_dtap(conn, msg, 0, 0);
-}
diff --git a/openbsc/src/libbsc/handover_decision.c b/openbsc/src/libbsc/handover_decision.c
deleted file mode 100644
index 0f07bcac6..000000000
--- a/openbsc/src/libbsc/handover_decision.c
+++ /dev/null
@@ -1,304 +0,0 @@
-/* Handover Decision making for Inter-BTS (Intra-BSC) Handover. This
- * only implements the handover algorithm/decision, but not execution
- * of it */
-
-/* (C) 2009 by Harald Welte <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 <errno.h>
-
-#include <osmocom/core/msgb.h>
-#include <openbsc/debug.h>
-#include <openbsc/gsm_data.h>
-#include <openbsc/meas_rep.h>
-#include <openbsc/signal.h>
-#include <osmocom/core/talloc.h>
-#include <openbsc/handover.h>
-#include <osmocom/gsm/gsm_utils.h>
-
-/* issue handover to a cell identified by ARFCN and BSIC */
-static int handover_to_arfcn_bsic(struct gsm_lchan *lchan,
- uint16_t arfcn, uint8_t bsic)
-{
- struct gsm_bts *new_bts;
-
- /* resolve the gsm_bts structure for the best neighbor */
- new_bts = gsm_bts_neighbor(lchan->ts->trx->bts, arfcn, bsic);
- if (!new_bts) {
- LOGP(DHO, LOGL_NOTICE, "unable to determine neighbor BTS "
- "for ARFCN %u BSIC %u ?!?\n", arfcn, bsic);
- return -EINVAL;
- }
-
- /* and actually try to handover to that cell */
- return bsc_handover_start(lchan, new_bts);
-}
-
-/* did we get a RXLEV for a given cell in the given report? */
-static int rxlev_for_cell_in_rep(struct gsm_meas_rep *mr,
- uint16_t arfcn, uint8_t bsic)
-{
- int i;
-
- for (i = 0; i < mr->num_cell; i++) {
- struct gsm_meas_rep_cell *mrc = &mr->cell[i];
-
- /* search for matching report */
- if (!(mrc->arfcn == arfcn && mrc->bsic == bsic))
- continue;
-
- mrc->flags |= MRC_F_PROCESSED;
- return mrc->rxlev;
- }
- return -ENODEV;
-}
-
-/* obtain averaged rxlev for given neighbor */
-static int neigh_meas_avg(struct neigh_meas_proc *nmp, int window)
-{
- unsigned int i, idx;
- int avg = 0;
-
- idx = calc_initial_idx(ARRAY_SIZE(nmp->rxlev),
- nmp->rxlev_cnt % ARRAY_SIZE(nmp->rxlev),
- window);
-
- for (i = 0; i < window; i++) {
- int j = (idx+i) % ARRAY_SIZE(nmp->rxlev);
-
- avg += nmp->rxlev[j];
- }
-
- return avg / window;
-}
-
-/* find empty or evict bad neighbor */
-static struct neigh_meas_proc *find_evict_neigh(struct gsm_lchan *lchan)
-{
- int j, worst = 999999;
- struct neigh_meas_proc *nmp_worst = NULL;
-
- /* first try to find an empty/unused slot */
- for (j = 0; j < ARRAY_SIZE(lchan->neigh_meas); j++) {
- struct neigh_meas_proc *nmp = &lchan->neigh_meas[j];
- if (!nmp->arfcn)
- return nmp;
- }
-
- /* no empty slot found. evict worst neighbor from list */
- for (j = 0; j < ARRAY_SIZE(lchan->neigh_meas); j++) {
- struct neigh_meas_proc *nmp = &lchan->neigh_meas[j];
- int avg = neigh_meas_avg(nmp, MAX_WIN_NEIGH_AVG);
- if (!nmp_worst || avg < worst) {
- worst = avg;
- nmp_worst = nmp;
- }
- }
-
- return nmp_worst;
-}
-
-/* process neighbor cell measurement reports */
-static void process_meas_neigh(struct gsm_meas_rep *mr)
-{
- int i, j, idx;
-
- /* for each reported cell, try to update global state */
- for (j = 0; j < ARRAY_SIZE(mr->lchan->neigh_meas); j++) {
- struct neigh_meas_proc *nmp = &mr->lchan->neigh_meas[j];
- unsigned int idx;
- int rxlev;
-
- /* skip unused entries */
- if (!nmp->arfcn)
- continue;
-
- rxlev = rxlev_for_cell_in_rep(mr, nmp->arfcn, nmp->bsic);
- idx = nmp->rxlev_cnt % ARRAY_SIZE(nmp->rxlev);
- if (rxlev >= 0) {
- nmp->rxlev[idx] = rxlev;
- nmp->last_seen_nr = mr->nr;
- } else
- nmp->rxlev[idx] = 0;
- nmp->rxlev_cnt++;
- }
-
- /* iterate over list of reported cells, check if we did not
- * process all of them */
- for (i = 0; i < mr->num_cell; i++) {
- struct gsm_meas_rep_cell *mrc = &mr->cell[i];
- struct neigh_meas_proc *nmp;
-
- if (mrc->flags & MRC_F_PROCESSED)
- continue;
-
- nmp = find_evict_neigh(mr->lchan);
-
- nmp->arfcn = mrc->arfcn;
- nmp->bsic = mrc->bsic;
-
- idx = nmp->rxlev_cnt % ARRAY_SIZE(nmp->rxlev);
- nmp->rxlev[idx] = mrc->rxlev;
- nmp->rxlev_cnt++;
- nmp->last_seen_nr = mr->nr;
-
- mrc->flags |= MRC_F_PROCESSED;
- }
-}
-
-/* attempt to do a handover */
-static int attempt_handover(struct gsm_meas_rep *mr)
-{
- struct gsm_network *net = mr->lchan->ts->trx->bts->network;
- struct neigh_meas_proc *best_cell = NULL;
- unsigned int best_better_db = 0;
- int i, rc;
-
- /* find the best cell in this report that is at least RXLEV_HYST
- * better than the current serving cell */
-
- for (i = 0; i < ARRAY_SIZE(mr->lchan->neigh_meas); i++) {
- struct neigh_meas_proc *nmp = &mr->lchan->neigh_meas[i];
- int avg, better;
-
- /* skip empty slots */
- if (nmp->arfcn == 0)
- continue;
-
- /* caculate average rxlev for this cell over the window */
- avg = neigh_meas_avg(nmp, net->handover.win_rxlev_avg_neigh);
-
- /* check if hysteresis is fulfilled */
- if (avg < mr->dl.full.rx_lev + net->handover.pwr_hysteresis)
- continue;
-
- better = avg - mr->dl.full.rx_lev;
- if (better > best_better_db) {
- best_cell = nmp;
- best_better_db = better;
- }
- }
-
- if (!best_cell)
- return 0;
-
- LOGP(DHO, LOGL_INFO, "%s: Cell on ARFCN %u is better: ",
- gsm_ts_name(mr->lchan->ts), best_cell->arfcn);
- if (!net->handover.active) {
- LOGPC(DHO, LOGL_INFO, "Skipping, Handover disabled\n");
- return 0;
- }
-
- rc = handover_to_arfcn_bsic(mr->lchan, best_cell->arfcn, best_cell->bsic);
- switch (rc) {
- case 0:
- LOGPC(DHO, LOGL_INFO, "Starting handover\n");
- break;
- case -ENOSPC:
- LOGPC(DHO, LOGL_INFO, "No channel available\n");
- break;
- case -EBUSY:
- LOGPC(DHO, LOGL_INFO, "Handover already active\n");
- break;
- default:
- LOGPC(DHO, LOGL_ERROR, "Unknown error\n");
- }
- return rc;
-}
-
-/* process an already parsed measurement report and decide if we want to
- * attempt a handover */
-static int process_meas_rep(struct gsm_meas_rep *mr)
-{
- struct gsm_network *net = mr->lchan->ts->trx->bts->network;
- enum meas_rep_field dlev, dqual;
- int av_rxlev;
-
- /* we currently only do handover for TCH channels */
- switch (mr->lchan->type) {
- case GSM_LCHAN_TCH_F:
- case GSM_LCHAN_TCH_H:
- break;
- default:
- return 0;
- }
-
- if (mr->flags & MEAS_REP_F_DL_DTX) {
- dlev = MEAS_REP_DL_RXLEV_SUB;
- dqual = MEAS_REP_DL_RXQUAL_SUB;
- } else {
- dlev = MEAS_REP_DL_RXLEV_FULL;
- dqual = MEAS_REP_DL_RXQUAL_FULL;
- }
-
- /* parse actual neighbor cell info */
- if (mr->num_cell > 0 && mr->num_cell < 7)
- process_meas_neigh(mr);
-
- av_rxlev = get_meas_rep_avg(mr->lchan, dlev,
- net->handover.win_rxlev_avg);
-
- /* Interference HO */
- if (rxlev2dbm(av_rxlev) > -85 &&
- meas_rep_n_out_of_m_be(mr->lchan, dqual, 3, 4, 5))
- return attempt_handover(mr);
-
- /* Bad Quality */
- if (meas_rep_n_out_of_m_be(mr->lchan, dqual, 3, 4, 5))
- return attempt_handover(mr);
-
- /* Low Level */
- if (rxlev2dbm(av_rxlev) <= -110)
- return attempt_handover(mr);
-
- /* Distance */
- if (mr->ms_l1.ta > net->handover.max_distance)
- return attempt_handover(mr);
-
- /* Power Budget AKA Better Cell */
- if ((mr->nr % net->handover.pwr_interval) == 0)
- return attempt_handover(mr);
-
- return 0;
-
-}
-
-static int ho_dec_sig_cb(unsigned int subsys, unsigned int signal,
- void *handler_data, void *signal_data)
-{
- struct lchan_signal_data *lchan_data;
-
- if (subsys != SS_LCHAN)
- return 0;
-
- lchan_data = signal_data;
- switch (signal) {
- case S_LCHAN_MEAS_REP:
- process_meas_rep(lchan_data->mr);
- break;
- }
-
- return 0;
-}
-
-void on_dso_load_ho_dec(void)
-{
- osmo_signal_register_handler(SS_LCHAN, ho_dec_sig_cb, NULL);
-}
diff --git a/openbsc/src/libbsc/handover_logic.c b/openbsc/src/libbsc/handover_logic.c
deleted file mode 100644
index 4dd913b15..000000000
--- a/openbsc/src/libbsc/handover_logic.c
+++ /dev/null
@@ -1,378 +0,0 @@
-/* Handover Logic for Inter-BTS (Intra-BSC) Handover. This does not
- * actually implement the handover algorithm/decision, but executes a
- * handover decision */
-
-/* (C) 2009 by Harald Welte <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 <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <time.h>
-#include <netinet/in.h>
-
-#include <osmocom/core/msgb.h>
-#include <openbsc/debug.h>
-#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>
-
-struct bsc_handover {
- struct llist_head list;
-
- struct gsm_lchan *old_lchan;
- struct gsm_lchan *new_lchan;
-
- struct osmo_timer_list T3103;
-
- uint8_t ho_ref;
-};
-
-static LLIST_HEAD(bsc_handovers);
-
-static void handover_free(struct bsc_handover *ho)
-{
- osmo_timer_del(&ho->T3103);
- llist_del(&ho->list);
- talloc_free(ho);
-}
-
-static struct bsc_handover *bsc_ho_by_new_lchan(struct gsm_lchan *new_lchan)
-{
- struct bsc_handover *ho;
-
- llist_for_each_entry(ho, &bsc_handovers, list) {
- if (ho->new_lchan == new_lchan)
- return ho;
- }
-
- return NULL;
-}
-
-static struct bsc_handover *bsc_ho_by_old_lchan(struct gsm_lchan *old_lchan)
-{
- struct bsc_handover *ho;
-
- llist_for_each_entry(ho, &bsc_handovers, list) {
- if (ho->old_lchan == old_lchan)
- return ho;
- }
-
- return NULL;
-}
-
-/*! \brief Hand over the specified logical channel to the specified new BTS.
- * This is the main entry point for the actual handover algorithm, after the
- * decision whether to initiate HO to a specific BTS. */
-int bsc_handover_start(struct gsm_lchan *old_lchan, struct gsm_bts *bts)
-{
- struct gsm_lchan *new_lchan;
- struct bsc_handover *ho;
- static uint8_t ho_ref;
- int rc;
-
- /* don't attempt multiple handovers for the same lchan at
- * the same time */
- if (bsc_ho_by_old_lchan(old_lchan))
- return -EBUSY;
-
- DEBUGP(DHO, "(old_lchan on BTS %u, new BTS %u)\n",
- old_lchan->ts->trx->bts->nr, bts->nr);
-
- rate_ctr_inc(&bts->network->bsc_ctrs->ctr[BSC_CTR_HANDOVER_ATTEMPTED]);
-
- if (!old_lchan->conn) {
- LOGP(DHO, LOGL_ERROR, "Old lchan lacks connection data.\n");
- return -ENOSPC;
- }
-
- new_lchan = lchan_alloc(bts, old_lchan->type, 0);
- if (!new_lchan) {
- LOGP(DHO, LOGL_NOTICE, "No free channel\n");
- rate_ctr_inc(&bts->network->bsc_ctrs->ctr[BSC_CTR_HANDOVER_NO_CHANNEL]);
- return -ENOSPC;
- }
-
- ho = talloc_zero(tall_bsc_ctx, struct bsc_handover);
- if (!ho) {
- LOGP(DHO, LOGL_FATAL, "Out of Memory\n");
- lchan_free(new_lchan);
- return -ENOMEM;
- }
- ho->old_lchan = old_lchan;
- ho->new_lchan = new_lchan;
- ho->ho_ref = ho_ref++;
-
- /* copy some parameters from old lchan */
- memcpy(&new_lchan->encr, &old_lchan->encr, sizeof(new_lchan->encr));
- new_lchan->ms_power = old_lchan->ms_power;
- new_lchan->bs_power = old_lchan->bs_power;
- new_lchan->rsl_cmode = old_lchan->rsl_cmode;
- new_lchan->tch_mode = old_lchan->tch_mode;
- memcpy(&new_lchan->mr_ms_lv, &old_lchan->mr_ms_lv, ARRAY_SIZE(new_lchan->mr_ms_lv));
- memcpy(&new_lchan->mr_bts_lv, &old_lchan->mr_bts_lv, ARRAY_SIZE(new_lchan->mr_bts_lv));
-
- new_lchan->conn = old_lchan->conn;
- new_lchan->conn->ho_lchan = new_lchan;
-
- /* FIXME: do we have a better idea of the timing advance? */
- rc = rsl_chan_activate_lchan(new_lchan, RSL_ACT_INTER_ASYNC, ho->ho_ref);
- if (rc < 0) {
- LOGP(DHO, LOGL_ERROR, "could not activate channel\n");
- new_lchan->conn->ho_lchan = NULL;
- new_lchan->conn = NULL;
- talloc_free(ho);
- lchan_free(new_lchan);
- return rc;
- }
-
- rsl_lchan_set_state(new_lchan, LCHAN_S_ACT_REQ);
- llist_add(&ho->list, &bsc_handovers);
- /* we continue in the SS_LCHAN handler / ho_chan_activ_ack */
-
- return 0;
-}
-
-void bsc_clear_handover(struct gsm_subscriber_connection *conn, int free_lchan)
-{
- struct bsc_handover *ho;
-
- ho = bsc_ho_by_new_lchan(conn->ho_lchan);
-
-
- if (!ho && conn->ho_lchan)
- LOGP(DHO, LOGL_ERROR, "BUG: We lost some state.\n");
-
- if (!ho) {
- LOGP(DHO, LOGL_ERROR, "unable to find HO record\n");
- return;
- }
-
- conn->ho_lchan->conn = NULL;
- conn->ho_lchan = NULL;
-
- if (free_lchan)
- lchan_release(ho->new_lchan, 0, RSL_REL_LOCAL_END);
-
- handover_free(ho);
-}
-
-/* T3103 expired: Handover has failed without HO COMPLETE or HO FAIL */
-static void ho_T3103_cb(void *_ho)
-{
- struct bsc_handover *ho = _ho;
- struct gsm_network *net = ho->new_lchan->ts->trx->bts->network;
-
- DEBUGP(DHO, "HO T3103 expired\n");
- rate_ctr_inc(&net->bsc_ctrs->ctr[BSC_CTR_HANDOVER_TIMEOUT]);
-
- ho->new_lchan->conn->ho_lchan = NULL;
- ho->new_lchan->conn = NULL;
- lchan_release(ho->new_lchan, 0, RSL_REL_LOCAL_END);
- handover_free(ho);
-}
-
-/* RSL has acknowledged activation of the new lchan */
-static int ho_chan_activ_ack(struct gsm_lchan *new_lchan)
-{
- struct bsc_handover *ho;
-
- /* we need to check if this channel activation is related to
- * a handover at all (and if, which particular handover) */
- ho = bsc_ho_by_new_lchan(new_lchan);
- if (!ho)
- return -ENODEV;
-
- DEBUGP(DHO, "handover activate ack, send HO Command\n");
-
- /* we can now send the 04.08 HANDOVER COMMAND to the MS
- * using the old lchan */
-
- gsm48_send_ho_cmd(ho->old_lchan, new_lchan, 0, ho->ho_ref);
-
- /* start T3103. We can continue either with T3103 expiration,
- * 04.08 HANDOVER COMPLETE or 04.08 HANDOVER FAIL */
- osmo_timer_setup(&ho->T3103, ho_T3103_cb, ho);
- osmo_timer_schedule(&ho->T3103, 10, 0);
-
- /* create a RTP connection */
- if (is_ipaccess_bts(new_lchan->ts->trx->bts))
- rsl_ipacc_crcx(new_lchan);
-
- return 0;
-}
-
-/* RSL has not acknowledged activation of the new lchan */
-static int ho_chan_activ_nack(struct gsm_lchan *new_lchan)
-{
- struct bsc_handover *ho;
-
- ho = bsc_ho_by_new_lchan(new_lchan);
- if (!ho) {
- LOGP(DHO, LOGL_INFO, "ACT NACK: unable to find HO record\n");
- return -ENODEV;
- }
-
- new_lchan->conn->ho_lchan = NULL;
- new_lchan->conn = NULL;
- handover_free(ho);
-
- /* FIXME: maybe we should try to allocate a new LCHAN here? */
-
- return 0;
-}
-
-/* GSM 04.08 HANDOVER COMPLETE has been received on new channel */
-static int ho_gsm48_ho_compl(struct gsm_lchan *new_lchan)
-{
- struct gsm_network *net;
- struct bsc_handover *ho;
-
- ho = bsc_ho_by_new_lchan(new_lchan);
- if (!ho) {
- LOGP(DHO, LOGL_ERROR, "unable to find HO record\n");
- return -ENODEV;
- }
-
- net = new_lchan->ts->trx->bts->network;
- LOGP(DHO, LOGL_INFO, "Subscriber %s HO from BTS %u->%u on ARFCN "
- "%u->%u\n", subscr_name(ho->old_lchan->conn->subscr),
- ho->old_lchan->ts->trx->bts->nr, new_lchan->ts->trx->bts->nr,
- ho->old_lchan->ts->trx->arfcn, new_lchan->ts->trx->arfcn);
-
- rate_ctr_inc(&net->bsc_ctrs->ctr[BSC_CTR_HANDOVER_COMPLETED]);
-
- osmo_timer_del(&ho->T3103);
-
- /* switch TRAU muxer for E1 based BTS from one channel to another */
- if (is_e1_bts(new_lchan->conn->bts))
- switch_trau_mux(ho->old_lchan, new_lchan);
-
- /* Replace the ho lchan with the primary one */
- if (ho->old_lchan != new_lchan->conn->lchan)
- LOGP(DHO, LOGL_ERROR, "Primary lchan changed during handover.\n");
-
- if (new_lchan != new_lchan->conn->ho_lchan)
- LOGP(DHO, LOGL_ERROR, "Handover channel changed during this handover.\n");
-
- new_lchan->conn->ho_lchan = NULL;
- new_lchan->conn->lchan = new_lchan;
- ho->old_lchan->conn = NULL;
-
- lchan_release(ho->old_lchan, 0, RSL_REL_LOCAL_END);
-
- handover_free(ho);
- return 0;
-}
-
-/* GSM 04.08 HANDOVER FAIL has been received */
-static int ho_gsm48_ho_fail(struct gsm_lchan *old_lchan)
-{
- struct gsm_network *net = old_lchan->ts->trx->bts->network;
- struct bsc_handover *ho;
- struct gsm_lchan *new_lchan;
-
- ho = bsc_ho_by_old_lchan(old_lchan);
- if (!ho) {
- LOGP(DHO, LOGL_ERROR, "unable to find HO record\n");
- return -ENODEV;
- }
-
- rate_ctr_inc(&net->bsc_ctrs->ctr[BSC_CTR_HANDOVER_FAILED]);
-
- new_lchan = ho->new_lchan;
-
- /* release the channel and forget about it */
- ho->new_lchan->conn->ho_lchan = NULL;
- ho->new_lchan->conn = NULL;
- handover_free(ho);
-
- lchan_release(new_lchan, 0, RSL_REL_LOCAL_END);
-
-
- return 0;
-}
-
-/* GSM 08.58 HANDOVER DETECT has been received */
-static int ho_rsl_detect(struct gsm_lchan *new_lchan)
-{
- struct bsc_handover *ho;
-
- ho = bsc_ho_by_new_lchan(new_lchan);
- if (!ho) {
- LOGP(DHO, LOGL_ERROR, "unable to find HO record\n");
- return -ENODEV;
- }
-
- /* FIXME: do we actually want to do something here ? */
-
- return 0;
-}
-
-static int ho_logic_sig_cb(unsigned int subsys, unsigned int signal,
- void *handler_data, void *signal_data)
-{
- struct lchan_signal_data *lchan_data;
- struct gsm_lchan *lchan;
-
- lchan_data = signal_data;
- switch (subsys) {
- case SS_LCHAN:
- lchan = lchan_data->lchan;
- switch (signal) {
- case S_LCHAN_ACTIVATE_ACK:
- return ho_chan_activ_ack(lchan);
- case S_LCHAN_ACTIVATE_NACK:
- return ho_chan_activ_nack(lchan);
- case S_LCHAN_HANDOVER_DETECT:
- return ho_rsl_detect(lchan);
- case S_LCHAN_HANDOVER_COMPL:
- return ho_gsm48_ho_compl(lchan);
- case S_LCHAN_HANDOVER_FAIL:
- return ho_gsm48_ho_fail(lchan);
- }
- break;
- default:
- break;
- }
-
- return 0;
-}
-
-struct gsm_lchan *bsc_handover_pending(struct gsm_lchan *new_lchan)
-{
- struct bsc_handover *ho;
- ho = bsc_ho_by_new_lchan(new_lchan);
- if (!ho)
- return NULL;
- return ho->old_lchan;
-}
-
-static __attribute__((constructor)) void on_dso_load_ho_logic(void)
-{
- osmo_signal_register_handler(SS_LCHAN, ho_logic_sig_cb, NULL);
-}
diff --git a/openbsc/src/libbsc/meas_proc.c b/openbsc/src/libbsc/meas_proc.c
deleted file mode 100644
index 5b97e74ee..000000000
--- a/openbsc/src/libbsc/meas_proc.c
+++ /dev/null
@@ -1,84 +0,0 @@
-/* Measurement Processing */
-
-/* (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 <errno.h>
-
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/msgb.h>
-#include <openbsc/debug.h>
-#include <openbsc/gsm_data.h>
-#include <openbsc/meas_rep.h>
-#include <openbsc/signal.h>
-
-/* process an already parsed measurement report */
-static int process_meas_rep(struct gsm_meas_rep *mr)
-{
- struct gsm_meas_rep_cell *mr_cell = NULL;
- unsigned int best_better_db;
- int i;
-
- /* FIXME: implement actual averaging over multiple measurement
- * reports */
-
- /* find the best cell in this report that is at least RXLEV_HYST
- * better than the current serving cell */
- for (i = 0; i < mr->num_cell; i++) {
- unsigned int better;
- if (mr->cell[i].rxlev < mr->dl.full.rx_lev + RXLEV_HYST)
- continue;
-
- better = mr->cell[i].rxlev - mr->dl.full.rx_lev;
- if (better > best_better_db) {
- mr_cell = &mr->cell[i];
- best_better_db = better;
- }
- }
-
- if (mr_cell)
- return handover_to_arfcn_bsic(mr->lchan, mr_cell->arfcn,
- mr_cell->bsic);
- return 0;
-}
-
-static int meas_proc_sig_cb(unsigned int subsys, unsigned int signal,
- void *handler_data, void *signal_data)
-{
- struct gsm_lchan *lchan;
- struct gsm_meas_rep *mr;
-
- if (subsys != SS_LCHAN)
- return 0;
-
- switch (signal) {
- case S_LCHAN_MEAS_REP:
- mr = signal_data;
- process_meas_rep(mr);
- break;
- }
-
- return 0;
-}
-
-static __attribute__((constructor)) void on_dso_load_meas(void)
-{
- osmo_signal_register_handler(SS_LCHAN, meas_proc_sig_cb, NULL);
-}
diff --git a/openbsc/src/libbsc/meas_rep.c b/openbsc/src/libbsc/meas_rep.c
deleted file mode 100644
index 808103d28..000000000
--- a/openbsc/src/libbsc/meas_rep.c
+++ /dev/null
@@ -1,115 +0,0 @@
-/* Measurement Report Processing */
-
-/* (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_data.h>
-#include <openbsc/meas_rep.h>
-
-static int get_field(const struct gsm_meas_rep *rep,
- enum meas_rep_field field)
-{
- switch (field) {
- case MEAS_REP_DL_RXLEV_FULL:
- return rep->dl.full.rx_lev;
- case MEAS_REP_DL_RXLEV_SUB:
- return rep->dl.sub.rx_lev;
- case MEAS_REP_DL_RXQUAL_FULL:
- return rep->dl.full.rx_qual;
- case MEAS_REP_DL_RXQUAL_SUB:
- return rep->dl.sub.rx_qual;
- case MEAS_REP_UL_RXLEV_FULL:
- return rep->ul.full.rx_lev;
- case MEAS_REP_UL_RXLEV_SUB:
- return rep->ul.sub.rx_lev;
- case MEAS_REP_UL_RXQUAL_FULL:
- return rep->ul.full.rx_qual;
- case MEAS_REP_UL_RXQUAL_SUB:
- return rep->ul.sub.rx_qual;
- }
-
- return 0;
-}
-
-
-unsigned int calc_initial_idx(unsigned int array_size,
- unsigned int meas_rep_idx,
- unsigned int num_values)
-{
- int offs, idx;
-
- /* from which element do we need to start if we're interested
- * in an average of 'num' elements */
- offs = meas_rep_idx - num_values;
-
- if (offs < 0)
- idx = array_size + offs;
- else
- idx = offs;
-
- return idx;
-}
-
-/* obtain an average over the last 'num' fields in the meas reps */
-int get_meas_rep_avg(const struct gsm_lchan *lchan,
- enum meas_rep_field field, unsigned int num)
-{
- unsigned int i, idx;
- int avg = 0;
-
- if (num < 1)
- return 0;
-
- idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep),
- lchan->meas_rep_idx, num);
-
- for (i = 0; i < num; i++) {
- int j = (idx+i) % ARRAY_SIZE(lchan->meas_rep);
-
- avg += get_field(&lchan->meas_rep[j], field);
- }
-
- return avg / num;
-}
-
-/* Check if N out of M last values for FIELD are >= bd */
-int meas_rep_n_out_of_m_be(const struct gsm_lchan *lchan,
- enum meas_rep_field field,
- unsigned int n, unsigned int m, int be)
-{
- unsigned int i, idx;
- int count = 0;
-
- idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep),
- lchan->meas_rep_idx, m);
-
- for (i = 0; i < m; i++) {
- int j = (idx + i) % ARRAY_SIZE(lchan->meas_rep);
- int val = get_field(&lchan->meas_rep[j], field);
-
- if (val >= be)
- count++;
-
- if (count >= n)
- return 1;
- }
-
- return 0;
-}
diff --git a/openbsc/src/libbsc/net_init.c b/openbsc/src/libbsc/net_init.c
deleted file mode 100644
index bc5ed3510..000000000
--- a/openbsc/src/libbsc/net_init.c
+++ /dev/null
@@ -1,69 +0,0 @@
-/* (C) 2008-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 <openbsc/common_cs.h>
-#include <openbsc/osmo_bsc.h>
-#include <openbsc/bsc_msc_data.h>
-
-struct gsm_network *bsc_network_init(void *ctx,
- uint16_t country_code,
- uint16_t network_code,
- mncc_recv_cb_t mncc_recv)
-{
- struct gsm_network *net;
-
- net = gsm_network_init(ctx, country_code, network_code, mncc_recv);
-
- net->bsc_data = talloc_zero(net, struct osmo_bsc_data);
- if (!net->bsc_data) {
- talloc_free(net);
- return NULL;
- }
-
- /* Init back pointer */
- net->bsc_data->auto_off_timeout = -1;
- net->bsc_data->network = net;
- INIT_LLIST_HEAD(&net->bsc_data->mscs);
-
- net->num_bts = 0;
- net->reject_cause = GSM48_REJECT_ROAMING_NOT_ALLOWED;
- net->T3101 = GSM_T3101_DEFAULT;
- net->T3105 = GSM_T3105_DEFAULT;
- net->T3113 = GSM_T3113_DEFAULT;
- net->T3122 = GSM_T3122_DEFAULT;
- /* FIXME: initialize all other timers! */
-
- /* default set of handover parameters */
- net->handover.win_rxlev_avg = 10;
- net->handover.win_rxqual_avg = 1;
- net->handover.win_rxlev_avg_neigh = 10;
- net->handover.pwr_interval = 6;
- net->handover.pwr_hysteresis = 3;
- net->handover.max_distance = 9999;
-
- INIT_LLIST_HEAD(&net->bts_list);
-
- /* init statistics */
- net->bsc_ctrs = rate_ctr_group_alloc(net, &bsc_ctrg_desc, 0);
-
- gsm_net_update_ctype(net);
-
- return net;
-}
-
diff --git a/openbsc/src/libbsc/paging.c b/openbsc/src/libbsc/paging.c
deleted file mode 100644
index 78e39c558..000000000
--- a/openbsc/src/libbsc/paging.c
+++ /dev/null
@@ -1,449 +0,0 @@
-/* Paging helper and manager.... */
-/* (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/>.
- *
- */
-
-/*
- * Relevant specs:
- * 12.21:
- * - 9.4.12 for CCCH Local Threshold
- *
- * 05.58:
- * - 8.5.2 CCCH Load indication
- * - 9.3.15 Paging Load
- *
- * Approach:
- * - Send paging command to subscriber
- * - On Channel Request we will remember the reason
- * - After the ACK we will request the identity
- * - Then we will send assign the gsm_subscriber and
- * - and call a callback
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <assert.h>
-
-#include <osmocom/core/talloc.h>
-#include <osmocom/gsm/gsm48.h>
-#include <osmocom/gsm/gsm0502.h>
-
-#include <openbsc/bsc_subscriber.h>
-#include <openbsc/paging.h>
-#include <openbsc/debug.h>
-#include <openbsc/signal.h>
-#include <openbsc/abis_rsl.h>
-#include <openbsc/gsm_data.h>
-#include <openbsc/chan_alloc.h>
-#include <openbsc/bsc_api.h>
-
-void *tall_paging_ctx;
-
-#define PAGING_TIMER 0, 500000
-
-/*
- * Kill one paging request update the internal list...
- */
-static void paging_remove_request(struct gsm_bts_paging_state *paging_bts,
- struct gsm_paging_request *to_be_deleted)
-{
- osmo_timer_del(&to_be_deleted->T3113);
- llist_del(&to_be_deleted->entry);
- bsc_subscr_put(to_be_deleted->bsub);
- talloc_free(to_be_deleted);
-}
-
-static void page_ms(struct gsm_paging_request *request)
-{
- uint8_t mi[128];
- unsigned int mi_len;
- unsigned int page_group;
- struct gsm_bts *bts = request->bts;
-
- /* the bts is down.. we will just wait for the paging to expire */
- if (!bts->oml_link)
- return;
-
- log_set_context(LOG_CTX_BSC_SUBSCR, request->bsub);
-
- LOGP(DPAG, LOGL_INFO, "Going to send paging commands: imsi: %s tmsi: "
- "0x%08x for ch. type %d (attempt %d)\n", request->bsub->imsi,
- request->bsub->tmsi, request->chan_type, request->attempts);
-
- if (request->bsub->tmsi == GSM_RESERVED_TMSI)
- mi_len = gsm48_generate_mid_from_imsi(mi, request->bsub->imsi);
- else
- mi_len = gsm48_generate_mid_from_tmsi(mi, request->bsub->tmsi);
-
- page_group = gsm0502_calc_paging_group(&bts->si_common.chan_desc,
- str_to_imsi(request->bsub->imsi));
- gsm0808_page(bts, page_group, mi_len, mi, request->chan_type);
- log_set_context(LOG_CTX_BSC_SUBSCR, NULL);
-}
-
-static void paging_schedule_if_needed(struct gsm_bts_paging_state *paging_bts)
-{
- if (llist_empty(&paging_bts->pending_requests))
- return;
-
- if (!osmo_timer_pending(&paging_bts->work_timer))
- osmo_timer_schedule(&paging_bts->work_timer, PAGING_TIMER);
-}
-
-
-static void paging_handle_pending_requests(struct gsm_bts_paging_state *paging_bts);
-static void paging_give_credit(void *data)
-{
- struct gsm_bts_paging_state *paging_bts = data;
-
- LOGP(DPAG, LOGL_NOTICE, "No slots available on bts nr %d\n", paging_bts->bts->nr);
- paging_bts->available_slots = 20;
- paging_handle_pending_requests(paging_bts);
-}
-
-static int can_send_pag_req(struct gsm_bts *bts, int rsl_type)
-{
- struct pchan_load pl;
- int count;
-
- memset(&pl, 0, sizeof(pl));
- bts_chan_load(&pl, bts);
-
- switch (rsl_type) {
- case RSL_CHANNEED_TCH_F:
- case RSL_CHANNEED_TCH_ForH:
- goto count_tch;
- break;
- case RSL_CHANNEED_SDCCH:
- goto count_sdcch;
- break;
- case RSL_CHANNEED_ANY:
- default:
- if (bts->network->pag_any_tch)
- goto count_tch;
- else
- goto count_sdcch;
- break;
- }
-
- return 0;
-
- /* could available SDCCH */
-count_sdcch:
- count = 0;
- count += pl.pchan[GSM_PCHAN_SDCCH8_SACCH8C].total
- - pl.pchan[GSM_PCHAN_SDCCH8_SACCH8C].used;
- count += pl.pchan[GSM_PCHAN_CCCH_SDCCH4].total
- - pl.pchan[GSM_PCHAN_CCCH_SDCCH4].used;
- return bts->paging.free_chans_need > count;
-
-count_tch:
- count = 0;
- count += pl.pchan[GSM_PCHAN_TCH_F].total
- - pl.pchan[GSM_PCHAN_TCH_F].used;
- if (bts->network->neci)
- count += pl.pchan[GSM_PCHAN_TCH_H].total
- - pl.pchan[GSM_PCHAN_TCH_H].used;
- return bts->paging.free_chans_need > count;
-}
-
-/*
- * This is kicked by the periodic PAGING LOAD Indicator
- * coming from abis_rsl.c
- *
- * We attempt to iterate once over the list of items but
- * only upto available_slots.
- */
-static void paging_handle_pending_requests(struct gsm_bts_paging_state *paging_bts)
-{
- struct gsm_paging_request *request = NULL;
-
- /*
- * Determine if the pending_requests list is empty and
- * return then.
- */
- if (llist_empty(&paging_bts->pending_requests)) {
- /* since the list is empty, no need to reschedule the timer */
- return;
- }
-
- /*
- * In case the BTS does not provide us with load indication and we
- * ran out of slots, call an autofill routine. It might be that the
- * BTS did not like our paging messages and then we have counted down
- * to zero and we do not get any messages.
- */
- if (paging_bts->available_slots == 0) {
- osmo_timer_setup(&paging_bts->credit_timer, paging_give_credit,
- paging_bts);
- osmo_timer_schedule(&paging_bts->credit_timer, 5, 0);
- return;
- }
-
- request = llist_entry(paging_bts->pending_requests.next,
- struct gsm_paging_request, entry);
-
- /* we need to determine the number of free channels */
- if (paging_bts->free_chans_need != -1) {
- if (can_send_pag_req(request->bts, request->chan_type) != 0)
- goto skip_paging;
- }
-
- /* handle the paging request now */
- page_ms(request);
- paging_bts->available_slots--;
- request->attempts++;
-
- /* take the current and add it to the back */
- llist_del(&request->entry);
- llist_add_tail(&request->entry, &paging_bts->pending_requests);
-
-skip_paging:
- osmo_timer_schedule(&paging_bts->work_timer, PAGING_TIMER);
-}
-
-static void paging_worker(void *data)
-{
- struct gsm_bts_paging_state *paging_bts = data;
-
- paging_handle_pending_requests(paging_bts);
-}
-
-static void paging_init_if_needed(struct gsm_bts *bts)
-{
- if (bts->paging.bts)
- return;
-
- bts->paging.bts = bts;
- INIT_LLIST_HEAD(&bts->paging.pending_requests);
- osmo_timer_setup(&bts->paging.work_timer, paging_worker,
- &bts->paging);
-
- /* Large number, until we get a proper message */
- bts->paging.available_slots = 20;
-}
-
-static int paging_pending_request(struct gsm_bts_paging_state *bts,
- struct bsc_subscr *bsub)
-{
- struct gsm_paging_request *req;
-
- llist_for_each_entry(req, &bts->pending_requests, entry) {
- if (bsub == req->bsub)
- return 1;
- }
-
- return 0;
-}
-
-static void paging_T3113_expired(void *data)
-{
- struct gsm_paging_request *req = (struct gsm_paging_request *)data;
- void *cbfn_param;
- gsm_cbfn *cbfn;
- int msg;
-
- log_set_context(LOG_CTX_BSC_SUBSCR, req->bsub);
-
- LOGP(DPAG, LOGL_INFO, "T3113 expired for request %p (%s)\n",
- req, bsc_subscr_name(req->bsub));
-
- /* must be destroyed before calling cbfn, to prevent double free */
- rate_ctr_inc(&req->bts->network->bsc_ctrs->ctr[BSC_CTR_PAGING_EXPIRED]);
- cbfn_param = req->cbfn_param;
- cbfn = req->cbfn;
-
- /* did we ever manage to page the subscriber */
- msg = req->attempts > 0 ? GSM_PAGING_EXPIRED : GSM_PAGING_BUSY;
-
- /* destroy it now. Do not access req afterwards */
- paging_remove_request(&req->bts->paging, req);
-
- if (cbfn)
- cbfn(GSM_HOOK_RR_PAGING, msg, NULL, NULL,
- cbfn_param);
-
-}
-
-static int _paging_request(struct gsm_bts *bts, struct bsc_subscr *bsub,
- int type, gsm_cbfn *cbfn, void *data)
-{
- struct gsm_bts_paging_state *bts_entry = &bts->paging;
- struct gsm_paging_request *req;
-
- if (paging_pending_request(bts_entry, bsub)) {
- LOGP(DPAG, LOGL_INFO, "Paging request already pending for %s\n",
- bsc_subscr_name(bsub));
- return -EEXIST;
- }
-
- LOGP(DPAG, LOGL_DEBUG, "Start paging of subscriber %s on bts %d.\n",
- bsc_subscr_name(bsub), bts->nr);
- req = talloc_zero(tall_paging_ctx, struct gsm_paging_request);
- req->bsub = bsc_subscr_get(bsub);
- req->bts = bts;
- req->chan_type = type;
- req->cbfn = cbfn;
- req->cbfn_param = data;
- osmo_timer_setup(&req->T3113, paging_T3113_expired, req);
- osmo_timer_schedule(&req->T3113, bts->network->T3113, 0);
- llist_add_tail(&req->entry, &bts_entry->pending_requests);
- paging_schedule_if_needed(bts_entry);
-
- return 0;
-}
-
-int paging_request_bts(struct gsm_bts *bts, struct bsc_subscr *bsub,
- int type, gsm_cbfn *cbfn, void *data)
-{
- int rc;
-
- /* skip all currently inactive TRX */
- if (!trx_is_usable(bts->c0))
- return 0;
-
- /* maybe it is the first time we use it */
- paging_init_if_needed(bts);
-
- /* Trigger paging, pass any error to the caller */
- rc = _paging_request(bts, bsub, type, cbfn, data);
- if (rc < 0)
- return rc;
- return 1;
-}
-
-int paging_request(struct gsm_network *network, struct bsc_subscr *bsub,
- int type, gsm_cbfn *cbfn, void *data)
-{
- struct gsm_bts *bts = NULL;
- int num_pages = 0;
-
- rate_ctr_inc(&network->bsc_ctrs->ctr[BSC_CTR_PAGING_ATTEMPTED]);
-
- /* start paging subscriber on all BTS within Location Area */
- do {
- int rc;
-
- bts = gsm_bts_by_lac(network, bsub->lac, bts);
- if (!bts)
- break;
-
- rc = paging_request_bts(bts, bsub, type, cbfn, data);
- if (rc < 0) {
- paging_request_stop(&network->bts_list, NULL, bsub,
- NULL, NULL);
- return rc;
- }
- num_pages += rc;
- } while (1);
-
- if (num_pages == 0)
- rate_ctr_inc(&network->bsc_ctrs->ctr[BSC_CTR_PAGING_DETACHED]);
-
- return num_pages;
-}
-
-
-/* we consciously ignore the type of the request here */
-static void _paging_request_stop(struct gsm_bts *bts, struct bsc_subscr *bsub,
- struct gsm_subscriber_connection *conn,
- struct msgb *msg)
-{
- struct gsm_bts_paging_state *bts_entry = &bts->paging;
- struct gsm_paging_request *req, *req2;
-
- paging_init_if_needed(bts);
-
- llist_for_each_entry_safe(req, req2, &bts_entry->pending_requests,
- entry) {
- if (req->bsub == bsub) {
- gsm_cbfn *cbfn = req->cbfn;
- void *param = req->cbfn_param;
-
- /* now give up the data structure */
- paging_remove_request(&bts->paging, req);
- req = NULL;
-
- if (conn && cbfn) {
- LOGP(DPAG, LOGL_DEBUG, "Stop paging %s on bts %d, calling cbfn.\n", bsub->imsi, bts->nr);
- cbfn(GSM_HOOK_RR_PAGING, GSM_PAGING_SUCCEEDED,
- msg, conn, param);
- } else
- LOGP(DPAG, LOGL_DEBUG, "Stop paging %s on bts %d silently.\n", bsub->imsi, bts->nr);
- break;
- }
- }
-}
-
-/* Stop paging on all other bts' */
-void paging_request_stop(struct llist_head *bts_list,
- struct gsm_bts *_bts, struct bsc_subscr *bsub,
- struct gsm_subscriber_connection *conn,
- struct msgb *msg)
-{
- struct gsm_bts *bts;
-
- log_set_context(LOG_CTX_BSC_SUBSCR, bsub);
-
- /* Stop this first and dispatch the request */
- if (_bts)
- _paging_request_stop(_bts, bsub, conn, msg);
-
- /* Make sure to cancel this everywhere else */
- llist_for_each_entry(bts, bts_list, list) {
- /* Sort of an optimization. */
- if (bts == _bts)
- continue;
- _paging_request_stop(bts, bsub, NULL, NULL);
- }
-}
-
-void paging_update_buffer_space(struct gsm_bts *bts, uint16_t free_slots)
-{
- paging_init_if_needed(bts);
-
- osmo_timer_del(&bts->paging.credit_timer);
- bts->paging.available_slots = free_slots;
- paging_schedule_if_needed(&bts->paging);
-}
-
-unsigned int paging_pending_requests_nr(struct gsm_bts *bts)
-{
- unsigned int requests = 0;
- struct gsm_paging_request *req;
-
- paging_init_if_needed(bts);
-
- llist_for_each_entry(req, &bts->paging.pending_requests, entry)
- ++requests;
-
- return requests;
-}
-
-/**
- * Find any paging data for the given subscriber at the given BTS.
- */
-void *paging_get_data(struct gsm_bts *bts, struct bsc_subscr *bsub)
-{
- struct gsm_paging_request *req;
-
- llist_for_each_entry(req, &bts->paging.pending_requests, entry)
- if (req->bsub == bsub)
- return req->cbfn_param;
-
- return NULL;
-}
diff --git a/openbsc/src/libbsc/pcu_sock.c b/openbsc/src/libbsc/pcu_sock.c
deleted file mode 100644
index 98e12fad4..000000000
--- a/openbsc/src/libbsc/pcu_sock.c
+++ /dev/null
@@ -1,742 +0,0 @@
-/* pcu_sock.c: Connect from PCU via unix domain socket */
-
-/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org>
- * (C) 2009-2012 by Andreas Eversberg <jolly@eversberg.eu>
- * (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/core/logging.h>
-#include <osmocom/gsm/l1sap.h>
-#include <osmocom/gsm/gsm0502.h>
-
-#include <openbsc/gsm_data.h>
-#include <openbsc/pcu_if.h>
-#include <openbsc/pcuif_proto.h>
-#include <openbsc/signal.h>
-#include <openbsc/debug.h>
-#include <openbsc/abis_rsl.h>
-
-static int pcu_sock_send(struct gsm_bts *bts, struct msgb *msg);
-uint32_t trx_get_hlayer1(struct gsm_bts_trx *trx);
-int pcu_direct = 1;
-
-static const char *sapi_string[] = {
- [PCU_IF_SAPI_RACH] = "RACH",
- [PCU_IF_SAPI_AGCH] = "AGCH",
- [PCU_IF_SAPI_PCH] = "PCH",
- [PCU_IF_SAPI_BCCH] = "BCCH",
- [PCU_IF_SAPI_PDTCH] = "PDTCH",
- [PCU_IF_SAPI_PRACH] = "PRACH",
- [PCU_IF_SAPI_PTCCH] = "PTCCH",
- [PCU_IF_SAPI_AGCH_DT] = "AGCH_DT",
-};
-
-/* Check if BTS has a PCU connection */
-static bool pcu_connected(struct gsm_bts *bts)
-{
- struct pcu_sock_state *state = bts->pcu_state;
-
- if (!state)
- return false;
- if (state->conn_bfd.fd <= 0)
- return false;
- return true;
-}
-
-/*
- * PCU messages
- */
-
-/* Set up an message buffer to package an pcu interface message */
-struct msgb *pcu_msgb_alloc(uint8_t msg_type, uint8_t bts_nr)
-{
- struct msgb *msg;
- struct gsm_pcu_if *pcu_prim;
-
- msg = msgb_alloc(sizeof(struct gsm_pcu_if), "pcu_sock_tx");
- if (!msg)
- return NULL;
-
- msgb_put(msg, sizeof(struct gsm_pcu_if));
- pcu_prim = (struct gsm_pcu_if *) msg->data;
- pcu_prim->msg_type = msg_type;
- pcu_prim->bts_nr = bts_nr;
-
- return msg;
-}
-
-/* Helper function exclusivly used by pcu_if_signal_cb() */
-static bool ts_should_be_pdch(struct gsm_bts_trx_ts *ts) {
- if (ts->pchan == GSM_PCHAN_PDCH)
- return true;
- if (ts->pchan == GSM_PCHAN_TCH_F_PDCH) {
- /* When we're busy deactivating the PDCH, we first set
- * DEACT_PENDING, tell the PCU about it and wait for a
- * response. So DEACT_PENDING means "no PDCH" to the PCU.
- * Similarly, when we're activating PDCH, we set the
- * ACT_PENDING and wait for an activation response from the
- * PCU, so ACT_PENDING means "is PDCH". */
- if (ts->flags & TS_F_PDCH_ACTIVE)
- return !(ts->flags & TS_F_PDCH_DEACT_PENDING);
- else
- return (ts->flags & TS_F_PDCH_ACT_PENDING);
- }
- if (ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH) {
- /*
- * When we're busy de-/activating the PDCH, we first set
- * ts->dyn.pchan_want, tell the PCU about it and wait for a
- * response. So only care about dyn.pchan_want here.
- */
- return ts->dyn.pchan_want == GSM_PCHAN_PDCH;
- }
- return false;
-}
-
-/* Send BTS properties to the PCU */
-static int pcu_tx_info_ind(struct gsm_bts *bts)
-{
- struct msgb *msg;
- struct gsm_pcu_if *pcu_prim;
- struct gsm_pcu_if_info_ind *info_ind;
- struct gprs_rlc_cfg *rlcc;
- struct gsm_bts_gprs_nsvc *nsvc;
- struct gsm_bts_trx *trx;
- struct gsm_bts_trx_ts *ts;
- int i, j;
-
- OSMO_ASSERT(bts);
- OSMO_ASSERT(bts->network);
-
- LOGP(DPCU, LOGL_INFO, "Sending info for BTS %d\n",bts->nr);
-
- rlcc = &bts->gprs.cell.rlc_cfg;
-
- msg = pcu_msgb_alloc(PCU_IF_MSG_INFO_IND, bts->nr);
- if (!msg)
- return -ENOMEM;
-
- pcu_prim = (struct gsm_pcu_if *) msg->data;
- info_ind = &pcu_prim->u.info_ind;
- info_ind->version = PCU_IF_VERSION;
- info_ind->flags |= PCU_IF_FLAG_ACTIVE;
-
- if (pcu_direct)
- info_ind->flags |= PCU_IF_FLAG_SYSMO;
-
- /* RAI */
- info_ind->mcc = bts->network->country_code;
- info_ind->mnc = bts->network->network_code;
- info_ind->lac = bts->location_area_code;
- info_ind->rac = bts->gprs.rac;
-
- /* NSE */
- info_ind->nsei = bts->gprs.nse.nsei;
- memcpy(info_ind->nse_timer, bts->gprs.nse.timer, 7);
- memcpy(info_ind->cell_timer, bts->gprs.cell.timer, 11);
-
- /* cell attributes */
- info_ind->cell_id = bts->cell_identity;
- info_ind->repeat_time = rlcc->paging.repeat_time;
- info_ind->repeat_count = rlcc->paging.repeat_count;
- info_ind->bvci = bts->gprs.cell.bvci;
- info_ind->t3142 = rlcc->parameter[RLC_T3142];
- info_ind->t3169 = rlcc->parameter[RLC_T3169];
- info_ind->t3191 = rlcc->parameter[RLC_T3191];
- info_ind->t3193_10ms = rlcc->parameter[RLC_T3193];
- info_ind->t3195 = rlcc->parameter[RLC_T3195];
- info_ind->n3101 = rlcc->parameter[RLC_N3101];
- info_ind->n3103 = rlcc->parameter[RLC_N3103];
- info_ind->n3105 = rlcc->parameter[RLC_N3105];
- info_ind->cv_countdown = rlcc->parameter[CV_COUNTDOWN];
- if (rlcc->cs_mask & (1 << GPRS_CS1))
- info_ind->flags |= PCU_IF_FLAG_CS1;
- if (rlcc->cs_mask & (1 << GPRS_CS2))
- info_ind->flags |= PCU_IF_FLAG_CS2;
- if (rlcc->cs_mask & (1 << GPRS_CS3))
- info_ind->flags |= PCU_IF_FLAG_CS3;
- if (rlcc->cs_mask & (1 << GPRS_CS4))
- info_ind->flags |= PCU_IF_FLAG_CS4;
- if (bts->gprs.mode == BTS_GPRS_EGPRS) {
- if (rlcc->cs_mask & (1 << GPRS_MCS1))
- info_ind->flags |= PCU_IF_FLAG_MCS1;
- if (rlcc->cs_mask & (1 << GPRS_MCS2))
- info_ind->flags |= PCU_IF_FLAG_MCS2;
- if (rlcc->cs_mask & (1 << GPRS_MCS3))
- info_ind->flags |= PCU_IF_FLAG_MCS3;
- if (rlcc->cs_mask & (1 << GPRS_MCS4))
- info_ind->flags |= PCU_IF_FLAG_MCS4;
- if (rlcc->cs_mask & (1 << GPRS_MCS5))
- info_ind->flags |= PCU_IF_FLAG_MCS5;
- if (rlcc->cs_mask & (1 << GPRS_MCS6))
- info_ind->flags |= PCU_IF_FLAG_MCS6;
- if (rlcc->cs_mask & (1 << GPRS_MCS7))
- info_ind->flags |= PCU_IF_FLAG_MCS7;
- if (rlcc->cs_mask & (1 << GPRS_MCS8))
- info_ind->flags |= PCU_IF_FLAG_MCS8;
- if (rlcc->cs_mask & (1 << GPRS_MCS9))
- info_ind->flags |= PCU_IF_FLAG_MCS9;
- }
-#warning "isn't dl_tbf_ext wrong?: * 10 and no ntohs"
- info_ind->dl_tbf_ext = rlcc->parameter[T_DL_TBF_EXT];
-#warning "isn't ul_tbf_ext wrong?: * 10 and no ntohs"
- info_ind->ul_tbf_ext = rlcc->parameter[T_UL_TBF_EXT];
- info_ind->initial_cs = rlcc->initial_cs;
- info_ind->initial_mcs = rlcc->initial_mcs;
-
- /* NSVC */
- for (i = 0; i < ARRAY_SIZE(info_ind->nsvci); i++) {
- nsvc = &bts->gprs.nsvc[i];
- info_ind->nsvci[i] = nsvc->nsvci;
- info_ind->local_port[i] = nsvc->local_port;
- info_ind->remote_port[i] = nsvc->remote_port;
- info_ind->remote_ip[i] = nsvc->remote_ip;
- }
-
- for (i = 0; i < ARRAY_SIZE(info_ind->trx); i++) {
- trx = gsm_bts_trx_num(bts, i);
- if (!trx)
- continue;
- info_ind->trx[i].hlayer1 = 0x2342;
- info_ind->trx[i].pdch_mask = 0;
- info_ind->trx[i].arfcn = trx->arfcn;
- for (j = 0; j < ARRAY_SIZE(trx->ts); j++) {
- ts = &trx->ts[j];
- if (ts->mo.nm_state.operational == NM_OPSTATE_ENABLED
- && ts_should_be_pdch(ts)) {
- info_ind->trx[i].pdch_mask |= (1 << j);
- info_ind->trx[i].tsc[j] =
- (ts->tsc >= 0) ? ts->tsc : bts->bsic & 7;
- LOGP(DPCU, LOGL_INFO, "trx=%d ts=%d: "
- "available (tsc=%d arfcn=%d)\n",
- trx->nr, ts->nr,
- info_ind->trx[i].tsc[j],
- info_ind->trx[i].arfcn);
- }
- }
- }
-
- return pcu_sock_send(bts, msg);
-}
-
-void pcu_info_update(struct gsm_bts *bts)
-{
- if (pcu_connected(bts))
- pcu_tx_info_ind(bts);
-}
-
-/* Forward rach indication to PCU */
-int pcu_tx_rach_ind(struct gsm_bts *bts, int16_t qta, uint16_t ra, uint32_t fn,
- uint8_t is_11bit, enum ph_burst_type burst_type)
-{
- struct msgb *msg;
- struct gsm_pcu_if *pcu_prim;
- struct gsm_pcu_if_rach_ind *rach_ind;
-
- /* Bail if no PCU is connected */
- if (!pcu_connected(bts)) {
- LOGP(DRSL, LOGL_ERROR, "BTS %d CHAN RQD(GPRS) but PCU not "
- "connected!\n", bts->nr);
- return -ENODEV;
- }
-
- LOGP(DPCU, LOGL_INFO, "Sending RACH indication: qta=%d, ra=%d, "
- "fn=%d\n", qta, ra, fn);
-
- msg = pcu_msgb_alloc(PCU_IF_MSG_RACH_IND, bts->nr);
- if (!msg)
- return -ENOMEM;
- pcu_prim = (struct gsm_pcu_if *) msg->data;
- rach_ind = &pcu_prim->u.rach_ind;
-
- rach_ind->sapi = PCU_IF_SAPI_RACH;
- rach_ind->ra = ra;
- rach_ind->qta = qta;
- rach_ind->fn = fn;
- rach_ind->is_11bit = is_11bit;
- rach_ind->burst_type = burst_type;
-
- return pcu_sock_send(bts, msg);
-}
-
-/* Confirm the sending of an immediate assignment to the pcu */
-int pcu_tx_imm_ass_sent(struct gsm_bts *bts, uint32_t tlli)
-{
- struct msgb *msg;
- struct gsm_pcu_if *pcu_prim;
- struct gsm_pcu_if_data_cnf_dt *data_cnf_dt;
-
- LOGP(DPCU, LOGL_INFO, "Sending PCH confirm with direct TLLI\n");
-
- msg = pcu_msgb_alloc(PCU_IF_MSG_DATA_CNF_DT, bts->nr);
- if (!msg)
- return -ENOMEM;
- pcu_prim = (struct gsm_pcu_if *) msg->data;
- data_cnf_dt = &pcu_prim->u.data_cnf_dt;
-
- data_cnf_dt->sapi = PCU_IF_SAPI_PCH;
- data_cnf_dt->tlli = tlli;
-
- return pcu_sock_send(bts, msg);
-}
-
-/* we need to decode the raw RR paging messsage (see PCU code
- * Encoding::write_paging_request) and extract the mobile identity
- * (P-TMSI) from it */
-static int pcu_rx_rr_paging(struct gsm_bts *bts, uint8_t paging_group,
- const uint8_t *raw_rr_msg)
-{
- struct gsm48_paging1 *p1 = (struct gsm48_paging1 *) raw_rr_msg;
- uint8_t chan_needed;
- unsigned int mi_len;
- uint8_t *mi;
- int rc;
-
- switch (p1->msg_type) {
- case GSM48_MT_RR_PAG_REQ_1:
- chan_needed = (p1->cneed2 << 2) | p1->cneed1;
- mi_len = p1->data[0];
- mi = p1->data+1;
- LOGP(DPCU, LOGL_ERROR, "PCU Sends paging "
- "request type %02x (chan_needed=%02x, mi_len=%u, mi=%s)\n",
- p1->msg_type, chan_needed, mi_len,
- osmo_hexdump_nospc(mi,mi_len));
- /* NOTE: We will have to add 2 to mi_len and subtract 2 from
- * the mi pointer because rsl_paging_cmd() will perform the
- * reverse operations. This is because rsl_paging_cmd() is
- * normally expected to chop off the element identifier (0xC0)
- * and the length field. In our parameter, we do not have
- * those fields included. */
- rc = rsl_paging_cmd(bts, paging_group, mi_len+2, mi-2,
- chan_needed, true);
- break;
- case GSM48_MT_RR_PAG_REQ_2:
- case GSM48_MT_RR_PAG_REQ_3:
- LOGP(DPCU, LOGL_ERROR, "PCU Sends unsupported paging "
- "request type %02x\n", p1->msg_type);
- rc = -EINVAL;
- break;
- default:
- LOGP(DPCU, LOGL_ERROR, "PCU Sends unknown paging "
- "request type %02x\n", p1->msg_type);
- rc = -EINVAL;
- break;
- }
-
- return rc;
-}
-
-static int pcu_rx_data_req(struct gsm_bts *bts, uint8_t msg_type,
- struct gsm_pcu_if_data *data_req)
-{
- uint8_t is_ptcch;
- struct gsm_bts_trx *trx;
- struct gsm_bts_trx_ts *ts;
- struct msgb *msg;
- char imsi_digit_buf[4];
- uint32_t tlli = -1;
- uint8_t pag_grp;
- int rc = 0;
-
- LOGP(DPCU, LOGL_DEBUG, "Data request received: sapi=%s arfcn=%d "
- "block=%d data=%s\n", sapi_string[data_req->sapi],
- data_req->arfcn, data_req->block_nr,
- osmo_hexdump(data_req->data, data_req->len));
-
- switch (data_req->sapi) {
- case PCU_IF_SAPI_PCH:
- /* the first three bytes are the last three digits of
- * the IMSI, which we need to compute the paging group */
- imsi_digit_buf[0] = data_req->data[0];
- imsi_digit_buf[1] = data_req->data[1];
- imsi_digit_buf[2] = data_req->data[2];
- imsi_digit_buf[3] = '\0';
- LOGP(DPCU, LOGL_DEBUG, "SAPI PCH imsi %s\n", imsi_digit_buf);
- pag_grp = gsm0502_calc_paging_group(&bts->si_common.chan_desc,
- str_to_imsi(imsi_digit_buf));
- pcu_rx_rr_paging(bts, pag_grp, data_req->data+3);
- break;
- case PCU_IF_SAPI_AGCH:
- msg = msgb_alloc(data_req->len, "pcu_agch");
- if (!msg) {
- rc = -ENOMEM;
- break;
- }
- msg->l3h = msgb_put(msg, data_req->len);
- memcpy(msg->l3h, data_req->data, data_req->len);
-
- if (rsl_imm_assign_cmd(bts, msg->len, msg->data)) {
- msgb_free(msg);
- rc = -EIO;
- }
- break;
- case PCU_IF_SAPI_AGCH_DT:
- /* DT = direct tlli. A tlli is prefixed */
-
- if (data_req->len < 5) {
- LOGP(DPCU, LOGL_ERROR, "Received PCU data request with "
- "invalid/small length %d\n", data_req->len);
- break;
- }
- tlli = *((uint32_t *)data_req->data);
-
- msg = msgb_alloc(data_req->len - 4, "pcu_agch");
- if (!msg) {
- rc = -ENOMEM;
- break;
- }
- msg->l3h = msgb_put(msg, data_req->len - 4);
- memcpy(msg->l3h, data_req->data + 4, data_req->len - 4);
-
- if (bts->type == GSM_BTS_TYPE_RBS2000)
- rc = rsl_ericsson_imm_assign_cmd(bts, tlli, msg->len, msg->data);
- else
- rc = rsl_imm_assign_cmd(bts, msg->len, msg->data);
-
- if (rc) {
- msgb_free(msg);
- rc = -EIO;
- }
- break;
- default:
- LOGP(DPCU, LOGL_ERROR, "Received PCU data request with "
- "unsupported sapi %d\n", data_req->sapi);
- rc = -EINVAL;
- }
-
- return rc;
-}
-
-static int pcu_rx(struct gsm_network *net, uint8_t msg_type,
- struct gsm_pcu_if *pcu_prim)
-{
- int rc = 0;
- struct gsm_bts *bts;
-
- /* FIXME: allow multiple BTS */
- bts = llist_entry(net->bts_list.next, struct gsm_bts, list);
-
- switch (msg_type) {
- case PCU_IF_MSG_DATA_REQ:
- case PCU_IF_MSG_PAG_REQ:
- rc = pcu_rx_data_req(bts, msg_type, &pcu_prim->u.data_req);
- break;
- default:
- LOGP(DPCU, LOGL_ERROR, "Received unknwon PCU msg type %d\n",
- msg_type);
- rc = -EINVAL;
- }
-
- return rc;
-}
-
-/*
- * PCU socket interface
- */
-
-static int pcu_sock_send(struct gsm_bts *bts, struct msgb *msg)
-{
- struct pcu_sock_state *state = bts->pcu_state;
- struct osmo_fd *conn_bfd;
- struct gsm_pcu_if *pcu_prim = (struct gsm_pcu_if *) msg->data;
-
- if (!state) {
- if (pcu_prim->msg_type != PCU_IF_MSG_TIME_IND)
- LOGP(DPCU, LOGL_INFO, "PCU socket not created, "
- "dropping message\n");
- msgb_free(msg);
- return -EINVAL;
- }
- conn_bfd = &state->conn_bfd;
- if (conn_bfd->fd <= 0) {
- if (pcu_prim->msg_type != PCU_IF_MSG_TIME_IND)
- LOGP(DPCU, LOGL_NOTICE, "PCU socket not connected, "
- "dropping message\n");
- msgb_free(msg);
- return -EIO;
- }
- msgb_enqueue(&state->upqueue, msg);
- conn_bfd->when |= BSC_FD_WRITE;
-
- return 0;
-}
-
-static void pcu_sock_close(struct pcu_sock_state *state)
-{
- struct osmo_fd *bfd = &state->conn_bfd;
- struct gsm_bts *bts;
- struct gsm_bts_trx *trx;
- struct gsm_bts_trx_ts *ts;
- int i, j;
-
- /* FIXME: allow multiple BTS */
- bts = llist_entry(state->net->bts_list.next, struct gsm_bts, list);
-
- LOGP(DPCU, LOGL_NOTICE, "PCU 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;
-
-#if 0
- /* remove si13, ... */
- bts->si_valid &= ~(1 << SYSINFO_TYPE_13);
- osmo_signal_dispatch(SS_GLOBAL, S_NEW_SYSINFO, bts);
-#endif
-
- /* release PDCH */
- for (i = 0; i < 8; i++) {
- trx = gsm_bts_trx_num(bts, i);
- if (!trx)
- break;
- for (j = 0; j < 8; j++) {
- ts = &trx->ts[j];
- if (ts->mo.nm_state.operational == NM_OPSTATE_ENABLED
- && ts->pchan == GSM_PCHAN_PDCH) {
- printf("l1sap_chan_rel(trx,gsm_lchan2chan_nr(ts->lchan));\n");
- }
- }
- }
-
- /* flush the queue */
- while (!llist_empty(&state->upqueue)) {
- struct msgb *msg = msgb_dequeue(&state->upqueue);
- msgb_free(msg);
- }
-}
-
-static int pcu_sock_read(struct osmo_fd *bfd)
-{
- struct pcu_sock_state *state = (struct pcu_sock_state *)bfd->data;
- struct gsm_pcu_if *pcu_prim;
- struct msgb *msg;
- int rc;
-
- msg = msgb_alloc(sizeof(*pcu_prim), "pcu_sock_rx");
- if (!msg)
- return -ENOMEM;
-
- pcu_prim = (struct gsm_pcu_if *) 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 = pcu_rx(state->net, pcu_prim->msg_type, pcu_prim);
-
- /* as we always synchronously process the message in pcu_rx() and
- * its callbacks, we can free the message here. */
- msgb_free(msg);
-
- return rc;
-
-close:
- msgb_free(msg);
- pcu_sock_close(state);
- return -1;
-}
-
-static int pcu_sock_write(struct osmo_fd *bfd)
-{
- struct pcu_sock_state *state = bfd->data;
- int rc;
-
- while (!llist_empty(&state->upqueue)) {
- struct msgb *msg, *msg2;
- struct gsm_pcu_if *pcu_prim;
-
- /* peek at the beginning of the queue */
- msg = llist_entry(state->upqueue.next, struct msgb, list);
- pcu_prim = (struct gsm_pcu_if *)msg->data;
-
- bfd->when &= ~BSC_FD_WRITE;
-
- /* bug hunter 8-): maybe someone forgot msgb_put(...) ? */
- if (!msgb_length(msg)) {
- LOGP(DPCU, LOGL_ERROR, "message type (%d) with ZERO "
- "bytes!\n", pcu_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(&state->upqueue);
- assert(msg == msg2);
- msgb_free(msg);
- }
- return 0;
-
-close:
- pcu_sock_close(state);
-
- return -1;
-}
-
-static int pcu_sock_cb(struct osmo_fd *bfd, unsigned int flags)
-{
- int rc = 0;
-
- if (flags & BSC_FD_READ)
- rc = pcu_sock_read(bfd);
- if (rc < 0)
- return rc;
-
- if (flags & BSC_FD_WRITE)
- rc = pcu_sock_write(bfd);
-
- return rc;
-}
-
-/* accept connection comming from PCU */
-static int pcu_sock_accept(struct osmo_fd *bfd, unsigned int flags)
-{
- struct pcu_sock_state *state = (struct pcu_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(DPCU, LOGL_ERROR, "Failed to accept a new connection\n");
- return -1;
- }
-
- if (conn_bfd->fd >= 0) {
- LOGP(DPCU, LOGL_NOTICE, "PCU connects but we already have "
- "another active connection ?!?\n");
- /* We already have one PCU 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 = pcu_sock_cb;
- conn_bfd->data = state;
-
- if (osmo_fd_register(conn_bfd) != 0) {
- LOGP(DPCU, LOGL_ERROR, "Failed to register new connection "
- "fd\n");
- close(conn_bfd->fd);
- conn_bfd->fd = -1;
- return -1;
- }
-
- LOGP(DPCU, LOGL_NOTICE, "PCU socket connected to external PCU\n");
-
- return 0;
-}
-
-/* Open connection to PCU */
-int pcu_sock_init(const char *path, struct gsm_bts *bts)
-{
- struct pcu_sock_state *state;
- struct osmo_fd *bfd;
- int rc;
-
- state = talloc_zero(NULL, struct pcu_sock_state);
- if (!state)
- return -ENOMEM;
-
- INIT_LLIST_HEAD(&state->upqueue);
- state->net = bts->network;
- state->conn_bfd.fd = -1;
-
- bfd = &state->listen_bfd;
-
- bfd->fd = osmo_sock_unix_init(SOCK_SEQPACKET, 0, path,
- OSMO_SOCK_F_BIND);
- if (bfd->fd < 0) {
- LOGP(DPCU, LOGL_ERROR, "Could not create unix socket: %s\n",
- strerror(errno));
- talloc_free(state);
- return -1;
- }
-
- bfd->when = BSC_FD_READ;
- bfd->cb = pcu_sock_accept;
- bfd->data = state;
-
- rc = osmo_fd_register(bfd);
- if (rc < 0) {
- LOGP(DPCU, LOGL_ERROR, "Could not register listen fd: %d\n",
- rc);
- close(bfd->fd);
- talloc_free(state);
- return rc;
- }
-
- bts->pcu_state = state;
- return 0;
-}
-
-/* Close connection to PCU */
-void pcu_sock_exit(struct gsm_bts *bts)
-{
- struct pcu_sock_state *state = bts->pcu_state;
- struct osmo_fd *bfd, *conn_bfd;
-
- if (!state)
- return;
-
- conn_bfd = &state->conn_bfd;
- if (conn_bfd->fd > 0)
- pcu_sock_close(state);
- bfd = &state->listen_bfd;
- close(bfd->fd);
- osmo_fd_unregister(bfd);
- talloc_free(state);
- bts->pcu_state = NULL;
-}
-
diff --git a/openbsc/src/libbsc/rest_octets.c b/openbsc/src/libbsc/rest_octets.c
deleted file mode 100644
index fdab70a0c..000000000
--- a/openbsc/src/libbsc/rest_octets.c
+++ /dev/null
@@ -1,860 +0,0 @@
-/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface,
- * rest octet handling according to
- * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */
-
-/* (C) 2009 by Harald Welte <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 <string.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <stdbool.h>
-
-#include <openbsc/debug.h>
-#include <openbsc/gsm_data.h>
-#include <osmocom/core/bitvec.h>
-#include <osmocom/gsm/bitvec_gsm.h>
-#include <openbsc/rest_octets.h>
-#include <openbsc/arfcn_range_encode.h>
-#include <openbsc/system_information.h>
-
-/* generate SI1 rest octets */
-int rest_octets_si1(uint8_t *data, uint8_t *nch_pos, int is1800_net)
-{
- struct bitvec bv;
-
- memset(&bv, 0, sizeof(bv));
- bv.data = data;
- bv.data_len = 1;
-
- if (nch_pos) {
- bitvec_set_bit(&bv, H);
- bitvec_set_uint(&bv, *nch_pos, 5);
- } else
- bitvec_set_bit(&bv, L);
-
- if (is1800_net)
- bitvec_set_bit(&bv, L);
- else
- bitvec_set_bit(&bv, H);
-
- bitvec_spare_padding(&bv, 6);
- return bv.data_len;
-}
-
-/* Append Repeated E-UTRAN Neighbour Cell to bitvec: see 3GPP TS 44.018 Table 10.5.2.33b.1 */
-static inline void append_eutran_neib_cell(struct bitvec *bv, struct gsm_bts *bts, uint8_t budget)
-{
- const struct osmo_earfcn_si2q *e = &bts->si_common.si2quater_neigh_list;
- unsigned i, skip = 0;
- size_t offset = bts->e_offset;
- uint8_t rem = budget - 6, earfcn_budget; /* account for mandatory stop bit and THRESH_E-UTRAN_high */
-
- if (budget <= 6)
- return;
-
- OSMO_ASSERT(budget <= SI2Q_MAX_LEN);
-
- /* first we have to properly adjust budget requirements */
- if (e->prio_valid) /* E-UTRAN_PRIORITY: 3GPP TS 45.008*/
- rem -= 4;
- else
- rem--;
-
- if (e->thresh_lo_valid) /* THRESH_E-UTRAN_low: */
- rem -= 6;
- else
- rem--;
-
- if (e->qrxlm_valid) /* E-UTRAN_QRXLEVMIN: */
- rem -= 6;
- else
- rem--;
-
- /* now we can proceed with actually adding EARFCNs within adjusted budget limit */
- for (i = 0; i < e->length; i++) {
- if (e->arfcn[i] != OSMO_EARFCN_INVALID) {
- if (skip < offset) {
- skip++; /* ignore EARFCNs added on previous calls */
- } else {
- earfcn_budget = 17; /* compute budget per-EARFCN */
- if (OSMO_EARFCN_MEAS_INVALID == e->meas_bw[i])
- earfcn_budget++;
- else
- earfcn_budget += 4;
-
- if (rem - earfcn_budget < 0)
- break;
- else {
- bts->e_offset++;
- rem -= earfcn_budget;
- bitvec_set_bit(bv, 1); /* EARFCN: */
- bitvec_set_uint(bv, e->arfcn[i], 16);
-
- if (OSMO_EARFCN_MEAS_INVALID == e->meas_bw[i])
- bitvec_set_bit(bv, 0);
- else { /* Measurement Bandwidth: 9.1.54 */
- bitvec_set_bit(bv, 1);
- bitvec_set_uint(bv, e->meas_bw[i], 3);
- }
- }
- }
- }
- }
-
- /* stop bit - end of EARFCN + Measurement Bandwidth sequence */
- bitvec_set_bit(bv, 0);
-
- /* Note: we don't support different EARFCN arrays each with different priority, threshold etc. */
-
- if (e->prio_valid) {
- /* E-UTRAN_PRIORITY: 3GPP TS 45.008*/
- bitvec_set_bit(bv, 1);
- bitvec_set_uint(bv, e->prio, 3);
- } else
- bitvec_set_bit(bv, 0);
-
- /* THRESH_E-UTRAN_high */
- bitvec_set_uint(bv, e->thresh_hi, 5);
-
- if (e->thresh_lo_valid) {
- /* THRESH_E-UTRAN_low: */
- bitvec_set_bit(bv, 1);
- bitvec_set_uint(bv, e->thresh_lo, 5);
- } else
- bitvec_set_bit(bv, 0);
-
- if (e->qrxlm_valid) {
- /* E-UTRAN_QRXLEVMIN: */
- bitvec_set_bit(bv, 1);
- bitvec_set_uint(bv, e->qrxlm, 5);
- } else
- bitvec_set_bit(bv, 0);
-}
-
-static inline void append_earfcn(struct bitvec *bv, struct gsm_bts *bts, uint8_t budget)
-{
- int rem = budget - 25;
- if (rem <= 0)
- return;
-
- OSMO_ASSERT(budget <= SI2Q_MAX_LEN);
-
- /* Additions in Rel-5: */
- bitvec_set_bit(bv, H);
- /* No 3G Additional Measurement Param. Descr. */
- bitvec_set_bit(bv, 0);
- /* No 3G ADDITIONAL MEASUREMENT Param. Descr. 2 */
- bitvec_set_bit(bv, 0);
- /* Additions in Rel-6: */
- bitvec_set_bit(bv, H);
- /* 3G_CCN_ACTIVE */
- bitvec_set_bit(bv, 0);
- /* Additions in Rel-7: */
- bitvec_set_bit(bv, H);
- /* No 700_REPORTING_OFFSET */
- bitvec_set_bit(bv, 0);
- /* No 810_REPORTING_OFFSET */
- bitvec_set_bit(bv, 0);
- /* Additions in Rel-8: */
- bitvec_set_bit(bv, H);
-
- /* Priority and E-UTRAN Parameters Description */
- bitvec_set_bit(bv, 1);
-
- /* No Serving Cell Priority Parameters Descr. */
- bitvec_set_bit(bv, 0);
- /* No 3G Priority Parameters Description */
- bitvec_set_bit(bv, 0);
- /* E-UTRAN Parameters Description */
- bitvec_set_bit(bv, 1);
-
- /* E-UTRAN_CCN_ACTIVE */
- bitvec_set_bit(bv, 0);
- /* E-UTRAN_Start: 9.1.54 */
- bitvec_set_bit(bv, 1);
- /* E-UTRAN_Stop: 9.1.54 */
- bitvec_set_bit(bv, 1);
-
- /* No E-UTRAN Measurement Parameters Descr. */
- bitvec_set_bit(bv, 0);
- /* No GPRS E-UTRAN Measurement Param. Descr. */
- bitvec_set_bit(bv, 0);
-
- /* Note: each of next 3 "repeated" structures might be repeated any
- (0, 1, 2...) times - we only support 1 and 0 */
-
- /* Repeated E-UTRAN Neighbour Cells */
- bitvec_set_bit(bv, 1);
-
- /* N. B: 25 bits are set in append_earfcn() - keep it in sync with budget adjustment below: */
- append_eutran_neib_cell(bv, bts, rem);
-
- /* stop bit - end of Repeated E-UTRAN Neighbour Cells sequence: */
- bitvec_set_bit(bv, 0);
-
- /* Note: following 2 repeated structs are not supported ATM */
- /* stop bit - end of Repeated E-UTRAN Not Allowed Cells sequence: */
- bitvec_set_bit(bv, 0);
- /* stop bit - end of Repeated E-UTRAN PCID to TA mapping sequence: */
- bitvec_set_bit(bv, 0);
-
- /* Priority and E-UTRAN Parameters Description ends here */
- /* No 3G CSG Description */
- bitvec_set_bit(bv, 0);
- /* No E-UTRAN CSG Description */
- bitvec_set_bit(bv, 0);
- /* No Additions in Rel-9: */
- bitvec_set_bit(bv, L);
-}
-
-static inline int f0_helper(int *sc, size_t length, uint8_t *chan_list)
-{
- int w[RANGE_ENC_MAX_ARFCNS] = { 0 };
-
- return range_encode(ARFCN_RANGE_1024, sc, length, w, 0, chan_list);
-}
-
-/* Estimate how many bits it'll take to append single FDD UARFCN */
-static inline int append_utran_fdd_length(uint16_t u, int *sc, size_t sc_len, size_t length)
-{
- uint8_t chan_list[16] = { 0 };
- int tmp[sc_len], f0;
-
- memcpy(tmp, sc, sizeof(tmp));
-
- f0 = f0_helper(tmp, length, chan_list);
- if (f0 < 0)
- return f0;
-
- return 21 + range1024_p(length);
-}
-
-/* Append single FDD UARFCN */
-static inline int append_utran_fdd(struct bitvec *bv, uint16_t u, int *sc, size_t length)
-{
- uint8_t chan_list[16] = { 0 };
- int f0 = f0_helper(sc, length, chan_list);
-
- if (f0 < 0)
- return f0;
-
- /* Repeated UTRAN FDD Neighbour Cells */
- bitvec_set_bit(bv, 1);
-
- /* FDD-ARFCN */
- bitvec_set_bit(bv, 0);
- bitvec_set_uint(bv, u, 14);
-
- /* FDD_Indic0: parameter value '0000000000' is a member of the set? */
- bitvec_set_bit(bv, f0);
- /* NR_OF_FDD_CELLS */
- bitvec_set_uint(bv, length, 5);
-
- f0 = bv->cur_bit;
- bitvec_add_range1024(bv, (struct gsm48_range_1024 *)chan_list);
- bv->cur_bit = f0 + range1024_p(length);
-
- return 21 + range1024_p(length);
-}
-
-/* Append multiple FDD UARFCNs */
-static inline int append_uarfcns(struct bitvec *bv, struct gsm_bts *bts, uint8_t budget)
-{
- const uint16_t *u = bts->si_common.data.uarfcn_list, *sc = bts->si_common.data.scramble_list;
- int i, j, k, rc, st = 0, a[bts->si_common.uarfcn_length];
- uint16_t cu = u[bts->u_offset]; /* caller ensures that length is positive */
- uint8_t rem = budget - 7, offset_diff; /* account for constant bits right away */
-
- OSMO_ASSERT(budget <= SI2Q_MAX_LEN);
-
- if (budget <= 7)
- return -ENOMEM;
-
- /* 3G Neighbour Cell Description */
- bitvec_set_bit(bv, 1);
- /* No Index_Start_3G */
- bitvec_set_bit(bv, 0);
- /* No Absolute_Index_Start_EMR */
- bitvec_set_bit(bv, 0);
-
- /* UTRAN FDD Description */
- bitvec_set_bit(bv, 1);
- /* No Bandwidth_FDD */
- bitvec_set_bit(bv, 0);
-
- for (i = bts->u_offset; i < bts->si_common.uarfcn_length; i++) {
- offset_diff = 0;
- for (j = st, k = 0; j < i; j++) {
- a[k++] = sc[j]; /* copy corresponding SCs */
- offset_diff++; /* compute proper offset step */
- }
- if (u[i] != cu) { /* we've reached new UARFCN */
- rc = append_utran_fdd_length(cu, a, bts->si_common.uarfcn_length, k);
- if (rc < 0) { /* estimate bit length requirements */
- return rc;
- }
-
- if (rem - rc <= 0)
- break; /* we have ran out of budget in current SI2q */
- else {
- rem -= append_utran_fdd(bv, cu, a, k);
- bts->u_offset += offset_diff;
- }
- cu = u[i];
- st = i; /* update start position */
- }
- }
-
- if (rem > 22) { /* add last UARFCN not covered by previous cycle if it could possibly fit into budget */
- offset_diff = 0;
- for (i = st, k = 0; i < bts->si_common.uarfcn_length; i++) {
- a[k++] = sc[i];
- offset_diff++;
- }
- rc = append_utran_fdd_length(cu, a, bts->si_common.uarfcn_length, k);
- if (rc < 0) {
- return rc;
- }
-
- if (rem - rc >= 0) {
- rem -= append_utran_fdd(bv, cu, a, k);
- bts->u_offset += offset_diff;
- }
- }
-
- /* stop bit - end of Repeated UTRAN FDD Neighbour Cells */
- bitvec_set_bit(bv, 0);
-
- /* UTRAN TDD Description */
- bitvec_set_bit(bv, 0);
-
- return 0;
-}
-
-/* generate SI2quater rest octets: 3GPP TS 44.018 § 10.5.2.33b */
-int rest_octets_si2quater(uint8_t *data, struct gsm_bts *bts)
-{
- int rc;
- struct bitvec bv;
-
- if (bts->si2q_count < bts->si2q_index)
- return -EINVAL;
-
- bv.data = data;
- bv.data_len = 20;
- bitvec_zero(&bv);
-
- /* BA_IND */
- bitvec_set_bit(&bv, 1);
- /* 3G_BA_IND */
- bitvec_set_bit(&bv, 1);
- /* MP_CHANGE_MARK */
- bitvec_set_bit(&bv, 0);
-
- /* SI2quater_INDEX */
- bitvec_set_uint(&bv, bts->si2q_index, 4);
- /* SI2quater_COUNT */
- bitvec_set_uint(&bv, bts->si2q_count, 4);
-
- /* No Measurement_Parameters Description */
- bitvec_set_bit(&bv, 0);
- /* No GPRS_Real Time Difference Description */
- bitvec_set_bit(&bv, 0);
- /* No GPRS_BSIC Description */
- bitvec_set_bit(&bv, 0);
- /* No GPRS_REPORT PRIORITY Description */
- bitvec_set_bit(&bv, 0);
- /* No GPRS_MEASUREMENT_Parameters Description */
- bitvec_set_bit(&bv, 0);
- /* No NC Measurement Parameters */
- bitvec_set_bit(&bv, 0);
- /* No extension (length) */
- bitvec_set_bit(&bv, 0);
-
- rc = SI2Q_MAX_LEN - (bv.cur_bit + 3);
- if (rc > 0 && bts->si_common.uarfcn_length - bts->u_offset > 0) {
- rc = append_uarfcns(&bv, bts, rc);
- if (rc < 0) {
- LOGP(DRR, LOGL_ERROR, "SI2quater [%u/%u]: failed to append %zu UARFCNs due to range encoding "
- "failure: %s\n",
- bts->si2q_index, bts->si2q_count, bts->si_common.uarfcn_length, strerror(-rc));
- return rc;
- }
- } else /* No 3G Neighbour Cell Description */
- bitvec_set_bit(&bv, 0);
-
- /* No 3G Measurement Parameters Description */
- bitvec_set_bit(&bv, 0);
- /* No GPRS_3G_MEASUREMENT Parameters Descr. */
- bitvec_set_bit(&bv, 0);
-
- rc = SI2Q_MAX_LEN - bv.cur_bit;
- if (rc > 0 && si2q_earfcn_count(&bts->si_common.si2quater_neigh_list) - bts->e_offset > 0)
- append_earfcn(&bv, bts, rc);
- else /* No Additions in Rel-5: */
- bitvec_set_bit(&bv, L);
-
- bitvec_spare_padding(&bv, (bv.data_len * 8) - 1);
- return bv.data_len;
-}
-
-/* Append selection parameters to bitvec */
-static void append_selection_params(struct bitvec *bv,
- const struct gsm48_si_selection_params *sp)
-{
- if (sp->present) {
- bitvec_set_bit(bv, H);
- bitvec_set_bit(bv, sp->cbq);
- bitvec_set_uint(bv, sp->cell_resel_off, 6);
- bitvec_set_uint(bv, sp->temp_offs, 3);
- bitvec_set_uint(bv, sp->penalty_time, 5);
- } else
- bitvec_set_bit(bv, L);
-}
-
-/* Append power offset to bitvec */
-static void append_power_offset(struct bitvec *bv,
- const struct gsm48_si_power_offset *po)
-{
- if (po->present) {
- bitvec_set_bit(bv, H);
- bitvec_set_uint(bv, po->power_offset, 2);
- } else
- bitvec_set_bit(bv, L);
-}
-
-/* Append GPRS indicator to bitvec */
-static void append_gprs_ind(struct bitvec *bv,
- const struct gsm48_si3_gprs_ind *gi)
-{
- if (gi->present) {
- bitvec_set_bit(bv, H);
- bitvec_set_uint(bv, gi->ra_colour, 3);
- /* 0 == SI13 in BCCH Norm, 1 == SI13 sent on BCCH Ext */
- bitvec_set_bit(bv, gi->si13_position);
- } else
- bitvec_set_bit(bv, L);
-}
-
-
-/* Generate SI3 Rest Octests (Chapter 10.5.2.34 / Table 10.4.72) */
-int rest_octets_si3(uint8_t *data, const struct gsm48_si_ro_info *si3)
-{
- struct bitvec bv;
-
- memset(&bv, 0, sizeof(bv));
- bv.data = data;
- bv.data_len = 4;
-
- /* Optional Selection Parameters */
- append_selection_params(&bv, &si3->selection_params);
-
- /* Optional Power Offset */
- append_power_offset(&bv, &si3->power_offset);
-
- /* Do we have a SI2ter on the BCCH? */
- if (si3->si2ter_indicator)
- bitvec_set_bit(&bv, H);
- else
- bitvec_set_bit(&bv, L);
-
- /* Early Classmark Sending Control */
- if (si3->early_cm_ctrl)
- bitvec_set_bit(&bv, H);
- else
- bitvec_set_bit(&bv, L);
-
- /* Do we have a SI Type 9 on the BCCH? */
- if (si3->scheduling.present) {
- bitvec_set_bit(&bv, H);
- bitvec_set_uint(&bv, si3->scheduling.where, 3);
- } else
- bitvec_set_bit(&bv, L);
-
- /* GPRS Indicator */
- append_gprs_ind(&bv, &si3->gprs_ind);
-
- /* 3G Early Classmark Sending Restriction controlled by
- * early_cm_ctrl above */
- bitvec_set_bit(&bv, H);
-
- if (si3->si2quater_indicator) {
- bitvec_set_bit(&bv, H); /* indicator struct present */
- bitvec_set_uint(&bv, 0, 1); /* message is sent on BCCH Norm */
- }
-
- bitvec_spare_padding(&bv, (bv.data_len*8)-1);
- return bv.data_len;
-}
-
-static int append_lsa_params(struct bitvec *bv,
- const struct gsm48_lsa_params *lsa_params)
-{
- /* FIXME */
- return -1;
-}
-
-/* Generate SI4 Rest Octets (Chapter 10.5.2.35) */
-int rest_octets_si4(uint8_t *data, const struct gsm48_si_ro_info *si4, int len)
-{
- struct bitvec bv;
-
- memset(&bv, 0, sizeof(bv));
- bv.data = data;
- bv.data_len = len;
-
- /* SI4 Rest Octets O */
- append_selection_params(&bv, &si4->selection_params);
- append_power_offset(&bv, &si4->power_offset);
- append_gprs_ind(&bv, &si4->gprs_ind);
-
- if (0 /* FIXME */) {
- /* H and SI4 Rest Octets S */
- bitvec_set_bit(&bv, H);
-
- /* LSA Parameters */
- if (si4->lsa_params.present) {
- bitvec_set_bit(&bv, H);
- append_lsa_params(&bv, &si4->lsa_params);
- } else
- bitvec_set_bit(&bv, L);
-
- /* Cell Identity */
- if (1) {
- bitvec_set_bit(&bv, H);
- bitvec_set_uint(&bv, si4->cell_id, 16);
- } else
- bitvec_set_bit(&bv, L);
-
- /* LSA ID Information */
- if (0) {
- bitvec_set_bit(&bv, H);
- /* FIXME */
- } else
- bitvec_set_bit(&bv, L);
- } else {
- /* L and break indicator */
- bitvec_set_bit(&bv, L);
- bitvec_set_bit(&bv, si4->break_ind ? H : L);
- }
-
- return bv.data_len;
-}
-
-
-/* GSM 04.18 ETSI TS 101 503 V8.27.0 (2006-05)
-
-<SI6 rest octets> ::=
-{L | H <PCH and NCH info>}
-{L | H <VBS/VGCS options : bit(2)>}
-{ < DTM_support : bit == L > I < DTM_support : bit == H >
-< RAC : bit (8) >
-< MAX_LAPDm : bit (3) > }
-< Band indicator >
-{ L | H < GPRS_MS_TXPWR_MAX_CCH : bit (5) > }
-<implicit spare >;
-*/
-int rest_octets_si6(uint8_t *data, bool is1800_net)
-{
- struct bitvec bv;
-
- memset(&bv, 0, sizeof(bv));
- bv.data = data;
- bv.data_len = 1;
-
- /* no PCH/NCH info */
- bitvec_set_bit(&bv, L);
- /* no VBS/VGCS options */
- bitvec_set_bit(&bv, L);
- /* no DTM_support */
- bitvec_set_bit(&bv, L);
- /* band indicator */
- if (is1800_net)
- bitvec_set_bit(&bv, L);
- else
- bitvec_set_bit(&bv, H);
- /* no GPRS_MS_TXPWR_MAX_CCH */
- bitvec_set_bit(&bv, L);
-
- bitvec_spare_padding(&bv, (bv.data_len * 8) - 1);
- return bv.data_len;
-}
-
-/* GPRS Mobile Allocation as per TS 04.60 Chapter 12.10a:
- < GPRS Mobile Allocation IE > ::=
- < HSN : bit (6) >
- { 0 | 1 < RFL number list : < RFL number list struct > > }
- { 0 < MA_LENGTH : bit (6) >
- < MA_BITMAP: bit (val(MA_LENGTH) + 1) >
- | 1 { 0 | 1 <ARFCN index list : < ARFCN index list struct > > } } ;
-
- < RFL number list struct > :: =
- < RFL_NUMBER : bit (4) >
- { 0 | 1 < RFL number list struct > } ;
- < ARFCN index list struct > ::=
- < ARFCN_INDEX : bit(6) >
- { 0 | 1 < ARFCN index list struct > } ;
- */
-static int append_gprs_mobile_alloc(struct bitvec *bv)
-{
- /* Hopping Sequence Number */
- bitvec_set_uint(bv, 0, 6);
-
- if (0) {
- /* We want to use a RFL number list */
- bitvec_set_bit(bv, 1);
- /* FIXME: RFL number list */
- } else
- bitvec_set_bit(bv, 0);
-
- if (0) {
- /* We want to use a MA_BITMAP */
- bitvec_set_bit(bv, 0);
- /* FIXME: MA_LENGTH, MA_BITMAP, ... */
- } else {
- bitvec_set_bit(bv, 1);
- if (0) {
- /* We want to provide an ARFCN index list */
- bitvec_set_bit(bv, 1);
- /* FIXME */
- } else
- bitvec_set_bit(bv, 0);
- }
- return 0;
-}
-
-static int encode_t3192(unsigned int t3192)
-{
- /* See also 3GPP TS 44.060
- Table 12.24.2: GPRS Cell Options information element details */
- if (t3192 == 0)
- return 3;
- else if (t3192 <= 80)
- return 4;
- else if (t3192 <= 120)
- return 5;
- else if (t3192 <= 160)
- return 6;
- else if (t3192 <= 200)
- return 7;
- else if (t3192 <= 500)
- return 0;
- else if (t3192 <= 1000)
- return 1;
- else if (t3192 <= 1500)
- return 2;
- else
- return -EINVAL;
-}
-
-static int encode_drx_timer(unsigned int drx)
-{
- if (drx == 0)
- return 0;
- else if (drx == 1)
- return 1;
- else if (drx == 2)
- return 2;
- else if (drx <= 4)
- return 3;
- else if (drx <= 8)
- return 4;
- else if (drx <= 16)
- return 5;
- else if (drx <= 32)
- return 6;
- else if (drx <= 64)
- return 7;
- else
- return -EINVAL;
-}
-
-/* GPRS Cell Options as per TS 04.60 Chapter 12.24
- < GPRS Cell Options IE > ::=
- < NMO : bit(2) >
- < T3168 : bit(3) >
- < T3192 : bit(3) >
- < DRX_TIMER_MAX: bit(3) >
- < ACCESS_BURST_TYPE: bit >
- < CONTROL_ACK_TYPE : bit >
- < BS_CV_MAX: bit(4) >
- { 0 | 1 < PAN_DEC : bit(3) >
- < PAN_INC : bit(3) >
- < PAN_MAX : bit(3) >
- { 0 | 1 < Extension Length : bit(6) >
- < bit (val(Extension Length) + 1
- & { < Extension Information > ! { bit ** = <no string> } } ;
- < Extension Information > ::=
- { 0 | 1 < EGPRS_PACKET_CHANNEL_REQUEST : bit >
- < BEP_PERIOD : bit(4) > }
- < PFC_FEATURE_MODE : bit >
- < DTM_SUPPORT : bit >
- <BSS_PAGING_COORDINATION: bit >
- <spare bit > ** ;
- */
-static int append_gprs_cell_opt(struct bitvec *bv,
- const struct gprs_cell_options *gco)
-{
- int t3192, drx_timer_max;
-
- t3192 = encode_t3192(gco->t3192);
- if (t3192 < 0)
- return t3192;
-
- drx_timer_max = encode_drx_timer(gco->drx_timer_max);
- if (drx_timer_max < 0)
- return drx_timer_max;
-
- bitvec_set_uint(bv, gco->nmo, 2);
-
- /* See also 3GPP TS 44.060
- Table 12.24.2: GPRS Cell Options information element details */
- bitvec_set_uint(bv, gco->t3168 / 500 - 1, 3);
-
- bitvec_set_uint(bv, t3192, 3);
- bitvec_set_uint(bv, drx_timer_max, 3);
- /* ACCESS_BURST_TYPE: Hard-code 8bit */
- bitvec_set_bit(bv, 0);
- /* CONTROL_ACK_TYPE: */
- bitvec_set_bit(bv, gco->ctrl_ack_type_use_block);
- bitvec_set_uint(bv, gco->bs_cv_max, 4);
-
- if (0) {
- /* hard-code no PAN_{DEC,INC,MAX} */
- bitvec_set_bit(bv, 0);
- } else {
- /* copied from ip.access BSC protocol trace */
- bitvec_set_bit(bv, 1);
- bitvec_set_uint(bv, 1, 3); /* DEC */
- bitvec_set_uint(bv, 1, 3); /* INC */
- bitvec_set_uint(bv, 15, 3); /* MAX */
- }
-
- if (!gco->ext_info_present) {
- /* no extension information */
- bitvec_set_bit(bv, 0);
- } else {
- /* extension information */
- bitvec_set_bit(bv, 1);
- if (!gco->ext_info.egprs_supported) {
- /* 6bit length of extension */
- bitvec_set_uint(bv, (1 + 3)-1, 6);
- /* EGPRS supported in the cell */
- bitvec_set_bit(bv, 0);
- } else {
- /* 6bit length of extension */
- bitvec_set_uint(bv, (1 + 5 + 3)-1, 6);
- /* EGPRS supported in the cell */
- bitvec_set_bit(bv, 1);
-
- /* 1bit EGPRS PACKET CHANNEL REQUEST */
- if (gco->supports_egprs_11bit_rach == 0) {
- bitvec_set_bit(bv,
- gco->ext_info.use_egprs_p_ch_req);
- } else {
- bitvec_set_bit(bv, 0);
- }
-
- /* 4bit BEP PERIOD */
- bitvec_set_uint(bv, gco->ext_info.bep_period, 4);
- }
- bitvec_set_bit(bv, gco->ext_info.pfc_supported);
- bitvec_set_bit(bv, gco->ext_info.dtm_supported);
- bitvec_set_bit(bv, gco->ext_info.bss_paging_coordination);
- }
-
- return 0;
-}
-
-static void append_gprs_pwr_ctrl_pars(struct bitvec *bv,
- const struct gprs_power_ctrl_pars *pcp)
-{
- bitvec_set_uint(bv, pcp->alpha, 4);
- bitvec_set_uint(bv, pcp->t_avg_w, 5);
- bitvec_set_uint(bv, pcp->t_avg_t, 5);
- bitvec_set_uint(bv, pcp->pc_meas_chan, 1);
- bitvec_set_uint(bv, pcp->n_avg_i, 4);
-}
-
-/* Generate SI13 Rest Octests (04.08 Chapter 10.5.2.37b) */
-int rest_octets_si13(uint8_t *data, const struct gsm48_si13_info *si13)
-{
- struct bitvec bv;
-
- memset(&bv, 0, sizeof(bv));
- bv.data = data;
- bv.data_len = 20;
-
- if (0) {
- /* No rest octets */
- bitvec_set_bit(&bv, L);
- } else {
- bitvec_set_bit(&bv, H);
- bitvec_set_uint(&bv, si13->bcch_change_mark, 3);
- bitvec_set_uint(&bv, si13->si_change_field, 4);
- if (1) {
- bitvec_set_bit(&bv, 0);
- } else {
- bitvec_set_bit(&bv, 1);
- bitvec_set_uint(&bv, si13->bcch_change_mark, 2);
- append_gprs_mobile_alloc(&bv);
- }
- if (!si13->pbcch_present) {
- /* PBCCH not present in cell */
- bitvec_set_bit(&bv, 0);
- bitvec_set_uint(&bv, si13->no_pbcch.rac, 8);
- bitvec_set_bit(&bv, si13->no_pbcch.spgc_ccch_sup);
- bitvec_set_uint(&bv, si13->no_pbcch.prio_acc_thr, 3);
- bitvec_set_uint(&bv, si13->no_pbcch.net_ctrl_ord, 2);
- append_gprs_cell_opt(&bv, &si13->cell_opts);
- append_gprs_pwr_ctrl_pars(&bv, &si13->pwr_ctrl_pars);
- } else {
- /* PBCCH present in cell */
- bitvec_set_bit(&bv, 1);
- bitvec_set_uint(&bv, si13->pbcch.psi1_rep_per, 4);
- /* PBCCH Descripiton */
- bitvec_set_uint(&bv, si13->pbcch.pb, 4);
- bitvec_set_uint(&bv, si13->pbcch.tsc, 3);
- bitvec_set_uint(&bv, si13->pbcch.tn, 3);
- switch (si13->pbcch.carrier_type) {
- case PBCCH_BCCH:
- bitvec_set_bit(&bv, 0);
- bitvec_set_bit(&bv, 0);
- break;
- case PBCCH_ARFCN:
- bitvec_set_bit(&bv, 0);
- bitvec_set_bit(&bv, 1);
- bitvec_set_uint(&bv, si13->pbcch.arfcn, 10);
- break;
- case PBCCH_MAIO:
- bitvec_set_bit(&bv, 1);
- bitvec_set_uint(&bv, si13->pbcch.maio, 6);
- break;
- }
- }
- /* 3GPP TS 44.018 Release 6 / 10.5.2.37b */
- bitvec_set_bit(&bv, H); /* added Release 99 */
- /* claim our SGSN is compatible with Release 99, as EDGE and EGPRS
- * was only added in this Release */
- bitvec_set_bit(&bv, 1);
- }
- bitvec_spare_padding(&bv, (bv.data_len*8)-1);
- return bv.data_len;
-}
diff --git a/openbsc/src/libbsc/system_information.c b/openbsc/src/libbsc/system_information.c
deleted file mode 100644
index dcabbbdd1..000000000
--- a/openbsc/src/libbsc/system_information.c
+++ /dev/null
@@ -1,1169 +0,0 @@
-/* GSM 04.08 System Information (SI) encoding and decoding
- * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */
-
-/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org>
- * (C) 2012 Holger Hans Peter Freyther
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <errno.h>
-#include <string.h>
-#include <stdio.h>
-#include <netinet/in.h>
-#include <stdbool.h>
-
-#include <osmocom/core/bitvec.h>
-#include <osmocom/core/utils.h>
-#include <osmocom/gsm/sysinfo.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>
-
-/*
- * DCS1800 and PCS1900 have overlapping ARFCNs. We would need to set the
- * ARFCN_PCS flag on the 1900 ARFCNs but this would increase cell_alloc
- * and other arrays to make sure (ARFCN_PCS + 1024)/8 ARFCNs fit into the
- * array. DCS1800 and PCS1900 can not be used at the same time so conserve
- * memory and do the below.
- */
-static int band_compatible(const struct gsm_bts *bts, int arfcn)
-{
- enum gsm_band band = gsm_arfcn2band(arfcn);
-
- /* normal case */
- if (band == bts->band)
- return 1;
- /* deal with ARFCN_PCS not set */
- if (band == GSM_BAND_1800 && bts->band == GSM_BAND_1900)
- return 1;
-
- return 0;
-}
-
-static int is_dcs_net(const struct gsm_bts *bts)
-{
- if (bts->band == GSM_BAND_850)
- return 0;
- if (bts->band == GSM_BAND_1900)
- return 0;
- return 1;
-}
-
-/* Return p(n) for given NR_OF_TDD_CELLS - see Table 9.1.54.1a, 3GPP TS 44.018 */
-unsigned range1024_p(unsigned n)
-{
- switch (n) {
- case 0: return 0;
- case 1: return 10;
- case 2: return 19;
- case 3: return 28;
- case 4: return 36;
- case 5: return 44;
- case 6: return 52;
- case 7: return 60;
- case 8: return 67;
- case 9: return 74;
- case 10: return 81;
- case 11: return 88;
- case 12: return 95;
- case 13: return 102;
- case 14: return 109;
- case 15: return 116;
- case 16: return 122;
- default: return 0;
- }
-}
-
-/* Return q(m) for given NR_OF_TDD_CELLS - see Table 9.1.54.1b, 3GPP TS 44.018 */
-unsigned range512_q(unsigned m)
-{
- switch (m) {
- case 0: return 0;
- case 1: return 9;
- case 2: return 17;
- case 3: return 25;
- case 4: return 32;
- case 5: return 39;
- case 6: return 46;
- case 7: return 53;
- case 8: return 59;
- case 9: return 65;
- case 10: return 71;
- case 11: return 77;
- case 12: return 83;
- case 13: return 89;
- case 14: return 95;
- case 15: return 101;
- case 16: return 106;
- case 17: return 111;
- case 18: return 116;
- case 19: return 121;
- case 20: return 126;
- default: return 0;
- }
-}
-
-size_t si2q_earfcn_count(const struct osmo_earfcn_si2q *e)
-{
- unsigned i, ret = 0;
-
- if (!e)
- return 0;
-
- for (i = 0; i < e->length; i++)
- if (e->arfcn[i] != OSMO_EARFCN_INVALID)
- ret++;
-
- return ret;
-}
-
-/* generate SI2quater messages, return rest octets length of last generated message or negative error code */
-static int make_si2quaters(struct gsm_bts *bts, bool counting)
-{
- int rc;
- bool memory_exceeded = true;
- struct gsm48_system_information_type_2quater *si2q;
-
- for (bts->si2q_index = 0; bts->si2q_index < SI2Q_MAX_NUM; bts->si2q_index++) {
- si2q = GSM_BTS_SI2Q(bts, bts->si2q_index);
- if (counting) { /* that's legitimate if we're called for counting purpose: */
- if (bts->si2q_count < bts->si2q_index)
- bts->si2q_count = bts->si2q_index;
- } else {
- memset(si2q, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN);
-
- si2q->header.l2_plen = GSM48_LEN2PLEN(22);
- si2q->header.rr_protocol_discriminator = GSM48_PDISC_RR;
- si2q->header.skip_indicator = 0;
- si2q->header.system_information = GSM48_MT_RR_SYSINFO_2quater;
- }
-
- rc = rest_octets_si2quater(si2q->rest_octets, bts);
- if (rc < 0)
- return rc;
-
- if (bts->u_offset >= bts->si_common.uarfcn_length &&
- bts->e_offset >= si2q_earfcn_count(&bts->si_common.si2quater_neigh_list)) {
- memory_exceeded = false;
- break;
- }
- }
-
- if (memory_exceeded)
- return -ENOMEM;
-
- return rc;
-}
-
-/* we generate SI2q rest octets twice to get proper estimation but it's one time cost anyway */
-uint8_t si2q_num(struct gsm_bts *bts)
-{
- int rc = make_si2quaters(bts, true);
- uint8_t num = bts->si2q_index + 1; /* number of SI2quater messages */
-
- /* N. B: si2q_num() should NEVER be called during actualSI2q rest octets generation
- we're not re-entrant because of the following code: */
- bts->u_offset = 0;
- bts->e_offset = 0;
-
- if (rc < 0)
- return 0xFF; /* return impossible index as an indicator of error in generating SI2quater */
-
- return num;
-}
-
-/* 3GPP TS 44.018, Table 9.1.54.1 - prepend diversity bit to scrambling code */
-static inline uint16_t encode_fdd(uint16_t scramble, bool diversity)
-{
- if (diversity)
- return scramble | (1 << 9);
- return scramble;
-}
-
-int bts_earfcn_add(struct gsm_bts *bts, uint16_t earfcn, uint8_t thresh_hi, uint8_t thresh_lo, uint8_t prio,
- uint8_t qrx, uint8_t meas_bw)
-{
- struct osmo_earfcn_si2q *e = &bts->si_common.si2quater_neigh_list;
- int r = osmo_earfcn_add(e, earfcn, (meas_bw < EARFCN_MEAS_BW_INVALID) ? meas_bw : OSMO_EARFCN_MEAS_INVALID);
-
- if (r < 0)
- return r;
-
- if (e->thresh_hi && thresh_hi != e->thresh_hi)
- r = 1;
-
- e->thresh_hi = thresh_hi;
-
- if (thresh_lo != EARFCN_THRESH_LOW_INVALID) {
- if (e->thresh_lo_valid && e->thresh_lo != thresh_lo)
- r = EARFCN_THRESH_LOW_INVALID;
- e->thresh_lo = thresh_lo;
- e->thresh_lo_valid = true;
- }
-
- if (qrx != EARFCN_QRXLV_INVALID) {
- if (e->qrxlm_valid && e->qrxlm != qrx)
- r = EARFCN_QRXLV_INVALID + 1;
- e->qrxlm = qrx;
- e->qrxlm_valid = true;
- }
-
- if (prio != EARFCN_PRIO_INVALID) {
- if (e->prio_valid && e->prio != prio)
- r = EARFCN_PRIO_INVALID;
- e->prio = prio;
- e->prio_valid = true;
- }
-
- return r;
-}
-
-int bts_uarfcn_del(struct gsm_bts *bts, uint16_t arfcn, uint16_t scramble)
-{
- uint16_t sc0 = encode_fdd(scramble, false), sc1 = encode_fdd(scramble, true),
- *ual = bts->si_common.data.uarfcn_list,
- *scl = bts->si_common.data.scramble_list;
- size_t len = bts->si_common.uarfcn_length, i;
- for (i = 0; i < len; i++) {
- if (arfcn == ual[i] && (sc0 == scl[i] || sc1 == scl[i])) {
- /* we rely on the assumption that (uarfcn, scramble)
- tuple is unique in the lists */
- if (i != len - 1) { /* move the tail if necessary */
- memmove(ual + i, ual + i + 1, 2 * (len - i + 1));
- memmove(scl + i, scl + i + 1, 2 * (len - i + 1));
- }
- break;
- }
- }
-
- if (i == len)
- return -EINVAL;
-
- bts->si_common.uarfcn_length--;
- return 0;
-}
-
-int bts_uarfcn_add(struct gsm_bts *bts, uint16_t arfcn, uint16_t scramble, bool diversity)
-{
- size_t len = bts->si_common.uarfcn_length, i, k = 0;
- uint16_t scr, chk,
- *ual = bts->si_common.data.uarfcn_list,
- *scl = bts->si_common.data.scramble_list,
- scramble1 = encode_fdd(scramble, true),
- scramble0 = encode_fdd(scramble, false);
-
- scr = diversity ? scramble1 : scramble0;
- chk = diversity ? scramble0 : scramble1;
-
- if (len == MAX_EARFCN_LIST)
- return -ENOMEM;
-
- for (i = 0; i < len; i++) /* find the position of arfcn if any */
- if (arfcn == ual[i])
- break;
-
- for (k = 0; i < len; i++) {
- if (arfcn == ual[i] && (scr == scl[i] || chk == scl[i]))
- return -EADDRINUSE;
- if (scr > scl[i])
- k = i + 1;
- }
- /* we keep lists sorted by scramble code:
- insert into appropriate position and move the tail */
- if (len - k) {
- memmove(ual + k + 1, ual + k, (len - k) * 2);
- memmove(scl + k + 1, scl + k, (len - k) * 2);
- }
-
- ual[k] = arfcn;
- scl[k] = scr;
- bts->si_common.uarfcn_length++;
-
- if (si2q_num(bts) <= SI2Q_MAX_NUM) {
- bts->si2q_count = si2q_num(bts) - 1;
- return 0;
- }
-
- bts_uarfcn_del(bts, arfcn, scramble);
- return -ENOSPC;
-}
-
-static inline int use_arfcn(const struct gsm_bts *bts, const bool bis, const bool ter,
- const bool pgsm, const int arfcn)
-{
- if (bts->force_combined_si)
- return !bis && !ter;
- if (!bis && !ter && band_compatible(bts, arfcn))
- return 1;
- /* Correct but somehow broken with either the nanoBTS or the iPhone5 */
- if (bis && pgsm && band_compatible(bts, arfcn) && (arfcn < 1 || arfcn > 124))
- return 1;
- if (ter && !band_compatible(bts, arfcn))
- return 1;
- return 0;
-}
-
-/* Frequency Lists as per TS 04.08 10.5.2.13 */
-
-/* 10.5.2.13.2: Bit map 0 format */
-static int freq_list_bm0_set_arfcn(uint8_t *chan_list, unsigned int arfcn)
-{
- unsigned int byte, bit;
-
- if (arfcn > 124 || arfcn < 1) {
- LOGP(DRR, LOGL_ERROR, "Bitmap 0 only supports ARFCN 1...124\n");
- return -EINVAL;
- }
-
- /* the bitmask is from 1..124, not from 0..123 */
- arfcn--;
-
- byte = arfcn / 8;
- bit = arfcn % 8;
-
- chan_list[GSM48_CELL_CHAN_DESC_SIZE-1-byte] |= (1 << bit);
-
- return 0;
-}
-
-/* 10.5.2.13.7: Variable bit map format */
-static int freq_list_bmrel_set_arfcn(uint8_t *chan_list, unsigned int arfcn)
-{
- unsigned int byte, bit;
- unsigned int min_arfcn;
- unsigned int bitno;
-
- min_arfcn = (chan_list[0] & 1) << 9;
- min_arfcn |= chan_list[1] << 1;
- min_arfcn |= (chan_list[2] >> 7) & 1;
-
- /* The lower end of our bitmaks is always implicitly included */
- if (arfcn == min_arfcn)
- return 0;
-
- if (((arfcn - min_arfcn) & 1023) > 111) {
- LOGP(DRR, LOGL_ERROR, "arfcn(%u) > min(%u) + 111\n", arfcn, min_arfcn);
- return -EINVAL;
- }
-
- bitno = (arfcn - min_arfcn) & 1023;
- byte = bitno / 8;
- bit = bitno % 8;
-
- chan_list[2 + byte] |= 1 << (7 - bit);
-
- return 0;
-}
-
-/* generate a variable bitmap */
-static inline int enc_freq_lst_var_bitmap(uint8_t *chan_list,
- struct bitvec *bv, const struct gsm_bts *bts,
- bool bis, bool ter, int min, bool pgsm)
-{
- int i;
-
- /* set it to 'Variable bitmap format' */
- chan_list[0] = 0x8e;
-
- chan_list[0] |= (min >> 9) & 1;
- chan_list[1] = (min >> 1);
- chan_list[2] = (min & 1) << 7;
-
- for (i = 0; i < bv->data_len*8; i++) {
- /* see notes in bitvec2freq_list */
- if (bitvec_get_bit_pos(bv, i)
- && ((!bis && !ter && band_compatible(bts,i))
- || (bis && pgsm && band_compatible(bts,i) && (i < 1 || i > 124))
- || (ter && !band_compatible(bts, i)))) {
- int rc = freq_list_bmrel_set_arfcn(chan_list, i);
- if (rc < 0)
- return rc;
- }
- }
-
- return 0;
-}
-
-int range_encode(enum gsm48_range r, int *arfcns, int arfcns_used, int *w,
- int f0, uint8_t *chan_list)
-{
- /*
- * Manipulate the ARFCN list according to the rules in J4 depending
- * on the selected range.
- */
- int rc, f0_included;
-
- range_enc_filter_arfcns(arfcns, arfcns_used, f0, &f0_included);
-
- rc = range_enc_arfcns(r, arfcns, arfcns_used, w, 0);
- if (rc < 0)
- return rc;
-
- /* Select the range and the amount of bits needed */
- switch (r) {
- case ARFCN_RANGE_128:
- return range_enc_range128(chan_list, f0, w);
- case ARFCN_RANGE_256:
- return range_enc_range256(chan_list, f0, w);
- case ARFCN_RANGE_512:
- return range_enc_range512(chan_list, f0, w);
- case ARFCN_RANGE_1024:
- return range_enc_range1024(chan_list, f0, f0_included, w);
- default:
- return -ERANGE;
- };
-
- return f0_included;
-}
-
-/* generate a frequency list with the range 512 format */
-static inline int enc_freq_lst_range(uint8_t *chan_list,
- struct bitvec *bv, const struct gsm_bts *bts,
- bool bis, bool ter, bool pgsm)
-{
- int arfcns[RANGE_ENC_MAX_ARFCNS];
- int w[RANGE_ENC_MAX_ARFCNS];
- int arfcns_used = 0;
- int i, range, f0;
-
- /*
- * Select ARFCNs according to the rules in bitvec2freq_list
- */
- for (i = 0; i < bv->data_len * 8; ++i) {
- /* More ARFCNs than the maximum */
- if (arfcns_used > ARRAY_SIZE(arfcns))
- return -1;
- /* Check if we can select it? */
- if (bitvec_get_bit_pos(bv, i) && use_arfcn(bts, bis, ter, pgsm, i))
- arfcns[arfcns_used++] = i;
- }
-
- /*
- * Check if the given list of ARFCNs can be encoded.
- */
- range = range_enc_determine_range(arfcns, arfcns_used, &f0);
- if (range == ARFCN_RANGE_INVALID)
- return -2;
-
- memset(w, 0, sizeof(w));
- return range_encode(range, arfcns, arfcns_used, w, f0, chan_list);
-}
-
-/* generate a cell channel list as per Section 10.5.2.1b of 04.08 */
-static int bitvec2freq_list(uint8_t *chan_list, struct bitvec *bv,
- const struct gsm_bts *bts, bool bis, bool ter)
-{
- int i, rc, min = -1, max = -1, arfcns = 0;
- bool pgsm = false;
- memset(chan_list, 0, 16);
-
- if (bts->band == GSM_BAND_900
- && bts->c0->arfcn >= 1 && bts->c0->arfcn <= 124)
- pgsm = true;
- /* P-GSM-only handsets only support 'bit map 0 format' */
- if (!bis && !ter && pgsm) {
- chan_list[0] = 0;
-
- for (i = 0; i < bv->data_len*8; i++) {
- if (i >= 1 && i <= 124
- && bitvec_get_bit_pos(bv, i)) {
- rc = freq_list_bm0_set_arfcn(chan_list, i);
- if (rc < 0)
- return rc;
- }
- }
- return 0;
- }
-
- for (i = 0; i < bv->data_len*8; i++) {
- /* in case of SI2 or SI5 allow all neighbours in same band
- * in case of SI*bis, allow neighbours in same band ouside pgsm
- * in case of SI*ter, allow neighbours in different bands
- */
- if (!bitvec_get_bit_pos(bv, i))
- continue;
- if (!use_arfcn(bts, bis, ter, pgsm, i))
- continue;
- /* count the arfcns we want to carry */
- arfcns += 1;
-
- /* 955..1023 < 0..885 */
- if (min < 0)
- min = i;
- if (i >= 955 && min < 955)
- min = i;
- if (i >= 955 && min >= 955 && i < min)
- min = i;
- if (i < 955 && min < 955 && i < min)
- min = i;
- if (max < 0)
- max = i;
- if (i < 955 && max >= 955)
- max = i;
- if (i >= 955 && max >= 955 && i > max)
- max = i;
- if (i < 955 && max < 955 && i > max)
- max = i;
- }
-
- if (max == -1) {
- /* Empty set, use 'bit map 0 format' */
- chan_list[0] = 0;
- return 0;
- }
-
- /* Now find the best encoding */
- if (((max - min) & 1023) <= 111)
- return enc_freq_lst_var_bitmap(chan_list, bv, bts, bis,
- ter, min, pgsm);
-
- /* Attempt to do the range encoding */
- rc = enc_freq_lst_range(chan_list, bv, bts, bis, ter, pgsm);
- if (rc >= 0)
- return 0;
-
- LOGP(DRR, LOGL_ERROR, "min_arfcn=%u, max_arfcn=%u, arfcns=%d "
- "can not generate ARFCN list", min, max, arfcns);
- return -EINVAL;
-}
-
-/* generate a cell channel list as per Section 10.5.2.1b of 04.08 */
-/* static*/ int generate_cell_chan_list(uint8_t *chan_list, struct gsm_bts *bts)
-{
- struct gsm_bts_trx *trx;
- struct bitvec *bv = &bts->si_common.cell_alloc;
-
- /* Zero-initialize the bit-vector */
- memset(bv->data, 0, bv->data_len);
-
- /* first we generate a bitvec of all TRX ARFCN's in our BTS */
- llist_for_each_entry(trx, &bts->trx_list, list) {
- unsigned int i, j;
- /* Always add the TRX's ARFCN */
- bitvec_set_bit_pos(bv, trx->arfcn, 1);
- for (i = 0; i < ARRAY_SIZE(trx->ts); i++) {
- struct gsm_bts_trx_ts *ts = &trx->ts[i];
- /* Add any ARFCNs present in hopping channels */
- for (j = 0; j < 1024; j++) {
- if (bitvec_get_bit_pos(&ts->hopping.arfcns, j))
- bitvec_set_bit_pos(bv, j, 1);
- }
- }
- }
-
- /* then we generate a GSM 04.08 frequency list from the bitvec */
- return bitvec2freq_list(chan_list, bv, bts, false, false);
-}
-
-/* generate a cell channel list as per Section 10.5.2.1b of 04.08 */
-static int generate_bcch_chan_list(uint8_t *chan_list, struct gsm_bts *bts,
- bool si5, bool bis, bool ter)
-{
- struct gsm_bts *cur_bts;
- struct bitvec *bv;
-
- if (si5 && bts->neigh_list_manual_mode == NL_MODE_MANUAL_SI5SEP)
- bv = &bts->si_common.si5_neigh_list;
- else
- bv = &bts->si_common.neigh_list;
-
- /* Generate list of neighbor cells if we are in automatic mode */
- if (bts->neigh_list_manual_mode == NL_MODE_AUTOMATIC) {
- /* Zero-initialize the bit-vector */
- memset(bv->data, 0, bv->data_len);
-
- /* first we generate a bitvec of the BCCH ARFCN's in our BSC */
- llist_for_each_entry(cur_bts, &bts->network->bts_list, list) {
- if (cur_bts == bts)
- continue;
- bitvec_set_bit_pos(bv, cur_bts->c0->arfcn, 1);
- }
- }
-
- /* then we generate a GSM 04.08 frequency list from the bitvec */
- return bitvec2freq_list(chan_list, bv, bts, bis, ter);
-}
-
-static int list_arfcn(uint8_t *chan_list, uint8_t mask, char *text)
-{
- int n = 0, i;
- struct gsm_sysinfo_freq freq[1024];
-
- memset(freq, 0, sizeof(freq));
- gsm48_decode_freq_list(freq, chan_list, 16, 0xce, 1);
- for (i = 0; i < 1024; i++) {
- if (freq[i].mask) {
- if (!n)
- LOGP(DRR, LOGL_INFO, "%s", text);
- LOGPC(DRR, LOGL_INFO, " %d", i);
- n++;
- }
- }
- if (n)
- LOGPC(DRR, LOGL_INFO, "\n");
-
- return n;
-}
-
-static int generate_si1(enum osmo_sysinfo_type t, struct gsm_bts *bts)
-{
- int rc;
- struct gsm48_system_information_type_1 *si1 = (struct gsm48_system_information_type_1 *) GSM_BTS_SI(bts, t);
-
- memset(si1, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN);
-
- si1->header.l2_plen = GSM48_LEN2PLEN(21);
- si1->header.rr_protocol_discriminator = GSM48_PDISC_RR;
- si1->header.skip_indicator = 0;
- si1->header.system_information = GSM48_MT_RR_SYSINFO_1;
-
- rc = generate_cell_chan_list(si1->cell_channel_description, bts);
- if (rc < 0)
- return rc;
- list_arfcn(si1->cell_channel_description, 0xce, "Serving cell:");
-
- si1->rach_control = bts->si_common.rach_control;
-
- /*
- * SI1 Rest Octets (10.5.2.32), contains NCH position and band
- * indicator but that is not in the 04.08.
- */
- rc = rest_octets_si1(si1->rest_octets, NULL, is_dcs_net(bts));
-
- return sizeof(*si1) + rc;
-}
-
-static int generate_si2(enum osmo_sysinfo_type t, struct gsm_bts *bts)
-{
- int rc;
- struct gsm48_system_information_type_2 *si2 = (struct gsm48_system_information_type_2 *) GSM_BTS_SI(bts, t);
-
- memset(si2, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN);
-
- si2->header.l2_plen = GSM48_LEN2PLEN(22);
- si2->header.rr_protocol_discriminator = GSM48_PDISC_RR;
- si2->header.skip_indicator = 0;
- si2->header.system_information = GSM48_MT_RR_SYSINFO_2;
-
- rc = generate_bcch_chan_list(si2->bcch_frequency_list, bts, false, false, false);
- if (rc < 0)
- return rc;
- list_arfcn(si2->bcch_frequency_list, 0xce,
- "SI2 Neighbour cells in same band:");
-
- si2->ncc_permitted = bts->si_common.ncc_permitted;
- si2->rach_control = bts->si_common.rach_control;
-
- return sizeof(*si2);
-}
-
-static int generate_si2bis(enum osmo_sysinfo_type t, struct gsm_bts *bts)
-{
- int rc;
- struct gsm48_system_information_type_2bis *si2b =
- (struct gsm48_system_information_type_2bis *) GSM_BTS_SI(bts, t);
- int n;
-
- memset(si2b, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN);
-
- si2b->header.l2_plen = GSM48_LEN2PLEN(22);
- si2b->header.rr_protocol_discriminator = GSM48_PDISC_RR;
- si2b->header.skip_indicator = 0;
- si2b->header.system_information = GSM48_MT_RR_SYSINFO_2bis;
-
- rc = generate_bcch_chan_list(si2b->bcch_frequency_list, bts, false, true, false);
- if (rc < 0)
- return rc;
- n = list_arfcn(si2b->bcch_frequency_list, 0xce,
- "Neighbour cells in same band, but outside P-GSM:");
- if (n) {
- /* indicate in SI2 and SI2bis: there is an extension */
- struct gsm48_system_information_type_2 *si2 =
- (struct gsm48_system_information_type_2 *) GSM_BTS_SI(bts, SYSINFO_TYPE_2);
- si2->bcch_frequency_list[0] |= 0x20;
- si2b->bcch_frequency_list[0] |= 0x20;
- } else
- bts->si_valid &= ~(1 << SYSINFO_TYPE_2bis);
-
- si2b->rach_control = bts->si_common.rach_control;
-
- return sizeof(*si2b);
-}
-
-static int generate_si2ter(enum osmo_sysinfo_type t, struct gsm_bts *bts)
-{
- int rc;
- struct gsm48_system_information_type_2ter *si2t =
- (struct gsm48_system_information_type_2ter *) GSM_BTS_SI(bts, t);
- int n;
-
- memset(si2t, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN);
-
- si2t->header.l2_plen = GSM48_LEN2PLEN(22);
- si2t->header.rr_protocol_discriminator = GSM48_PDISC_RR;
- si2t->header.skip_indicator = 0;
- si2t->header.system_information = GSM48_MT_RR_SYSINFO_2ter;
-
- rc = generate_bcch_chan_list(si2t->ext_bcch_frequency_list, bts, false, false, true);
- if (rc < 0)
- return rc;
- n = list_arfcn(si2t->ext_bcch_frequency_list, 0x8e,
- "Neighbour cells in different band:");
- if (!n)
- bts->si_valid &= ~(1 << SYSINFO_TYPE_2ter);
-
- return sizeof(*si2t);
-}
-
-/* SI2quater messages are optional - we only generate them when neighbor UARFCNs or EARFCNs are configured */
-static inline bool si2quater_not_needed(struct gsm_bts *bts)
-{
- unsigned i = MAX_EARFCN_LIST;
-
- if (bts->si_common.si2quater_neigh_list.arfcn)
- for (i = 0; i < MAX_EARFCN_LIST; i++)
- if (bts->si_common.si2quater_neigh_list.arfcn[i] != OSMO_EARFCN_INVALID)
- break;
-
- if (!bts->si_common.uarfcn_length && i == MAX_EARFCN_LIST) {
- bts->si_valid &= ~(1 << SYSINFO_TYPE_2quater); /* mark SI2q as invalid if no (E|U)ARFCNs are present */
- return true;
- }
-
- return false;
-}
-
-static int generate_si2quater(enum osmo_sysinfo_type t, struct gsm_bts *bts)
-{
- int rc;
- struct gsm48_system_information_type_2quater *si2q;
-
- if (si2quater_not_needed(bts)) /* generate rest_octets for SI2q only when necessary */
- return GSM_MACBLOCK_LEN;
-
- bts->u_offset = 0;
- bts->e_offset = 0;
- bts->si2q_index = 0;
- bts->si2q_count = si2q_num(bts) - 1;
-
- rc = make_si2quaters(bts, false);
- if (rc < 0)
- return rc;
-
- OSMO_ASSERT(bts->si2q_count == bts->si2q_index);
- OSMO_ASSERT(bts->si2q_count <= SI2Q_MAX_NUM);
-
- return sizeof(*si2q) + rc;
-}
-
-static struct gsm48_si_ro_info si_info = {
- .selection_params = {
- .present = 0,
- },
- .power_offset = {
- .present = 0,
- },
- .si2ter_indicator = 0,
- .early_cm_ctrl = 1,
- .scheduling = {
- .present = 0,
- },
- .gprs_ind = {
- .si13_position = 0,
- .ra_colour = 0,
- .present = 1,
- },
- .si2quater_indicator = 0,
- .lsa_params = {
- .present = 0,
- },
- .cell_id = 0, /* FIXME: doesn't the bts have this? */
- .break_ind = 0,
-};
-
-static int generate_si3(enum osmo_sysinfo_type t, struct gsm_bts *bts)
-{
- int rc;
- struct gsm48_system_information_type_3 *si3 = (struct gsm48_system_information_type_3 *) GSM_BTS_SI(bts, t);
-
- memset(si3, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN);
-
- si3->header.l2_plen = GSM48_LEN2PLEN(18);
- si3->header.rr_protocol_discriminator = GSM48_PDISC_RR;
- si3->header.skip_indicator = 0;
- si3->header.system_information = GSM48_MT_RR_SYSINFO_3;
-
- si3->cell_identity = htons(bts->cell_identity);
- gsm48_generate_lai(&si3->lai, bts->network->country_code,
- bts->network->network_code,
- bts->location_area_code);
- si3->control_channel_desc = bts->si_common.chan_desc;
- si3->cell_options = bts->si_common.cell_options;
- si3->cell_sel_par = bts->si_common.cell_sel_par;
- si3->rach_control = bts->si_common.rach_control;
-
- /* allow/disallow DTXu */
- gsm48_set_dtx(&si3->cell_options, bts->dtxu, bts->dtxu, true);
-
- if (GSM_BTS_HAS_SI(bts, SYSINFO_TYPE_2ter)) {
- LOGP(DRR, LOGL_INFO, "SI 2ter is included.\n");
- si_info.si2ter_indicator = 1;
- } else {
- si_info.si2ter_indicator = 0;
- }
- if (GSM_BTS_HAS_SI(bts, SYSINFO_TYPE_2quater)) {
- LOGP(DRR, LOGL_INFO, "SI 2quater is included, based on %zu EARFCNs and %zu UARFCNs.\n",
- si2q_earfcn_count(&bts->si_common.si2quater_neigh_list), bts->si_common.uarfcn_length);
- si_info.si2quater_indicator = 1;
- } else {
- si_info.si2quater_indicator = 0;
- }
- si_info.early_cm_ctrl = bts->early_classmark_allowed;
-
- /* SI3 Rest Octets (10.5.2.34), containing
- CBQ, CELL_RESELECT_OFFSET, TEMPORARY_OFFSET, PENALTY_TIME
- Power Offset, 2ter Indicator, Early Classmark Sending,
- Scheduling if and WHERE, GPRS Indicator, SI13 position */
- rc = rest_octets_si3(si3->rest_octets, &si_info);
-
- return sizeof(*si3) + rc;
-}
-
-static int generate_si4(enum osmo_sysinfo_type t, struct gsm_bts *bts)
-{
- int rc;
- struct gsm48_system_information_type_4 *si4 = (struct gsm48_system_information_type_4 *) GSM_BTS_SI(bts, t);
- struct gsm_lchan *cbch_lchan;
- uint8_t *restoct = si4->data;
-
- /* length of all IEs present except SI4 rest octets and l2_plen */
- int l2_plen = sizeof(*si4) - 1;
-
- memset(si4, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN);
-
- si4->header.rr_protocol_discriminator = GSM48_PDISC_RR;
- si4->header.skip_indicator = 0;
- si4->header.system_information = GSM48_MT_RR_SYSINFO_4;
-
- gsm48_generate_lai(&si4->lai, bts->network->country_code,
- bts->network->network_code,
- bts->location_area_code);
- si4->cell_sel_par = bts->si_common.cell_sel_par;
- si4->rach_control = bts->si_common.rach_control;
-
- /* Optional: CBCH Channel Description + CBCH Mobile Allocation */
- cbch_lchan = gsm_bts_get_cbch(bts);
- if (cbch_lchan) {
- struct gsm48_chan_desc cd;
- gsm48_lchan2chan_desc(&cd, cbch_lchan);
- tv_fixed_put(si4->data, GSM48_IE_CBCH_CHAN_DESC, 3,
- (uint8_t *) &cd);
- l2_plen += 3 + 1;
- restoct += 3 + 1;
- /* we don't use hopping and thus don't need a CBCH MA */
- }
-
- si4->header.l2_plen = GSM48_LEN2PLEN(l2_plen);
-
- /* SI4 Rest Octets (10.5.2.35), containing
- Optional Power offset, GPRS Indicator,
- Cell Identity, LSA ID, Selection Parameter */
- rc = rest_octets_si4(restoct, &si_info, (uint8_t *)GSM_BTS_SI(bts, t) + GSM_MACBLOCK_LEN - restoct);
-
- return l2_plen + 1 + rc;
-}
-
-static int generate_si5(enum osmo_sysinfo_type t, struct gsm_bts *bts)
-{
- struct gsm48_system_information_type_5 *si5;
- uint8_t *output = GSM_BTS_SI(bts, t);
- int rc, l2_plen = 18;
-
- memset(output, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN);
-
- /* ip.access nanoBTS needs l2_plen!! */
- switch (bts->type) {
- case GSM_BTS_TYPE_NANOBTS:
- case GSM_BTS_TYPE_OSMOBTS:
- *output++ = GSM48_LEN2PLEN(l2_plen);
- l2_plen++;
- break;
- default:
- break;
- }
-
- si5 = (struct gsm48_system_information_type_5 *) GSM_BTS_SI(bts, t);
-
- /* l2 pseudo length, not part of msg: 18 */
- si5->rr_protocol_discriminator = GSM48_PDISC_RR;
- si5->skip_indicator = 0;
- si5->system_information = GSM48_MT_RR_SYSINFO_5;
- rc = generate_bcch_chan_list(si5->bcch_frequency_list, bts, true, false, false);
- if (rc < 0)
- return rc;
- list_arfcn(si5->bcch_frequency_list, 0xce,
- "SI5 Neighbour cells in same band:");
-
- /* 04.08 9.1.37: L2 Pseudo Length of 18 */
- return l2_plen;
-}
-
-static int generate_si5bis(enum osmo_sysinfo_type t, struct gsm_bts *bts)
-{
- struct gsm48_system_information_type_5bis *si5b;
- uint8_t *output = GSM_BTS_SI(bts, t);
- int rc, l2_plen = 18;
- int n;
-
- memset(output, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN);
-
- /* ip.access nanoBTS needs l2_plen!! */
- switch (bts->type) {
- case GSM_BTS_TYPE_NANOBTS:
- case GSM_BTS_TYPE_OSMOBTS:
- *output++ = GSM48_LEN2PLEN(l2_plen);
- l2_plen++;
- break;
- default:
- break;
- }
-
- si5b = (struct gsm48_system_information_type_5bis *) GSM_BTS_SI(bts, t);
-
- /* l2 pseudo length, not part of msg: 18 */
- si5b->rr_protocol_discriminator = GSM48_PDISC_RR;
- si5b->skip_indicator = 0;
- si5b->system_information = GSM48_MT_RR_SYSINFO_5bis;
- rc = generate_bcch_chan_list(si5b->bcch_frequency_list, bts, true, true, false);
- if (rc < 0)
- return rc;
- n = list_arfcn(si5b->bcch_frequency_list, 0xce,
- "Neighbour cells in same band, but outside P-GSM:");
- if (n) {
- /* indicate in SI5 and SI5bis: there is an extension */
- struct gsm48_system_information_type_5 *si5 =
- (struct gsm48_system_information_type_5 *) GSM_BTS_SI(bts, SYSINFO_TYPE_5);
- si5->bcch_frequency_list[0] |= 0x20;
- si5b->bcch_frequency_list[0] |= 0x20;
- } else
- bts->si_valid &= ~(1 << SYSINFO_TYPE_5bis);
-
- /* 04.08 9.1.37: L2 Pseudo Length of 18 */
- return l2_plen;
-}
-
-static int generate_si5ter(enum osmo_sysinfo_type t, struct gsm_bts *bts)
-{
- struct gsm48_system_information_type_5ter *si5t;
- uint8_t *output = GSM_BTS_SI(bts, t);
- int rc, l2_plen = 18;
- int n;
-
- memset(output, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN);
-
- /* ip.access nanoBTS needs l2_plen!! */
- switch (bts->type) {
- case GSM_BTS_TYPE_NANOBTS:
- case GSM_BTS_TYPE_OSMOBTS:
- *output++ = GSM48_LEN2PLEN(l2_plen);
- l2_plen++;
- break;
- default:
- break;
- }
-
- si5t = (struct gsm48_system_information_type_5ter *) GSM_BTS_SI(bts, t);
-
- /* l2 pseudo length, not part of msg: 18 */
- si5t->rr_protocol_discriminator = GSM48_PDISC_RR;
- si5t->skip_indicator = 0;
- si5t->system_information = GSM48_MT_RR_SYSINFO_5ter;
- rc = generate_bcch_chan_list(si5t->bcch_frequency_list, bts, true, false, true);
- if (rc < 0)
- return rc;
- n = list_arfcn(si5t->bcch_frequency_list, 0x8e,
- "Neighbour cells in different band:");
- if (!n)
- bts->si_valid &= ~(1 << SYSINFO_TYPE_5ter);
-
- /* 04.08 9.1.37: L2 Pseudo Length of 18 */
- return l2_plen;
-}
-
-static int generate_si6(enum osmo_sysinfo_type t, struct gsm_bts *bts)
-{
- struct gsm48_system_information_type_6 *si6;
- uint8_t *output = GSM_BTS_SI(bts, t);
- int l2_plen = 11;
- int rc;
-
- memset(output, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN);
-
- /* ip.access nanoBTS needs l2_plen!! */
- switch (bts->type) {
- case GSM_BTS_TYPE_NANOBTS:
- case GSM_BTS_TYPE_OSMOBTS:
- *output++ = GSM48_LEN2PLEN(l2_plen);
- l2_plen++;
- break;
- default:
- break;
- }
-
- si6 = (struct gsm48_system_information_type_6 *) GSM_BTS_SI(bts, t);
-
- /* l2 pseudo length, not part of msg: 11 */
- si6->rr_protocol_discriminator = GSM48_PDISC_RR;
- si6->skip_indicator = 0;
- si6->system_information = GSM48_MT_RR_SYSINFO_6;
- si6->cell_identity = htons(bts->cell_identity);
- gsm48_generate_lai(&si6->lai, bts->network->country_code,
- bts->network->network_code,
- bts->location_area_code);
- si6->cell_options = bts->si_common.cell_options;
- si6->ncc_permitted = bts->si_common.ncc_permitted;
- /* allow/disallow DTXu */
- gsm48_set_dtx(&si6->cell_options, bts->dtxu, bts->dtxu, false);
-
- /* SI6 Rest Octets: 10.5.2.35a: PCH / NCH info, VBS/VGCS options */
- rc = rest_octets_si6(si6->rest_octets, is_dcs_net(bts));
-
- return l2_plen + rc;
-}
-
-static struct gsm48_si13_info si13_default = {
- .cell_opts = {
- .nmo = GPRS_NMO_II,
- .t3168 = 2000,
- .t3192 = 1500,
- .drx_timer_max = 3,
- .bs_cv_max = 15,
- .ctrl_ack_type_use_block = true,
- .ext_info_present = 0,
- .supports_egprs_11bit_rach = 0,
- .ext_info = {
- /* The values below are just guesses ! */
- .egprs_supported = 0,
- .use_egprs_p_ch_req = 1,
- .bep_period = 5,
- .pfc_supported = 0,
- .dtm_supported = 0,
- .bss_paging_coordination = 0,
- },
- },
- .pwr_ctrl_pars = {
- .alpha = 0, /* a = 0.0 */
- .t_avg_w = 16,
- .t_avg_t = 16,
- .pc_meas_chan = 0, /* downling measured on CCCH */
- .n_avg_i = 8,
- },
- .bcch_change_mark = 1,
- .si_change_field = 0,
- .pbcch_present = 0,
- {
- .no_pbcch = {
- .rac = 0, /* needs to be patched */
- .spgc_ccch_sup = 0,
- .net_ctrl_ord = 0,
- .prio_acc_thr = 6,
- },
- },
-};
-
-static int generate_si13(enum osmo_sysinfo_type t, struct gsm_bts *bts)
-{
- struct gsm48_system_information_type_13 *si13 =
- (struct gsm48_system_information_type_13 *) GSM_BTS_SI(bts, t);
- int ret;
-
- memset(si13, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN);
-
- si13->header.rr_protocol_discriminator = GSM48_PDISC_RR;
- si13->header.skip_indicator = 0;
- si13->header.system_information = GSM48_MT_RR_SYSINFO_13;
-
- si13_default.no_pbcch.rac = bts->gprs.rac;
- si13_default.no_pbcch.net_ctrl_ord = bts->gprs.net_ctrl_ord;
-
- si13_default.cell_opts.ctrl_ack_type_use_block =
- bts->gprs.ctrl_ack_type_use_block;
-
- /* Information about the other SIs */
- si13_default.bcch_change_mark = bts->bcch_change_mark;
- si13_default.cell_opts.supports_egprs_11bit_rach =
- bts->gprs.supports_egprs_11bit_rach;
-
- ret = rest_octets_si13(si13->rest_octets, &si13_default);
- if (ret < 0)
- return ret;
-
- /* length is coded in bit 2 an up */
- si13->header.l2_plen = 0x01;
-
- return sizeof (*si13) + ret;
-}
-
-typedef int (*gen_si_fn_t)(enum osmo_sysinfo_type t, struct gsm_bts *bts);
-
-static const gen_si_fn_t gen_si_fn[_MAX_SYSINFO_TYPE] = {
- [SYSINFO_TYPE_1] = &generate_si1,
- [SYSINFO_TYPE_2] = &generate_si2,
- [SYSINFO_TYPE_2bis] = &generate_si2bis,
- [SYSINFO_TYPE_2ter] = &generate_si2ter,
- [SYSINFO_TYPE_2quater] = &generate_si2quater,
- [SYSINFO_TYPE_3] = &generate_si3,
- [SYSINFO_TYPE_4] = &generate_si4,
- [SYSINFO_TYPE_5] = &generate_si5,
- [SYSINFO_TYPE_5bis] = &generate_si5bis,
- [SYSINFO_TYPE_5ter] = &generate_si5ter,
- [SYSINFO_TYPE_6] = &generate_si6,
- [SYSINFO_TYPE_13] = &generate_si13,
-};
-
-int gsm_generate_si(struct gsm_bts *bts, enum osmo_sysinfo_type si_type)
-{
- gen_si_fn_t gen_si;
-
- switch (bts->gprs.mode) {
- case BTS_GPRS_EGPRS:
- si13_default.cell_opts.ext_info_present = 1;
- si13_default.cell_opts.ext_info.egprs_supported = 1;
- /* fallthrough */
- case BTS_GPRS_GPRS:
- si_info.gprs_ind.present = 1;
- break;
- case BTS_GPRS_NONE:
- si_info.gprs_ind.present = 0;
- break;
- }
-
- memcpy(&si_info.selection_params,
- &bts->si_common.cell_ro_sel_par,
- sizeof(struct gsm48_si_selection_params));
-
- gen_si = gen_si_fn[si_type];
- if (!gen_si)
- return -EINVAL;
-
- return gen_si(si_type, bts);
-}
diff --git a/openbsc/src/libcommon-cs/Makefile.am b/openbsc/src/libcommon-cs/Makefile.am
deleted file mode 100644
index f3921ba5f..000000000
--- a/openbsc/src/libcommon-cs/Makefile.am
+++ /dev/null
@@ -1,20 +0,0 @@
-AM_CPPFLAGS = \
- $(all_includes) \
- -I$(top_srcdir)/include \
- -I$(top_builddir) \
- $(NULL)
-
-AM_CFLAGS = \
- -Wall \
- $(LIBOSMOCORE_CFLAGS) \
- $(LIBOSMOGSM_CFLAGS) \
- $(LIBOSMOVTY_CFLAGS) \
- $(LIBOSMOABIS_CFLAGS) \
- $(COVERAGE_CFLAGS) \
- $(NULL)
-
-noinst_LIBRARIES = libcommon-cs.a
-
-libcommon_cs_a_SOURCES = \
- common_cs.c \
- common_cs_vty.c
diff --git a/openbsc/src/libcommon-cs/common_cs.c b/openbsc/src/libcommon-cs/common_cs.c
deleted file mode 100644
index 7905802bf..000000000
--- a/openbsc/src/libcommon-cs/common_cs.c
+++ /dev/null
@@ -1,133 +0,0 @@
-/* Code used by both libbsc and libmsc (common_cs means "BSC or MSC").
- *
- * (C) 2016 by sysmocom s.m.f.c. <info@sysmocom.de>
- * (C) 2008-2010 by Harald Welte <laforge@gnumonks.org>
- * (C) 2014 by Holger Hans Peter Freyther
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <stdbool.h>
-
-#include <osmocom/gsm/gsm0480.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>
-
-/* 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
- * particular the llist heads and stats counters.)
- * The long term aim should be to have entirely separate structs for libbsc and
- * libmsc with some common general items.
- */
-struct gsm_network *gsm_network_init(void *ctx,
- uint16_t country_code,
- uint16_t network_code,
- mncc_recv_cb_t mncc_recv)
-{
- struct gsm_network *net;
-
- const char *default_regexp = ".*";
-
- net = talloc_zero(ctx, struct gsm_network);
- if (!net)
- return NULL;
-
- net->subscr_group = talloc_zero(net, struct gsm_subscriber_group);
- if (!net->subscr_group) {
- talloc_free(net);
- return NULL;
- }
-
- if (gsm_parse_reg(net, &net->authorized_regexp, &net->authorized_reg_str, 1,
- &default_regexp) != 0)
- return NULL;
-
- net->subscr_group->net = net;
- net->auto_create_subscr = true;
- net->auto_assign_exten = true;
-
- net->country_code = country_code;
- net->network_code = network_code;
-
- INIT_LLIST_HEAD(&net->trans_list);
- INIT_LLIST_HEAD(&net->upqueue);
- INIT_LLIST_HEAD(&net->subscr_conns);
-
- net->bsc_subscribers = talloc_zero(net, struct llist_head);
- INIT_LLIST_HEAD(net->bsc_subscribers);
-
- /* init statistics */
- net->msc_ctrs = rate_ctr_group_alloc(net, &msc_ctrg_desc, 0);
- net->active_calls = osmo_counter_alloc("msc.active_calls");
-
- net->mncc_recv = mncc_recv;
- net->ext_min = GSM_MIN_EXTEN;
- net->ext_max = GSM_MAX_EXTEN;
-
- net->dyn_ts_allow_tch_f = true;
-
- return net;
-}
-
-struct msgb *gsm48_create_mm_serv_rej(enum gsm48_reject_value value)
-{
- struct msgb *msg;
- struct gsm48_hdr *gh;
-
- msg = gsm48_msgb_alloc_name("GSM 04.08 SERV REJ");
- if (!msg)
- return NULL;
-
- gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1);
- gh->proto_discr = GSM48_PDISC_MM;
- gh->msg_type = GSM48_MT_MM_CM_SERV_REJ;
- gh->data[0] = value;
-
- return msg;
-}
-
-struct msgb *gsm48_create_loc_upd_rej(uint8_t cause)
-{
- struct gsm48_hdr *gh;
- struct msgb *msg;
-
- msg = gsm48_msgb_alloc_name("GSM 04.08 LOC UPD REJ");
- if (!msg)
- return NULL;
-
- gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1);
- gh->proto_discr = GSM48_PDISC_MM;
- gh->msg_type = GSM48_MT_MM_LOC_UPD_REJECT;
- gh->data[0] = cause;
- return msg;
-}
-
-uint8_t sms_next_rp_msg_ref(uint8_t *next_rp_ref)
-{
- const uint8_t rp_msg_ref = *next_rp_ref;
- /*
- * This should wrap as the valid range is 0 to 255. We only
- * transfer one SMS at a time so we don't need to check if
- * the id has been already assigned.
- */
- *next_rp_ref += 1;
-
- return rp_msg_ref;
-}
diff --git a/openbsc/src/libcommon-cs/common_cs_vty.c b/openbsc/src/libcommon-cs/common_cs_vty.c
deleted file mode 100644
index bcc001d58..000000000
--- a/openbsc/src/libcommon-cs/common_cs_vty.c
+++ /dev/null
@@ -1,325 +0,0 @@
-/* Code used by both libbsc and libmsc (common_cs means "BSC or MSC").
- *
- * (C) 2016 by sysmocom s.m.f.c. <info@sysmocom.de>
- * (C) 2008-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/core/utils.h>
-
-#include <osmocom/vty/command.h>
-#include <osmocom/vty/logging.h>
-#include <osmocom/vty/stats.h>
-
-#include <openbsc/vty.h>
-
-#include <openbsc/gsm_data.h>
-#include <openbsc/gsm_subscriber.h>
-
-struct cmd_node net_node = {
- GSMNET_NODE,
- "%s(config-net)# ",
- 1,
-};
-
-#define NETWORK_STR "Configure the GSM network\n"
-#define CODE_CMD_STR "Code commands\n"
-#define NAME_CMD_STR "Name Commands\n"
-#define NAME_STR "Name to use\n"
-
-DEFUN(cfg_net,
- cfg_net_cmd,
- "network", NETWORK_STR)
-{
- vty->index = gsmnet_from_vty(vty);
- vty->node = GSMNET_NODE;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_ncc,
- cfg_net_ncc_cmd,
- "network country code <1-999>",
- "Set the GSM network country code\n"
- "Country commands\n"
- CODE_CMD_STR
- "Network Country Code to use\n")
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
-
- gsmnet->country_code = atoi(argv[0]);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_mnc,
- cfg_net_mnc_cmd,
- "mobile network code <0-999>",
- "Set the GSM mobile network code\n"
- "Network Commands\n"
- CODE_CMD_STR
- "Mobile Network Code to use\n")
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
-
- gsmnet->network_code = atoi(argv[0]);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_name_short,
- cfg_net_name_short_cmd,
- "short name NAME",
- "Set the short GSM network name\n" NAME_CMD_STR NAME_STR)
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
-
- osmo_talloc_replace_string(gsmnet, &gsmnet->name_short, argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_name_long,
- cfg_net_name_long_cmd,
- "long name NAME",
- "Set the long GSM network name\n" NAME_CMD_STR NAME_STR)
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
-
- osmo_talloc_replace_string(gsmnet, &gsmnet->name_long, argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_auth_policy,
- cfg_net_auth_policy_cmd,
- "auth policy (closed|accept-all|regexp|token)",
- "Authentication (not cryptographic)\n"
- "Set the GSM network authentication policy\n"
- "Require the MS to be activated in HLR\n"
- "Accept all MS, whether in HLR or not\n"
- "Use regular expression for IMSI authorization decision\n"
- "Use SMS-token based authentication\n")
-{
- enum gsm_auth_policy policy = gsm_auth_policy_parse(argv[0]);
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
-
- gsmnet->auth_policy = policy;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_authorize_regexp, cfg_net_authorize_regexp_cmd,
- "authorized-regexp REGEXP",
- "Set regexp for IMSI which will be used for authorization decision\n"
- "Regular expression, IMSIs matching it are allowed to use the network\n")
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- if (gsm_parse_reg(gsmnet, &gsmnet->authorized_regexp,
- &gsmnet->authorized_reg_str, argc, argv) != 0) {
- vty_out(vty, "%%Failed to parse the authorized-regexp: '%s'%s",
- argv[0], VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_reject_cause,
- cfg_net_reject_cause_cmd,
- "location updating reject cause <2-111>",
- "Set the reject cause of location updating reject\n"
- "Set the reject cause of location updating reject\n"
- "Set the reject cause of location updating reject\n"
- "Set the reject cause of location updating reject\n"
- "Cause Value as Per GSM TS 04.08\n")
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
-
- gsmnet->reject_cause = atoi(argv[0]);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_encryption,
- cfg_net_encryption_cmd,
- "encryption a5 (0|1|2|3)",
- "Encryption options\n"
- "A5 encryption\n" "A5/0: No encryption\n"
- "A5/1: Encryption\n" "A5/2: Export-grade Encryption\n"
- "A5/3: 'New' Secure Encryption\n")
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
-
- gsmnet->a5_encryption = atoi(argv[0]);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_rrlp_mode, cfg_net_rrlp_mode_cmd,
- "rrlp mode (none|ms-based|ms-preferred|ass-preferred)",
- "Radio Resource Location Protocol\n"
- "Set the Radio Resource Location Protocol Mode\n"
- "Don't send RRLP request\n"
- "Request MS-based location\n"
- "Request any location, prefer MS-based\n"
- "Request any location, prefer MS-assisted\n")
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
-
- gsmnet->rrlp.mode = rrlp_mode_parse(argv[0]);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_mm_info, cfg_net_mm_info_cmd,
- "mm info (0|1)",
- "Mobility Management\n"
- "Send MM INFO after LOC UPD ACCEPT\n"
- "Disable\n" "Enable\n")
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
-
- gsmnet->send_mm_info = atoi(argv[0]);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_dyn_ts_allow_tch_f,
- cfg_net_dyn_ts_allow_tch_f_cmd,
- "dyn_ts_allow_tch_f (0|1)",
- "Allow or disallow allocating TCH/F on TCH_F_TCH_H_PDCH timeslots\n"
- "Disallow TCH/F on TCH_F_TCH_H_PDCH (default)\n"
- "Allow TCH/F on TCH_F_TCH_H_PDCH\n")
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- gsmnet->dyn_ts_allow_tch_f = atoi(argv[0]) ? true : false;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_subscr_keep,
- cfg_net_subscr_keep_cmd,
- "subscriber-keep-in-ram (0|1)",
- "Keep unused subscribers in RAM.\n"
- "Delete unused subscribers\n" "Keep unused subscribers\n")
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- gsmnet->subscr_group->keep_subscr = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_timezone,
- cfg_net_timezone_cmd,
- "timezone <-19-19> (0|15|30|45)",
- "Set the Timezone Offset of the network\n"
- "Timezone offset (hours)\n"
- "Timezone offset (00 minutes)\n"
- "Timezone offset (15 minutes)\n"
- "Timezone offset (30 minutes)\n"
- "Timezone offset (45 minutes)\n"
- )
-{
- struct gsm_network *net = vty->index;
- int tzhr = atoi(argv[0]);
- int tzmn = atoi(argv[1]);
-
- net->tz.hr = tzhr;
- net->tz.mn = tzmn;
- net->tz.dst = 0;
- net->tz.override = 1;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_timezone_dst,
- cfg_net_timezone_dst_cmd,
- "timezone <-19-19> (0|15|30|45) <0-2>",
- "Set the Timezone Offset of the network\n"
- "Timezone offset (hours)\n"
- "Timezone offset (00 minutes)\n"
- "Timezone offset (15 minutes)\n"
- "Timezone offset (30 minutes)\n"
- "Timezone offset (45 minutes)\n"
- "DST offset (hours)\n"
- )
-{
- struct gsm_network *net = vty->index;
- int tzhr = atoi(argv[0]);
- int tzmn = atoi(argv[1]);
- int tzdst = atoi(argv[2]);
-
- net->tz.hr = tzhr;
- net->tz.mn = tzmn;
- net->tz.dst = tzdst;
- net->tz.override = 1;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_no_timezone,
- cfg_net_no_timezone_cmd,
- "no timezone",
- NO_STR
- "Disable network timezone override, use system tz\n")
-{
- struct gsm_network *net = vty->index;
-
- net->tz.override = 0;
-
- return CMD_SUCCESS;
-}
-
-static struct gsm_network *vty_global_gsm_network = NULL;
-
-/* initialize VTY elements used in both BSC and MSC */
-int common_cs_vty_init(struct gsm_network *network,
- int (* config_write_net )(struct vty *))
-{
- OSMO_ASSERT(vty_global_gsm_network == NULL);
- vty_global_gsm_network = network;
-
- install_element(CONFIG_NODE, &cfg_net_cmd);
- install_node(&net_node, config_write_net);
- vty_install_default(GSMNET_NODE);
- install_element(GSMNET_NODE, &cfg_net_ncc_cmd);
- install_element(GSMNET_NODE, &cfg_net_mnc_cmd);
- install_element(GSMNET_NODE, &cfg_net_name_short_cmd);
- install_element(GSMNET_NODE, &cfg_net_name_long_cmd);
- install_element(GSMNET_NODE, &cfg_net_auth_policy_cmd);
- install_element(GSMNET_NODE, &cfg_net_authorize_regexp_cmd);
- install_element(GSMNET_NODE, &cfg_net_reject_cause_cmd);
- install_element(GSMNET_NODE, &cfg_net_encryption_cmd);
- install_element(GSMNET_NODE, &cfg_net_rrlp_mode_cmd);
- install_element(GSMNET_NODE, &cfg_net_mm_info_cmd);
- install_element(GSMNET_NODE, &cfg_net_subscr_keep_cmd);
- install_element(GSMNET_NODE, &cfg_net_timezone_cmd);
- install_element(GSMNET_NODE, &cfg_net_timezone_dst_cmd);
- install_element(GSMNET_NODE, &cfg_net_no_timezone_cmd);
- install_element(GSMNET_NODE, &cfg_net_dyn_ts_allow_tch_f_cmd);
-
- return CMD_SUCCESS;
-}
-
-struct gsm_network *gsmnet_from_vty(struct vty *v)
-{
- /* It can't hurt to force callers to continue to pass the vty instance
- * to this function, in case we'd like to retrieve the global
- * gsm_network instance from the vty at some point in the future. But
- * until then, just return the global pointer, which should have been
- * initialized by common_cs_vty_init().
- */
- OSMO_ASSERT(vty_global_gsm_network);
- return vty_global_gsm_network;
-}
diff --git a/openbsc/src/libcommon/Makefile.am b/openbsc/src/libcommon/Makefile.am
deleted file mode 100644
index 0b258c08a..000000000
--- a/openbsc/src/libcommon/Makefile.am
+++ /dev/null
@@ -1,47 +0,0 @@
-AM_CPPFLAGS = \
- $(all_includes) \
- -I$(top_srcdir)/include \
- -I$(top_builddir) \
- $(NULL)
-
-AM_CFLAGS = \
- -Wall \
- $(LIBOSMOCORE_CFLAGS) \
- $(LIBOSMOGSM_CFLAGS) \
- $(LIBOSMOVTY_CFLAGS) \
- $(LIBOSMOABIS_CFLAGS) \
- $(COVERAGE_CFLAGS) \
- $(NULL)
-
-noinst_LIBRARIES = \
- libcommon.a \
- $(NULL)
-
-libcommon_a_SOURCES = \
- bsc_version.c \
- common_vty.c \
- 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/openbsc/src/libcommon/bsc_version.c b/openbsc/src/libcommon/bsc_version.c
deleted file mode 100644
index f0369bffe..000000000
--- a/openbsc/src/libcommon/bsc_version.c
+++ /dev/null
@@ -1,30 +0,0 @@
-/* Hold the copyright and version string */
-/* (C) 2010-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 "../../bscconfig.h"
-
-const char *openbsc_copyright =
- "Copyright (C) 2008-2016 Harald Welte, Holger Freyther\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";
-
-
diff --git a/openbsc/src/libcommon/common_vty.c b/openbsc/src/libcommon/common_vty.c
deleted file mode 100644
index 6e1c10b00..000000000
--- a/openbsc/src/libcommon/common_vty.c
+++ /dev/null
@@ -1,145 +0,0 @@
-/* OpenBSC VTY common helpers */
-/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
- * (C) 2009-2010 by Holger Hans Peter Freyther
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <stdlib.h>
-#include <string.h>
-
-#include <osmocom/core/talloc.h>
-
-#include <openbsc/vty.h>
-#include <openbsc/gsm_data.h>
-#include <openbsc/debug.h>
-#include <openbsc/gsm_subscriber.h>
-#include <openbsc/bsc_nat.h>
-#include <openbsc/abis_om2000.h>
-
-#include <osmocom/vty/telnet_interface.h>
-#include <osmocom/vty/command.h>
-#include <osmocom/vty/buffer.h>
-#include <osmocom/vty/vty.h>
-
-
-int bsc_vty_go_parent(struct vty *vty)
-{
- switch (vty->node) {
- case GSMNET_NODE:
- vty->node = CONFIG_NODE;
- vty->index = NULL;
- break;
- case BTS_NODE:
- vty->node = GSMNET_NODE;
- {
- /* set vty->index correctly ! */
- struct gsm_bts *bts = vty->index;
- vty->index = bts->network;
- vty->index_sub = NULL;
- }
- break;
- case TRX_NODE:
- vty->node = BTS_NODE;
- {
- /* set vty->index correctly ! */
- struct gsm_bts_trx *trx = vty->index;
- vty->index = trx->bts;
- vty->index_sub = &trx->bts->description;
- }
- break;
- case TS_NODE:
- vty->node = TRX_NODE;
- {
- /* set vty->index correctly ! */
- struct gsm_bts_trx_ts *ts = vty->index;
- vty->index = ts->trx;
- vty->index_sub = &ts->trx->description;
- }
- break;
- case OML_NODE:
- case OM2K_NODE:
- vty->node = ENABLE_NODE;
- /* NOTE: this only works because it's not part of the config
- * tree, where outer commands are searched via vty_go_parent()
- * and only (!) executed when a matching one is found.
- */
- talloc_free(vty->index);
- vty->index = NULL;
- break;
- case OM2K_CON_GROUP_NODE:
- vty->node = BTS_NODE;
- {
- struct con_group *cg = vty->index;
- struct gsm_bts *bts = cg->bts;
- vty->index = bts;
- vty->index_sub = &bts->description;
- }
- break;
- case NAT_BSC_NODE:
- vty->node = NAT_NODE;
- {
- struct bsc_config *bsc_config = vty->index;
- vty->index = bsc_config->nat;
- }
- break;
- case PGROUP_NODE:
- vty->node = NAT_NODE;
- vty->index = NULL;
- break;
- case TRUNK_NODE:
- vty->node = MGCP_NODE;
- vty->index = NULL;
- break;
- case SMPP_ESME_NODE:
- vty->node = SMPP_NODE;
- vty->index = NULL;
- break;
- case SMPP_NODE:
- case MGCP_NODE:
- case GBPROXY_NODE:
- case SGSN_NODE:
- case NAT_NODE:
- case BSC_NODE:
- case MSC_NODE:
- case MNCC_INT_NODE:
- case NITB_NODE:
- default:
- if (bsc_vty_is_config_node(vty, vty->node))
- vty->node = CONFIG_NODE;
- else
- vty->node = ENABLE_NODE;
-
- vty->index = NULL;
- }
-
- return vty->node;
-}
-
-int bsc_vty_is_config_node(struct vty *vty, int node)
-{
- switch (node) {
- /* add items that are not config */
- case OML_NODE:
- case OM2K_NODE:
- case SUBSCR_NODE:
- case CONFIG_NODE:
- return 0;
-
- default:
- return 1;
- }
-}
diff --git a/openbsc/src/libcommon/debug.c b/openbsc/src/libcommon/debug.c
deleted file mode 100644
index f29f1683d..000000000
--- a/openbsc/src/libcommon/debug.c
+++ /dev/null
@@ -1,235 +0,0 @@
-/* OpenBSC Debugging/Logging support code */
-
-/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org>
- * (C) 2008 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 <stdarg.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <strings.h>
-#include <time.h>
-#include <errno.h>
-
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/utils.h>
-#include <osmocom/core/logging.h>
-#include <osmocom/gprs/gprs_msgb.h>
-#include <openbsc/gsm_data.h>
-#include <openbsc/gsm_subscriber.h>
-#include <openbsc/debug.h>
-
-/* default categories */
-static const struct log_info_cat default_categories[] = {
- [DRLL] = {
- .name = "DRLL",
- .description = "A-bis Radio Link Layer (RLL)",
- .color = "\033[1;31m",
- .enabled = 1, .loglevel = LOGL_NOTICE,
- },
- [DCC] = {
- .name = "DCC",
- .description = "Layer3 Call Control (CC)",
- .color = "\033[1;32m",
- .enabled = 1, .loglevel = LOGL_NOTICE,
- },
- [DMM] = {
- .name = "DMM",
- .description = "Layer3 Mobility Management (MM)",
- .color = "\033[1;33m",
- .enabled = 1, .loglevel = LOGL_NOTICE,
- },
- [DRR] = {
- .name = "DRR",
- .description = "Layer3 Radio Resource (RR)",
- .color = "\033[1;34m",
- .enabled = 1, .loglevel = LOGL_NOTICE,
- },
- [DRSL] = {
- .name = "DRSL",
- .description = "A-bis Radio Siganlling Link (RSL)",
- .color = "\033[1;35m",
- .enabled = 1, .loglevel = LOGL_NOTICE,
- },
- [DNM] = {
- .name = "DNM",
- .description = "A-bis Network Management / O&M (NM/OML)",
- .color = "\033[1;36m",
- .enabled = 1, .loglevel = LOGL_INFO,
- },
- [DMNCC] = {
- .name = "DMNCC",
- .description = "MNCC API for Call Control application",
- .color = "\033[1;39m",
- .enabled = 1, .loglevel = LOGL_NOTICE,
- },
- [DPAG] = {
- .name = "DPAG",
- .description = "Paging Subsystem",
- .color = "\033[1;38m",
- .enabled = 1, .loglevel = LOGL_NOTICE,
- },
- [DMEAS] = {
- .name = "DMEAS",
- .description = "Radio Measurement Processing",
- .enabled = 0, .loglevel = LOGL_NOTICE,
- },
- [DSCCP] = {
- .name = "DSCCP",
- .description = "SCCP Protocol",
- .enabled = 1, .loglevel = LOGL_NOTICE,
- },
- [DMSC] = {
- .name = "DMSC",
- .description = "Mobile Switching Center",
- .enabled = 1, .loglevel = LOGL_NOTICE,
- },
- [DMGCP] = {
- .name = "DMGCP",
- .description = "Media Gateway Control Protocol",
- .enabled = 1, .loglevel = LOGL_NOTICE,
- },
- [DHO] = {
- .name = "DHO",
- .description = "Hand-Over",
- .enabled = 1, .loglevel = LOGL_NOTICE,
- },
- [DDB] = {
- .name = "DDB",
- .description = "Database Layer",
- .enabled = 1, .loglevel = LOGL_NOTICE,
- },
- [DREF] = {
- .name = "DREF",
- .description = "Reference Counting",
- .enabled = 0, .loglevel = LOGL_NOTICE,
- },
- [DGPRS] = {
- .name = "DGPRS",
- .description = "GPRS Packet Service",
- .enabled = 1, .loglevel = LOGL_DEBUG,
- },
- [DNS] = {
- .name = "DNS",
- .description = "GPRS Network Service (NS)",
- .enabled = 1, .loglevel = LOGL_INFO,
- },
- [DBSSGP] = {
- .name = "DBSSGP",
- .description = "GPRS BSS Gateway Protocol (BSSGP)",
- .enabled = 1, .loglevel = LOGL_DEBUG,
- },
- [DLLC] = {
- .name = "DLLC",
- .description = "GPRS Logical Link Control Protocol (LLC)",
- .enabled = 1, .loglevel = LOGL_DEBUG,
- },
- [DSNDCP] = {
- .name = "DSNDCP",
- .description = "GPRS Sub-Network Dependent Control Protocol (SNDCP)",
- .enabled = 1, .loglevel = LOGL_DEBUG,
- },
- [DNAT] = {
- .name = "DNAT",
- .description = "GSM 08.08 NAT/Multiplexer",
- .enabled = 1, .loglevel = LOGL_NOTICE,
- },
- [DCTRL] = {
- .name = "DCTRL",
- .description = "Control interface",
- .enabled = 1, .loglevel = LOGL_NOTICE,
- },
- [DSMPP] = {
- .name = "DSMPP",
- .description = "SMPP interface for external SMS apps",
- .enabled = 1, .loglevel = LOGL_DEBUG,
- },
- [DFILTER] = {
- .name = "DFILTER",
- .description = "BSC/NAT IMSI based filtering",
- .enabled = 1, .loglevel = LOGL_DEBUG,
- },
- [DRANAP] = {
- .name = "DRANAP",
- .description = "Radio Access Network Application Part Protocol",
- .enabled = 1, .loglevel = LOGL_DEBUG,
- },
- [DSUA] = {
- .name = "DSUA",
- .description = "SCCP User Adaptation Protocol",
- .enabled = 1, .loglevel = LOGL_DEBUG,
- },
- [DPCU] = {
- .name = "DPCU",
- .description = "PCU Interface",
- .enabled = 1, .loglevel = LOGL_DEBUG,
- },
-};
-
-static int filter_fn(const struct log_context *ctx, struct log_target *tar)
-{
- const struct gsm_subscriber *subscr = ctx->ctx[LOG_CTX_VLR_SUBSCR];
- const struct bsc_subscr *bsub = ctx->ctx[LOG_CTX_BSC_SUBSCR];
- const struct gprs_nsvc *nsvc = ctx->ctx[LOG_CTX_GB_NSVC];
- const struct gprs_nsvc *bvc = ctx->ctx[LOG_CTX_GB_BVC];
-
- if ((tar->filter_map & (1 << LOG_FLT_VLR_SUBSCR)) != 0
- && subscr && subscr == tar->filter_data[LOG_FLT_VLR_SUBSCR])
- return 1;
-
- if ((tar->filter_map & (1 << LOG_FLT_BSC_SUBSCR)) != 0
- && bsub && bsub == tar->filter_data[LOG_FLT_BSC_SUBSCR])
- return 1;
-
- /* Filter on the NS Virtual Connection */
- if ((tar->filter_map & (1 << LOG_FLT_GB_NSVC)) != 0
- && nsvc && (nsvc == tar->filter_data[LOG_FLT_GB_NSVC]))
- return 1;
-
- /* Filter on the NS Virtual Connection */
- if ((tar->filter_map & (1 << LOG_FLT_GB_BVC)) != 0
- && bvc && (bvc == tar->filter_data[LOG_FLT_GB_BVC]))
- return 1;
-
- return 0;
-}
-
-const struct log_info log_info = {
- .filter_fn = filter_fn,
- .cat = default_categories,
- .num_cat = ARRAY_SIZE(default_categories),
-};
-
-void log_set_filter_vlr_subscr(struct log_target *target,
- struct gsm_subscriber *vlr_subscr)
-{
- struct gsm_subscriber **fsub = (void*)&target->filter_data[LOG_FLT_VLR_SUBSCR];
-
- /* free the old data */
- if (*fsub) {
- subscr_put(*fsub);
- *fsub = NULL;
- }
-
- if (vlr_subscr) {
- target->filter_map |= (1 << LOG_FLT_VLR_SUBSCR);
- *fsub = subscr_get(vlr_subscr);
- } else
- target->filter_map &= ~(1 << LOG_FLT_VLR_SUBSCR);
-}
diff --git a/openbsc/src/libcommon/gsm_data.c b/openbsc/src/libcommon/gsm_data.c
deleted file mode 100644
index db7de082d..000000000
--- a/openbsc/src/libcommon/gsm_data.c
+++ /dev/null
@@ -1,473 +0,0 @@
-/* (C) 2008-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 <stdio.h>
-#include <string.h>
-#include <errno.h>
-#include <ctype.h>
-#include <stdbool.h>
-#include <netinet/in.h>
-
-#include <osmocom/core/linuxlist.h>
-#include <osmocom/core/talloc.h>
-#include <osmocom/gsm/gsm_utils.h>
-#include <osmocom/gsm/abis_nm.h>
-#include <osmocom/core/statistics.h>
-#include <osmocom/gsm/protocol/gsm_04_08.h>
-
-#include <openbsc/gsm_data.h>
-#include <openbsc/bsc_msc_data.h>
-#include <openbsc/abis_nm.h>
-
-void *tall_bsc_ctx;
-
-static LLIST_HEAD(bts_models);
-
-void set_ts_e1link(struct gsm_bts_trx_ts *ts, uint8_t e1_nr,
- uint8_t e1_ts, uint8_t e1_ts_ss)
-{
- ts->e1_link.e1_nr = e1_nr;
- ts->e1_link.e1_ts = e1_ts;
- ts->e1_link.e1_ts_ss = e1_ts_ss;
-}
-
-static struct gsm_bts_model *bts_model_find(enum gsm_bts_type type)
-{
- struct gsm_bts_model *model;
-
- llist_for_each_entry(model, &bts_models, list) {
- if (model->type == type)
- return model;
- }
-
- return NULL;
-}
-
-int gsm_bts_model_register(struct gsm_bts_model *model)
-{
- if (bts_model_find(model->type))
- return -EEXIST;
-
- tlv_def_patch(&model->nm_att_tlvdef, &abis_nm_att_tlvdef);
- llist_add_tail(&model->list, &bts_models);
- return 0;
-}
-
-/* Get reference to a neighbor cell on a given BCCH ARFCN */
-struct gsm_bts *gsm_bts_neighbor(const struct gsm_bts *bts,
- uint16_t arfcn, uint8_t bsic)
-{
- struct gsm_bts *neigh;
- /* FIXME: use some better heuristics here to determine which cell
- * using this ARFCN really is closest to the target cell. For
- * now we simply assume that each ARFCN will only be used by one
- * cell */
-
- llist_for_each_entry(neigh, &bts->network->bts_list, list) {
- if (neigh->c0->arfcn == arfcn &&
- neigh->bsic == bsic)
- return neigh;
- }
-
- return NULL;
-}
-
-const struct value_string bts_type_descs[_NUM_GSM_BTS_TYPE+1] = {
- { GSM_BTS_TYPE_UNKNOWN, "Unknown BTS Type" },
- { GSM_BTS_TYPE_BS11, "Siemens BTS (BS-11 or compatible)" },
- { GSM_BTS_TYPE_NANOBTS, "ip.access nanoBTS or compatible" },
- { GSM_BTS_TYPE_RBS2000, "Ericsson RBS2000 Series" },
- { GSM_BTS_TYPE_NOKIA_SITE, "Nokia {Metro,Ultra,In}Site" },
- { GSM_BTS_TYPE_OSMOBTS, "sysmocom sysmoBTS" },
- { 0, NULL }
-};
-
-struct gsm_bts_trx *gsm_bts_trx_by_nr(struct gsm_bts *bts, int nr)
-{
- struct gsm_bts_trx *trx;
-
- llist_for_each_entry(trx, &bts->trx_list, list) {
- if (trx->nr == nr)
- return trx;
- }
- return NULL;
-}
-
-/* Search for a BTS in the given Location Area; optionally start searching
- * with start_bts (for continuing to search after the first result) */
-struct gsm_bts *gsm_bts_by_lac(struct gsm_network *net, unsigned int lac,
- struct gsm_bts *start_bts)
-{
- int i;
- struct gsm_bts *bts;
- int skip = 0;
-
- if (start_bts)
- skip = 1;
-
- for (i = 0; i < net->num_bts; i++) {
- bts = gsm_bts_num(net, i);
-
- if (skip) {
- if (start_bts == bts)
- skip = 0;
- continue;
- }
-
- if (lac == GSM_LAC_RESERVED_ALL_BTS || bts->location_area_code == lac)
- return bts;
- }
- return NULL;
-}
-
-static const struct value_string auth_policy_names[] = {
- { GSM_AUTH_POLICY_CLOSED, "closed" },
- { GSM_AUTH_POLICY_ACCEPT_ALL, "accept-all" },
- { GSM_AUTH_POLICY_TOKEN, "token" },
- { GSM_AUTH_POLICY_REGEXP, "regexp" },
- { 0, NULL }
-};
-
-enum gsm_auth_policy gsm_auth_policy_parse(const char *arg)
-{
- return get_string_value(auth_policy_names, arg);
-}
-
-const char *gsm_auth_policy_name(enum gsm_auth_policy policy)
-{
- return get_value_string(auth_policy_names, policy);
-}
-
-static const struct value_string rrlp_mode_names[] = {
- { RRLP_MODE_NONE, "none" },
- { RRLP_MODE_MS_BASED, "ms-based" },
- { RRLP_MODE_MS_PREF, "ms-preferred" },
- { RRLP_MODE_ASS_PREF, "ass-preferred" },
- { 0, NULL }
-};
-
-enum rrlp_mode rrlp_mode_parse(const char *arg)
-{
- return get_string_value(rrlp_mode_names, arg);
-}
-
-const char *rrlp_mode_name(enum rrlp_mode mode)
-{
- return get_value_string(rrlp_mode_names, mode);
-}
-
-static const struct value_string bts_gprs_mode_names[] = {
- { BTS_GPRS_NONE, "none" },
- { BTS_GPRS_GPRS, "gprs" },
- { BTS_GPRS_EGPRS, "egprs" },
- { 0, NULL }
-};
-
-enum bts_gprs_mode bts_gprs_mode_parse(const char *arg, int *valid)
-{
- int rc;
-
- rc = get_string_value(bts_gprs_mode_names, arg);
- if (valid)
- *valid = rc != -EINVAL;
- return rc;
-}
-
-const char *bts_gprs_mode_name(enum bts_gprs_mode mode)
-{
- return get_value_string(bts_gprs_mode_names, mode);
-}
-
-int bts_gprs_mode_is_compat(struct gsm_bts *bts, enum bts_gprs_mode mode)
-{
- if (mode != BTS_GPRS_NONE &&
- !gsm_btsmodel_has_feature(bts->model, BTS_FEAT_GPRS)) {
- return 0;
- }
- if (mode == BTS_GPRS_EGPRS &&
- !gsm_btsmodel_has_feature(bts->model, BTS_FEAT_EGPRS)) {
- return 0;
- }
-
- return 1;
-}
-
-struct gsm_meas_rep *lchan_next_meas_rep(struct gsm_lchan *lchan)
-{
- struct gsm_meas_rep *meas_rep;
-
- meas_rep = &lchan->meas_rep[lchan->meas_rep_idx];
- memset(meas_rep, 0, sizeof(*meas_rep));
- meas_rep->lchan = lchan;
- lchan->meas_rep_idx = (lchan->meas_rep_idx + 1)
- % ARRAY_SIZE(lchan->meas_rep);
-
- return meas_rep;
-}
-
-int gsm_btsmodel_set_feature(struct gsm_bts_model *model, enum gsm_bts_features feat)
-{
- OSMO_ASSERT(_NUM_BTS_FEAT < MAX_BTS_FEATURES);
- return bitvec_set_bit_pos(&model->features, feat, 1);
-}
-
-bool gsm_btsmodel_has_feature(struct gsm_bts_model *model, enum gsm_bts_features feat)
-{
- OSMO_ASSERT(_NUM_BTS_FEAT < MAX_BTS_FEATURES);
- return bitvec_get_bit_pos(&model->features, feat);
-}
-
-int gsm_set_bts_type(struct gsm_bts *bts, enum gsm_bts_type type)
-{
- struct gsm_bts_model *model;
-
- model = bts_model_find(type);
- if (!model)
- return -EINVAL;
-
- bts->type = type;
- bts->model = model;
-
- if (model->start && !model->started) {
- int ret = model->start(bts->network);
- if (ret < 0)
- return ret;
-
- model->started = true;
- }
-
- switch (bts->type) {
- case GSM_BTS_TYPE_NANOBTS:
- case GSM_BTS_TYPE_OSMOBTS:
- /* Set the default OML Stream ID to 0xff */
- bts->oml_tei = 0xff;
- bts->c0->nominal_power = 23;
- break;
- case GSM_BTS_TYPE_RBS2000:
- INIT_LLIST_HEAD(&bts->rbs2000.is.conn_groups);
- INIT_LLIST_HEAD(&bts->rbs2000.con.conn_groups);
- break;
- case GSM_BTS_TYPE_BS11:
- case GSM_BTS_TYPE_UNKNOWN:
- case GSM_BTS_TYPE_NOKIA_SITE:
- /* Set default BTS reset timer */
- bts->nokia.bts_reset_timer_cnf = 15;
- case _NUM_GSM_BTS_TYPE:
- break;
- }
-
- return 0;
-}
-
-struct gsm_bts *gsm_bts_alloc_register(struct gsm_network *net, enum gsm_bts_type type,
- uint8_t bsic)
-{
- struct gsm_bts_model *model = bts_model_find(type);
- struct gsm_bts *bts;
-
- if (!model && type != GSM_BTS_TYPE_UNKNOWN)
- return NULL;
-
- bts = gsm_bts_alloc(net);
- if (!bts)
- return NULL;
-
- bts->network = net;
- bts->nr = net->num_bts++;
- bts->type = type;
- bts->model = model;
- bts->bsic = bsic;
- bts->dtxu = GSM48_DTX_SHALL_NOT_BE_USED;
- bts->dtxd = false;
- bts->gprs.ctrl_ack_type_use_block = true; /* use RLC/MAC control block */
- bts->neigh_list_manual_mode = 0;
- bts->si_common.cell_sel_par.cell_resel_hyst = 2; /* 4 dB */
- bts->si_common.cell_sel_par.rxlev_acc_min = 0;
- bts->si_common.si2quater_neigh_list.arfcn = bts->si_common.data.earfcn_list;
- bts->si_common.si2quater_neigh_list.meas_bw = bts->si_common.data.meas_bw_list;
- bts->si_common.si2quater_neigh_list.length = MAX_EARFCN_LIST;
- bts->si_common.si2quater_neigh_list.thresh_hi = 0;
- osmo_earfcn_init(&bts->si_common.si2quater_neigh_list);
- bts->si_common.neigh_list.data = bts->si_common.data.neigh_list;
- bts->si_common.neigh_list.data_len =
- sizeof(bts->si_common.data.neigh_list);
- bts->si_common.si5_neigh_list.data = bts->si_common.data.si5_neigh_list;
- bts->si_common.si5_neigh_list.data_len =
- sizeof(bts->si_common.data.si5_neigh_list);
- bts->si_common.cell_alloc.data = bts->si_common.data.cell_alloc;
- bts->si_common.cell_alloc.data_len =
- sizeof(bts->si_common.data.cell_alloc);
- bts->si_common.rach_control.re = 1; /* no re-establishment */
- bts->si_common.rach_control.tx_integer = 9; /* 12 slots spread - 217/115 slots delay */
- bts->si_common.rach_control.max_trans = 3; /* 7 retransmissions */
- bts->si_common.rach_control.t2 = 4; /* no emergency calls */
- bts->si_common.chan_desc.att = 1; /* attachment required */
- bts->si_common.chan_desc.bs_pa_mfrms = RSL_BS_PA_MFRMS_5; /* paging frames */
- bts->si_common.chan_desc.bs_ag_blks_res = 1; /* reserved AGCH blocks */
- bts->si_common.chan_desc.t3212 = 5; /* Use 30 min periodic update interval as sane default */
- gsm_bts_set_radio_link_timeout(bts, 32); /* Use RADIO LINK TIMEOUT of 32 */
-
- llist_add_tail(&bts->list, &net->bts_list);
-
- INIT_LLIST_HEAD(&bts->abis_queue);
-
- INIT_LLIST_HEAD(&bts->loc_list);
-
- return bts;
-}
-
-void gprs_ra_id_by_bts(struct gprs_ra_id *raid, struct gsm_bts *bts)
-{
- raid->mcc = bts->network->country_code;
- raid->mnc = bts->network->network_code;
- raid->lac = bts->location_area_code;
- raid->rac = bts->gprs.rac;
-}
-
-int gsm48_ra_id_by_bts(uint8_t *buf, struct gsm_bts *bts)
-{
- struct gprs_ra_id raid;
-
- gprs_ra_id_by_bts(&raid, bts);
-
- return gsm48_construct_ra(buf, &raid);
-}
-
-int gsm_parse_reg(void *ctx, regex_t *reg, char **str, int argc, const char **argv)
-{
- int ret;
-
- ret = 0;
- if (*str) {
- talloc_free(*str);
- *str = NULL;
- }
- regfree(reg);
-
- if (argc > 0) {
- *str = talloc_strdup(ctx, argv[0]);
- ret = regcomp(reg, argv[0], 0);
-
- /* handle compilation failures */
- if (ret != 0) {
- talloc_free(*str);
- *str = NULL;
- }
- }
-
- return ret;
-}
-
-/* Assume there are only 256 possible bts */
-osmo_static_assert(sizeof(((struct gsm_bts *) 0)->nr) == 1, _bts_nr_is_256);
-static void depends_calc_index_bit(int bts_nr, int *idx, int *bit)
-{
- *idx = bts_nr / (8 * 4);
- *bit = bts_nr % (8 * 4);
-}
-
-void bts_depend_mark(struct gsm_bts *bts, int dep)
-{
- int idx, bit;
- depends_calc_index_bit(dep, &idx, &bit);
-
- bts->depends_on[idx] |= 1 << bit;
-}
-
-void bts_depend_clear(struct gsm_bts *bts, int dep)
-{
- int idx, bit;
- depends_calc_index_bit(dep, &idx, &bit);
-
- bts->depends_on[idx] &= ~(1 << bit);
-}
-
-int bts_depend_is_depedency(struct gsm_bts *base, struct gsm_bts *other)
-{
- int idx, bit;
- depends_calc_index_bit(other->nr, &idx, &bit);
-
- /* Check if there is a depends bit */
- return (base->depends_on[idx] & (1 << bit)) > 0;
-}
-
-static int bts_is_online(struct gsm_bts *bts)
-{
- /* TODO: support E1 BTS too */
- if (!is_ipaccess_bts(bts))
- return 1;
-
- if (!bts->oml_link)
- return 0;
-
- return bts->mo.nm_state.operational == NM_OPSTATE_ENABLED;
-}
-
-int bts_depend_check(struct gsm_bts *bts)
-{
- struct gsm_bts *other_bts;
-
- llist_for_each_entry(other_bts, &bts->network->bts_list, list) {
- if (!bts_depend_is_depedency(bts, other_bts))
- continue;
- if (bts_is_online(other_bts))
- continue;
- return 0;
- }
- return 1;
-}
-
-/* get the radio link timeout (based on SACCH decode errors, according
- * to algorithm specified in TS 05.08 section 5.2. A value of -1
- * indicates we should use an infinitely long timeout, which only works
- * with OsmoBTS as the BTS implementation */
-int gsm_bts_get_radio_link_timeout(const struct gsm_bts *bts)
-{
- const struct gsm48_cell_options *cell_options = &bts->si_common.cell_options;
-
- if (bts->infinite_radio_link_timeout)
- return -1;
- else {
- /* Encoding as per Table 10.5.21 of TS 04.08 */
- return (cell_options->radio_link_timeout + 1) << 2;
- }
-}
-
-/* set the radio link timeout (based on SACCH decode errors, according
- * to algorithm specified in TS 05.08 Section 5.2. A value of -1
- * indicates we should use an infinitely long timeout, which only works
- * with OsmoBTS as the BTS implementation */
-void gsm_bts_set_radio_link_timeout(struct gsm_bts *bts, int value)
-{
- struct gsm48_cell_options *cell_options = &bts->si_common.cell_options;
-
- if (value < 0)
- bts->infinite_radio_link_timeout = true;
- else {
- bts->infinite_radio_link_timeout = false;
- /* Encoding as per Table 10.5.21 of TS 04.08 */
- if (value < 4)
- value = 4;
- if (value > 64)
- value = 64;
- cell_options->radio_link_timeout = (value >> 2) - 1;
- }
-}
diff --git a/openbsc/src/libcommon/gsm_data_shared.c b/openbsc/src/libcommon/gsm_data_shared.c
deleted file mode 100644
index 89926364c..000000000
--- a/openbsc/src/libcommon/gsm_data_shared.c
+++ /dev/null
@@ -1,849 +0,0 @@
-/* (C) 2008-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 <stdio.h>
-#include <string.h>
-#include <errno.h>
-#include <ctype.h>
-
-#include <netinet/in.h>
-
-#include <osmocom/core/linuxlist.h>
-#include <osmocom/core/talloc.h>
-#include <osmocom/gsm/gsm_utils.h>
-#include <osmocom/gsm/abis_nm.h>
-#include <osmocom/core/statistics.h>
-
-#include <openbsc/gsm_data.h>
-
-void gsm_abis_mo_reset(struct gsm_abis_mo *mo)
-{
- mo->nm_state.operational = NM_OPSTATE_NULL;
- mo->nm_state.availability = NM_AVSTATE_POWER_OFF;
-}
-
-static void gsm_mo_init(struct gsm_abis_mo *mo, struct gsm_bts *bts,
- uint8_t obj_class, uint8_t p1, uint8_t p2, uint8_t p3)
-{
- mo->bts = bts;
- mo->obj_class = obj_class;
- mo->obj_inst.bts_nr = p1;
- mo->obj_inst.trx_nr = p2;
- mo->obj_inst.ts_nr = p3;
- gsm_abis_mo_reset(mo);
-}
-
-const struct value_string bts_attribute_names[] = {
- OSMO_VALUE_STRING(BTS_TYPE_VARIANT),
- OSMO_VALUE_STRING(BTS_SUB_MODEL),
- OSMO_VALUE_STRING(TRX_PHY_VERSION),
- { 0, NULL }
-};
-
-enum bts_attribute str2btsattr(const char *s)
-{
- return get_string_value(bts_attribute_names, s);
-}
-
-const char *btsatttr2str(enum bts_attribute v)
-{
- return get_value_string(bts_attribute_names, v);
-}
-
-const struct value_string osmo_bts_variant_names[_NUM_BTS_VARIANT + 1] = {
- { BTS_UNKNOWN, "unknown" },
- { BTS_OSMO_LITECELL15, "osmo-bts-lc15" },
- { BTS_OSMO_OCTPHY, "osmo-bts-octphy" },
- { BTS_OSMO_SYSMO, "osmo-bts-sysmo" },
- { BTS_OSMO_TRX, "omso-bts-trx" },
- { 0, NULL }
-};
-
-enum gsm_bts_type_variant str2btsvariant(const char *arg)
-{
- return get_string_value(osmo_bts_variant_names, arg);
-}
-
-const char *btsvariant2str(enum gsm_bts_type_variant v)
-{
- return get_value_string(osmo_bts_variant_names, v);
-}
-
-const struct value_string bts_type_names[_NUM_GSM_BTS_TYPE + 1] = {
- { GSM_BTS_TYPE_UNKNOWN, "unknown" },
- { GSM_BTS_TYPE_BS11, "bs11" },
- { GSM_BTS_TYPE_NANOBTS, "nanobts" },
- { GSM_BTS_TYPE_RBS2000, "rbs2000" },
- { GSM_BTS_TYPE_NOKIA_SITE, "nokia_site" },
- { GSM_BTS_TYPE_OSMOBTS, "sysmobts" },
- { 0, NULL }
-};
-
-enum gsm_bts_type str2btstype(const char *arg)
-{
- return get_string_value(bts_type_names, arg);
-}
-
-const char *btstype2str(enum gsm_bts_type type)
-{
- return get_value_string(bts_type_names, type);
-}
-
-const struct value_string gsm_bts_features_descs[] = {
- { BTS_FEAT_HSCSD, "HSCSD" },
- { BTS_FEAT_GPRS, "GPRS" },
- { BTS_FEAT_EGPRS, "EGPRS" },
- { BTS_FEAT_ECSD, "ECSD" },
- { BTS_FEAT_HOPPING, "Frequency Hopping" },
- { BTS_FEAT_MULTI_TSC, "Multi-TSC" },
- { BTS_FEAT_OML_ALERTS, "OML Alerts" },
- { BTS_FEAT_AGCH_PCH_PROP, "AGCH/PCH proportional allocation" },
- { BTS_FEAT_CBCH, "CBCH" },
- { 0, NULL }
-};
-
-const struct value_string gsm_chreq_descs[] = {
- { GSM_CHREQ_REASON_EMERG, "emergency call" },
- { GSM_CHREQ_REASON_PAG, "answer to paging" },
- { GSM_CHREQ_REASON_CALL, "call re-establishment" },
- { GSM_CHREQ_REASON_LOCATION_UPD,"Location updating" },
- { GSM_CHREQ_REASON_PDCH, "one phase packet access" },
- { GSM_CHREQ_REASON_OTHER, "other" },
- { 0, NULL }
-};
-
-const struct value_string gsm_pchant_names[13] = {
- { GSM_PCHAN_NONE, "NONE" },
- { GSM_PCHAN_CCCH, "CCCH" },
- { GSM_PCHAN_CCCH_SDCCH4,"CCCH+SDCCH4" },
- { GSM_PCHAN_TCH_F, "TCH/F" },
- { GSM_PCHAN_TCH_H, "TCH/H" },
- { GSM_PCHAN_SDCCH8_SACCH8C, "SDCCH8" },
- { GSM_PCHAN_PDCH, "PDCH" },
- { GSM_PCHAN_TCH_F_PDCH, "TCH/F_PDCH" },
- { GSM_PCHAN_UNKNOWN, "UNKNOWN" },
- { GSM_PCHAN_CCCH_SDCCH4_CBCH, "CCCH+SDCCH4+CBCH" },
- { GSM_PCHAN_SDCCH8_SACCH8C_CBCH, "SDCCH8+CBCH" },
- { GSM_PCHAN_TCH_F_TCH_H_PDCH, "TCH/F_TCH/H_PDCH" },
- { 0, NULL }
-};
-
-const struct value_string gsm_pchant_descs[13] = {
- { GSM_PCHAN_NONE, "Physical Channel not configured" },
- { GSM_PCHAN_CCCH, "FCCH + SCH + BCCH + CCCH (Comb. IV)" },
- { GSM_PCHAN_CCCH_SDCCH4,
- "FCCH + SCH + BCCH + CCCH + 4 SDCCH + 2 SACCH (Comb. V)" },
- { GSM_PCHAN_TCH_F, "TCH/F + FACCH/F + SACCH (Comb. I)" },
- { GSM_PCHAN_TCH_H, "2 TCH/H + 2 FACCH/H + 2 SACCH (Comb. II)" },
- { GSM_PCHAN_SDCCH8_SACCH8C, "8 SDCCH + 4 SACCH (Comb. VII)" },
- { GSM_PCHAN_PDCH, "Packet Data Channel for GPRS/EDGE" },
- { GSM_PCHAN_TCH_F_PDCH, "Dynamic TCH/F or GPRS PDCH" },
- { GSM_PCHAN_UNKNOWN, "Unknown / Unsupported channel combination" },
- { GSM_PCHAN_CCCH_SDCCH4_CBCH, "FCCH + SCH + BCCH + CCCH + CBCH + 3 SDCCH + 2 SACCH (Comb. V)" },
- { GSM_PCHAN_SDCCH8_SACCH8C_CBCH, "7 SDCCH + 4 SACCH + CBCH (Comb. VII)" },
- { GSM_PCHAN_TCH_F_TCH_H_PDCH, "Dynamic TCH/F or TCH/H or GPRS PDCH" },
- { 0, NULL }
-};
-
-const char *gsm_pchan_name(enum gsm_phys_chan_config c)
-{
- return get_value_string(gsm_pchant_names, c);
-}
-
-enum gsm_phys_chan_config gsm_pchan_parse(const char *name)
-{
- return get_string_value(gsm_pchant_names, name);
-}
-
-/* TODO: move to libosmocore, next to gsm_chan_t_names? */
-const char *gsm_lchant_name(enum gsm_chan_t c)
-{
- return get_value_string(gsm_chan_t_names, c);
-}
-
-static const struct value_string lchan_s_names[] = {
- { LCHAN_S_NONE, "NONE" },
- { LCHAN_S_ACT_REQ, "ACTIVATION REQUESTED" },
- { LCHAN_S_ACTIVE, "ACTIVE" },
- { LCHAN_S_INACTIVE, "INACTIVE" },
- { LCHAN_S_REL_REQ, "RELEASE REQUESTED" },
- { LCHAN_S_REL_ERR, "RELEASE DUE ERROR" },
- { LCHAN_S_BROKEN, "BROKEN UNUSABLE" },
- { 0, NULL }
-};
-
-const char *gsm_lchans_name(enum gsm_lchan_state s)
-{
- return get_value_string(lchan_s_names, s);
-}
-
-static const struct value_string chreq_names[] = {
- { GSM_CHREQ_REASON_EMERG, "EMERGENCY" },
- { GSM_CHREQ_REASON_PAG, "PAGING" },
- { GSM_CHREQ_REASON_CALL, "CALL" },
- { GSM_CHREQ_REASON_LOCATION_UPD,"LOCATION_UPDATE" },
- { GSM_CHREQ_REASON_OTHER, "OTHER" },
- { 0, NULL }
-};
-
-const char *gsm_chreq_name(enum gsm_chreq_reason_t c)
-{
- return get_value_string(chreq_names, c);
-}
-
-struct gsm_bts *gsm_bts_num(struct gsm_network *net, int num)
-{
- struct gsm_bts *bts;
-
- if (num >= net->num_bts)
- return NULL;
-
- llist_for_each_entry(bts, &net->bts_list, list) {
- if (bts->nr == num)
- return bts;
- }
-
- return NULL;
-}
-
-struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts)
-{
- struct gsm_bts_trx *trx = talloc_zero(bts, struct gsm_bts_trx);
- int k;
-
- if (!trx)
- return NULL;
-
- trx->bts = bts;
- trx->nr = bts->num_trx++;
- trx->mo.nm_state.administrative = NM_STATE_UNLOCKED;
-
- gsm_mo_init(&trx->mo, bts, NM_OC_RADIO_CARRIER,
- bts->nr, trx->nr, 0xff);
- gsm_mo_init(&trx->bb_transc.mo, bts, NM_OC_BASEB_TRANSC,
- bts->nr, trx->nr, 0xff);
-
- for (k = 0; k < TRX_NR_TS; k++) {
- struct gsm_bts_trx_ts *ts = &trx->ts[k];
- int l;
-
- ts->trx = trx;
- ts->nr = k;
- ts->pchan = GSM_PCHAN_NONE;
- ts->dyn.pchan_is = GSM_PCHAN_NONE;
- ts->dyn.pchan_want = GSM_PCHAN_NONE;
- ts->tsc = -1;
-
- gsm_mo_init(&ts->mo, bts, NM_OC_CHANNEL,
- bts->nr, trx->nr, ts->nr);
-
- ts->hopping.arfcns.data_len = sizeof(ts->hopping.arfcns_data);
- ts->hopping.arfcns.data = ts->hopping.arfcns_data;
- ts->hopping.ma.data_len = sizeof(ts->hopping.ma_data);
- ts->hopping.ma.data = ts->hopping.ma_data;
-
- for (l = 0; l < TS_MAX_LCHAN; l++) {
- struct gsm_lchan *lchan;
- char *name;
- lchan = &ts->lchan[l];
-
- lchan->ts = ts;
- lchan->nr = l;
- lchan->type = GSM_LCHAN_NONE;
-
- name = gsm_lchan_name_compute(lchan);
- lchan->name = talloc_strdup(trx, name);
-#ifndef ROLE_BSC
- INIT_LLIST_HEAD(&lchan->sapi_cmds);
-#endif
- }
- }
-
- if (trx->nr != 0)
- trx->nominal_power = bts->c0->nominal_power;
-
- llist_add_tail(&trx->list, &bts->trx_list);
-
- return trx;
-}
-
-
-static const uint8_t bts_nse_timer_default[] = { 3, 3, 3, 3, 30, 3, 10 };
-static const uint8_t bts_cell_timer_default[] =
- { 3, 3, 3, 3, 3, 10, 3, 10, 3, 10, 3 };
-static const struct gprs_rlc_cfg rlc_cfg_default = {
- .parameter = {
- [RLC_T3142] = 20,
- [RLC_T3169] = 5,
- [RLC_T3191] = 5,
- [RLC_T3193] = 160, /* 10ms */
- [RLC_T3195] = 5,
- [RLC_N3101] = 10,
- [RLC_N3103] = 4,
- [RLC_N3105] = 8,
- [CV_COUNTDOWN] = 15,
- [T_DL_TBF_EXT] = 250 * 10, /* ms */
- [T_UL_TBF_EXT] = 250 * 10, /* ms */
- },
- .paging = {
- .repeat_time = 5 * 50, /* ms */
- .repeat_count = 3,
- },
- .cs_mask = 0x1fff,
- .initial_cs = 2,
- .initial_mcs = 6,
-};
-
-struct gsm_bts *gsm_bts_alloc(void *ctx)
-{
- struct gsm_bts *bts = talloc_zero(ctx, struct gsm_bts);
- int i;
-
- if (!bts)
- return NULL;
-
- bts->num_trx = 0;
- INIT_LLIST_HEAD(&bts->trx_list);
- bts->ms_max_power = 15; /* dBm */
-
- gsm_mo_init(&bts->mo, bts, NM_OC_BTS,
- bts->nr, 0xff, 0xff);
- gsm_mo_init(&bts->site_mgr.mo, bts, NM_OC_SITE_MANAGER,
- 0xff, 0xff, 0xff);
-
- for (i = 0; i < ARRAY_SIZE(bts->gprs.nsvc); i++) {
- bts->gprs.nsvc[i].bts = bts;
- bts->gprs.nsvc[i].id = i;
- gsm_mo_init(&bts->gprs.nsvc[i].mo, bts, NM_OC_GPRS_NSVC,
- bts->nr, i, 0xff);
- }
- memcpy(&bts->gprs.nse.timer, bts_nse_timer_default,
- sizeof(bts->gprs.nse.timer));
- gsm_mo_init(&bts->gprs.nse.mo, bts, NM_OC_GPRS_NSE,
- bts->nr, 0xff, 0xff);
- memcpy(&bts->gprs.cell.timer, bts_cell_timer_default,
- sizeof(bts->gprs.cell.timer));
- gsm_mo_init(&bts->gprs.cell.mo, bts, NM_OC_GPRS_CELL,
- bts->nr, 0xff, 0xff);
- memcpy(&bts->gprs.cell.rlc_cfg, &rlc_cfg_default,
- sizeof(bts->gprs.cell.rlc_cfg));
-
- /* create our primary TRX */
- bts->c0 = gsm_bts_trx_alloc(bts);
- if (!bts->c0) {
- talloc_free(bts);
- return NULL;
- }
- bts->c0->ts[0].pchan = GSM_PCHAN_CCCH_SDCCH4;
-
- bts->rach_b_thresh = -1;
- bts->rach_ldavg_slots = -1;
- bts->paging.free_chans_need = -1;
- bts->features.data = &bts->_features_data[0];
- bts->features.data_len = sizeof(bts->_features_data);
-
- /* si handling */
- bts->bcch_change_mark = 1;
-
- return bts;
-}
-
-/* reset the state of all MO in the BTS */
-void gsm_bts_mo_reset(struct gsm_bts *bts)
-{
- struct gsm_bts_trx *trx;
- unsigned int i;
-
- gsm_abis_mo_reset(&bts->mo);
- gsm_abis_mo_reset(&bts->site_mgr.mo);
- for (i = 0; i < ARRAY_SIZE(bts->gprs.nsvc); i++)
- gsm_abis_mo_reset(&bts->gprs.nsvc[i].mo);
- gsm_abis_mo_reset(&bts->gprs.nse.mo);
- gsm_abis_mo_reset(&bts->gprs.cell.mo);
-
- llist_for_each_entry(trx, &bts->trx_list, list) {
- gsm_abis_mo_reset(&trx->mo);
- gsm_abis_mo_reset(&trx->bb_transc.mo);
-
- for (i = 0; i < ARRAY_SIZE(trx->ts); i++) {
- struct gsm_bts_trx_ts *ts = &trx->ts[i];
- gsm_abis_mo_reset(&ts->mo);
- }
- }
-}
-
-struct gsm_bts_trx *gsm_bts_trx_num(const struct gsm_bts *bts, int num)
-{
- struct gsm_bts_trx *trx;
-
- if (num >= bts->num_trx)
- return NULL;
-
- llist_for_each_entry(trx, &bts->trx_list, list) {
- if (trx->nr == num)
- return trx;
- }
-
- return NULL;
-}
-
-static char ts2str[255];
-
-char *gsm_trx_name(const struct gsm_bts_trx *trx)
-{
- if (!trx)
- snprintf(ts2str, sizeof(ts2str), "(trx=NULL)");
- else
- snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d)",
- trx->bts->nr, trx->nr);
-
- return ts2str;
-}
-
-
-char *gsm_ts_name(const struct gsm_bts_trx_ts *ts)
-{
- snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d,ts=%d)",
- ts->trx->bts->nr, ts->trx->nr, ts->nr);
-
- return ts2str;
-}
-
-/*! Log timeslot number with full pchan information */
-char *gsm_ts_and_pchan_name(const struct gsm_bts_trx_ts *ts)
-{
- switch (ts->pchan) {
- case GSM_PCHAN_TCH_F_TCH_H_PDCH:
- if (ts->dyn.pchan_is == ts->dyn.pchan_want)
- snprintf(ts2str, sizeof(ts2str),
- "(bts=%d,trx=%d,ts=%d,pchan=%s as %s)",
- ts->trx->bts->nr, ts->trx->nr, ts->nr,
- gsm_pchan_name(ts->pchan),
- gsm_pchan_name(ts->dyn.pchan_is));
- else
- snprintf(ts2str, sizeof(ts2str),
- "(bts=%d,trx=%d,ts=%d,pchan=%s"
- " switching %s -> %s)",
- ts->trx->bts->nr, ts->trx->nr, ts->nr,
- gsm_pchan_name(ts->pchan),
- gsm_pchan_name(ts->dyn.pchan_is),
- gsm_pchan_name(ts->dyn.pchan_want));
- break;
- case GSM_PCHAN_TCH_F_PDCH:
- if ((ts->flags & TS_F_PDCH_PENDING_MASK) == 0)
- snprintf(ts2str, sizeof(ts2str),
- "(bts=%d,trx=%d,ts=%d,pchan=%s as %s)",
- ts->trx->bts->nr, ts->trx->nr, ts->nr,
- gsm_pchan_name(ts->pchan),
- (ts->flags & TS_F_PDCH_ACTIVE)? "PDCH"
- : "TCH/F");
- else
- snprintf(ts2str, sizeof(ts2str),
- "(bts=%d,trx=%d,ts=%d,pchan=%s"
- " switching %s -> %s)",
- ts->trx->bts->nr, ts->trx->nr, ts->nr,
- gsm_pchan_name(ts->pchan),
- (ts->flags & TS_F_PDCH_ACTIVE)? "PDCH"
- : "TCH/F",
- (ts->flags & TS_F_PDCH_ACT_PENDING)? "PDCH"
- : "TCH/F");
- break;
- default:
- snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d,ts=%d,pchan=%s)",
- ts->trx->bts->nr, ts->trx->nr, ts->nr,
- gsm_pchan_name(ts->pchan));
- break;
- }
-
- return ts2str;
-}
-
-char *gsm_lchan_name_compute(const struct gsm_lchan *lchan)
-{
- struct gsm_bts_trx_ts *ts = lchan->ts;
-
- snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d,ts=%d,ss=%d)",
- ts->trx->bts->nr, ts->trx->nr, ts->nr, lchan->nr);
-
- return ts2str;
-}
-
-/* obtain the MO structure for a given object instance */
-struct gsm_abis_mo *
-gsm_objclass2mo(struct gsm_bts *bts, uint8_t obj_class,
- const struct abis_om_obj_inst *obj_inst)
-{
- struct gsm_bts_trx *trx;
- struct gsm_abis_mo *mo = NULL;
-
- switch (obj_class) {
- case NM_OC_BTS:
- mo = &bts->mo;
- break;
- case NM_OC_RADIO_CARRIER:
- if (obj_inst->trx_nr >= bts->num_trx) {
- return NULL;
- }
- trx = gsm_bts_trx_num(bts, obj_inst->trx_nr);
- mo = &trx->mo;
- break;
- case NM_OC_BASEB_TRANSC:
- if (obj_inst->trx_nr >= bts->num_trx) {
- return NULL;
- }
- trx = gsm_bts_trx_num(bts, obj_inst->trx_nr);
- mo = &trx->bb_transc.mo;
- break;
- case NM_OC_CHANNEL:
- if (obj_inst->trx_nr >= bts->num_trx) {
- return NULL;
- }
- trx = gsm_bts_trx_num(bts, obj_inst->trx_nr);
- if (obj_inst->ts_nr >= TRX_NR_TS)
- return NULL;
- mo = &trx->ts[obj_inst->ts_nr].mo;
- break;
- case NM_OC_SITE_MANAGER:
- mo = &bts->site_mgr.mo;
- break;
- case NM_OC_BS11:
- switch (obj_inst->bts_nr) {
- case BS11_OBJ_CCLK:
- mo = &bts->bs11.cclk.mo;
- break;
- case BS11_OBJ_BBSIG:
- if (obj_inst->ts_nr > bts->num_trx)
- return NULL;
- trx = gsm_bts_trx_num(bts, obj_inst->trx_nr);
- mo = &trx->bs11.bbsig.mo;
- break;
- case BS11_OBJ_PA:
- if (obj_inst->ts_nr > bts->num_trx)
- return NULL;
- trx = gsm_bts_trx_num(bts, obj_inst->trx_nr);
- mo = &trx->bs11.pa.mo;
- break;
- default:
- return NULL;
- }
- break;
- case NM_OC_BS11_RACK:
- mo = &bts->bs11.rack.mo;
- break;
- case NM_OC_BS11_ENVABTSE:
- if (obj_inst->trx_nr >= ARRAY_SIZE(bts->bs11.envabtse))
- return NULL;
- mo = &bts->bs11.envabtse[obj_inst->trx_nr].mo;
- break;
- case NM_OC_GPRS_NSE:
- mo = &bts->gprs.nse.mo;
- break;
- case NM_OC_GPRS_CELL:
- mo = &bts->gprs.cell.mo;
- break;
- case NM_OC_GPRS_NSVC:
- if (obj_inst->trx_nr >= ARRAY_SIZE(bts->gprs.nsvc))
- return NULL;
- mo = &bts->gprs.nsvc[obj_inst->trx_nr].mo;
- break;
- }
- return mo;
-}
-
-/* obtain the gsm_nm_state data structure for a given object instance */
-struct gsm_nm_state *
-gsm_objclass2nmstate(struct gsm_bts *bts, uint8_t obj_class,
- const struct abis_om_obj_inst *obj_inst)
-{
- struct gsm_abis_mo *mo;
-
- mo = gsm_objclass2mo(bts, obj_class, obj_inst);
- if (!mo)
- return NULL;
-
- return &mo->nm_state;
-}
-
-/* obtain the in-memory data structure of a given object instance */
-void *
-gsm_objclass2obj(struct gsm_bts *bts, uint8_t obj_class,
- const struct abis_om_obj_inst *obj_inst)
-{
- struct gsm_bts_trx *trx;
- void *obj = NULL;
-
- switch (obj_class) {
- case NM_OC_BTS:
- obj = bts;
- break;
- case NM_OC_RADIO_CARRIER:
- if (obj_inst->trx_nr >= bts->num_trx) {
- return NULL;
- }
- trx = gsm_bts_trx_num(bts, obj_inst->trx_nr);
- obj = trx;
- break;
- case NM_OC_BASEB_TRANSC:
- if (obj_inst->trx_nr >= bts->num_trx) {
- return NULL;
- }
- trx = gsm_bts_trx_num(bts, obj_inst->trx_nr);
- obj = &trx->bb_transc;
- break;
- case NM_OC_CHANNEL:
- if (obj_inst->trx_nr >= bts->num_trx) {
- return NULL;
- }
- trx = gsm_bts_trx_num(bts, obj_inst->trx_nr);
- if (obj_inst->ts_nr >= TRX_NR_TS)
- return NULL;
- obj = &trx->ts[obj_inst->ts_nr];
- break;
- case NM_OC_SITE_MANAGER:
- obj = &bts->site_mgr;
- break;
- case NM_OC_GPRS_NSE:
- obj = &bts->gprs.nse;
- break;
- case NM_OC_GPRS_CELL:
- obj = &bts->gprs.cell;
- break;
- case NM_OC_GPRS_NSVC:
- if (obj_inst->trx_nr >= ARRAY_SIZE(bts->gprs.nsvc))
- return NULL;
- obj = &bts->gprs.nsvc[obj_inst->trx_nr];
- break;
- }
- return obj;
-}
-
-/* See Table 10.5.25 of GSM04.08 */
-uint8_t gsm_pchan2chan_nr(enum gsm_phys_chan_config pchan,
- uint8_t ts_nr, uint8_t lchan_nr)
-{
- uint8_t cbits, chan_nr;
-
- switch (pchan) {
- case GSM_PCHAN_TCH_F:
- case GSM_PCHAN_PDCH:
- case GSM_PCHAN_TCH_F_PDCH:
- OSMO_ASSERT(lchan_nr == 0);
- cbits = 0x01;
- break;
- case GSM_PCHAN_TCH_H:
- OSMO_ASSERT(lchan_nr < 2);
- cbits = 0x02;
- cbits += lchan_nr;
- break;
- case GSM_PCHAN_CCCH_SDCCH4:
- case GSM_PCHAN_CCCH_SDCCH4_CBCH:
- /*
- * As a special hack for BCCH, lchan_nr == 4 may be passed
- * here. This should never be sent in an RSL message.
- * See osmo-bts-xxx/oml.c:opstart_compl().
- */
- if (lchan_nr == CCCH_LCHAN)
- chan_nr = 0;
- else
- OSMO_ASSERT(lchan_nr < 4);
- cbits = 0x04;
- cbits += lchan_nr;
- break;
- case GSM_PCHAN_SDCCH8_SACCH8C:
- case GSM_PCHAN_SDCCH8_SACCH8C_CBCH:
- OSMO_ASSERT(lchan_nr < 8);
- cbits = 0x08;
- cbits += lchan_nr;
- break;
- default:
- case GSM_PCHAN_CCCH:
-#ifdef ROLE_BSC
- OSMO_ASSERT(lchan_nr == 0);
-#else
- /*
- * FIXME: On octphy and litecell, we hit above assertion (see
- * Max's comment at https://gerrit.osmocom.org/589 ); disabled
- * for BTS until this is clarified; remove the #ifdef when it
- * is fixed.
- */
-#warning "fix caller that passes lchan_nr != 0"
-#endif
- cbits = 0x10;
- break;
- }
-
- chan_nr = (cbits << 3) | (ts_nr & 0x7);
-
- return chan_nr;
-}
-
-uint8_t gsm_lchan2chan_nr(const struct gsm_lchan *lchan)
-{
- enum gsm_phys_chan_config pchan = lchan->ts->pchan;
- if (pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH)
- return gsm_lchan_as_pchan2chan_nr(lchan,
- lchan->ts->dyn.pchan_is);
- return gsm_pchan2chan_nr(lchan->ts->pchan, lchan->ts->nr, lchan->nr);
-}
-
-uint8_t gsm_lchan_as_pchan2chan_nr(const struct gsm_lchan *lchan,
- enum gsm_phys_chan_config as_pchan)
-{
- if (lchan->ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH
- && as_pchan == GSM_PCHAN_PDCH)
- return RSL_CHAN_OSMO_PDCH | (lchan->ts->nr & ~RSL_CHAN_NR_MASK);
- return gsm_pchan2chan_nr(as_pchan, lchan->ts->nr, lchan->nr);
-}
-
-/* return the gsm_lchan for the CBCH (if it exists at all) */
-struct gsm_lchan *gsm_bts_get_cbch(struct gsm_bts *bts)
-{
- struct gsm_lchan *lchan = NULL;
- struct gsm_bts_trx *trx = bts->c0;
-
- if (trx->ts[0].pchan == GSM_PCHAN_CCCH_SDCCH4_CBCH)
- lchan = &trx->ts[0].lchan[2];
- else {
- int i;
- for (i = 0; i < 8; i++) {
- if (trx->ts[i].pchan == GSM_PCHAN_SDCCH8_SACCH8C_CBCH) {
- lchan = &trx->ts[i].lchan[2];
- break;
- }
- }
- }
-
- return lchan;
-}
-
-/* determine logical channel based on TRX and channel number IE */
-struct gsm_lchan *rsl_lchan_lookup(struct gsm_bts_trx *trx, uint8_t chan_nr,
- int *rc)
-{
- uint8_t ts_nr = chan_nr & 0x07;
- uint8_t cbits = chan_nr >> 3;
- uint8_t lch_idx;
- struct gsm_bts_trx_ts *ts = &trx->ts[ts_nr];
- bool ok = true;
-
- if (rc)
- *rc = -EINVAL;
-
- if (cbits == 0x01) {
- lch_idx = 0; /* TCH/F */
- if (ts->pchan != GSM_PCHAN_TCH_F &&
- ts->pchan != GSM_PCHAN_PDCH &&
- ts->pchan != GSM_PCHAN_TCH_F_PDCH
- && !(ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH
- && (ts->dyn.pchan_is == GSM_PCHAN_TCH_F
- || ts->dyn.pchan_want == GSM_PCHAN_TCH_F)))
- ok = false;
- } else if ((cbits & 0x1e) == 0x02) {
- lch_idx = cbits & 0x1; /* TCH/H */
- if (ts->pchan != GSM_PCHAN_TCH_H
- && !(ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH
- && (ts->dyn.pchan_is == GSM_PCHAN_TCH_H
- || ts->dyn.pchan_want == GSM_PCHAN_TCH_H)))
- ok = false;
- } else if ((cbits & 0x1c) == 0x04) {
- lch_idx = cbits & 0x3; /* SDCCH/4 */
- if (ts->pchan != GSM_PCHAN_CCCH_SDCCH4 &&
- ts->pchan != GSM_PCHAN_CCCH_SDCCH4_CBCH)
- ok = false;
- } else if ((cbits & 0x18) == 0x08) {
- lch_idx = cbits & 0x7; /* SDCCH/8 */
- if (ts->pchan != GSM_PCHAN_SDCCH8_SACCH8C &&
- ts->pchan != GSM_PCHAN_SDCCH8_SACCH8C_CBCH)
- ok = false;
- } else if (cbits == 0x10 || cbits == 0x11 || cbits == 0x12) {
- lch_idx = 0;
- if (ts->pchan != GSM_PCHAN_CCCH &&
- ts->pchan != GSM_PCHAN_CCCH_SDCCH4 &&
- ts->pchan != GSM_PCHAN_CCCH_SDCCH4_CBCH)
- ok = false;
- /* FIXME: we should not return first sdcch4 !!! */
- } else if ((chan_nr & RSL_CHAN_NR_MASK) == RSL_CHAN_OSMO_PDCH) {
- lch_idx = 0;
- if (ts->pchan != GSM_PCHAN_TCH_F_TCH_H_PDCH)
- ok = false;
- } else
- return NULL;
-
- if (rc && ok)
- *rc = 0;
-
- return &ts->lchan[lch_idx];
-}
-
-static const uint8_t subslots_per_pchan[] = {
- [GSM_PCHAN_NONE] = 0,
- [GSM_PCHAN_CCCH] = 0,
- [GSM_PCHAN_PDCH] = 0,
- [GSM_PCHAN_CCCH_SDCCH4] = 4,
- [GSM_PCHAN_TCH_F] = 1,
- [GSM_PCHAN_TCH_H] = 2,
- [GSM_PCHAN_SDCCH8_SACCH8C] = 8,
- [GSM_PCHAN_CCCH_SDCCH4_CBCH] = 4,
- [GSM_PCHAN_SDCCH8_SACCH8C_CBCH] = 8,
- /*
- * GSM_PCHAN_TCH_F_PDCH and GSM_PCHAN_TCH_F_TCH_H_PDCH should not be
- * part of this, those TS are handled according to their dynamic state.
- */
-};
-
-/*! Return the actual pchan type, also heeding dynamic TS. */
-enum gsm_phys_chan_config ts_pchan(struct gsm_bts_trx_ts *ts)
-{
- switch (ts->pchan) {
- case GSM_PCHAN_TCH_F_TCH_H_PDCH:
- return ts->dyn.pchan_is;
- case GSM_PCHAN_TCH_F_PDCH:
- if (ts->flags & TS_F_PDCH_ACTIVE)
- return GSM_PCHAN_PDCH;
- else
- return GSM_PCHAN_TCH_F;
- default:
- return ts->pchan;
- }
-}
-
-/*! According to ts->pchan and possibly ts->dyn_pchan, return the number of
- * logical channels available in the timeslot. */
-uint8_t ts_subslots(struct gsm_bts_trx_ts *ts)
-{
- return subslots_per_pchan[ts_pchan(ts)];
-}
-
-static bool pchan_is_tch(enum gsm_phys_chan_config pchan)
-{
- switch (pchan) {
- case GSM_PCHAN_TCH_F:
- case GSM_PCHAN_TCH_H:
- return true;
- default:
- return false;
- }
-}
-
-bool ts_is_tch(struct gsm_bts_trx_ts *ts)
-{
- return pchan_is_tch(ts_pchan(ts));
-}
diff --git a/openbsc/src/libcommon/gsm_subscriber_base.c b/openbsc/src/libcommon/gsm_subscriber_base.c
deleted file mode 100644
index 1ecdee5a5..000000000
--- a/openbsc/src/libcommon/gsm_subscriber_base.c
+++ /dev/null
@@ -1,163 +0,0 @@
-/* The concept of a subscriber as seen by the BSC */
-
-/* (C) 2008 by Harald Welte <laforge@gnumonks.org>
- * (C) 2009-2010 by Holger Hans Peter Freyther <zecke@selfish.org>
- * (C) 2010 by On-Waves
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <unistd.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <assert.h>
-
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/utils.h>
-#include <openbsc/gsm_subscriber.h>
-#include <openbsc/debug.h>
-
-LLIST_HEAD(active_subscribers);
-void *tall_subscr_ctx;
-
-/* for the gsm_subscriber.c */
-struct llist_head *subscr_bsc_active_subscribers(void)
-{
- return &active_subscribers;
-}
-
-
-char *subscr_name(struct gsm_subscriber *subscr)
-{
- if (!subscr)
- return "unknown";
-
- if (strlen(subscr->name))
- return subscr->name;
-
- return subscr->imsi;
-}
-
-struct gsm_subscriber *subscr_alloc(void)
-{
- struct gsm_subscriber *s;
-
- s = talloc_zero(tall_subscr_ctx, struct gsm_subscriber);
- if (!s)
- return NULL;
-
- llist_add_tail(&s->entry, &active_subscribers);
- s->use_count = 1;
- s->tmsi = GSM_RESERVED_TMSI;
-
- INIT_LLIST_HEAD(&s->requests);
-
- return s;
-}
-
-static void subscr_free(struct gsm_subscriber *subscr)
-{
- llist_del(&subscr->entry);
- talloc_free(subscr);
-}
-
-void subscr_direct_free(struct gsm_subscriber *subscr)
-{
- OSMO_ASSERT(subscr->use_count == 1);
- subscr_free(subscr);
-}
-
-struct gsm_subscriber *subscr_get(struct gsm_subscriber *subscr)
-{
- subscr->use_count++;
- DEBUGP(DREF, "subscr %s usage increases usage to: %d\n",
- subscr->extension, subscr->use_count);
- return subscr;
-}
-
-struct gsm_subscriber *subscr_put(struct gsm_subscriber *subscr)
-{
- subscr->use_count--;
- DEBUGP(DREF, "subscr %s usage decreased usage to: %d\n",
- subscr->extension, subscr->use_count);
- if (subscr->use_count <= 0 &&
- !((subscr->group && subscr->group->keep_subscr) ||
- subscr->keep_in_ram))
- subscr_free(subscr);
- return NULL;
-}
-
-struct gsm_subscriber *subscr_get_or_create(struct gsm_subscriber_group *sgrp,
- const char *imsi)
-{
- struct gsm_subscriber *subscr;
-
- llist_for_each_entry(subscr, subscr_bsc_active_subscribers(), entry) {
- if (strcmp(subscr->imsi, imsi) == 0 && subscr->group == sgrp)
- return subscr_get(subscr);
- }
-
- subscr = subscr_alloc();
- if (!subscr)
- return NULL;
-
- osmo_strlcpy(subscr->imsi, imsi, sizeof(subscr->imsi));
- subscr->group = sgrp;
- return subscr;
-}
-
-struct gsm_subscriber *subscr_active_by_tmsi(struct gsm_subscriber_group *sgrp,
- uint32_t tmsi)
-{
- struct gsm_subscriber *subscr;
-
- llist_for_each_entry(subscr, subscr_bsc_active_subscribers(), entry) {
- if (subscr->tmsi == tmsi && subscr->group == sgrp)
- return subscr_get(subscr);
- }
-
- return NULL;
-}
-
-struct gsm_subscriber *subscr_active_by_imsi(struct gsm_subscriber_group *sgrp,
- const char *imsi)
-{
- struct gsm_subscriber *subscr;
-
- llist_for_each_entry(subscr, subscr_bsc_active_subscribers(), entry) {
- if (strcmp(subscr->imsi, imsi) == 0 && subscr->group == sgrp)
- return subscr_get(subscr);
- }
-
- return NULL;
-}
-
-int subscr_purge_inactive(struct gsm_subscriber_group *sgrp)
-{
- struct gsm_subscriber *subscr, *tmp;
- int purged = 0;
-
- llist_for_each_entry_safe(subscr, tmp, subscr_bsc_active_subscribers(), entry) {
- if (subscr->group == sgrp && subscr->use_count <= 0) {
- subscr_free(subscr);
- purged += 1;
- }
- }
-
- return purged;
-}
diff --git a/openbsc/src/libcommon/gsup_client.c b/openbsc/src/libcommon/gsup_client.c
deleted file mode 100644
index de00d8d4f..000000000
--- a/openbsc/src/libcommon/gsup_client.c
+++ /dev/null
@@ -1,341 +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_INFO, "GSUP connecting to %s:%d\n",
- gsupc->link->addr, gsupc->link->port);
- return 0;
- }
-
- LOGP(DLGSUP, LOGL_INFO, "GSUP failed to connect to %s:%d: %s\n",
- gsupc->link->addr, gsupc->link->port, strerror(-rc));
-
- if (rc == -EBADF || rc == -ENOTSOCK || rc == -EAFNOSUPPORT ||
- rc == -EINVAL)
- return rc;
-
- osmo_timer_schedule(&gsupc->connect_timer,
- 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;
- static struct ipaccess_unit ipa_dev = {
- .unit_name = "SGSN"
- };
-
- msg->l2h = &hh->data[0];
-
- rc = ipaccess_bts_handle_ccm(link, &ipa_dev, msg);
-
- if (rc < 0) {
- LOGP(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 *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);
-
- /* 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) {
- msgb_free(msg);
- return -ENOTCONN;
- }
-
- if (!gsupc->is_connected) {
- msgb_free(msg);
- return -EAGAIN;
- }
-
- 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/openbsc/src/libcommon/gsup_test_client.c b/openbsc/src/libcommon/gsup_test_client.c
deleted file mode 100644
index 8fc38d60d..000000000
--- a/openbsc/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 = 2222;
-
- osmo_init_logging(&gsup_test_client_log_info);
-
- g_gc = gsup_client_create(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/openbsc/src/libcommon/oap_client.c b/openbsc/src/libcommon/oap_client.c
deleted file mode 100644
index 5128ac119..000000000
--- a/openbsc/src/libcommon/oap_client.c
+++ /dev/null
@@ -1,280 +0,0 @@
-/* Osmocom Authentication Protocol API */
-
-/* (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 <errno.h>
-
-#include <osmocom/core/utils.h>
-#include <osmocom/crypt/auth.h>
-#include <osmocom/gsm/oap.h>
-
-#include <openbsc/oap_client.h>
-#include <openbsc/debug.h>
-
-int oap_client_init(struct oap_client_config *config,
- struct oap_client_state *state)
-{
- OSMO_ASSERT(state->state == OAP_UNINITIALIZED);
-
- if (!config)
- goto disable;
-
- if (config->client_id == 0)
- goto disable;
-
- if (config->secret_k_present == 0) {
- LOGP(DLOAP, LOGL_NOTICE, "OAP: client ID set, but secret K missing.\n");
- goto disable;
- }
-
- if (config->secret_opc_present == 0) {
- LOGP(DLOAP, LOGL_NOTICE, "OAP: client ID set, but secret OPC missing.\n");
- goto disable;
- }
-
- state->client_id = config->client_id;
- memcpy(state->secret_k, config->secret_k, sizeof(state->secret_k));
- memcpy(state->secret_opc, config->secret_opc, sizeof(state->secret_opc));
- state->state = OAP_INITIALIZED;
- return 0;
-
-disable:
- state->state = OAP_DISABLED;
- return 0;
-}
-
-/* From the given state and received RAND and AUTN octets, validate the
- * server's authenticity and formulate the matching milenage reply octets in
- * *tx_xres. The state is not modified.
- * On success, and if tx_res is not NULL, exactly 8 octets will be written to
- * *tx_res. If not NULL, tx_res must point at allocated memory of at least 8
- * octets. The caller will want to send XRES back to the server in a challenge
- * response message and update the state.
- * Return 0 on success; -1 if OAP is disabled; -2 if rx_random and rx_autn fail
- * the authentication check; -3 for any other errors. */
-static int oap_evaluate_challenge(const struct oap_client_state *state,
- const uint8_t *rx_random,
- const uint8_t *rx_autn,
- uint8_t *tx_xres)
-{
- struct osmo_auth_vector vec;
-
- struct osmo_sub_auth_data auth = {
- .type = OSMO_AUTH_TYPE_UMTS,
- .algo = OSMO_AUTH_ALG_MILENAGE,
- };
-
- osmo_static_assert(sizeof(((struct osmo_sub_auth_data*)0)->u.umts.k)
- == sizeof(state->secret_k), _secret_k_size_match);
- osmo_static_assert(sizeof(((struct osmo_sub_auth_data*)0)->u.umts.opc)
- == sizeof(state->secret_opc), _secret_opc_size_match);
-
- switch (state->state) {
- case OAP_UNINITIALIZED:
- case OAP_DISABLED:
- return -1;
- default:
- break;
- }
-
- memcpy(auth.u.umts.k, state->secret_k, sizeof(auth.u.umts.k));
- memcpy(auth.u.umts.opc, state->secret_opc, sizeof(auth.u.umts.opc));
- memset(auth.u.umts.amf, '\0', sizeof(auth.u.umts.amf));
- auth.u.umts.sqn = 41; /* TODO use incrementing sequence nr */
-
- memset(&vec, 0, sizeof(vec));
- osmo_auth_gen_vec(&vec, &auth, rx_random);
-
- if (vec.res_len != 8) {
- LOGP(DLOAP, LOGL_ERROR, "OAP: Expected XRES to be 8 octets, got %d\n",
- vec.res_len);
- return -3;
- }
-
- if (osmo_constant_time_cmp(vec.autn, rx_autn, sizeof(vec.autn)) != 0) {
- LOGP(DLOAP, LOGL_ERROR, "OAP: AUTN mismatch!\n");
- LOGP(DLOAP, LOGL_INFO, "OAP: AUTN from server: %s\n",
- osmo_hexdump_nospc(rx_autn, sizeof(vec.autn)));
- LOGP(DLOAP, LOGL_INFO, "OAP: AUTN expected: %s\n",
- osmo_hexdump_nospc(vec.autn, sizeof(vec.autn)));
- return -2;
- }
-
- if (tx_xres != NULL)
- memcpy(tx_xres, vec.res, 8);
- return 0;
-}
-
-struct msgb *oap_client_encoded(const struct osmo_oap_message *oap_msg)
-{
- struct msgb *msg = msgb_alloc_headroom(1000, 64, __func__);
- OSMO_ASSERT(msg);
- osmo_oap_encode(msg, oap_msg);
- return msg;
-}
-
-/* Create a new msgb containing an OAP registration message.
- * On error, return NULL. */
-static struct msgb* oap_msg_register(uint16_t client_id)
-{
- struct osmo_oap_message oap_msg = {0};
-
- if (client_id < 1) {
- LOGP(DLOAP, LOGL_ERROR, "OAP: Invalid client ID: %d\n", client_id);
- return NULL;
- }
-
- oap_msg.message_type = OAP_MSGT_REGISTER_REQUEST;
- oap_msg.client_id = client_id;
- return oap_client_encoded(&oap_msg);
-}
-
-int oap_client_register(struct oap_client_state *state, struct msgb **msg_tx)
-{
- *msg_tx = oap_msg_register(state->client_id);
- if (!(*msg_tx))
- return -1;
-
- state->state = OAP_REQUESTED_CHALLENGE;
- return 0;
-}
-
-/* Create a new msgb containing an OAP challenge response message.
- * xres must point at 8 octets to return as challenge response.
- * On error, return NULL. */
-static struct msgb* oap_msg_challenge_response(uint8_t *xres)
-{
- struct osmo_oap_message oap_reply = {0};
-
- oap_reply.message_type = OAP_MSGT_CHALLENGE_RESULT;
- memcpy(oap_reply.xres, xres, sizeof(oap_reply.xres));
- oap_reply.xres_present = 1;
- return oap_client_encoded(&oap_reply);
-}
-
-static int handle_challenge(struct oap_client_state *state,
- struct osmo_oap_message *oap_rx,
- struct msgb **msg_tx)
-{
- int rc;
- uint8_t xres[8];
-
- if (!(oap_rx->rand_present && oap_rx->autn_present)) {
- LOGP(DLOAP, LOGL_ERROR,
- "OAP challenge incomplete (rand_present: %d, autn_present: %d)\n",
- oap_rx->rand_present, oap_rx->autn_present);
- rc = -2;
- goto failure;
- }
-
- rc = oap_evaluate_challenge(state,
- oap_rx->rand,
- oap_rx->autn,
- xres);
- if (rc < 0)
- goto failure;
-
- *msg_tx = oap_msg_challenge_response(xres);
- if ((*msg_tx) == NULL) {
- rc = -1;
- goto failure;
- }
-
- state->state = OAP_SENT_CHALLENGE_RESULT;
- return 0;
-
-failure:
- OSMO_ASSERT(rc < 0);
- state->state = OAP_INITIALIZED;
- return rc;
-}
-
-int oap_client_handle(struct oap_client_state *state,
- const struct msgb *msg_rx, struct msgb **msg_tx)
-{
- uint8_t *data = msgb_l2(msg_rx);
- size_t data_len = msgb_l2len(msg_rx);
- struct osmo_oap_message oap_msg = {0};
- int rc = 0;
-
- *msg_tx = NULL;
-
- OSMO_ASSERT(data);
-
- rc = osmo_oap_decode(&oap_msg, data, data_len);
- if (rc < 0) {
- LOGP(DLOAP, LOGL_ERROR,
- "Decoding OAP message failed with error '%s' (%d)\n",
- get_value_string(gsm48_gmm_cause_names, -rc), -rc);
- return -10;
- }
-
- switch (state->state) {
- case OAP_UNINITIALIZED:
- LOGP(DLOAP, LOGL_ERROR,
- "Received OAP message %d, but the OAP client is"
- " not initialized\n", oap_msg.message_type);
- return -ENOTCONN;
- case OAP_DISABLED:
- LOGP(DLOAP, LOGL_ERROR,
- "Received OAP message %d, but the OAP client is"
- " disabled\n", oap_msg.message_type);
- return -ENOTCONN;
- default:
- break;
- }
-
- switch (oap_msg.message_type) {
- case OAP_MSGT_CHALLENGE_REQUEST:
- return handle_challenge(state, &oap_msg, msg_tx);
-
- case OAP_MSGT_REGISTER_RESULT:
- /* successfully registered */
- state->state = OAP_REGISTERED;
- break;
-
- case OAP_MSGT_REGISTER_ERROR:
- LOGP(DLOAP, LOGL_ERROR,
- "OAP registration failed\n");
- state->state = OAP_INITIALIZED;
- if (state->registration_failures < 3) {
- state->registration_failures ++;
- return oap_client_register(state, msg_tx);
- }
- return -11;
-
- case OAP_MSGT_REGISTER_REQUEST:
- case OAP_MSGT_CHALLENGE_RESULT:
- LOGP(DLOAP, LOGL_ERROR,
- "Received invalid OAP message type for OAP client side: %d\n",
- (int)oap_msg.message_type);
- return -12;
-
- default:
- LOGP(DLOAP, LOGL_ERROR,
- "Unknown OAP message type: %d\n",
- (int)oap_msg.message_type);
- return -13;
- }
-
- return 0;
-}
diff --git a/openbsc/src/libcommon/socket.c b/openbsc/src/libcommon/socket.c
deleted file mode 100644
index 2a64767f8..000000000
--- a/openbsc/src/libcommon/socket.c
+++ /dev/null
@@ -1,111 +0,0 @@
-/* OpenBSC sokcet code, taken from Abis input driver for ip.access */
-
-/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
- * (C) 2010 by Holger Hans Peter Freyther
- * (C) 2010 by On-Waves
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <stdio.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <string.h>
-#include <time.h>
-#include <sys/fcntl.h>
-#include <sys/socket.h>
-#include <sys/ioctl.h>
-#include <arpa/inet.h>
-
-#include <osmocom/core/select.h>
-#include <osmocom/gsm/tlv.h>
-#include <osmocom/core/msgb.h>
-#include <openbsc/debug.h>
-#include <openbsc/gsm_data.h>
-#include <osmocom/core/talloc.h>
-
-int make_sock(struct osmo_fd *bfd, int proto,
- uint32_t ip, uint16_t port, int priv_nr,
- int (*cb)(struct osmo_fd *fd, unsigned int what), void *data)
-{
- struct sockaddr_in addr;
- int ret, on = 1;
- int type = SOCK_STREAM;
-
- switch (proto) {
- case IPPROTO_TCP:
- type = SOCK_STREAM;
- break;
- case IPPROTO_UDP:
- type = SOCK_DGRAM;
- break;
-#ifdef IPPROTO_GRE
- case IPPROTO_GRE:
- type = SOCK_RAW;
- break;
-#endif
- default:
- return -EINVAL;
- }
-
- bfd->fd = socket(AF_INET, type, proto);
- bfd->cb = cb;
- bfd->when = BSC_FD_READ;
- bfd->data = data;
- bfd->priv_nr = priv_nr;
-
- if (bfd->fd < 0) {
- LOGP(DLINP, LOGL_ERROR, "could not create socket.\n");
- return -EIO;
- }
-
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_port = htons(port);
- if (ip != INADDR_ANY)
- addr.sin_addr.s_addr = htonl(ip);
- else
- addr.sin_addr.s_addr = INADDR_ANY;
-
- setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
-
- ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr));
- if (ret < 0) {
- LOGP(DLINP, LOGL_ERROR, "could not bind socket %s\n",
- strerror(errno));
- close(bfd->fd);
- return -EIO;
- }
-
- if (proto == IPPROTO_TCP) {
- ret = listen(bfd->fd, 1);
- if (ret < 0) {
- perror("listen");
- close(bfd->fd);
- return ret;
- }
- }
-
- ret = osmo_fd_register(bfd);
- if (ret < 0) {
- perror("register_listen_fd");
- close(bfd->fd);
- return ret;
- }
- return 0;
-}
diff --git a/openbsc/src/libcommon/talloc_ctx.c b/openbsc/src/libcommon/talloc_ctx.c
deleted file mode 100644
index 5e3d9aebe..000000000
--- a/openbsc/src/libcommon/talloc_ctx.c
+++ /dev/null
@@ -1,56 +0,0 @@
-/* OpenBSC allocation contexts initialization code */
-/* (C) 2011-2016 by sysmocom s.f.m.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/talloc.h>
-#include <osmocom/core/msgb.h>
-
-extern void *tall_bsc_ctx;
-extern void *tall_fle_ctx;
-extern void *tall_locop_ctx;
-extern void *tall_authciphop_ctx;
-extern void *tall_gsms_ctx;
-extern void *tall_subscr_ctx;
-extern void *tall_sub_req_ctx;
-extern void *tall_call_ctx;
-extern void *tall_paging_ctx;
-extern void *tall_sigh_ctx;
-extern void *tall_tqe_ctx;
-extern void *tall_trans_ctx;
-extern void *tall_map_ctx;
-extern void *tall_upq_ctx;
-extern void *tall_ctr_ctx;
-
-void talloc_ctx_init(void *ctx_root)
-{
- msgb_talloc_ctx_init(ctx_root, 0);
- tall_fle_ctx = talloc_named_const(ctx_root, 0, "bs11_file_list_entry");
- tall_locop_ctx = talloc_named_const(ctx_root, 0, "loc_updating_oper");
- tall_authciphop_ctx = talloc_named_const(ctx_root, 0, "auth_ciph_oper");
- tall_gsms_ctx = talloc_named_const(ctx_root, 0, "sms");
- tall_subscr_ctx = talloc_named_const(ctx_root, 0, "subscriber");
- tall_sub_req_ctx = talloc_named_const(ctx_root, 0, "subscr_request");
- tall_call_ctx = talloc_named_const(ctx_root, 0, "gsm_call");
- tall_paging_ctx = talloc_named_const(ctx_root, 0, "paging_request");
- tall_sigh_ctx = talloc_named_const(ctx_root, 0, "signal_handler");
- tall_tqe_ctx = talloc_named_const(ctx_root, 0, "subch_txq_entry");
- tall_trans_ctx = talloc_named_const(ctx_root, 0, "transaction");
- tall_map_ctx = talloc_named_const(ctx_root, 0, "trau_map_entry");
- tall_upq_ctx = talloc_named_const(ctx_root, 0, "trau_upq_entry");
- tall_ctr_ctx = talloc_named_const(ctx_root, 0, "counter");
-}
diff --git a/openbsc/src/libfilter/Makefile.am b/openbsc/src/libfilter/Makefile.am
deleted file mode 100644
index 6d3db0b90..000000000
--- a/openbsc/src/libfilter/Makefile.am
+++ /dev/null
@@ -1,26 +0,0 @@
-AM_CPPFLAGS = \
- $(all_includes) \
- -I$(top_srcdir)/include \
- -I$(top_builddir) \
- $(NULL)
-
-AM_CFLAGS = \
- -Wall \
- $(LIBOSMOCORE_CFLAGS) \
- $(LIBOSMOGSM_CFLAGS) \
- $(LIBOSMOVTY_CFLAGS) \
- $(LIBOSMOABIS_CFLAGS) \
- $(LIBOSMOSCCP_CFLAGS) \
- $(COVERAGE_CFLAGS) \
- $(NULL)
-
-noinst_LIBRARIES = \
- libfilter.a \
- $(NULL)
-
-libfilter_a_SOURCES = \
- bsc_msg_filter.c \
- bsc_msg_acc.c \
- bsc_msg_vty.c \
- $(NULL)
-
diff --git a/openbsc/src/libfilter/bsc_msg_acc.c b/openbsc/src/libfilter/bsc_msg_acc.c
deleted file mode 100644
index bfc5bdd3f..000000000
--- a/openbsc/src/libfilter/bsc_msg_acc.c
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * (C) 2010-2015 by Holger Hans Peter Freyther <zecke@selfish.org>
- * (C) 2010-2011 by On-Waves
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <openbsc/bsc_msg_filter.h>
-#include <openbsc/bsc_nat.h>
-
-#include <osmocom/core/rate_ctr.h>
-#include <osmocom/core/stats.h>
-
-#include <string.h>
-
-static const struct rate_ctr_desc acc_list_ctr_description[] = {
- [ACC_LIST_LOCAL_FILTER] = { "access-list.local-filter", "Rejected by rule for local"},
- [ACC_LIST_GLOBAL_FILTER]= { "access-list.global-filter", "Rejected by rule for global"},
-};
-
-static const struct rate_ctr_group_desc bsc_cfg_acc_list_desc = {
- .group_name_prefix = "nat.filter",
- .group_description = "NAT Access-List Statistics",
- .num_ctr = ARRAY_SIZE(acc_list_ctr_description),
- .ctr_desc = acc_list_ctr_description,
- .class_id = OSMO_STATS_CLASS_GLOBAL,
-};
-
-
-int bsc_msg_acc_lst_check_allow(struct bsc_msg_acc_lst *lst, const char *mi_string)
-{
- struct bsc_msg_acc_lst_entry *entry;
-
- llist_for_each_entry(entry, &lst->fltr_list, list) {
- if (!entry->imsi_allow)
- continue;
- if (regexec(&entry->imsi_allow_re, mi_string, 0, NULL, 0) == 0)
- return 0;
- }
-
- return 1;
-}
-
-struct bsc_msg_acc_lst *bsc_msg_acc_lst_find(struct llist_head *head, const char *name)
-{
- struct bsc_msg_acc_lst *lst;
-
- if (!name)
- return NULL;
-
- llist_for_each_entry(lst, head, list)
- if (strcmp(lst->name, name) == 0)
- return lst;
-
- return NULL;
-}
-
-struct bsc_msg_acc_lst *bsc_msg_acc_lst_get(void *ctx, struct llist_head *head, const char *name)
-{
- struct bsc_msg_acc_lst *lst;
-
- lst = bsc_msg_acc_lst_find(head, name);
- if (lst)
- return lst;
-
- lst = talloc_zero(ctx, struct bsc_msg_acc_lst);
- if (!lst) {
- LOGP(DNAT, LOGL_ERROR, "Failed to allocate access list");
- return NULL;
- }
-
- /* TODO: get the index right */
- lst->stats = rate_ctr_group_alloc(lst, &bsc_cfg_acc_list_desc, 0);
- if (!lst->stats) {
- talloc_free(lst);
- return NULL;
- }
-
- INIT_LLIST_HEAD(&lst->fltr_list);
- lst->name = talloc_strdup(lst, name);
- llist_add_tail(&lst->list, head);
- return lst;
-}
-
-void bsc_msg_acc_lst_delete(struct bsc_msg_acc_lst *lst)
-{
- llist_del(&lst->list);
- rate_ctr_group_free(lst->stats);
- talloc_free(lst);
-}
-
-struct bsc_msg_acc_lst_entry *bsc_msg_acc_lst_entry_create(struct bsc_msg_acc_lst *lst)
-{
- struct bsc_msg_acc_lst_entry *entry;
-
- entry = talloc_zero(lst, struct bsc_msg_acc_lst_entry);
- if (!entry)
- return NULL;
-
- entry->cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED;
- entry->lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED;
- llist_add_tail(&entry->list, &lst->fltr_list);
- return entry;
-}
-
diff --git a/openbsc/src/libfilter/bsc_msg_filter.c b/openbsc/src/libfilter/bsc_msg_filter.c
deleted file mode 100644
index 115d376cb..000000000
--- a/openbsc/src/libfilter/bsc_msg_filter.c
+++ /dev/null
@@ -1,398 +0,0 @@
-/*
- * Access filtering
- */
-/*
- * (C) 2010-2015 by Holger Hans Peter Freyther <zecke@selfish.org>
- * (C) 2010-2012 by On-Waves
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <openbsc/bsc_msg_filter.h>
-
-#include <openbsc/bsc_nat.h>
-#include <openbsc/bsc_msc.h>
-#include <openbsc/gsm_data.h>
-#include <openbsc/debug.h>
-#include <openbsc/ipaccess.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>
-
-int bsc_filter_barr_find(struct rb_root *root, const char *imsi, int *cm, int *lu)
-{
- struct bsc_filter_barr_entry *n;
- n = rb_entry(root->rb_node, struct bsc_filter_barr_entry, node);
-
- while (n) {
- int rc = strcmp(imsi, n->imsi);
- if (rc == 0) {
- *cm = n->cm_reject_cause;
- *lu = n->lu_reject_cause;
- return 1;
- }
-
- n = rb_entry(
- (rc < 0) ? n->node.rb_left : n->node.rb_right,
- struct bsc_filter_barr_entry, node);
- };
-
- return 0;
-}
-
-static int insert_barr_node(struct bsc_filter_barr_entry *entry, struct rb_root *root)
-{
- struct rb_node **new = &root->rb_node, *parent = NULL;
-
- while (*new) {
- int rc;
- struct bsc_filter_barr_entry *this;
- this = rb_entry(*new, struct bsc_filter_barr_entry, node);
- parent = *new;
-
- rc = strcmp(entry->imsi, this->imsi);
- if (rc < 0)
- new = &((*new)->rb_left);
- else if (rc > 0)
- new = &((*new)->rb_right);
- else {
- LOGP(DFILTER, LOGL_ERROR,
- "Duplicate entry for IMSI(%s)\n", entry->imsi);
- talloc_free(entry);
- return -1;
- }
- }
-
- rb_link_node(&entry->node, parent, new);
- rb_insert_color(&entry->node, root);
- return 0;
-}
-
-int bsc_filter_barr_adapt(void *ctx, struct rb_root *root,
- const struct osmo_config_list *list)
-{
- struct osmo_config_entry *cfg_entry;
- int err = 0;
-
- /* free the old data */
- while (!RB_EMPTY_ROOT(root)) {
- struct rb_node *node = rb_first(root);
- rb_erase(node, root);
- talloc_free(node);
- }
-
- if (!list)
- return 0;
-
- /* now adapt the new list */
- llist_for_each_entry(cfg_entry, &list->entry, list) {
- struct bsc_filter_barr_entry *entry;
- entry = talloc_zero(ctx, struct bsc_filter_barr_entry);
- if (!entry) {
- LOGP(DFILTER, LOGL_ERROR,
- "Allocation of the barr entry failed.\n");
- continue;
- }
-
- entry->imsi = talloc_strdup(entry, cfg_entry->mcc);
- entry->cm_reject_cause = atoi(cfg_entry->mnc);
- entry->lu_reject_cause = atoi(cfg_entry->option);
- err |= insert_barr_node(entry, root);
- }
-
- return err;
-}
-
-
-static int lst_check_deny(struct bsc_msg_acc_lst *lst, const char *mi_string,
- int *cm_cause, int *lu_cause)
-{
- struct bsc_msg_acc_lst_entry *entry;
-
- llist_for_each_entry(entry, &lst->fltr_list, list) {
- if (!entry->imsi_deny)
- continue;
- if (regexec(&entry->imsi_deny_re, mi_string, 0, NULL, 0) == 0) {
- *cm_cause = entry->cm_reject_cause;
- *lu_cause = entry->lu_reject_cause;
- return 0;
- }
- }
-
- return 1;
-}
-
-/* apply white/black list */
-static int auth_imsi(struct bsc_filter_request *req,
- const char *imsi,
- struct bsc_filter_reject_cause *cause)
-{
- /*
- * Now apply blacklist/whitelist of the BSC and the NAT.
- * 1.) Check the global IMSI barr list
- * 2.) Allow directly if the IMSI is allowed at the BSC
- * 3.) Reject if the IMSI is not allowed at the BSC
- * 4.) Reject if the IMSI not allowed at the global level.
- * 5.) Allow directly if the IMSI is allowed at the global level
- */
- int cm, lu;
- struct bsc_msg_acc_lst *nat_lst = NULL;
- struct bsc_msg_acc_lst *bsc_lst = NULL;
-
- /* 1. global check for barred imsis */
- if (req->black_list && bsc_filter_barr_find(req->black_list, imsi, &cm, &lu)) {
- cause->cm_reject_cause = cm;
- cause->lu_reject_cause = lu;
- LOGP(DFILTER, LOGL_DEBUG,
- "Blocking subscriber IMSI %s with CM: %d LU: %d\n",
- imsi, cm, lu);
- return -4;
- }
-
-
- bsc_lst = bsc_msg_acc_lst_find(req->access_lists, req->local_lst_name);
- nat_lst = bsc_msg_acc_lst_find(req->access_lists, req->global_lst_name);
-
-
- if (bsc_lst) {
- /* 2. BSC allow */
- if (bsc_msg_acc_lst_check_allow(bsc_lst, imsi) == 0)
- return 1;
-
- /* 3. BSC deny */
- if (lst_check_deny(bsc_lst, imsi, &cm, &lu) == 0) {
- LOGP(DFILTER, LOGL_ERROR,
- "Filtering %s by imsi_deny on config nr: %d.\n", imsi, req->bsc_nr);
- rate_ctr_inc(&bsc_lst->stats->ctr[ACC_LIST_LOCAL_FILTER]);
- cause->cm_reject_cause = cm;
- cause->lu_reject_cause = lu;
- return -2;
- }
-
- }
-
- /* 4. NAT deny */
- if (nat_lst) {
- if (lst_check_deny(nat_lst, imsi, &cm, &lu) == 0) {
- LOGP(DFILTER, LOGL_ERROR,
- "Filtering %s global imsi_deny on bsc nr: %d.\n", imsi, req->bsc_nr);
- rate_ctr_inc(&nat_lst->stats->ctr[ACC_LIST_GLOBAL_FILTER]);
- cause->cm_reject_cause = cm;
- cause->lu_reject_cause = lu;
- return -3;
- }
- }
-
- return 1;
-}
-
-static int _cr_check_loc_upd(void *ctx,
- uint8_t *data, unsigned int length,
- char **imsi)
-{
- uint8_t mi_type;
- struct gsm48_loc_upd_req *lu;
- char mi_string[GSM48_MI_SIZE];
-
- if (length < sizeof(*lu)) {
- LOGP(DFILTER, LOGL_ERROR,
- "LU does not fit. Length is %d \n", length);
- return -1;
- }
-
- lu = (struct gsm48_loc_upd_req *) data;
- mi_type = lu->mi[0] & GSM_MI_TYPE_MASK;
-
- /*
- * We can only deal with the IMSI. This will fail for a phone that
- * will send the TMSI of a previous network to us.
- */
- if (mi_type != GSM_MI_TYPE_IMSI)
- return 0;
-
- gsm48_mi_to_string(mi_string, sizeof(mi_string), lu->mi, lu->mi_len);
- *imsi = talloc_strdup(ctx, mi_string);
- return 1;
-}
-
-static int _cr_check_cm_serv_req(void *ctx,
- uint8_t *data, unsigned int length,
- int *con_type, char **imsi)
-{
- static const uint32_t classmark_offset =
- offsetof(struct gsm48_service_request, classmark);
-
- char mi_string[GSM48_MI_SIZE];
- uint8_t mi_type;
- int rc;
- struct gsm48_service_request *req;
-
- /* unfortunately in Phase1 the classmark2 length is variable */
-
- if (length < sizeof(*req)) {
- LOGP(DFILTER, LOGL_ERROR,
- "CM Serv Req does not fit. Length is %d\n", length);
- return -1;
- }
-
- req = (struct gsm48_service_request *) data;
- if (req->cm_service_type == 0x8)
- *con_type = FLT_CON_TYPE_SSA;
- rc = gsm48_extract_mi((uint8_t *) &req->classmark,
- length - classmark_offset, mi_string, &mi_type);
- if (rc < 0) {
- LOGP(DFILTER, LOGL_ERROR, "Failed to parse the classmark2/mi. error: %d\n", rc);
- return -1;
- }
-
- /* we have to let the TMSI or such pass */
- if (mi_type != GSM_MI_TYPE_IMSI)
- return 0;
-
- *imsi = talloc_strdup(ctx, mi_string);
- return 1;
-}
-
-static int _cr_check_pag_resp(void *ctx,
- uint8_t *data, unsigned int length, char **imsi)
-{
- struct gsm48_pag_resp *resp;
- char mi_string[GSM48_MI_SIZE];
- uint8_t mi_type;
-
- if (length < sizeof(*resp)) {
- LOGP(DFILTER, LOGL_ERROR, "PAG RESP does not fit. Length was %d.\n", length);
- return -1;
- }
-
- resp = (struct gsm48_pag_resp *) data;
- if (gsm48_paging_extract_mi(resp, length, mi_string, &mi_type) < 0) {
- LOGP(DFILTER, LOGL_ERROR, "Failed to extract the MI.\n");
- return -1;
- }
-
- /* we need to let it pass for now */
- if (mi_type != GSM_MI_TYPE_IMSI)
- return 0;
-
- *imsi = talloc_strdup(ctx, mi_string);
- return 1;
-}
-
-static int _dt_check_id_resp(struct bsc_filter_request *req,
- uint8_t *data, unsigned int length,
- struct bsc_filter_state *state,
- struct bsc_filter_reject_cause *cause)
-{
- char mi_string[GSM48_MI_SIZE];
- uint8_t mi_type;
-
- if (length < 2) {
- LOGP(DFILTER, LOGL_ERROR, "mi does not fit.\n");
- return -1;
- }
-
- if (data[0] < length - 1) {
- LOGP(DFILTER, LOGL_ERROR, "mi length too big.\n");
- return -2;
- }
-
- mi_type = data[1] & GSM_MI_TYPE_MASK;
- gsm48_mi_to_string(mi_string, sizeof(mi_string), &data[1], data[0]);
-
- if (mi_type != GSM_MI_TYPE_IMSI)
- return 0;
-
- state->imsi_checked = 1;
- state->imsi = talloc_strdup(req->ctx, mi_string);
- return auth_imsi(req, mi_string, cause);
-}
-
-
-/* Filter out CR data... */
-int bsc_msg_filter_initial(struct gsm48_hdr *hdr48, size_t hdr48_len,
- struct bsc_filter_request *req,
- int *con_type,
- char **imsi, struct bsc_filter_reject_cause *cause)
-{
- int ret = 0;
- uint8_t msg_type, proto;
-
- *con_type = FLT_CON_TYPE_NONE;
- cause->cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED;
- cause->lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED;
- *imsi = NULL;
-
- proto = gsm48_hdr_pdisc(hdr48);
- msg_type = gsm48_hdr_msg_type(hdr48);
- if (proto == GSM48_PDISC_MM &&
- msg_type == GSM48_MT_MM_LOC_UPD_REQUEST) {
- *con_type = FLT_CON_TYPE_LU;
- ret = _cr_check_loc_upd(req->ctx, &hdr48->data[0],
- hdr48_len - sizeof(*hdr48), imsi);
- } else if (proto == GSM48_PDISC_MM &&
- msg_type == GSM48_MT_MM_CM_SERV_REQ) {
- *con_type = FLT_CON_TYPE_CM_SERV_REQ;
- ret = _cr_check_cm_serv_req(req->ctx, &hdr48->data[0],
- hdr48_len - sizeof(*hdr48),
- con_type, imsi);
- } else if (proto == GSM48_PDISC_RR &&
- msg_type == GSM48_MT_RR_PAG_RESP) {
- *con_type = FLT_CON_TYPE_PAG_RESP;
- ret = _cr_check_pag_resp(req->ctx, &hdr48->data[0],
- hdr48_len - sizeof(*hdr48), imsi);
- } else {
- /* We only want to filter the above, let other things pass */
- *con_type = FLT_CON_TYPE_OTHER;
- return 0;
- }
-
- /* check if we are done */
- if (ret != 1)
- return ret;
-
- /* the memory allocation failed */
- if (!*imsi)
- return -1;
-
- /* now check the imsi */
- return auth_imsi(req, *imsi, cause);
-}
-
-int bsc_msg_filter_data(struct gsm48_hdr *hdr48, size_t len,
- struct bsc_filter_request *req,
- struct bsc_filter_state *state,
- struct bsc_filter_reject_cause *cause)
-{
- uint8_t msg_type, proto;
-
- cause->cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED;
- cause->lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED;
-
- if (state->imsi_checked)
- return 0;
-
- proto = gsm48_hdr_pdisc(hdr48);
- msg_type = gsm48_hdr_msg_type(hdr48);
- if (proto != GSM48_PDISC_MM || msg_type != GSM48_MT_MM_ID_RESP)
- return 0;
-
- return _dt_check_id_resp(req, &hdr48->data[0],
- len - sizeof(*hdr48), state, cause);
-}
diff --git a/openbsc/src/libfilter/bsc_msg_vty.c b/openbsc/src/libfilter/bsc_msg_vty.c
deleted file mode 100644
index c342fdca0..000000000
--- a/openbsc/src/libfilter/bsc_msg_vty.c
+++ /dev/null
@@ -1,140 +0,0 @@
-/* (C) 2010-2015 by Holger Hans Peter Freyther
- * (C) 2010-2013 by On-Waves
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <openbsc/bsc_msg_filter.h>
-#include <openbsc/gsm_data.h>
-#include <openbsc/vty.h>
-
-#include <osmocom/vty/misc.h>
-
-static struct llist_head *_acc_lst;
-static void *_ctx;
-
-DEFUN(cfg_lst_no,
- cfg_lst_no_cmd,
- "no access-list NAME",
- NO_STR "Remove an access-list by name\n"
- "The access-list to remove\n")
-{
- struct bsc_msg_acc_lst *acc;
- acc = bsc_msg_acc_lst_find(_acc_lst, argv[0]);
- if (!acc)
- return CMD_WARNING;
-
- bsc_msg_acc_lst_delete(acc);
- return CMD_SUCCESS;
-}
-
-DEFUN(show_acc_lst,
- show_acc_lst_cmd,
- "show access-list NAME",
- SHOW_STR "IMSI access list\n" "Name of the access list\n")
-{
- struct bsc_msg_acc_lst *acc;
- acc = bsc_msg_acc_lst_find(_acc_lst, argv[0]);
- if (!acc)
- return CMD_WARNING;
-
- vty_out(vty, "access-list %s%s", acc->name, VTY_NEWLINE);
- vty_out_rate_ctr_group(vty, " ", acc->stats);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_lst_imsi_allow,
- cfg_lst_imsi_allow_cmd,
- "access-list NAME imsi-allow [REGEXP]",
- "Access list commands\n"
- "Name of the access list\n"
- "Add allowed IMSI to the list\n"
- "Regexp for IMSIs\n")
-{
- struct bsc_msg_acc_lst *acc;
- struct bsc_msg_acc_lst_entry *entry;
-
- acc = bsc_msg_acc_lst_get(_ctx, _acc_lst, argv[0]);
- if (!acc)
- return CMD_WARNING;
-
- entry = bsc_msg_acc_lst_entry_create(acc);
- if (!entry)
- return CMD_WARNING;
-
- if (gsm_parse_reg(acc, &entry->imsi_allow_re, &entry->imsi_allow, argc - 1, &argv[1]) != 0)
- return CMD_WARNING;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_lst_imsi_deny,
- cfg_lst_imsi_deny_cmd,
- "access-list NAME imsi-deny [REGEXP] (<0-256>) (<0-256>)",
- "Access list commands\n"
- "Name of the access list\n"
- "Add denied IMSI to the list\n"
- "Regexp for IMSIs\n"
- "CM Service Reject reason\n"
- "LU Reject reason\n")
-{
- struct bsc_msg_acc_lst *acc;
- struct bsc_msg_acc_lst_entry *entry;
-
- acc = bsc_msg_acc_lst_get(_ctx, _acc_lst, argv[0]);
- if (!acc)
- return CMD_WARNING;
-
- entry = bsc_msg_acc_lst_entry_create(acc);
- if (!entry)
- return CMD_WARNING;
-
- if (gsm_parse_reg(acc, &entry->imsi_deny_re, &entry->imsi_deny, argc - 1, &argv[1]) != 0)
- return CMD_WARNING;
- if (argc >= 3)
- entry->cm_reject_cause = atoi(argv[2]);
- if (argc >= 4)
- entry->lu_reject_cause = atoi(argv[3]);
- return CMD_SUCCESS;
-}
-
-void bsc_msg_acc_lst_write(struct vty *vty, struct bsc_msg_acc_lst *lst)
-{
- struct bsc_msg_acc_lst_entry *entry;
-
- llist_for_each_entry(entry, &lst->fltr_list, list) {
- if (entry->imsi_allow)
- vty_out(vty, " access-list %s imsi-allow %s%s",
- lst->name, entry->imsi_allow, VTY_NEWLINE);
- if (entry->imsi_deny)
- vty_out(vty, " access-list %s imsi-deny %s %d %d%s",
- lst->name, entry->imsi_deny,
- entry->cm_reject_cause, entry->lu_reject_cause,
- VTY_NEWLINE);
- }
-}
-
-void bsc_msg_lst_vty_init(void *ctx, struct llist_head *lst, int node)
-{
- _ctx = ctx;
- _acc_lst = lst;
- install_element_ve(&show_acc_lst_cmd);
-
- /* access-list */
- install_element(node, &cfg_lst_imsi_allow_cmd);
- install_element(node, &cfg_lst_imsi_deny_cmd);
- install_element(node, &cfg_lst_no_cmd);
-}
diff --git a/openbsc/src/libiu/Makefile.am b/openbsc/src/libiu/Makefile.am
deleted file mode 100644
index e5f9e2751..000000000
--- a/openbsc/src/libiu/Makefile.am
+++ /dev/null
@@ -1,28 +0,0 @@
-AM_CPPFLAGS = \
- $(all_includes) \
- -I$(top_srcdir)/include \
- -I$(top_builddir) \
- $(NULL)
-
-AM_CFLAGS = \
- -Wall \
- $(COVERAGE_CFLAGS) \
- $(LIBCRYPTO_CFLAGS) \
- $(LIBASN1C_CFLAGS) \
- $(LIBOSMOCORE_CFLAGS) \
- $(LIBOSMOVTY_CFLAGS) \
- $(LIBOSMOGSM_CFLAGS) \
- $(LIBOSMOABIS_CFLAGS) \
- $(LIBOSMOSIGTRAN_CFLAGS) \
- $(LIBOSMORANAP_CFLAGS) \
- $(NULL)
-
-noinst_LIBRARIES = \
- libiu.a \
- $(NULL)
-
-libiu_a_SOURCES = \
- iu.c \
- iu_vty.c \
- $(NULL)
-
diff --git a/openbsc/src/libiu/iu.c b/openbsc/src/libiu/iu.c
deleted file mode 100644
index 8ba6fa49d..000000000
--- a/openbsc/src/libiu/iu.c
+++ /dev/null
@@ -1,759 +0,0 @@
-/* Common parts of IuCS and IuPS interfaces implementation */
-
-/* (C) 2016 by sysmocom s.f.m.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 <stdint.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdbool.h>
-
-#include <osmocom/core/select.h>
-#include <osmocom/core/prim.h>
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/logging.h>
-#include <osmocom/core/application.h>
-#include <osmocom/core/utils.h>
-#include <osmocom/vty/logging.h>
-
-#include <osmocom/gsm/gsm48.h>
-#include <osmocom/gprs/gprs_msgb.h>
-
-#include <osmocom/sigtran/sua.h>
-#include <osmocom/sigtran/sccp_sap.h>
-#include <osmocom/sigtran/sccp_helpers.h>
-
-#include <openbsc/gprs_sgsn.h>
-#include <openbsc/iu.h>
-#include <openbsc/debug.h>
-
-#include <pdp.h>
-
-#include <osmocom/ranap/ranap_ies_defs.h>
-#include <osmocom/ranap/ranap_common.h>
-#include <osmocom/ranap/ranap_common_cn.h>
-#include <osmocom/ranap/ranap_msg_factory.h>
-
-#include <asn1c/asn1helpers.h>
-
-/* Parsed global RNC id. See also struct RANAP_GlobalRNC_ID, and note that the
- * PLMN identity is a BCD representation of the MCC and MNC.
- * See iu_grnc_id_parse(). */
-struct iu_grnc_id {
- uint16_t mcc;
- uint16_t mnc;
- uint16_t rnc_id;
-};
-
-/* A remote RNC (Radio Network Controller, like BSC but for UMTS) that has
- * called us and is currently reachable at the given osmo_sccp_link. So, when we
- * know a LAC for a subscriber, we can page it at the RNC matching that LAC or
- * RAC. An HNB-GW typically presents itself as if it were a single RNC, even
- * though it may have several RNCs in hNodeBs connected to it. Those will then
- * share the same RNC id, which they actually receive and adopt from the HNB-GW
- * in the HNBAP HNB REGISTER ACCEPT message. */
-struct iu_rnc {
- struct llist_head entry;
-
- uint16_t rnc_id;
- uint16_t lac; /* Location Area Code (used for CS and PS) */
- uint8_t rac; /* Routing Area Code (used for PS only) */
- struct osmo_sccp_link *link;
-};
-
-void *talloc_iu_ctx;
-
-int asn1_xer_print = 1;
-void *talloc_asn1_ctx;
-
-iu_recv_cb_t global_iu_recv_cb = NULL;
-iu_event_cb_t global_iu_event_cb = NULL;
-
-static LLIST_HEAD(ue_conn_ctx_list);
-static LLIST_HEAD(rnc_list);
-
-const struct value_string iu_event_type_names[] = {
- OSMO_VALUE_STRING(IU_EVENT_RAB_ASSIGN),
- OSMO_VALUE_STRING(IU_EVENT_SECURITY_MODE_COMPLETE),
- OSMO_VALUE_STRING(IU_EVENT_IU_RELEASE),
- OSMO_VALUE_STRING(IU_EVENT_LINK_INVALIDATED),
- { 0, NULL }
-};
-
-struct ue_conn_ctx *ue_conn_ctx_alloc(struct osmo_sccp_link *link, uint32_t conn_id)
-{
- struct ue_conn_ctx *ctx = talloc_zero(talloc_iu_ctx, struct ue_conn_ctx);
-
- ctx->link = link;
- ctx->conn_id = conn_id;
- llist_add(&ctx->list, &ue_conn_ctx_list);
-
- return ctx;
-}
-
-struct ue_conn_ctx *ue_conn_ctx_find(struct osmo_sccp_link *link,
- uint32_t conn_id)
-{
- struct ue_conn_ctx *ctx;
-
- llist_for_each_entry(ctx, &ue_conn_ctx_list, list) {
- if (ctx->link == link && ctx->conn_id == conn_id)
- return ctx;
- }
- return NULL;
-}
-
-static struct iu_rnc *iu_rnc_alloc(uint16_t rnc_id, uint16_t lac, uint8_t rac,
- struct osmo_sccp_link *link)
-{
- struct iu_rnc *rnc = talloc_zero(talloc_iu_ctx, struct iu_rnc);
-
- rnc->rnc_id = rnc_id;
- rnc->lac = lac;
- rnc->rac = rac;
- rnc->link = link;
- llist_add(&rnc->entry, &rnc_list);
-
- LOGP(DRANAP, LOGL_NOTICE, "New RNC %d (LAC=%d RAC=%d)\n",
- rnc->rnc_id, rnc->lac, rnc->rac);
-
- return rnc;
-}
-
-static struct iu_rnc *iu_rnc_register(uint16_t rnc_id, uint16_t lac,
- uint8_t rac, struct osmo_sccp_link *link)
-{
- struct iu_rnc *rnc;
- llist_for_each_entry(rnc, &rnc_list, entry) {
- if (rnc->rnc_id != rnc_id)
- continue;
-
- /* We have this RNC Id registered already. Make sure that the
- * details match. */
-
- /* TODO should a mismatch be an error? */
- if (rnc->lac != lac || rnc->rac != rac)
- LOGP(DRANAP, LOGL_NOTICE, "RNC %d changes its details:"
- " LAC=%d RAC=%d --> LAC=%d RAC=%d\n",
- rnc->rnc_id, rnc->lac, rnc->rac,
- lac, rac);
- rnc->lac = lac;
- rnc->rac = rac;
-
- if (link && rnc->link != link)
- LOGP(DRANAP, LOGL_NOTICE, "RNC %d on new link"
- " (LAC=%d RAC=%d)\n",
- rnc->rnc_id, rnc->lac, rnc->rac);
- rnc->link = link;
- return rnc;
- }
-
- /* Not found, make a new one. */
- return iu_rnc_alloc(rnc_id, lac, rac, link);
-}
-
-/* Discard/invalidate all ue_conn_ctx and iu_rnc entries that reference the
- * given link, since this link is invalid and about to be deallocated. For
- * each ue_conn_ctx, invoke the iu_event_cb_t with IU_EVENT_LINK_INVALIDATED.
- */
-void iu_link_del(struct osmo_sccp_link *link)
-{
- struct iu_rnc *rnc, *rnc_next;
- llist_for_each_entry_safe(rnc, rnc_next, &rnc_list, entry) {
- if (!rnc->link)
- continue;
- if (rnc->link != link)
- continue;
- rnc->link = NULL;
- llist_del(&rnc->entry);
- talloc_free(rnc);
- }
-
- struct ue_conn_ctx *uec, *uec_next;
- llist_for_each_entry_safe(uec, uec_next, &ue_conn_ctx_list, list) {
- if (uec->link != link)
- continue;
- uec->link = NULL;
- global_iu_event_cb(uec, IU_EVENT_LINK_INVALIDATED, NULL);
- }
-}
-
-/***********************************************************************
- * RANAP handling
- ***********************************************************************/
-
-int iu_rab_act(struct ue_conn_ctx *ue_ctx, struct msgb *msg)
-{
- struct osmo_scu_prim *prim;
-
- /* wrap RANAP message in SCCP N-DATA.req */
- prim = (struct osmo_scu_prim *) msgb_push(msg, sizeof(*prim));
- prim->u.data.conn_id = ue_ctx->conn_id;
- osmo_prim_init(&prim->oph,
- SCCP_SAP_USER,
- OSMO_SCU_PRIM_N_DATA,
- PRIM_OP_REQUEST,
- msg);
- return osmo_sua_user_link_down(ue_ctx->link, &prim->oph);
-}
-
-int iu_rab_deact(struct ue_conn_ctx *ue_ctx, uint8_t rab_id)
-{
- /* FIXME */
- return -1;
-}
-
-int iu_tx_sec_mode_cmd(struct ue_conn_ctx *uectx, struct gsm_auth_tuple *tp,
- int send_ck, int new_key)
-{
- struct osmo_scu_prim *prim;
- struct msgb *msg;
- uint8_t ik[16];
- uint8_t ck[16];
- unsigned int i;
-
- /* C5 function to derive IK from Kc */
- for (i = 0; i < 4; i++)
- ik[i] = tp->vec.kc[i] ^ tp->vec.kc[i+4];
- memcpy(ik+4, tp->vec.kc, 8);
- for (i = 12; i < 16; i++)
- ik[i] = ik[i-12];
-
- if (send_ck) {
- /* C4 function to derive CK from Kc */
- memcpy(ck, tp->vec.kc, 8);
- memcpy(ck+8, tp->vec.kc, 8);
- }
-
- /* create RANAP message */
- msg = ranap_new_msg_sec_mod_cmd(ik, send_ck? ck : NULL, new_key ? RANAP_KeyStatus_new : RANAP_KeyStatus_old);
- msg->l2h = msg->data;
- /* wrap RANAP message in SCCP N-DATA.req */
- prim = (struct osmo_scu_prim *) msgb_push(msg, sizeof(*prim));
- prim->u.data.conn_id = uectx->conn_id;
- osmo_prim_init(&prim->oph, SCCP_SAP_USER,
- OSMO_SCU_PRIM_N_DATA,
- PRIM_OP_REQUEST, msg);
- osmo_sua_user_link_down(uectx->link, &prim->oph);
-
- return 0;
-}
-
-static int iu_grnc_id_parse(struct iu_grnc_id *dst,
- struct RANAP_GlobalRNC_ID *src)
-{
- /* The size is coming from arbitrary sender, check it gracefully */
- if (src->pLMNidentity.size != 3) {
- LOGP(DRANAP, LOGL_ERROR, "Invalid PLMN Identity size:"
- " should be 3, is %d\n", src->pLMNidentity.size);
- return -1;
- }
- gsm48_mcc_mnc_from_bcd(&src->pLMNidentity.buf[0],
- &dst->mcc, &dst->mnc);
- dst->rnc_id = (uint16_t)src->rNC_ID;
- return 0;
-}
-
-#if 0
- -- not used at present --
-static int iu_grnc_id_compose(struct iu_grnc_id *src,
- struct RANAP_GlobalRNC_ID *dst)
-{
- /* The caller must ensure proper size */
- OSMO_ASSERT(dst->pLMNidentity.size == 3);
- gsm48_mcc_mnc_to_bcd(&dst->pLMNidentity.buf[0],
- src->mcc, src->mnc);
- dst->rNC_ID = src->rnc_id;
- return 0;
-}
-#endif
-
-static int ranap_handle_co_initial_ue(void *ctx, RANAP_InitialUE_MessageIEs_t *ies)
-{
- struct ue_conn_ctx *ue_conn = ctx;
- struct gprs_ra_id ra_id;
- struct iu_grnc_id grnc_id;
- uint16_t sai;
- struct msgb *msg = msgb_alloc(256, "RANAP->NAS");
-
- if (ranap_parse_lai(&ra_id, &ies->lai) != 0) {
- LOGP(DRANAP, LOGL_ERROR, "Failed to parse RANAP LAI IE\n");
- return -1;
- }
-
- if (ies->presenceMask & INITIALUE_MESSAGEIES_RANAP_RAC_PRESENT) {
- ra_id.rac = asn1str_to_u8(&ies->rac);
- }
-
- if (iu_grnc_id_parse(&grnc_id, &ies->globalRNC_ID) != 0) {
- LOGP(DRANAP, LOGL_ERROR,
- "Failed to parse RANAP Global-RNC-ID IE\n");
- return -1;
- }
-
- sai = asn1str_to_u16(&ies->sai.sAC);
- msgb_gmmh(msg) = msgb_put(msg, ies->nas_pdu.size);
- memcpy(msgb_gmmh(msg), ies->nas_pdu.buf, ies->nas_pdu.size);
-
- /* Make sure we know the RNC Id and LAC+RAC coming in on this connection. */
- iu_rnc_register(grnc_id.rnc_id, ra_id.lac, ra_id.rac, ue_conn->link);
- ue_conn->ra_id = ra_id;
-
- /* Feed into the MM layer */
- msg->dst = ctx;
- global_iu_recv_cb(msg, &ra_id, &sai);
-
- msgb_free(msg);
-
- return 0;
-}
-
-static int ranap_handle_co_dt(void *ctx, RANAP_DirectTransferIEs_t *ies)
-{
- struct gprs_ra_id _ra_id, *ra_id = NULL;
- uint16_t _sai, *sai = NULL;
- struct msgb *msg = msgb_alloc(256, "RANAP->NAS");
-
- if (ies->presenceMask & DIRECTTRANSFERIES_RANAP_LAI_PRESENT) {
- if (ranap_parse_lai(&_ra_id, &ies->lai) != 0) {
- LOGP(DRANAP, LOGL_ERROR, "Failed to parse RANAP LAI IE\n");
- return -1;
- }
- ra_id = &_ra_id;
- if (ies->presenceMask & DIRECTTRANSFERIES_RANAP_RAC_PRESENT) {
- _ra_id.rac = asn1str_to_u8(&ies->rac);
- }
- if (ies->presenceMask & DIRECTTRANSFERIES_RANAP_SAI_PRESENT) {
- _sai = asn1str_to_u16(&ies->sai.sAC);
- sai = &_sai;
- }
- }
-
- msgb_gmmh(msg) = msgb_put(msg, ies->nas_pdu.size);
- memcpy(msgb_gmmh(msg), ies->nas_pdu.buf, ies->nas_pdu.size);
-
- /* Feed into the MM/CC/SMS-CP layer */
- msg->dst = ctx;
- global_iu_recv_cb(msg, ra_id, sai);
-
- msgb_free(msg);
-
- return 0;
-}
-
-static int ranap_handle_co_err_ind(void *ctx, RANAP_ErrorIndicationIEs_t *ies)
-{
- if (ies->presenceMask & ERRORINDICATIONIES_RANAP_CAUSE_PRESENT)
- LOGP(DRANAP, LOGL_ERROR, "Rx Error Indication (%s)\n",
- ranap_cause_str(&ies->cause));
- else
- LOGP(DRANAP, LOGL_ERROR, "Rx Error Indication\n");
-
- return 0;
-}
-
-int iu_tx(struct msgb *msg_nas, uint8_t sapi)
-{
- struct ue_conn_ctx *uectx = msg_nas->dst;
- struct msgb *msg;
- struct osmo_scu_prim *prim;
-
- LOGP(DRANAP, LOGL_INFO, "Transmitting L3 Message as RANAP DT (SUA link %p conn_id %u)\n",
- uectx->link, uectx->conn_id);
-
- msg = ranap_new_msg_dt(sapi, msg_nas->data, msgb_length(msg_nas));
- msgb_free(msg_nas);
- msg->l2h = msg->data;
- prim = (struct osmo_scu_prim *) msgb_push(msg, sizeof(*prim));
- prim->u.data.conn_id = uectx->conn_id;
- osmo_prim_init(&prim->oph, SCCP_SAP_USER,
- OSMO_SCU_PRIM_N_DATA,
- PRIM_OP_REQUEST, msg);
- osmo_sua_user_link_down(uectx->link, &prim->oph);
- return 0;
-}
-
-static int ranap_handle_co_iu_rel_req(struct ue_conn_ctx *ctx, RANAP_Iu_ReleaseRequestIEs_t *ies)
-{
- struct msgb *msg;
- struct osmo_scu_prim *prim;
-
- LOGP(DRANAP, LOGL_INFO, "Received Iu Release Request, Sending Release Command\n");
- msg = ranap_new_msg_iu_rel_cmd(&ies->cause);
- msg->l2h = msg->data;
- prim = (struct osmo_scu_prim *) msgb_push(msg, sizeof(*prim));
- prim->u.data.conn_id = ctx->conn_id;
- osmo_prim_init(&prim->oph, SCCP_SAP_USER,
- OSMO_SCU_PRIM_N_DATA,
- PRIM_OP_REQUEST, msg);
- osmo_sua_user_link_down(ctx->link, &prim->oph);
- return 0;
-}
-
-static int ranap_handle_co_rab_ass_resp(struct ue_conn_ctx *ctx, RANAP_RAB_AssignmentResponseIEs_t *ies)
-{
- int rc = -1;
-
- LOGP(DRANAP, LOGL_INFO,
- "Rx RAB Assignment Response for UE conn_id %u\n", ctx->conn_id);
- if (ies->presenceMask & RAB_ASSIGNMENTRESPONSEIES_RANAP_RAB_SETUPORMODIFIEDLIST_PRESENT) {
- /* TODO: Iterate over list of SetupOrModifiedList IEs and handle each one */
- RANAP_IE_t *ranap_ie = ies->raB_SetupOrModifiedList.raB_SetupOrModifiedList_ies.list.array[0];
- RANAP_RAB_SetupOrModifiedItemIEs_t setup_ies;
-
- rc = ranap_decode_rab_setupormodifieditemies_fromlist(&setup_ies, &ranap_ie->value);
- if (rc) {
- LOGP(DRANAP, LOGL_ERROR, "Error in ranap_decode_rab_setupormodifieditemies()\n");
- return rc;
- }
-
- rc = global_iu_event_cb(ctx, IU_EVENT_RAB_ASSIGN, &setup_ies);
-
- ranap_free_rab_setupormodifieditemies(&setup_ies);
- }
-
- return rc;
-}
-
-/* Entry point for connection-oriented RANAP message */
-static void cn_ranap_handle_co(void *ctx, ranap_message *message)
-{
- int rc;
-
- LOGP(DRANAP, LOGL_NOTICE, "handle_co(dir=%u, proc=%u)\n", message->direction, message->procedureCode);
-
- switch (message->direction) {
- case RANAP_RANAP_PDU_PR_initiatingMessage:
- switch (message->procedureCode) {
- case RANAP_ProcedureCode_id_InitialUE_Message:
- rc = ranap_handle_co_initial_ue(ctx, &message->msg.initialUE_MessageIEs);
- break;
- case RANAP_ProcedureCode_id_DirectTransfer:
- rc = ranap_handle_co_dt(ctx, &message->msg.directTransferIEs);
- break;
- case RANAP_ProcedureCode_id_ErrorIndication:
- rc = ranap_handle_co_err_ind(ctx, &message->msg.errorIndicationIEs);
- break;
- case RANAP_ProcedureCode_id_Iu_ReleaseRequest:
- /* Iu Release Request */
- rc = ranap_handle_co_iu_rel_req(ctx, &message->msg.iu_ReleaseRequestIEs);
- break;
- default:
- LOGP(DRANAP, LOGL_ERROR, "Received Initiating Message: unknown Procedure Code %d\n",
- message->procedureCode);
- rc = -1;
- break;
- }
- break;
- case RANAP_RANAP_PDU_PR_successfulOutcome:
- switch (message->procedureCode) {
- case RANAP_ProcedureCode_id_SecurityModeControl:
- /* Security Mode Complete */
- rc = global_iu_event_cb(ctx, IU_EVENT_SECURITY_MODE_COMPLETE, NULL);
- break;
- case RANAP_ProcedureCode_id_Iu_Release:
- /* Iu Release Complete */
- rc = global_iu_event_cb(ctx, IU_EVENT_IU_RELEASE, NULL);
- if (rc) {
- LOGP(DRANAP, LOGL_ERROR, "Iu Release event: Iu Event callback returned %d\n",
- rc);
- }
- break;
- default:
- LOGP(DRANAP, LOGL_ERROR, "Received Successful Outcome: unknown Procedure Code %d\n",
- message->procedureCode);
- rc = -1;
- break;
- }
- break;
- case RANAP_RANAP_PDU_PR_outcome:
- switch (message->procedureCode) {
- case RANAP_ProcedureCode_id_RAB_Assignment:
- /* RAB Assignment Response */
- rc = ranap_handle_co_rab_ass_resp(ctx, &message->msg.raB_AssignmentResponseIEs);
- break;
- default:
- LOGP(DRANAP, LOGL_ERROR, "Received Outcome: unknown Procedure Code %d\n",
- message->procedureCode);
- rc = -1;
- break;
- }
- break;
- case RANAP_RANAP_PDU_PR_unsuccessfulOutcome:
- default:
- LOGP(DRANAP, LOGL_ERROR, "Received Unsuccessful Outcome: Procedure Code %d\n",
- message->procedureCode);
- rc = -1;
- break;
- }
-
- if (rc) {
- LOGP(DRANAP, LOGL_ERROR, "Error in cn_ranap_handle_co (%d)\n",
- rc);
- /* TODO handling of the error? */
- }
-}
-
-static int ranap_handle_cl_reset_req(void *ctx, RANAP_ResetIEs_t *ies)
-{
- /* FIXME: send reset response */
- return -1;
-}
-
-static int ranap_handle_cl_err_ind(void *ctx, RANAP_ErrorIndicationIEs_t *ies)
-{
- if (ies->presenceMask & ERRORINDICATIONIES_RANAP_CAUSE_PRESENT)
- LOGP(DRANAP, LOGL_ERROR, "Rx Error Indication (%s)\n",
- ranap_cause_str(&ies->cause));
- else
- LOGP(DRANAP, LOGL_ERROR, "Rx Error Indication\n");
-
- return 0;
-}
-
-/* Entry point for connection-less RANAP message */
-static void cn_ranap_handle_cl(void *ctx, ranap_message *message)
-{
- int rc;
-
- switch (message->direction) {
- case RANAP_RANAP_PDU_PR_initiatingMessage:
- switch (message->procedureCode) {
- case RANAP_ProcedureCode_id_Reset:
- /* received reset.req, send reset.resp */
- rc = ranap_handle_cl_reset_req(ctx, &message->msg.resetIEs);
- break;
- case RANAP_ProcedureCode_id_ErrorIndication:
- rc = ranap_handle_cl_err_ind(ctx, &message->msg.errorIndicationIEs);
- break;
- default:
- rc = -1;
- break;
- }
- break;
- case RANAP_RANAP_PDU_PR_successfulOutcome:
- case RANAP_RANAP_PDU_PR_unsuccessfulOutcome:
- case RANAP_RANAP_PDU_PR_outcome:
- default:
- rc = -1;
- break;
- }
-
- if (rc) {
- LOGP(DRANAP, LOGL_ERROR, "Error in cn_ranap_handle_cl (%d)\n",
- rc);
- /* TODO handling of the error? */
- }
-}
-
-/***********************************************************************
- * Paging
- ***********************************************************************/
-
-/* Send a paging command down a given SUA link. tmsi and paging_cause are
- * optional and may be passed NULL and 0, respectively, to disable their use.
- * See enum RANAP_PagingCause.
- *
- * If TMSI is given, the IMSI is not sent over the air interface. Nevertheless,
- * the IMSI is still required for resolution in the HNB-GW and/or(?) RNC. */
-static int iu_tx_paging_cmd(struct osmo_sccp_link *link,
- const char *imsi, const uint32_t *tmsi,
- bool is_ps, uint32_t paging_cause)
-{
- struct msgb *msg;
- msg = ranap_new_msg_paging_cmd(imsi, tmsi, is_ps? 1 : 0, paging_cause);
- msg->l2h = msg->data;
- return osmo_sccp_tx_unitdata_ranap(link, 1, 2, msg->data,
- msgb_length(msg));
-}
-
-static int iu_page(const char *imsi, const uint32_t *tmsi_or_ptimsi,
- uint16_t lac, uint8_t rac, bool is_ps)
-{
- struct iu_rnc *rnc;
- int pagings_sent = 0;
-
- if (tmsi_or_ptimsi) {
- LOGP(DRANAP, LOGL_DEBUG, "%s: Looking for RNCs to page for IMSI %s"
- " (paging will use %s %x)\n",
- is_ps? "IuPS" : "IuCS",
- imsi,
- is_ps? "PTMSI" : "TMSI",
- *tmsi_or_ptimsi);
- } else {
- LOGP(DRANAP, LOGL_DEBUG, "%s: Looking for RNCs to page for IMSI %s"
- " (paging will use IMSI)\n",
- is_ps? "IuPS" : "IuCS",
- imsi
- );
- }
-
- llist_for_each_entry(rnc, &rnc_list, entry) {
- if (!rnc->link) {
- /* Not actually connected, don't count it. */
- continue;
- }
- if (rnc->lac != lac)
- continue;
- if (is_ps && rnc->rac != rac)
- continue;
-
- /* Found a match! */
- if (iu_tx_paging_cmd(rnc->link, imsi, tmsi_or_ptimsi, is_ps, 0)
- == 0) {
- LOGP(DRANAP, LOGL_DEBUG,
- "%s: Paged for IMSI %s on RNC %d, on SUA link %p\n",
- is_ps? "IuPS" : "IuCS",
- imsi, rnc->rnc_id, rnc->link);
- pagings_sent ++;
- }
- }
-
- /* Some logging... */
- if (pagings_sent > 0) {
- LOGP(DRANAP, LOGL_DEBUG,
- "%s: %d RNCs were paged for IMSI %s.\n",
- is_ps? "IuPS" : "IuCS",
- pagings_sent, imsi);
- }
- else {
- if (is_ps) {
- LOGP(DRANAP, LOGL_ERROR, "IuPS: Found no RNC to page for"
- " LAC %d RAC %d (would have paged IMSI %s)\n",
- lac, rac, imsi);
- }
- else {
- LOGP(DRANAP, LOGL_ERROR, "IuCS: Found no RNC to page for"
- " LAC %d (would have paged IMSI %s)\n",
- lac, imsi);
- }
- }
-
- return pagings_sent;
-}
-
-int iu_page_cs(const char *imsi, const uint32_t *tmsi, uint16_t lac)
-{
- return iu_page(imsi, tmsi, lac, 0, false);
-}
-
-int iu_page_ps(const char *imsi, const uint32_t *ptmsi, uint16_t lac, uint8_t rac)
-{
- return iu_page(imsi, ptmsi, lac, rac, true);
-}
-
-
-/***********************************************************************
- *
- ***********************************************************************/
-
-int tx_unitdata(struct osmo_sccp_link *link);
-int tx_conn_req(struct osmo_sccp_link *link, uint32_t conn_id);
-
-struct osmo_prim_hdr *make_conn_req(uint32_t conn_id);
-struct osmo_prim_hdr *make_dt1_req(uint32_t conn_id, const uint8_t *data, unsigned int len);
-
-struct osmo_prim_hdr *make_conn_resp(struct osmo_scu_connect_param *param)
-{
- struct msgb *msg = msgb_alloc(1024, "conn_resp");
- struct osmo_scu_prim *prim;
-
- prim = (struct osmo_scu_prim *) msgb_put(msg, sizeof(*prim));
- osmo_prim_init(&prim->oph, SCCP_SAP_USER,
- OSMO_SCU_PRIM_N_CONNECT,
- PRIM_OP_RESPONSE, msg);
- memcpy(&prim->u.connect, param, sizeof(prim->u.connect));
- return &prim->oph;
-}
-
-static int sccp_sap_up(struct osmo_prim_hdr *oph, void *link)
-{
- struct osmo_scu_prim *prim = (struct osmo_scu_prim *) oph;
- struct osmo_prim_hdr *resp = NULL;
- int rc;
- struct ue_conn_ctx *ue;
-
- DEBUGP(DRANAP, "sccp_sap_up(%s)\n", osmo_scu_prim_name(oph));
-
- switch (OSMO_PRIM_HDR(oph)) {
- case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_CONFIRM):
- /* confirmation of outbound connection */
- rc = -1;
- break;
- case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_INDICATION):
- /* indication of new inbound connection request*/
- DEBUGP(DRANAP, "N-CONNECT.ind(X->%u)\n", prim->u.connect.conn_id);
- if (/* prim->u.connect.called_addr.ssn != OSMO_SCCP_SSN_RANAP || */
- !msgb_l2(oph->msg) || msgb_l2len(oph->msg) == 0) {
- LOGP(DRANAP, LOGL_NOTICE,
- "Received invalid N-CONNECT.ind\n");
- return 0;
- }
- ue = ue_conn_ctx_alloc(link, prim->u.connect.conn_id);
- /* first ensure the local SUA/SCCP socket is ACTIVE */
- resp = make_conn_resp(&prim->u.connect);
- osmo_sua_user_link_down(link, resp);
- /* then handle the RANAP payload */
- rc = ranap_cn_rx_co(cn_ranap_handle_co, ue, msgb_l2(oph->msg), msgb_l2len(oph->msg));
- break;
- case OSMO_PRIM(OSMO_SCU_PRIM_N_DISCONNECT, PRIM_OP_INDICATION):
- /* indication of disconnect */
- DEBUGP(DRANAP, "N-DISCONNECT.ind(%u)\n",
- prim->u.disconnect.conn_id);
- ue = ue_conn_ctx_find(link, prim->u.disconnect.conn_id);
- rc = ranap_cn_rx_co(cn_ranap_handle_co, ue, msgb_l2(oph->msg), msgb_l2len(oph->msg));
- break;
- case OSMO_PRIM(OSMO_SCU_PRIM_N_DATA, PRIM_OP_INDICATION):
- /* connection-oriented data received */
- DEBUGP(DRANAP, "N-DATA.ind(%u, %s)\n", prim->u.data.conn_id,
- osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg)));
- /* resolve UE context */
- ue = ue_conn_ctx_find(link, prim->u.data.conn_id);
- rc = ranap_cn_rx_co(cn_ranap_handle_co, ue, msgb_l2(oph->msg), msgb_l2len(oph->msg));
- break;
- case OSMO_PRIM(OSMO_SCU_PRIM_N_UNITDATA, PRIM_OP_INDICATION):
- /* connection-less data received */
- DEBUGP(DRANAP, "N-UNITDATA.ind(%s)\n",
- osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg)));
- rc = ranap_cn_rx_cl(cn_ranap_handle_cl, link, msgb_l2(oph->msg), msgb_l2len(oph->msg));
- break;
- default:
- rc = -1;
- break;
- }
-
- msgb_free(oph->msg);
- return rc;
-}
-
-int iu_init(void *ctx, const char *listen_addr, uint16_t listen_port,
- iu_recv_cb_t iu_recv_cb, iu_event_cb_t iu_event_cb)
-{
- struct osmo_sccp_user *user;
- talloc_iu_ctx = talloc_named_const(ctx, 1, "iu");
- talloc_asn1_ctx = talloc_named_const(talloc_iu_ctx, 1, "asn1");
-
- global_iu_recv_cb = iu_recv_cb;
- global_iu_event_cb = iu_event_cb;
- osmo_sua_set_log_area(DSUA);
- user = osmo_sua_user_create(talloc_iu_ctx, sccp_sap_up, talloc_iu_ctx);
- return osmo_sua_server_listen(user, listen_addr, listen_port);
-}
-
diff --git a/openbsc/src/libiu/iu_vty.c b/openbsc/src/libiu/iu_vty.c
deleted file mode 100644
index 91eed96be..000000000
--- a/openbsc/src/libiu/iu_vty.c
+++ /dev/null
@@ -1,50 +0,0 @@
-/* OpenBSC Iu related interface to quagga VTY */
-/* (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 <stdlib.h>
-
-#include <osmocom/vty/command.h>
-#include <osmocom/vty/logging.h>
-
-/* Pointer to the actual asn_debug value as passed from main scopes. */
-static int *g_asn_debug_p = NULL;
-
-DEFUN(logging_asn_debug,
- logging_asn_debug_cmd,
- "logging asn1-debug (1|0)",
- LOGGING_STR
- "Log human readable representations of all ASN.1 messages to stderr\n"
- "Log decoded ASN.1 messages to stderr\n"
- "Do not log decoded ASN.1 messages to stderr\n")
-{
- if (!g_asn_debug_p) {
- vty_out(vty, "%%ASN.1 debugging not available%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- *g_asn_debug_p = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
-void iu_vty_init(int *asn_debug_p)
-{
- g_asn_debug_p = asn_debug_p;
-
- install_element(CFG_LOG_NODE, &logging_asn_debug_cmd);
-}
diff --git a/openbsc/src/libmgcp/Makefile.am b/openbsc/src/libmgcp/Makefile.am
deleted file mode 100644
index 5faf6027a..000000000
--- a/openbsc/src/libmgcp/Makefile.am
+++ /dev/null
@@ -1,43 +0,0 @@
-AM_CPPFLAGS = \
- $(all_includes) \
- -I$(top_srcdir)/include \
- -I$(top_builddir) \
- $(NULL)
-
-AM_CFLAGS = \
- -Wall \
- $(LIBOSMOCORE_CFLAGS) \
- $(LIBOSMOVTY_CFLAGS) \
- $(LIBOSMONETIF_CFLAGS) \
- $(COVERAGE_CFLAGS) \
- $(LIBBCG729_CFLAGS) \
- $(NULL)
-
-AM_LDFLAGS = \
- $(LIBOSMOCORE_LIBS) \
- $(LIBOSMOGSM_LIBS) \
- $(LIBOSMONETIF_LIBS) \
- $(COVERAGE_LDFLAGS) \
- $(LIBBCG729_LIBS) \
- $(NULL)
-
-noinst_LIBRARIES = \
- libmgcp.a \
- $(NULL)
-
-noinst_HEADERS = \
- g711common.h \
- $(NULL)
-
-libmgcp_a_SOURCES = \
- mgcp_protocol.c \
- mgcp_network.c \
- mgcp_vty.c \
- mgcp_osmux.c \
- mgcp_sdp.c \
- $(NULL)
-if BUILD_MGCP_TRANSCODING
-libmgcp_a_SOURCES += \
- mgcp_transcode.c \
- $(NULL)
-endif
diff --git a/openbsc/src/libmgcp/g711common.h b/openbsc/src/libmgcp/g711common.h
deleted file mode 100644
index cb35fc651..000000000
--- a/openbsc/src/libmgcp/g711common.h
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * PCM - A-Law conversion
- * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
- *
- * Wrapper for linphone Codec class by Simon Morlat <simon.morlat@linphone.org>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-static inline int val_seg(int val)
-{
- int r = 0;
- val >>= 7; /*7 = 4 + 3*/
- if (val & 0xf0) {
- val >>= 4;
- r += 4;
- }
- if (val & 0x0c) {
- val >>= 2;
- r += 2;
- }
- if (val & 0x02)
- r += 1;
- return r;
-}
-
-/*
- * s16_to_alaw() - Convert a 16-bit linear PCM value to 8-bit A-law
- *
- * s16_to_alaw() accepts an 16-bit integer and encodes it as A-law data.
- *
- * Linear Input Code Compressed Code
- * ------------------------ ---------------
- * 0000000wxyza 000wxyz
- * 0000001wxyza 001wxyz
- * 000001wxyzab 010wxyz
- * 00001wxyzabc 011wxyz
- * 0001wxyzabcd 100wxyz
- * 001wxyzabcde 101wxyz
- * 01wxyzabcdef 110wxyz
- * 1wxyzabcdefg 111wxyz
- *
- * For further information see John C. Bellamy's Digital Telephony, 1982,
- * John Wiley & Sons, pps 98-111 and 472-476.
- * G711 is designed for 13 bits input signal, this function add extra shifting to take this into account.
- */
-
-static inline unsigned char s16_to_alaw(int pcm_val)
-{
- int mask;
- int seg;
- unsigned char aval;
-
- if (pcm_val >= 0) {
- mask = 0xD5;
- } else {
- mask = 0x55;
- pcm_val = -pcm_val;
- if (pcm_val > 0x7fff)
- pcm_val = 0x7fff;
- }
-
- if (pcm_val < 256) /*256 = 32 << 3*/
- aval = pcm_val >> 4; /*4 = 1 + 3*/
- else {
- /* Convert the scaled magnitude to segment number. */
- seg = val_seg(pcm_val);
- aval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0x0f);
- }
- return aval ^ mask;
-}
-
-/*
- * alaw_to_s16() - Convert an A-law value to 16-bit linear PCM
- *
- */
-static inline int alaw_to_s16(unsigned char a_val)
-{
- int t;
- int seg;
-
- a_val ^= 0x55;
- t = a_val & 0x7f;
- if (t < 16)
- t = (t << 4) + 8;
- else {
- seg = (t >> 4) & 0x07;
- t = ((t & 0x0f) << 4) + 0x108;
- t <<= seg -1;
- }
- return ((a_val & 0x80) ? t : -t);
-}
-/*
- * s16_to_ulaw() - Convert a linear PCM value to u-law
- *
- * In order to simplify the encoding process, the original linear magnitude
- * is biased by adding 33 which shifts the encoding range from (0 - 8158) to
- * (33 - 8191). The result can be seen in the following encoding table:
- *
- * Biased Linear Input Code Compressed Code
- * ------------------------ ---------------
- * 00000001wxyza 000wxyz
- * 0000001wxyzab 001wxyz
- * 000001wxyzabc 010wxyz
- * 00001wxyzabcd 011wxyz
- * 0001wxyzabcde 100wxyz
- * 001wxyzabcdef 101wxyz
- * 01wxyzabcdefg 110wxyz
- * 1wxyzabcdefgh 111wxyz
- *
- * Each biased linear code has a leading 1 which identifies the segment
- * number. The value of the segment number is equal to 7 minus the number
- * of leading 0's. The quantization interval is directly available as the
- * four bits wxyz. * The trailing bits (a - h) are ignored.
- *
- * Ordinarily the complement of the resulting code word is used for
- * transmission, and so the code word is complemented before it is returned.
- *
- * For further information see John C. Bellamy's Digital Telephony, 1982,
- * John Wiley & Sons, pps 98-111 and 472-476.
- */
-
-static inline unsigned char s16_to_ulaw(int pcm_val) /* 2's complement (16-bit range) */
-{
- int mask;
- int seg;
- unsigned char uval;
-
- if (pcm_val < 0) {
- pcm_val = 0x84 - pcm_val;
- mask = 0x7f;
- } else {
- pcm_val += 0x84;
- mask = 0xff;
- }
- if (pcm_val > 0x7fff)
- pcm_val = 0x7fff;
-
- /* Convert the scaled magnitude to segment number. */
- seg = val_seg(pcm_val);
-
- /*
- * Combine the sign, segment, quantization bits;
- * and complement the code word.
- */
- uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0x0f);
- return uval ^ mask;
-}
-
-/*
- * ulaw_to_s16() - Convert a u-law value to 16-bit linear PCM
- *
- * First, a biased linear code is derived from the code word. An unbiased
- * output can then be obtained by subtracting 33 from the biased code.
- *
- * Note that this function expects to be passed the complement of the
- * original code word. This is in keeping with ISDN conventions.
- */
-static inline int ulaw_to_s16(unsigned char u_val)
-{
- int t;
-
- /* Complement to obtain normal u-law value. */
- u_val = ~u_val;
-
- /*
- * Extract and bias the quantization bits. Then
- * shift up by the segment number and subtract out the bias.
- */
- t = ((u_val & 0x0f) << 3) + 0x84;
- t <<= (u_val & 0x70) >> 4;
-
- return ((u_val & 0x80) ? (0x84 - t) : (t - 0x84));
-}
diff --git a/openbsc/src/libmgcp/mgcp_network.c b/openbsc/src/libmgcp/mgcp_network.c
deleted file mode 100644
index abce6e49d..000000000
--- a/openbsc/src/libmgcp/mgcp_network.c
+++ /dev/null
@@ -1,1064 +0,0 @@
-/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
-/* The protocol implementation */
-
-/*
- * (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
- * (C) 2009-2012 by On-Waves
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <string.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <errno.h>
-#include <time.h>
-#include <limits.h>
-
-#include <sys/socket.h>
-#include <arpa/inet.h>
-
-#include <osmocom/core/msgb.h>
-#include <osmocom/core/select.h>
-
-#include <osmocom/netif/rtp.h>
-
-#include <openbsc/mgcp.h>
-#include <openbsc/mgcp_internal.h>
-
-#include <openbsc/osmux.h>
-
-#warning "Make use of the rtp proxy code"
-
-
-#define RTP_SEQ_MOD (1 << 16)
-#define RTP_MAX_DROPOUT 3000
-#define RTP_MAX_MISORDER 100
-#define RTP_BUF_SIZE 4096
-
-enum {
- MGCP_PROTO_RTP,
- MGCP_PROTO_RTCP,
-};
-
-/**
- * This does not need to be a precision timestamp and
- * is allowed to wrap quite fast. The returned value is
- * 1/unit seconds.
- */
-static uint32_t get_current_ts(unsigned unit)
-{
- struct timespec tp;
- uint64_t ret;
-
- if (!unit)
- return 0;
-
- memset(&tp, 0, sizeof(tp));
- if (clock_gettime(CLOCK_MONOTONIC, &tp) != 0)
- LOGP(DMGCP, LOGL_NOTICE,
- "Getting the clock failed.\n");
-
- /* convert it to 1/unit seconds */
- ret = tp.tv_sec;
- ret *= unit;
- ret += (int64_t)tp.tv_nsec * unit / 1000 / 1000 / 1000;
-
- return ret;
-}
-
-int mgcp_udp_send(int fd, struct in_addr *addr, int port, char *buf, int len)
-{
- struct sockaddr_in out;
- out.sin_family = AF_INET;
- out.sin_port = port;
- memcpy(&out.sin_addr, addr, sizeof(*addr));
-
- return sendto(fd, buf, len, 0, (struct sockaddr *)&out, sizeof(out));
-}
-
-int mgcp_send_dummy(struct mgcp_endpoint *endp)
-{
- static char buf[] = { MGCP_DUMMY_LOAD };
- int rc;
- int was_rtcp = 0;
-
- rc = mgcp_udp_send(endp->net_end.rtp.fd, &endp->net_end.addr,
- endp->net_end.rtp_port, buf, 1);
-
- if (rc == -1)
- goto failed;
-
- if (endp->tcfg->omit_rtcp)
- return rc;
-
- was_rtcp = 1;
- rc = mgcp_udp_send(endp->net_end.rtcp.fd, &endp->net_end.addr,
- endp->net_end.rtcp_port, buf, 1);
-
- if (rc >= 0)
- return rc;
-
-failed:
- LOGP(DMGCP, LOGL_ERROR,
- "Failed to send dummy %s packet: %s on: 0x%x to %s:%d\n",
- was_rtcp ? "RTCP" : "RTP",
- strerror(errno), ENDPOINT_NUMBER(endp), inet_ntoa(endp->net_end.addr),
- was_rtcp ? endp->net_end.rtcp_port : endp->net_end.rtp_port);
-
- return -1;
-}
-
-static int32_t compute_timestamp_aligment_error(struct mgcp_rtp_stream_state *sstate,
- int ptime, uint32_t timestamp)
-{
- int32_t timestamp_delta;
-
- if (ptime == 0)
- return 0;
-
- /* Align according to: T - Tlast = k * Tptime */
- timestamp_delta = timestamp - sstate->last_timestamp;
-
- return timestamp_delta % ptime;
-}
-
-static int check_rtp_timestamp(struct mgcp_endpoint *endp,
- struct mgcp_rtp_state *state,
- struct mgcp_rtp_stream_state *sstate,
- struct mgcp_rtp_end *rtp_end,
- struct sockaddr_in *addr,
- uint16_t seq, uint32_t timestamp,
- const char *text, int32_t *tsdelta_out)
-{
- int32_t tsdelta;
- int32_t timestamp_error;
-
- /* Not fully intialized, skip */
- if (sstate->last_tsdelta == 0 && timestamp == sstate->last_timestamp)
- return 0;
-
- if (seq == sstate->last_seq) {
- if (timestamp != sstate->last_timestamp) {
- sstate->err_ts_counter += 1;
- LOGP(DMGCP, LOGL_ERROR,
- "The %s timestamp delta is != 0 but the sequence "
- "number %d is the same, "
- "TS offset: %d, SeqNo offset: %d "
- "on 0x%x SSRC: %u timestamp: %u "
- "from %s:%d in %d\n",
- text, seq,
- state->timestamp_offset, state->seq_offset,
- ENDPOINT_NUMBER(endp), sstate->ssrc, timestamp,
- inet_ntoa(addr->sin_addr), ntohs(addr->sin_port),
- endp->conn_mode);
- }
- return 0;
- }
-
- tsdelta =
- (int32_t)(timestamp - sstate->last_timestamp) /
- (int16_t)(seq - sstate->last_seq);
-
- if (tsdelta == 0) {
- /* Don't update *tsdelta_out */
- LOGP(DMGCP, LOGL_NOTICE,
- "The %s timestamp delta is %d "
- "on 0x%x SSRC: %u timestamp: %u "
- "from %s:%d in %d\n",
- text, tsdelta,
- ENDPOINT_NUMBER(endp), sstate->ssrc, timestamp,
- inet_ntoa(addr->sin_addr), ntohs(addr->sin_port),
- endp->conn_mode);
-
- return 0;
- }
-
- if (sstate->last_tsdelta != tsdelta) {
- if (sstate->last_tsdelta) {
- LOGP(DMGCP, LOGL_INFO,
- "The %s timestamp delta changes from %d to %d "
- "on 0x%x SSRC: %u timestamp: %u from %s:%d in %d\n",
- text, sstate->last_tsdelta, tsdelta,
- ENDPOINT_NUMBER(endp), sstate->ssrc, timestamp,
- inet_ntoa(addr->sin_addr), ntohs(addr->sin_port),
- endp->conn_mode);
- }
- }
-
- if (tsdelta_out)
- *tsdelta_out = tsdelta;
-
- timestamp_error =
- compute_timestamp_aligment_error(sstate, state->packet_duration,
- timestamp);
-
- if (timestamp_error) {
- sstate->err_ts_counter += 1;
- LOGP(DMGCP, LOGL_NOTICE,
- "The %s timestamp has an alignment error of %d "
- "on 0x%x SSRC: %u "
- "SeqNo delta: %d, TS delta: %d, dTS/dSeq: %d "
- "from %s:%d in mode %d. ptime: %d\n",
- text, timestamp_error,
- ENDPOINT_NUMBER(endp), sstate->ssrc,
- (int16_t)(seq - sstate->last_seq),
- (int32_t)(timestamp - sstate->last_timestamp),
- tsdelta,
- inet_ntoa(addr->sin_addr), ntohs(addr->sin_port),
- endp->conn_mode, state->packet_duration);
- }
- return 1;
-}
-
-/* Set the timestamp offset according to the packet duration. */
-static int adjust_rtp_timestamp_offset(struct mgcp_endpoint *endp,
- struct mgcp_rtp_state *state,
- struct mgcp_rtp_end *rtp_end,
- struct sockaddr_in *addr,
- int16_t delta_seq, uint32_t in_timestamp)
-{
- int32_t tsdelta = state->packet_duration;
- int timestamp_offset;
- uint32_t out_timestamp;
-
- if (tsdelta == 0) {
- tsdelta = state->out_stream.last_tsdelta;
- if (tsdelta != 0) {
- LOGP(DMGCP, LOGL_NOTICE,
- "A fixed packet duration is not available on 0x%x, "
- "using last output timestamp delta instead: %d "
- "from %s:%d in %d\n",
- ENDPOINT_NUMBER(endp), tsdelta,
- inet_ntoa(addr->sin_addr), ntohs(addr->sin_port),
- endp->conn_mode);
- } else {
- tsdelta = rtp_end->codec.rate * 20 / 1000;
- LOGP(DMGCP, LOGL_NOTICE,
- "Fixed packet duration and last timestamp delta "
- "are not available on 0x%x, "
- "using fixed 20ms instead: %d "
- "from %s:%d in %d\n",
- ENDPOINT_NUMBER(endp), tsdelta,
- inet_ntoa(addr->sin_addr), ntohs(addr->sin_port),
- endp->conn_mode);
- }
- }
-
- out_timestamp = state->out_stream.last_timestamp + delta_seq * tsdelta;
- timestamp_offset = out_timestamp - in_timestamp;
-
- if (state->timestamp_offset != timestamp_offset) {
- state->timestamp_offset = timestamp_offset;
-
- LOGP(DMGCP, LOGL_NOTICE,
- "Timestamp offset change on 0x%x SSRC: %u "
- "SeqNo delta: %d, TS offset: %d, "
- "from %s:%d in %d\n",
- ENDPOINT_NUMBER(endp), state->in_stream.ssrc,
- delta_seq, state->timestamp_offset,
- inet_ntoa(addr->sin_addr), ntohs(addr->sin_port),
- endp->conn_mode);
- }
-
- return timestamp_offset;
-}
-
-/* Set the timestamp offset according to the packet duration. */
-static int align_rtp_timestamp_offset(struct mgcp_endpoint *endp,
- struct mgcp_rtp_state *state,
- struct mgcp_rtp_end *rtp_end,
- struct sockaddr_in *addr,
- uint32_t timestamp)
-{
- int timestamp_error = 0;
- int ptime = state->packet_duration;
-
- /* Align according to: T + Toffs - Tlast = k * Tptime */
-
- timestamp_error = compute_timestamp_aligment_error(
- &state->out_stream, ptime,
- timestamp + state->timestamp_offset);
-
- if (timestamp_error) {
- state->timestamp_offset += ptime - timestamp_error;
-
- LOGP(DMGCP, LOGL_NOTICE,
- "Corrected timestamp alignment error of %d on 0x%x SSRC: %u "
- "new TS offset: %d, "
- "from %s:%d in %d\n",
- timestamp_error,
- ENDPOINT_NUMBER(endp), state->in_stream.ssrc,
- state->timestamp_offset, inet_ntoa(addr->sin_addr),
- ntohs(addr->sin_port), endp->conn_mode);
- }
-
- OSMO_ASSERT(compute_timestamp_aligment_error(&state->out_stream, ptime,
- timestamp + state->timestamp_offset) == 0);
-
- return timestamp_error;
-}
-
-int mgcp_rtp_processing_default(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end,
- char *data, int *len, int buf_size)
-{
- return 0;
-}
-
-int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp,
- struct mgcp_rtp_end *dst_end,
- struct mgcp_rtp_end *src_end)
-{
- return 0;
-}
-
-void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp,
- int *payload_type,
- const char**audio_name,
- const char**fmtp_extra)
-{
- /* Use the BTS side parameters when passing the SDP data (for
- * downlink) to the net peer.
- */
- *payload_type = endp->bts_end.codec.payload_type;
- *audio_name = endp->bts_end.codec.audio_name;
- *fmtp_extra = endp->bts_end.fmtp_extra;
-}
-
-
-void mgcp_rtp_annex_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *state,
- const uint16_t seq, const int32_t transit,
- const uint32_t ssrc)
-{
- int32_t d;
-
- /* initialize or re-initialize */
- if (!state->stats_initialized || state->stats_ssrc != ssrc) {
- state->stats_initialized = 1;
- state->stats_base_seq = seq;
- state->stats_max_seq = seq - 1;
- state->stats_ssrc = ssrc;
- state->stats_jitter = 0;
- state->stats_transit = transit;
- state->stats_cycles = 0;
- } else {
- uint16_t udelta;
-
- /*
- * The below takes the shape of the validation of
- * Appendix A. Check if there is something weird with
- * the sequence number, otherwise check for a wrap
- * around in the sequence number.
- * It can't wrap during the initialization so let's
- * skip it here. The Appendix A probably doesn't have
- * this issue because of the probation.
- */
- udelta = seq - state->stats_max_seq;
- if (udelta < RTP_MAX_DROPOUT) {
- if (seq < state->stats_max_seq)
- state->stats_cycles += RTP_SEQ_MOD;
- } else if (udelta <= RTP_SEQ_MOD - RTP_MAX_MISORDER) {
- LOGP(DMGCP, LOGL_NOTICE,
- "RTP seqno made a very large jump on 0x%x delta: %u\n",
- ENDPOINT_NUMBER(endp), udelta);
- }
- }
-
- /*
- * Calculate the jitter between the two packages. The TS should be
- * taken closer to the read function. This was taken from the
- * Appendix A of RFC 3550. Timestamp and arrival_time have a 1/rate
- * resolution.
- */
- d = transit - state->stats_transit;
- state->stats_transit = transit;
- if (d < 0)
- d = -d;
- state->stats_jitter += d - ((state->stats_jitter + 8) >> 4);
- state->stats_max_seq = seq;
-}
-
-
-
-/**
- * The RFC 3550 Appendix A assumes there are multiple sources but
- * some of the supported endpoints (e.g. the nanoBTS) can only handle
- * one source and this code will patch RTP header to appear as if there
- * is only one source.
- * There is also no probation period for new sources. Every RTP header
- * we receive will be seen as a switch in streams.
- */
-void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *state,
- struct mgcp_rtp_end *rtp_end, struct sockaddr_in *addr,
- char *data, int len)
-{
- uint32_t arrival_time;
- int32_t transit;
- uint16_t seq;
- uint32_t timestamp, ssrc;
- struct rtp_hdr *rtp_hdr;
- int payload = rtp_end->codec.payload_type;
-
- if (len < sizeof(*rtp_hdr))
- return;
-
- rtp_hdr = (struct rtp_hdr *) data;
- seq = ntohs(rtp_hdr->sequence);
- timestamp = ntohl(rtp_hdr->timestamp);
- arrival_time = get_current_ts(rtp_end->codec.rate);
- ssrc = ntohl(rtp_hdr->ssrc);
- transit = arrival_time - timestamp;
-
- mgcp_rtp_annex_count(endp, state, seq, transit, ssrc);
-
- if (!state->initialized) {
- state->initialized = 1;
- state->in_stream.last_seq = seq - 1;
- state->in_stream.ssrc = state->orig_ssrc = ssrc;
- state->in_stream.last_tsdelta = 0;
- state->packet_duration = mgcp_rtp_packet_duration(endp, rtp_end);
- state->out_stream = state->in_stream;
- state->out_stream.last_timestamp = timestamp;
- state->out_stream.ssrc = ssrc - 1; /* force output SSRC change */
- LOGP(DMGCP, LOGL_INFO,
- "Initializing stream on 0x%x SSRC: %u timestamp: %u "
- "pkt-duration: %d, from %s:%d in %d\n",
- ENDPOINT_NUMBER(endp), state->in_stream.ssrc,
- state->seq_offset, state->packet_duration,
- inet_ntoa(addr->sin_addr), ntohs(addr->sin_port),
- endp->conn_mode);
- if (state->packet_duration == 0) {
- state->packet_duration = rtp_end->codec.rate * 20 / 1000;
- LOGP(DMGCP, LOGL_NOTICE,
- "Fixed packet duration is not available on 0x%x, "
- "using fixed 20ms instead: %d from %s:%d in %d\n",
- ENDPOINT_NUMBER(endp), state->packet_duration,
- inet_ntoa(addr->sin_addr), ntohs(addr->sin_port),
- endp->conn_mode);
- }
- } else if (state->in_stream.ssrc != ssrc) {
- LOGP(DMGCP, LOGL_NOTICE,
- "The SSRC changed on 0x%x: %u -> %u "
- "from %s:%d in %d\n",
- ENDPOINT_NUMBER(endp),
- state->in_stream.ssrc, rtp_hdr->ssrc,
- inet_ntoa(addr->sin_addr), ntohs(addr->sin_port),
- endp->conn_mode);
-
- state->in_stream.ssrc = ssrc;
- if (rtp_end->force_constant_ssrc) {
- int16_t delta_seq;
-
- /* Always increment seqno by 1 */
- state->seq_offset =
- (state->out_stream.last_seq + 1) - seq;
-
- /* Estimate number of packets that would have been sent */
- delta_seq =
- (arrival_time - state->in_stream.last_arrival_time
- + state->packet_duration/2) /
- state->packet_duration;
-
- adjust_rtp_timestamp_offset(endp, state, rtp_end, addr,
- delta_seq, timestamp);
-
- state->patch_ssrc = 1;
- ssrc = state->orig_ssrc;
- if (rtp_end->force_constant_ssrc != -1)
- rtp_end->force_constant_ssrc -= 1;
-
- LOGP(DMGCP, LOGL_NOTICE,
- "SSRC patching enabled on 0x%x SSRC: %u "
- "SeqNo offset: %d, TS offset: %d "
- "from %s:%d in %d\n",
- ENDPOINT_NUMBER(endp), state->in_stream.ssrc,
- state->seq_offset, state->timestamp_offset,
- inet_ntoa(addr->sin_addr), ntohs(addr->sin_port),
- endp->conn_mode);
- }
-
- state->in_stream.last_tsdelta = 0;
- } else {
- /* Compute current per-packet timestamp delta */
- check_rtp_timestamp(endp, state, &state->in_stream, rtp_end, addr,
- seq, timestamp, "input",
- &state->in_stream.last_tsdelta);
-
- if (state->patch_ssrc)
- ssrc = state->orig_ssrc;
- }
-
- /* Save before patching */
- state->in_stream.last_timestamp = timestamp;
- state->in_stream.last_seq = seq;
- state->in_stream.last_arrival_time = arrival_time;
-
- if (rtp_end->force_aligned_timing &&
- state->out_stream.ssrc == ssrc && state->packet_duration)
- /* Align the timestamp offset */
- align_rtp_timestamp_offset(endp, state, rtp_end, addr, timestamp);
-
- /* Store the updated SSRC back to the packet */
- if (state->patch_ssrc)
- rtp_hdr->ssrc = htonl(ssrc);
-
- /* Apply the offset and store it back to the packet.
- * This won't change anything if the offset is 0, so the conditional is
- * omitted. */
- seq += state->seq_offset;
- rtp_hdr->sequence = htons(seq);
- timestamp += state->timestamp_offset;
- rtp_hdr->timestamp = htonl(timestamp);
-
- /* Check again, whether the timestamps are still valid */
- if (state->out_stream.ssrc == ssrc)
- check_rtp_timestamp(endp, state, &state->out_stream, rtp_end,
- addr, seq, timestamp, "output",
- &state->out_stream.last_tsdelta);
-
- /* Save output values */
- state->out_stream.last_seq = seq;
- state->out_stream.last_timestamp = timestamp;
- state->out_stream.ssrc = ssrc;
-
- if (payload < 0)
- return;
-
- rtp_hdr->payload_type = payload;
-}
-
-/*
- * The below code is for dispatching. We have a dedicated port for
- * the data coming from the net and one to discover the BTS.
- */
-static int forward_data(int fd, struct mgcp_rtp_tap *tap, const char *buf, int len)
-{
- if (!tap->enabled)
- return 0;
-
- return sendto(fd, buf, len, 0,
- (struct sockaddr *)&tap->forward, sizeof(tap->forward));
-}
-
-static int mgcp_send_transcoder(struct mgcp_rtp_end *end,
- struct mgcp_config *cfg, int is_rtp,
- const char *buf, int len)
-{
- int rc;
- int port;
- struct sockaddr_in addr;
-
- port = is_rtp ? end->rtp_port : end->rtcp_port;
-
- addr.sin_family = AF_INET;
- addr.sin_addr = cfg->transcoder_in;
- addr.sin_port = port;
-
- rc = sendto(is_rtp ?
- end->rtp.fd :
- end->rtcp.fd, buf, len, 0,
- (struct sockaddr *) &addr, sizeof(addr));
-
- if (rc != len)
- LOGP(DMGCP, LOGL_ERROR,
- "Failed to send data to the transcoder: %s\n",
- strerror(errno));
-
- return rc;
-}
-
-int mgcp_send(struct mgcp_endpoint *endp, int dest, int is_rtp,
- struct sockaddr_in *addr, char *buf, int rc)
-{
- struct mgcp_trunk_config *tcfg = endp->tcfg;
- struct mgcp_rtp_end *rtp_end;
- struct mgcp_rtp_state *rtp_state;
- int tap_idx;
-
- /* For loop toggle the destination and then dispatch. */
- if (tcfg->audio_loop)
- dest = !dest;
-
- /* Loop based on the conn_mode, maybe undoing the above */
- if (endp->conn_mode == MGCP_CONN_LOOPBACK)
- dest = !dest;
-
- if (dest == MGCP_DEST_NET) {
- rtp_end = &endp->net_end;
- rtp_state = &endp->bts_state;
- tap_idx = MGCP_TAP_NET_OUT;
- } else {
- rtp_end = &endp->bts_end;
- rtp_state = &endp->net_state;
- tap_idx = MGCP_TAP_BTS_OUT;
- }
-
- if (!rtp_end->output_enabled)
- rtp_end->dropped_packets += 1;
- else if (is_rtp) {
- int cont;
- int nbytes = 0;
- int len = rc;
- do {
- cont = endp->cfg->rtp_processing_cb(endp, rtp_end,
- buf, &len, RTP_BUF_SIZE);
- if (cont < 0)
- break;
-
- mgcp_patch_and_count(endp, rtp_state, rtp_end, addr, buf, len);
- forward_data(rtp_end->rtp.fd, &endp->taps[tap_idx],
- buf, len);
- rc = mgcp_udp_send(rtp_end->rtp.fd,
- &rtp_end->addr,
- rtp_end->rtp_port, buf, len);
-
- if (rc <= 0)
- return rc;
- nbytes += rc;
- len = cont;
- } while (len > 0);
- return nbytes;
- } else if (!tcfg->omit_rtcp) {
- return mgcp_udp_send(rtp_end->rtcp.fd,
- &rtp_end->addr,
- rtp_end->rtcp_port, buf, rc);
- }
-
- return 0;
-}
-
-static int receive_from(struct mgcp_endpoint *endp, int fd, struct sockaddr_in *addr,
- char *buf, int bufsize)
-{
- int rc;
- socklen_t slen = sizeof(*addr);
-
- rc = recvfrom(fd, buf, bufsize, 0,
- (struct sockaddr *) addr, &slen);
- if (rc < 0) {
- LOGP(DMGCP, LOGL_ERROR, "Failed to receive message on: 0x%x errno: %d/%s\n",
- ENDPOINT_NUMBER(endp), errno, strerror(errno));
- return -1;
- }
-
- /* do not forward aynthing... maybe there is a packet from the bts */
- if (!endp->allocated)
- return -1;
-
- #warning "Slight spec violation. With connection mode recvonly we should attempt to forward."
-
- return rc;
-}
-
-static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
-{
- char buf[RTP_BUF_SIZE];
- struct sockaddr_in addr;
- struct mgcp_endpoint *endp;
- int rc, proto;
-
- endp = (struct mgcp_endpoint *) fd->data;
-
- rc = receive_from(endp, fd->fd, &addr, buf, sizeof(buf));
- if (rc <= 0)
- return -1;
-
- if (memcmp(&addr.sin_addr, &endp->net_end.addr, sizeof(addr.sin_addr)) != 0) {
- LOGP(DMGCP, LOGL_ERROR,
- "Endpoint 0x%x data from wrong address %s vs. ",
- ENDPOINT_NUMBER(endp), inet_ntoa(addr.sin_addr));
- LOGPC(DMGCP, LOGL_ERROR,
- "%s\n", inet_ntoa(endp->net_end.addr));
- return -1;
- }
-
- switch(endp->type) {
- case MGCP_RTP_DEFAULT:
- case MGCP_RTP_TRANSCODED:
- if (endp->net_end.rtp_port != addr.sin_port &&
- endp->net_end.rtcp_port != addr.sin_port) {
- LOGP(DMGCP, LOGL_ERROR,
- "Data from wrong source port %d on 0x%x\n",
- ntohs(addr.sin_port), ENDPOINT_NUMBER(endp));
- return -1;
- }
- break;
- case MGCP_OSMUX_BSC:
- case MGCP_OSMUX_BSC_NAT:
- break;
- }
-
- /* throw away the dummy message */
- if (rc == 1 && buf[0] == MGCP_DUMMY_LOAD) {
- LOGP(DMGCP, LOGL_NOTICE, "Filtered dummy from network on 0x%x\n",
- ENDPOINT_NUMBER(endp));
- return 0;
- }
-
- proto = fd == &endp->net_end.rtp ? MGCP_PROTO_RTP : MGCP_PROTO_RTCP;
- endp->net_end.packets += 1;
- endp->net_end.octets += rc;
-
- forward_data(fd->fd, &endp->taps[MGCP_TAP_NET_IN], buf, rc);
-
- switch (endp->type) {
- case MGCP_RTP_DEFAULT:
- return mgcp_send(endp, MGCP_DEST_BTS, proto == MGCP_PROTO_RTP,
- &addr, buf, rc);
- case MGCP_RTP_TRANSCODED:
- return mgcp_send_transcoder(&endp->trans_net, endp->cfg,
- proto == MGCP_PROTO_RTP, buf, rc);
- case MGCP_OSMUX_BSC_NAT:
- return osmux_xfrm_to_osmux(MGCP_DEST_BTS, buf, rc, endp);
- case MGCP_OSMUX_BSC: /* Should not happen */
- break;
- }
-
- LOGP(DMGCP, LOGL_ERROR, "Bad MGCP type %u on endpoint %u\n",
- endp->type, ENDPOINT_NUMBER(endp));
- return 0;
-}
-
-static void discover_bts(struct mgcp_endpoint *endp, int proto, struct sockaddr_in *addr)
-{
- struct mgcp_config *cfg = endp->cfg;
-
- if (proto == MGCP_PROTO_RTP && endp->bts_end.rtp_port == 0) {
- if (!cfg->bts_ip ||
- memcmp(&addr->sin_addr,
- &cfg->bts_in, sizeof(cfg->bts_in)) == 0 ||
- memcmp(&addr->sin_addr,
- &endp->bts_end.addr, sizeof(endp->bts_end.addr)) == 0) {
-
- endp->bts_end.rtp_port = addr->sin_port;
- endp->bts_end.addr = addr->sin_addr;
-
- LOGP(DMGCP, LOGL_NOTICE,
- "Found BTS for endpoint: 0x%x on port: %d/%d of %s\n",
- ENDPOINT_NUMBER(endp), ntohs(endp->bts_end.rtp_port),
- ntohs(endp->bts_end.rtcp_port), inet_ntoa(addr->sin_addr));
- }
- } else if (proto == MGCP_PROTO_RTCP && endp->bts_end.rtcp_port == 0) {
- if (memcmp(&endp->bts_end.addr, &addr->sin_addr,
- sizeof(endp->bts_end.addr)) == 0) {
- endp->bts_end.rtcp_port = addr->sin_port;
- }
- }
-}
-
-static int rtp_data_bts(struct osmo_fd *fd, unsigned int what)
-{
- char buf[RTP_BUF_SIZE];
- struct sockaddr_in addr;
- struct mgcp_endpoint *endp;
- int rc, proto;
-
- endp = (struct mgcp_endpoint *) fd->data;
-
- rc = receive_from(endp, fd->fd, &addr, buf, sizeof(buf));
- if (rc <= 0)
- return -1;
-
- proto = fd == &endp->bts_end.rtp ? MGCP_PROTO_RTP : MGCP_PROTO_RTCP;
-
- /* We have no idea who called us, maybe it is the BTS. */
- /* it was the BTS... */
- discover_bts(endp, proto, &addr);
-
- if (memcmp(&endp->bts_end.addr, &addr.sin_addr, sizeof(addr.sin_addr)) != 0) {
- LOGP(DMGCP, LOGL_ERROR,
- "Data from wrong bts %s on 0x%x\n",
- inet_ntoa(addr.sin_addr), ENDPOINT_NUMBER(endp));
- return -1;
- }
-
- if (endp->bts_end.rtp_port != addr.sin_port &&
- endp->bts_end.rtcp_port != addr.sin_port) {
- LOGP(DMGCP, LOGL_ERROR,
- "Data from wrong bts source port %d on 0x%x\n",
- ntohs(addr.sin_port), ENDPOINT_NUMBER(endp));
- return -1;
- }
-
- /* throw away the dummy message */
- if (rc == 1 && buf[0] == MGCP_DUMMY_LOAD) {
- LOGP(DMGCP, LOGL_NOTICE, "Filtered dummy from bts on 0x%x\n",
- ENDPOINT_NUMBER(endp));
- return 0;
- }
-
- /* do this before the loop handling */
- endp->bts_end.packets += 1;
- endp->bts_end.octets += rc;
-
- forward_data(fd->fd, &endp->taps[MGCP_TAP_BTS_IN], buf, rc);
-
- switch (endp->type) {
- case MGCP_RTP_DEFAULT:
- return mgcp_send(endp, MGCP_DEST_NET, proto == MGCP_PROTO_RTP,
- &addr, buf, rc);
- case MGCP_RTP_TRANSCODED:
- return mgcp_send_transcoder(&endp->trans_bts, endp->cfg,
- proto == MGCP_PROTO_RTP, buf, rc);
- case MGCP_OSMUX_BSC:
- /* OSMUX translation: BTS -> BSC */
- return osmux_xfrm_to_osmux(MGCP_DEST_NET, buf, rc, endp);
- case MGCP_OSMUX_BSC_NAT:
- break; /* Should not happen */
- }
-
- LOGP(DMGCP, LOGL_ERROR, "Bad MGCP type %u on endpoint %u\n",
- endp->type, ENDPOINT_NUMBER(endp));
- return 0;
-}
-
-static int rtp_data_transcoder(struct mgcp_rtp_end *end, struct mgcp_endpoint *_endp,
- int dest, struct osmo_fd *fd)
-{
- char buf[RTP_BUF_SIZE];
- struct sockaddr_in addr;
- struct mgcp_config *cfg;
- int rc, proto;
-
- cfg = _endp->cfg;
- rc = receive_from(_endp, fd->fd, &addr, buf, sizeof(buf));
- if (rc <= 0)
- return -1;
-
- proto = fd == &end->rtp ? MGCP_PROTO_RTP : MGCP_PROTO_RTCP;
-
- if (memcmp(&addr.sin_addr, &cfg->transcoder_in, sizeof(addr.sin_addr)) != 0) {
- LOGP(DMGCP, LOGL_ERROR,
- "Data not coming from transcoder dest: %d %s on 0x%x\n",
- dest, inet_ntoa(addr.sin_addr), ENDPOINT_NUMBER(_endp));
- return -1;
- }
-
- if (end->rtp_port != addr.sin_port &&
- end->rtcp_port != addr.sin_port) {
- LOGP(DMGCP, LOGL_ERROR,
- "Data from wrong transcoder dest %d source port %d on 0x%x\n",
- dest, ntohs(addr.sin_port), ENDPOINT_NUMBER(_endp));
- return -1;
- }
-
- /* throw away the dummy message */
- if (rc == 1 && buf[0] == MGCP_DUMMY_LOAD) {
- LOGP(DMGCP, LOGL_NOTICE, "Filtered dummy from transcoder dest %d on 0x%x\n",
- dest, ENDPOINT_NUMBER(_endp));
- return 0;
- }
-
- end->packets += 1;
- return mgcp_send(_endp, dest, proto == MGCP_PROTO_RTP, &addr, buf, rc);
-}
-
-static int rtp_data_trans_net(struct osmo_fd *fd, unsigned int what)
-{
- struct mgcp_endpoint *endp;
- endp = (struct mgcp_endpoint *) fd->data;
-
- return rtp_data_transcoder(&endp->trans_net, endp, MGCP_DEST_NET, fd);
-}
-
-static int rtp_data_trans_bts(struct osmo_fd *fd, unsigned int what)
-{
- struct mgcp_endpoint *endp;
- endp = (struct mgcp_endpoint *) fd->data;
-
- return rtp_data_transcoder(&endp->trans_bts, endp, MGCP_DEST_BTS, fd);
-}
-
-int mgcp_create_bind(const char *source_addr, struct osmo_fd *fd, int port)
-{
- struct sockaddr_in addr;
- int on = 1;
-
- fd->fd = socket(AF_INET, SOCK_DGRAM, 0);
- if (fd->fd < 0) {
- LOGP(DMGCP, LOGL_ERROR, "Failed to create UDP port.\n");
- return -1;
- }
-
- setsockopt(fd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_port = htons(port);
- inet_aton(source_addr, &addr.sin_addr);
-
- if (bind(fd->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- close(fd->fd);
- fd->fd = -1;
- return -1;
- }
-
- return 0;
-}
-
-int mgcp_set_ip_tos(int fd, int tos)
-{
- int ret;
- ret = setsockopt(fd, IPPROTO_IP, IP_TOS,
- &tos, sizeof(tos));
- return ret != 0;
-}
-
-static int bind_rtp(struct mgcp_config *cfg, const char *source_addr,
- struct mgcp_rtp_end *rtp_end, int endpno)
-{
- if (mgcp_create_bind(source_addr, &rtp_end->rtp,
- rtp_end->local_port) != 0) {
- LOGP(DMGCP, LOGL_ERROR, "Failed to create RTP port: %s:%d on 0x%x\n",
- source_addr, rtp_end->local_port, endpno);
- goto cleanup0;
- }
-
- if (mgcp_create_bind(source_addr, &rtp_end->rtcp,
- rtp_end->local_port + 1) != 0) {
- LOGP(DMGCP, LOGL_ERROR, "Failed to create RTCP port: %s:%d on 0x%x\n",
- source_addr, rtp_end->local_port + 1, endpno);
- goto cleanup1;
- }
-
- mgcp_set_ip_tos(rtp_end->rtp.fd, cfg->endp_dscp);
- mgcp_set_ip_tos(rtp_end->rtcp.fd, cfg->endp_dscp);
-
- rtp_end->rtp.when = BSC_FD_READ;
- if (osmo_fd_register(&rtp_end->rtp) != 0) {
- LOGP(DMGCP, LOGL_ERROR, "Failed to register RTP port %d on 0x%x\n",
- rtp_end->local_port, endpno);
- goto cleanup2;
- }
-
- rtp_end->rtcp.when = BSC_FD_READ;
- if (osmo_fd_register(&rtp_end->rtcp) != 0) {
- LOGP(DMGCP, LOGL_ERROR, "Failed to register RTCP port %d on 0x%x\n",
- rtp_end->local_port + 1, endpno);
- goto cleanup3;
- }
-
- return 0;
-
-cleanup3:
- osmo_fd_unregister(&rtp_end->rtp);
-cleanup2:
- close(rtp_end->rtcp.fd);
- rtp_end->rtcp.fd = -1;
-cleanup1:
- close(rtp_end->rtp.fd);
- rtp_end->rtp.fd = -1;
-cleanup0:
- return -1;
-}
-
-static int int_bind(const char *port,
- struct mgcp_rtp_end *end, int (*cb)(struct osmo_fd *, unsigned),
- struct mgcp_endpoint *_endp,
- const char *source_addr, int rtp_port)
-{
- if (end->rtp.fd != -1 || end->rtcp.fd != -1) {
- LOGP(DMGCP, LOGL_ERROR, "Previous %s was still bound on %d\n",
- port, ENDPOINT_NUMBER(_endp));
- mgcp_free_rtp_port(end);
- }
-
- end->local_port = rtp_port;
- end->rtp.cb = cb;
- end->rtp.data = _endp;
- end->rtcp.data = _endp;
- end->rtcp.cb = cb;
- return bind_rtp(_endp->cfg, source_addr, end, ENDPOINT_NUMBER(_endp));
-}
-
-int mgcp_bind_bts_rtp_port(struct mgcp_endpoint *endp, int rtp_port)
-{
- return int_bind("bts-port", &endp->bts_end,
- rtp_data_bts, endp,
- mgcp_bts_src_addr(endp), rtp_port);
-}
-
-int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port)
-{
- return int_bind("net-port", &endp->net_end,
- rtp_data_net, endp,
- mgcp_net_src_addr(endp), rtp_port);
-}
-
-int mgcp_bind_trans_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port)
-{
- return int_bind("trans-net", &endp->trans_net,
- rtp_data_trans_net, endp,
- endp->cfg->source_addr, rtp_port);
-}
-
-int mgcp_bind_trans_bts_rtp_port(struct mgcp_endpoint *endp, int rtp_port)
-{
- return int_bind("trans-bts", &endp->trans_bts,
- rtp_data_trans_bts, endp,
- endp->cfg->source_addr, rtp_port);
-}
-
-int mgcp_free_rtp_port(struct mgcp_rtp_end *end)
-{
- if (end->rtp.fd != -1) {
- close(end->rtp.fd);
- end->rtp.fd = -1;
- osmo_fd_unregister(&end->rtp);
- }
-
- if (end->rtcp.fd != -1) {
- close(end->rtcp.fd);
- end->rtcp.fd = -1;
- osmo_fd_unregister(&end->rtcp);
- }
-
- return 0;
-}
-
-
-void mgcp_state_calc_loss(struct mgcp_rtp_state *state,
- struct mgcp_rtp_end *end, uint32_t *expected,
- int *loss)
-{
- *expected = state->stats_cycles + state->stats_max_seq;
- *expected = *expected - state->stats_base_seq + 1;
-
- if (!state->stats_initialized) {
- *expected = 0;
- *loss = 0;
- return;
- }
-
- /*
- * Make sure the sign is correct and use the biggest
- * positive/negative number that fits.
- */
- *loss = *expected - end->packets;
- if (*expected < end->packets) {
- if (*loss > 0)
- *loss = INT_MIN;
- } else {
- if (*loss < 0)
- *loss = INT_MAX;
- }
-}
-
-uint32_t mgcp_state_calc_jitter(struct mgcp_rtp_state *state)
-{
- if (!state->stats_initialized)
- return 0;
- return state->stats_jitter >> 4;
-}
diff --git a/openbsc/src/libmgcp/mgcp_osmux.c b/openbsc/src/libmgcp/mgcp_osmux.c
deleted file mode 100644
index b46a80e7f..000000000
--- a/openbsc/src/libmgcp/mgcp_osmux.c
+++ /dev/null
@@ -1,586 +0,0 @@
-/*
- * (C) 2012-2013 by Pablo Neira Ayuso <pablo@gnumonks.org>
- * (C) 2012-2013 by On Waves ehf <http://www.on-waves.com>
- * All rights not specifically granted under this license are reserved.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Affero General Public License as published by the
- * Free Software Foundation; either version 3 of the License, or (at your
- * option) any later version.
- */
-
-#include <stdio.h> /* for printf */
-#include <string.h> /* for memcpy */
-#include <stdlib.h> /* for abs */
-#include <inttypes.h> /* for PRIu64 */
-#include <netinet/in.h>
-#include <osmocom/core/msgb.h>
-#include <osmocom/core/talloc.h>
-
-#include <osmocom/netif/osmux.h>
-#include <osmocom/netif/rtp.h>
-
-#include <openbsc/mgcp.h>
-#include <openbsc/mgcp_internal.h>
-#include <openbsc/osmux.h>
-
-static struct osmo_fd osmux_fd;
-
-static LLIST_HEAD(osmux_handle_list);
-
-struct osmux_handle {
- struct llist_head head;
- struct osmux_in_handle *in;
- struct in_addr rem_addr;
- int rem_port;
- int refcnt;
-};
-
-static void *osmux;
-
-static void osmux_deliver(struct msgb *batch_msg, void *data)
-{
- struct osmux_handle *handle = data;
- struct sockaddr_in out = {
- .sin_family = AF_INET,
- .sin_port = handle->rem_port,
- };
-
- memcpy(&out.sin_addr, &handle->rem_addr, sizeof(handle->rem_addr));
- sendto(osmux_fd.fd, batch_msg->data, batch_msg->len, 0,
- (struct sockaddr *)&out, sizeof(out));
- msgb_free(batch_msg);
-}
-
-static struct osmux_handle *
-osmux_handle_find_get(struct in_addr *addr, int rem_port)
-{
- struct osmux_handle *h;
-
- /* Lookup for existing OSMUX handle for this destination address. */
- llist_for_each_entry(h, &osmux_handle_list, head) {
- if (memcmp(&h->rem_addr, addr, sizeof(struct in_addr)) == 0 &&
- h->rem_port == rem_port) {
- LOGP(DMGCP, LOGL_DEBUG, "using existing OSMUX handle "
- "for addr=%s:%d\n",
- inet_ntoa(*addr), ntohs(rem_port));
- h->refcnt++;
- return h;
- }
- }
-
- return NULL;
-}
-
-static void osmux_handle_put(struct osmux_in_handle *in)
-{
- struct osmux_handle *h;
-
- /* Lookup for existing OSMUX handle for this destination address. */
- llist_for_each_entry(h, &osmux_handle_list, head) {
- if (h->in == in) {
- if (--h->refcnt == 0) {
- LOGP(DMGCP, LOGL_INFO,
- "Releasing unused osmux handle for %s:%d\n",
- inet_ntoa(h->rem_addr),
- ntohs(h->rem_port));
- LOGP(DMGCP, LOGL_INFO, "Stats: "
- "input RTP msgs: %u bytes: %"PRIu64" "
- "output osmux msgs: %u bytes: %"PRIu64"\n",
- in->stats.input_rtp_msgs,
- in->stats.input_rtp_bytes,
- in->stats.output_osmux_msgs,
- in->stats.output_osmux_bytes);
- llist_del(&h->head);
- osmux_xfrm_input_fini(h->in);
- talloc_free(h);
- }
- return;
- }
- }
- LOGP(DMGCP, LOGL_ERROR, "cannot find Osmux input handle %p\n", in);
-}
-
-static struct osmux_handle *
-osmux_handle_alloc(struct mgcp_config *cfg, struct in_addr *addr, int rem_port)
-{
- struct osmux_handle *h;
-
- h = talloc_zero(osmux, struct osmux_handle);
- if (!h)
- return NULL;
- h->rem_addr = *addr;
- h->rem_port = rem_port;
- h->refcnt++;
-
- h->in = talloc_zero(h, struct osmux_in_handle);
- if (!h->in) {
- talloc_free(h);
- return NULL;
- }
-
- h->in->osmux_seq = 0; /* sequence number to start OSmux message from */
- h->in->batch_factor = cfg->osmux_batch;
- /* If batch size is zero, the library defaults to 1470 bytes. */
- h->in->batch_size = cfg->osmux_batch_size;
- h->in->deliver = osmux_deliver;
- osmux_xfrm_input_init(h->in);
- h->in->data = h;
-
- llist_add(&h->head, &osmux_handle_list);
-
- LOGP(DMGCP, LOGL_DEBUG, "created new OSMUX handle for addr=%s:%d\n",
- inet_ntoa(*addr), ntohs(rem_port));
-
- return h;
-}
-
-static struct osmux_in_handle *
-osmux_handle_lookup(struct mgcp_config *cfg, struct in_addr *addr, int rem_port)
-{
- struct osmux_handle *h;
-
- h = osmux_handle_find_get(addr, rem_port);
- if (h != NULL)
- return h->in;
-
- h = osmux_handle_alloc(cfg, addr, rem_port);
- if (h == NULL)
- return NULL;
-
- return h->in;
-}
-
-int osmux_xfrm_to_osmux(int type, char *buf, int rc, struct mgcp_endpoint *endp)
-{
- int ret;
- struct msgb *msg;
-
- msg = msgb_alloc(4096, "RTP");
- if (!msg)
- return 0;
-
- memcpy(msg->data, buf, rc);
- msgb_put(msg, rc);
-
- while ((ret = osmux_xfrm_input(endp->osmux.in, msg, endp->osmux.cid)) > 0) {
- /* batch full, build and deliver it */
- osmux_xfrm_input_deliver(endp->osmux.in);
- }
- return 0;
-}
-
-static struct mgcp_endpoint *
-endpoint_lookup(struct mgcp_config *cfg, int cid,
- struct in_addr *from_addr, int type)
-{
- struct mgcp_endpoint *tmp = NULL;
- int i;
-
- /* Lookup for the endpoint that corresponds to this port */
- for (i=0; i<cfg->trunk.number_endpoints; i++) {
- struct in_addr *this;
-
- tmp = &cfg->trunk.endpoints[i];
-
- if (!tmp->allocated)
- continue;
-
- switch(type) {
- case MGCP_DEST_NET:
- this = &tmp->net_end.addr;
- break;
- case MGCP_DEST_BTS:
- this = &tmp->bts_end.addr;
- break;
- default:
- /* Should not ever happen */
- LOGP(DMGCP, LOGL_ERROR, "Bad type %d. Fix your code.\n", type);
- return NULL;
- }
-
- if (tmp->osmux.cid == cid && this->s_addr == from_addr->s_addr)
- return tmp;
- }
-
- LOGP(DMGCP, LOGL_ERROR, "Cannot find endpoint with cid=%d\n", cid);
-
- return NULL;
-}
-
-static void scheduled_tx_net_cb(struct msgb *msg, void *data)
-{
- struct mgcp_endpoint *endp = data;
- struct sockaddr_in addr = {
- .sin_addr = endp->net_end.addr,
- .sin_port = endp->net_end.rtp_port,
- };
-
- endp->bts_end.octets += msg->len;
- endp->bts_end.packets++;
-
- mgcp_send(endp, MGCP_DEST_NET, 1, &addr, (char *)msg->data, msg->len);
- msgb_free(msg);
-}
-
-static void scheduled_tx_bts_cb(struct msgb *msg, void *data)
-{
- struct mgcp_endpoint *endp = data;
- struct sockaddr_in addr = {
- .sin_addr = endp->bts_end.addr,
- .sin_port = endp->bts_end.rtp_port,
- };
-
- endp->net_end.octets += msg->len;
- endp->net_end.packets++;
-
- mgcp_send(endp, MGCP_DEST_BTS, 1, &addr, (char *)msg->data, msg->len);
- msgb_free(msg);
-}
-
-static struct msgb *osmux_recv(struct osmo_fd *ofd, struct sockaddr_in *addr)
-{
- struct msgb *msg;
- socklen_t slen = sizeof(*addr);
- int ret;
-
- msg = msgb_alloc(4096, "OSMUX");
- if (!msg) {
- LOGP(DMGCP, LOGL_ERROR, "cannot allocate message\n");
- return NULL;
- }
- ret = recvfrom(ofd->fd, msg->data, msg->data_len, 0,
- (struct sockaddr *)addr, &slen);
- if (ret <= 0) {
- msgb_free(msg);
- LOGP(DMGCP, LOGL_ERROR, "cannot receive message\n");
- return NULL;
- }
- msgb_put(msg, ret);
-
- return msg;
-}
-
-#define osmux_chunk_length(msg, rem) (rem - msg->len);
-
-int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what)
-{
- struct msgb *msg;
- struct osmux_hdr *osmuxh;
- struct llist_head list;
- struct sockaddr_in addr;
- struct mgcp_config *cfg = ofd->data;
- uint32_t rem;
-
- msg = osmux_recv(ofd, &addr);
- if (!msg)
- return -1;
-
- /* not any further processing dummy messages */
- if (msg->data[0] == MGCP_DUMMY_LOAD)
- goto out;
-
- rem = msg->len;
- while((osmuxh = osmux_xfrm_output_pull(msg)) != NULL) {
- struct mgcp_endpoint *endp;
-
- /* Yes, we use MGCP_DEST_NET to locate the origin */
- endp = endpoint_lookup(cfg, osmuxh->circuit_id,
- &addr.sin_addr, MGCP_DEST_NET);
- if (!endp) {
- LOGP(DMGCP, LOGL_ERROR,
- "Cannot find an endpoint for circuit_id=%d\n",
- osmuxh->circuit_id);
- goto out;
- }
- endp->osmux.stats.octets += osmux_chunk_length(msg, rem);
- endp->osmux.stats.chunks++;
- rem = msg->len;
-
- osmux_xfrm_output(osmuxh, &endp->osmux.out, &list);
- osmux_tx_sched(&list, scheduled_tx_bts_cb, endp);
- }
-out:
- msgb_free(msg);
- return 0;
-}
-
-/* This is called from the bsc-nat */
-static int osmux_handle_dummy(struct mgcp_config *cfg, struct sockaddr_in *addr,
- struct msgb *msg)
-{
- struct mgcp_endpoint *endp;
- uint8_t osmux_cid;
-
- if (msg->len < 1 + sizeof(osmux_cid)) {
- LOGP(DMGCP, LOGL_ERROR,
- "Discarding truncated Osmux dummy load\n");
- goto out;
- }
-
- LOGP(DMGCP, LOGL_DEBUG, "Received Osmux dummy load from %s\n",
- inet_ntoa(addr->sin_addr));
-
- if (!cfg->osmux) {
- LOGP(DMGCP, LOGL_ERROR,
- "bsc wants to use Osmux but bsc-nat did not request it\n");
- goto out;
- }
-
- /* extract the osmux CID from the dummy message */
- memcpy(&osmux_cid, &msg->data[1], sizeof(osmux_cid));
-
- endp = endpoint_lookup(cfg, osmux_cid, &addr->sin_addr, MGCP_DEST_BTS);
- if (!endp) {
- LOGP(DMGCP, LOGL_ERROR,
- "Cannot find endpoint for Osmux CID %d\n", osmux_cid);
- goto out;
- }
-
- if (endp->osmux.state == OSMUX_STATE_ENABLED)
- goto out;
-
- if (osmux_enable_endpoint(endp, OSMUX_ROLE_BSC_NAT,
- &addr->sin_addr, addr->sin_port) < 0 ){
- LOGP(DMGCP, LOGL_ERROR,
- "Could not enable osmux in endpoint %d\n",
- ENDPOINT_NUMBER(endp));
- goto out;
- }
-
- LOGP(DMGCP, LOGL_INFO, "Enabling osmux in endpoint %d for %s:%u\n",
- ENDPOINT_NUMBER(endp), inet_ntoa(addr->sin_addr),
- ntohs(addr->sin_port));
-out:
- msgb_free(msg);
- return 0;
-}
-
-int osmux_read_from_bsc_cb(struct osmo_fd *ofd, unsigned int what)
-{
- struct msgb *msg;
- struct osmux_hdr *osmuxh;
- struct llist_head list;
- struct sockaddr_in addr;
- struct mgcp_config *cfg = ofd->data;
- uint32_t rem;
-
- msg = osmux_recv(ofd, &addr);
- if (!msg)
- return -1;
-
- /* not any further processing dummy messages */
- if (msg->data[0] == MGCP_DUMMY_LOAD)
- return osmux_handle_dummy(cfg, &addr, msg);
-
- rem = msg->len;
- while((osmuxh = osmux_xfrm_output_pull(msg)) != NULL) {
- struct mgcp_endpoint *endp;
-
- /* Yes, we use MGCP_DEST_BTS to locate the origin */
- endp = endpoint_lookup(cfg, osmuxh->circuit_id,
- &addr.sin_addr, MGCP_DEST_BTS);
- if (!endp) {
- LOGP(DMGCP, LOGL_ERROR,
- "Cannot find an endpoint for circuit_id=%d\n",
- osmuxh->circuit_id);
- goto out;
- }
- endp->osmux.stats.octets += osmux_chunk_length(msg, rem);
- endp->osmux.stats.chunks++;
- rem = msg->len;
-
- osmux_xfrm_output(osmuxh, &endp->osmux.out, &list);
- osmux_tx_sched(&list, scheduled_tx_net_cb, endp);
- }
-out:
- msgb_free(msg);
- return 0;
-}
-
-int osmux_init(int role, struct mgcp_config *cfg)
-{
- int ret;
-
- switch(role) {
- case OSMUX_ROLE_BSC:
- osmux_fd.cb = osmux_read_from_bsc_nat_cb;
- break;
- case OSMUX_ROLE_BSC_NAT:
- osmux_fd.cb = osmux_read_from_bsc_cb;
- break;
- default:
- LOGP(DMGCP, LOGL_ERROR, "wrong role for OSMUX\n");
- return -1;
- }
- osmux_fd.data = cfg;
-
- ret = mgcp_create_bind(cfg->osmux_addr, &osmux_fd, cfg->osmux_port);
- if (ret < 0) {
- LOGP(DMGCP, LOGL_ERROR, "cannot bind OSMUX socket\n");
- return ret;
- }
- mgcp_set_ip_tos(osmux_fd.fd, cfg->endp_dscp);
- osmux_fd.when |= BSC_FD_READ;
-
- ret = osmo_fd_register(&osmux_fd);
- if (ret < 0) {
- LOGP(DMGCP, LOGL_ERROR, "cannot register OSMUX socket\n");
- return ret;
- }
- cfg->osmux_init = 1;
-
- return 0;
-}
-
-int osmux_enable_endpoint(struct mgcp_endpoint *endp, int role,
- struct in_addr *addr, uint16_t port)
-{
- /* If osmux is enabled, initialize the output handler. This handler is
- * used to reconstruct the RTP flow from osmux. The RTP SSRC is
- * allocated based on the circuit ID (endp->osmux.cid), which is unique
- * in the local scope to the BSC/BSC-NAT. We use it to divide the RTP
- * SSRC space (2^32) by the 256 possible circuit IDs, then randomly
- * select one value from that window. Thus, we have no chance to have
- * overlapping RTP SSRC traveling to the BTSes behind the BSC,
- * similarly, for flows traveling to the MSC.
- */
- static const uint32_t rtp_ssrc_winlen = UINT32_MAX / 256;
-
- if (endp->osmux.state == OSMUX_STATE_DISABLED) {
- LOGP(DMGCP, LOGL_ERROR, "Endpoint %u didn't request Osmux\n",
- ENDPOINT_NUMBER(endp));
- return -1;
- }
-
- osmux_xfrm_output_init(&endp->osmux.out,
- (endp->osmux.cid * rtp_ssrc_winlen) +
- (random() % rtp_ssrc_winlen));
-
- endp->osmux.in = osmux_handle_lookup(endp->cfg, addr, port);
- if (!endp->osmux.in) {
- LOGP(DMGCP, LOGL_ERROR, "Cannot allocate input osmux handle\n");
- return -1;
- }
- if (!osmux_xfrm_input_open_circuit(endp->osmux.in, endp->osmux.cid,
- endp->cfg->osmux_dummy)) {
- LOGP(DMGCP, LOGL_ERROR, "Cannot open osmux circuit %u\n",
- endp->osmux.cid);
- return -1;
- }
-
- switch (endp->cfg->role) {
- case MGCP_BSC_NAT:
- endp->type = MGCP_OSMUX_BSC_NAT;
- break;
- case MGCP_BSC:
- endp->type = MGCP_OSMUX_BSC;
- break;
- }
- endp->osmux.state = OSMUX_STATE_ENABLED;
-
- return 0;
-}
-
-void osmux_disable_endpoint(struct mgcp_endpoint *endp)
-{
- LOGP(DMGCP, LOGL_INFO, "Releasing endpoint %u using Osmux CID %u\n",
- ENDPOINT_NUMBER(endp), endp->osmux.cid);
- osmux_xfrm_input_close_circuit(endp->osmux.in, endp->osmux.cid);
- endp->osmux.state = OSMUX_STATE_DISABLED;
- endp->osmux.cid = -1;
- osmux_handle_put(endp->osmux.in);
-}
-
-void osmux_release_cid(struct mgcp_endpoint *endp)
-{
- if (endp->osmux.allocated_cid >= 0)
- osmux_put_cid(endp->osmux.allocated_cid);
- endp->osmux.allocated_cid = -1;
-}
-
-void osmux_allocate_cid(struct mgcp_endpoint *endp)
-{
- osmux_release_cid(endp);
- endp->osmux.allocated_cid = osmux_get_cid();
-}
-
-/* We don't need to send the dummy load for osmux so often as another endpoint
- * may have already punched the hole in the firewall. This approach is simple
- * though.
- */
-int osmux_send_dummy(struct mgcp_endpoint *endp)
-{
- char buf[1 + sizeof(uint8_t)];
- struct in_addr addr_unset = {};
-
- buf[0] = MGCP_DUMMY_LOAD;
- memcpy(&buf[1], &endp->osmux.cid, sizeof(endp->osmux.cid));
-
- /* Wait until we have the connection information from MDCX */
- if (memcmp(&endp->net_end.addr, &addr_unset, sizeof(addr_unset)) == 0)
- return 0;
-
- if (endp->osmux.state == OSMUX_STATE_ACTIVATING) {
- if (osmux_enable_endpoint(endp, OSMUX_ROLE_BSC,
- &endp->net_end.addr,
- htons(endp->cfg->osmux_port)) < 0) {
- LOGP(DMGCP, LOGL_ERROR,
- "Could not activate osmux in endpoint %d\n",
- ENDPOINT_NUMBER(endp));
- }
- LOGP(DMGCP, LOGL_ERROR,
- "Osmux CID %u for %s:%u is now enabled\n",
- endp->osmux.cid, inet_ntoa(endp->net_end.addr),
- endp->cfg->osmux_port);
- }
- LOGP(DMGCP, LOGL_DEBUG,
- "sending OSMUX dummy load to %s CID %u\n",
- inet_ntoa(endp->net_end.addr), endp->osmux.cid);
-
- return mgcp_udp_send(osmux_fd.fd, &endp->net_end.addr,
- htons(endp->cfg->osmux_port), buf, sizeof(buf));
-}
-
-/* bsc-nat allocates/releases the Osmux circuit ID */
-static uint8_t osmux_cid_bitmap[(OSMUX_CID_MAX + 1) / 8];
-
-int osmux_used_cid(void)
-{
- int i, j, used = 0;
-
- for (i = 0; i < sizeof(osmux_cid_bitmap); i++) {
- for (j = 0; j < 8; j++) {
- if (osmux_cid_bitmap[i] & (1 << j))
- used += 1;
- }
- }
-
- return used;
-}
-
-int osmux_get_cid(void)
-{
- int i, j;
-
- for (i = 0; i < sizeof(osmux_cid_bitmap); i++) {
- for (j = 0; j < 8; j++) {
- if (osmux_cid_bitmap[i] & (1 << j))
- continue;
-
- osmux_cid_bitmap[i] |= (1 << j);
- LOGP(DMGCP, LOGL_DEBUG,
- "Allocating Osmux CID %u from pool\n", (i * 8) + j);
- return (i * 8) + j;
- }
- }
-
- LOGP(DMGCP, LOGL_ERROR, "All Osmux circuits are in use!\n");
- return -1;
-}
-
-void osmux_put_cid(uint8_t osmux_cid)
-{
- LOGP(DMGCP, LOGL_DEBUG, "Osmux CID %u is back to the pool\n", osmux_cid);
- osmux_cid_bitmap[osmux_cid / 8] &= ~(1 << (osmux_cid % 8));
-}
diff --git a/openbsc/src/libmgcp/mgcp_protocol.c b/openbsc/src/libmgcp/mgcp_protocol.c
deleted file mode 100644
index 4fcadd949..000000000
--- a/openbsc/src/libmgcp/mgcp_protocol.c
+++ /dev/null
@@ -1,1598 +0,0 @@
-/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
-/* The protocol implementation */
-
-/*
- * (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
- * (C) 2009-2012 by On-Waves
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <ctype.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <time.h>
-#include <limits.h>
-#include <unistd.h>
-#include <errno.h>
-
-#include <osmocom/core/msgb.h>
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/select.h>
-
-#include <openbsc/mgcp.h>
-#include <openbsc/mgcp_internal.h>
-
-#define for_each_non_empty_line(line, save) \
- for (line = strtok_r(NULL, "\r\n", &save); line;\
- line = strtok_r(NULL, "\r\n", &save))
-
-
-static void mgcp_rtp_end_reset(struct mgcp_rtp_end *end);
-
-struct mgcp_request {
- char *name;
- struct msgb *(*handle_request) (struct mgcp_parse_data *data);
- char *debug_name;
-};
-
-#define MGCP_REQUEST(NAME, REQ, DEBUG_NAME) \
- { .name = NAME, .handle_request = REQ, .debug_name = DEBUG_NAME },
-
-static struct msgb *handle_audit_endpoint(struct mgcp_parse_data *data);
-static struct msgb *handle_create_con(struct mgcp_parse_data *data);
-static struct msgb *handle_delete_con(struct mgcp_parse_data *data);
-static struct msgb *handle_modify_con(struct mgcp_parse_data *data);
-static struct msgb *handle_rsip(struct mgcp_parse_data *data);
-static struct msgb *handle_noti_req(struct mgcp_parse_data *data);
-
-static void create_transcoder(struct mgcp_endpoint *endp);
-static void delete_transcoder(struct mgcp_endpoint *endp);
-
-static int setup_rtp_processing(struct mgcp_endpoint *endp);
-
-static int mgcp_analyze_header(struct mgcp_parse_data *parse, char *data);
-
-static int mgcp_check_param(const struct mgcp_endpoint *endp, const char *line)
-{
- const size_t line_len = strlen(line);
- if (line[0] != '\0' && line_len < 2) {
- LOGP(DMGCP, LOGL_ERROR,
- "Wrong MGCP option format: '%s' on 0x%x\n",
- line, ENDPOINT_NUMBER(endp));
- return 0;
- }
-
- return 1;
-}
-
-static uint32_t generate_call_id(struct mgcp_config *cfg)
-{
- int i;
-
- /* use the call id */
- ++cfg->last_call_id;
-
- /* handle wrap around */
- if (cfg->last_call_id == CI_UNUSED)
- ++cfg->last_call_id;
-
- /* callstack can only be of size number_of_endpoints */
- /* verify that the call id is free, e.g. in case of overrun */
- for (i = 1; i < cfg->trunk.number_endpoints; ++i)
- if (cfg->trunk.endpoints[i].ci == cfg->last_call_id)
- return generate_call_id(cfg);
-
- return cfg->last_call_id;
-}
-
-/*
- * array of function pointers for handling various
- * messages. In the future this might be binary sorted
- * for performance reasons.
- */
-static const struct mgcp_request mgcp_requests [] = {
- MGCP_REQUEST("AUEP", handle_audit_endpoint, "AuditEndpoint")
- MGCP_REQUEST("CRCX", handle_create_con, "CreateConnection")
- MGCP_REQUEST("DLCX", handle_delete_con, "DeleteConnection")
- MGCP_REQUEST("MDCX", handle_modify_con, "ModifiyConnection")
- MGCP_REQUEST("RQNT", handle_noti_req, "NotificationRequest")
-
- /* SPEC extension */
- MGCP_REQUEST("RSIP", handle_rsip, "ReSetInProgress")
-};
-
-static struct msgb *mgcp_msgb_alloc(void)
-{
- struct msgb *msg;
- msg = msgb_alloc_headroom(4096, 128, "MGCP msg");
- if (!msg)
- LOGP(DMGCP, LOGL_ERROR, "Failed to msgb for MGCP data.\n");
-
- return msg;
-}
-
-static struct msgb *do_retransmission(const struct mgcp_endpoint *endp)
-{
- struct msgb *msg = mgcp_msgb_alloc();
- if (!msg)
- return NULL;
-
- msg->l2h = msgb_put(msg, strlen(endp->last_response));
- memcpy(msg->l2h, endp->last_response, msgb_l2len(msg));
- return msg;
-}
-
-static struct msgb *create_resp(struct mgcp_endpoint *endp, int code,
- const char *txt, const char *msg,
- const char *trans, const char *param,
- const char *sdp)
-{
- int len;
- struct msgb *res;
-
- res = mgcp_msgb_alloc();
- if (!res)
- return NULL;
-
- len = snprintf((char *) res->data, 2048, "%d %s%s%s\r\n%s",
- code, trans, txt, param ? param : "", sdp ? sdp : "");
- if (len < 0) {
- LOGP(DMGCP, LOGL_ERROR, "Failed to sprintf MGCP response.\n");
- msgb_free(res);
- return NULL;
- }
-
- res->l2h = msgb_put(res, len);
- LOGP(DMGCP, LOGL_DEBUG, "Generated response: code: %d for '%s'\n", code, res->l2h);
-
- /*
- * Remember the last transmission per endpoint.
- */
- if (endp) {
- struct mgcp_trunk_config *tcfg = endp->tcfg;
- talloc_free(endp->last_response);
- talloc_free(endp->last_trans);
- endp->last_trans = talloc_strdup(tcfg->endpoints, trans);
- endp->last_response = talloc_strndup(tcfg->endpoints,
- (const char *) res->l2h,
- msgb_l2len(res));
- }
-
- return res;
-}
-
-static struct msgb *create_ok_resp_with_param(struct mgcp_endpoint *endp,
- int code, const char *msg,
- const char *trans, const char *param)
-{
- return create_resp(endp, code, " OK", msg, trans, param, NULL);
-}
-
-static struct msgb *create_ok_response(struct mgcp_endpoint *endp,
- int code, const char *msg, const char *trans)
-{
- return create_ok_resp_with_param(endp, code, msg, trans, NULL);
-}
-
-static struct msgb *create_err_response(struct mgcp_endpoint *endp,
- int code, const char *msg, const char *trans)
-{
- return create_resp(endp, code, " FAIL", msg, trans, NULL, NULL);
-}
-
-static int write_response_sdp(struct mgcp_endpoint *endp,
- char *sdp_record, size_t size, const char *addr)
-{
- const char *fmtp_extra;
- const char *audio_name;
- int payload_type;
- int len;
- int nchars;
-
- endp->cfg->get_net_downlink_format_cb(endp, &payload_type,
- &audio_name, &fmtp_extra);
-
- len = snprintf(sdp_record, size,
- "v=0\r\n"
- "o=- %u 23 IN IP4 %s\r\n"
- "s=-\r\n"
- "c=IN IP4 %s\r\n"
- "t=0 0\r\n",
- endp->ci, addr, addr);
-
- if (len < 0 || len >= size)
- goto buffer_too_small;
-
- if (payload_type >= 0) {
- nchars = snprintf(sdp_record + len, size - len,
- "m=audio %d RTP/AVP %d\r\n",
- endp->net_end.local_port, payload_type);
- if (nchars < 0 || nchars >= size - len)
- goto buffer_too_small;
-
- len += nchars;
-
- if (audio_name && endp->tcfg->audio_send_name) {
- nchars = snprintf(sdp_record + len, size - len,
- "a=rtpmap:%d %s\r\n",
- payload_type, audio_name);
-
- if (nchars < 0 || nchars >= size - len)
- goto buffer_too_small;
-
- len += nchars;
- }
-
- if (fmtp_extra) {
- nchars = snprintf(sdp_record + len, size - len,
- "%s\r\n", fmtp_extra);
-
- if (nchars < 0 || nchars >= size - len)
- goto buffer_too_small;
-
- len += nchars;
- }
- }
- if (endp->bts_end.packet_duration_ms > 0 && endp->tcfg->audio_send_ptime) {
- nchars = snprintf(sdp_record + len, size - len,
- "a=ptime:%d\r\n",
- endp->bts_end.packet_duration_ms);
- if (nchars < 0 || nchars >= size - len)
- goto buffer_too_small;
-
- len += nchars;
- }
-
- return len;
-
-buffer_too_small:
- LOGP(DMGCP, LOGL_ERROR, "SDP buffer too small: %zu (needed %d)\n",
- size, len);
- return -1;
-}
-
-static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
- const char *msg, const char *trans_id)
-{
- const char *addr = endp->cfg->local_ip;
- char sdp_record[4096];
- int len;
- int nchars;
- char osmux_extension[strlen("\nX-Osmux: 255") + 1];
-
- if (!addr)
- addr = mgcp_net_src_addr(endp);
-
- if (endp->osmux.state == OSMUX_STATE_NEGOTIATING)
- sprintf(osmux_extension, "\nX-Osmux: %u", endp->osmux.cid);
- else
- osmux_extension[0] = '\0';
-
- len = snprintf(sdp_record, sizeof(sdp_record),
- "I: %u%s\n\n", endp->ci, osmux_extension);
- if (len < 0)
- return NULL;
-
- nchars = write_response_sdp(endp, sdp_record + len,
- sizeof(sdp_record) - len - 1, addr);
- if (nchars < 0)
- return NULL;
-
- len += nchars;
-
- sdp_record[sizeof(sdp_record) - 1] = '\0';
-
- return create_resp(endp, 200, " OK", msg, trans_id, NULL, sdp_record);
-}
-
-static void send_dummy(struct mgcp_endpoint *endp)
-{
- if (endp->osmux.state != OSMUX_STATE_DISABLED)
- osmux_send_dummy(endp);
- else
- mgcp_send_dummy(endp);
-}
-
-/*
- * handle incoming messages:
- * - this can be a command (four letters, space, transaction id)
- * - or a response (three numbers, space, transaction id)
- */
-struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
-{
- struct mgcp_parse_data pdata;
- int i, code, handled = 0;
- struct msgb *resp = NULL;
- char *data;
- unsigned char *tail = msg->l2h + msgb_l2len(msg); /* char after l2 data */
-
- if (msgb_l2len(msg) < 4) {
- LOGP(DMGCP, LOGL_ERROR, "msg too short: %d\n", msg->len);
- return NULL;
- }
-
- /* Ensure that the msg->l2h is NUL terminated. */
- if (tail[-1] == '\0')
- /* nothing to do */;
- else if (msgb_tailroom(msg) > 0)
- tail[0] = '\0';
- else if (tail[-1] == '\r' || tail[-1] == '\n')
- tail[-1] = '\0';
- else {
- LOGP(DMGCP, LOGL_ERROR, "Cannot NUL terminate MGCP message: "
- "Length: %d, Buffer size: %d\n",
- msgb_l2len(msg), msg->data_len);
- return NULL;
- }
-
- /* attempt to treat it as a response */
- if (sscanf((const char *)&msg->l2h[0], "%3d %*s", &code) == 1) {
- LOGP(DMGCP, LOGL_DEBUG, "Response: Code: %d\n", code);
- return NULL;
- }
-
- msg->l3h = &msg->l2h[4];
-
-
- /*
- * Check for a duplicate message and respond.
- */
- memset(&pdata, 0, sizeof(pdata));
- pdata.cfg = cfg;
- data = strline_r((char *) msg->l3h, &pdata.save);
- pdata.found = mgcp_analyze_header(&pdata, data);
- if (pdata.endp && pdata.trans
- && pdata.endp->last_trans
- && strcmp(pdata.endp->last_trans, pdata.trans) == 0) {
- return do_retransmission(pdata.endp);
- }
-
- for (i = 0; i < ARRAY_SIZE(mgcp_requests); ++i) {
- if (strncmp(mgcp_requests[i].name, (const char *) &msg->l2h[0], 4) == 0) {
- handled = 1;
- resp = mgcp_requests[i].handle_request(&pdata);
- break;
- }
- }
-
- if (!handled)
- LOGP(DMGCP, LOGL_NOTICE, "MSG with type: '%.4s' not handled\n", &msg->l2h[0]);
-
- return resp;
-}
-
-/**
- * We have a null terminated string with the endpoint name here. We only
- * support two kinds. Simple ones as seen on the BSC level and the ones
- * seen on the trunk side.
- */
-static struct mgcp_endpoint *find_e1_endpoint(struct mgcp_config *cfg,
- const char *mgcp)
-{
- char *rest = NULL;
- struct mgcp_trunk_config *tcfg;
- int trunk, endp;
-
- trunk = strtoul(mgcp + 6, &rest, 10);
- if (rest == NULL || rest[0] != '/' || trunk < 1) {
- LOGP(DMGCP, LOGL_ERROR, "Wrong trunk name '%s'\n", mgcp);
- return NULL;
- }
-
- endp = strtoul(rest + 1, &rest, 10);
- if (rest == NULL || rest[0] != '@') {
- LOGP(DMGCP, LOGL_ERROR, "Wrong endpoint name '%s'\n", mgcp);
- return NULL;
- }
-
- /* signalling is on timeslot 1 */
- if (endp == 1)
- return NULL;
-
- tcfg = mgcp_trunk_num(cfg, trunk);
- if (!tcfg) {
- LOGP(DMGCP, LOGL_ERROR, "The trunk %d is not declared.\n", trunk);
- return NULL;
- }
-
- if (!tcfg->endpoints) {
- LOGP(DMGCP, LOGL_ERROR, "Endpoints of trunk %d not allocated.\n", trunk);
- return NULL;
- }
-
- if (endp < 1 || endp >= tcfg->number_endpoints) {
- LOGP(DMGCP, LOGL_ERROR, "Failed to find endpoint '%s'\n", mgcp);
- return NULL;
- }
-
- return &tcfg->endpoints[endp];
-}
-
-static struct mgcp_endpoint *find_endpoint(struct mgcp_config *cfg, const char *mgcp)
-{
- char *endptr = NULL;
- unsigned int gw = INT_MAX;
-
- if (strncmp(mgcp, "ds/e1", 5) == 0)
- return find_e1_endpoint(cfg, mgcp);
-
- gw = strtoul(mgcp, &endptr, 16);
- if (gw > 0 && gw < cfg->trunk.number_endpoints && endptr[0] == '@')
- return &cfg->trunk.endpoints[gw];
-
- LOGP(DMGCP, LOGL_ERROR, "Not able to find the endpoint: '%s'\n", mgcp);
- return NULL;
-}
-
-/**
- * @returns 0 when the status line was complete and transaction_id and
- * endp out parameters are set.
- */
-static int mgcp_analyze_header(struct mgcp_parse_data *pdata, char *data)
-{
- int i = 0;
- char *elem, *save = NULL;
-
- OSMO_ASSERT(data);
- pdata->trans = "000000";
-
- for (elem = strtok_r(data, " ", &save); elem;
- elem = strtok_r(NULL, " ", &save)) {
- switch (i) {
- case 0:
- pdata->trans = elem;
- break;
- case 1:
- pdata->endp = find_endpoint(pdata->cfg, elem);
- if (!pdata->endp) {
- LOGP(DMGCP, LOGL_ERROR,
- "Unable to find Endpoint `%s'\n", elem);
- return -1;
- }
- break;
- case 2:
- if (strcmp("MGCP", elem)) {
- LOGP(DMGCP, LOGL_ERROR,
- "MGCP header parsing error\n");
- return -1;
- }
- break;
- case 3:
- if (strcmp("1.0", elem)) {
- LOGP(DMGCP, LOGL_ERROR, "MGCP version `%s' "
- "not supported\n", elem);
- return -1;
- }
- break;
- }
- i++;
- }
-
- if (i != 4) {
- LOGP(DMGCP, LOGL_ERROR, "MGCP status line too short.\n");
- pdata->trans = "000000";
- pdata->endp = NULL;
- return -1;
- }
-
- return 0;
-}
-
-static int verify_call_id(const struct mgcp_endpoint *endp,
- const char *callid)
-{
- if (strcmp(endp->callid, callid) != 0) {
- LOGP(DMGCP, LOGL_ERROR, "CallIDs does not match on 0x%x. '%s' != '%s'\n",
- ENDPOINT_NUMBER(endp), endp->callid, callid);
- return -1;
- }
-
- return 0;
-}
-
-static int verify_ci(const struct mgcp_endpoint *endp,
- const char *_ci)
-{
- uint32_t ci = strtoul(_ci, NULL, 10);
-
- if (ci != endp->ci) {
- LOGP(DMGCP, LOGL_ERROR, "ConnectionIdentifiers do not match on 0x%x. %u != %s\n",
- ENDPOINT_NUMBER(endp), endp->ci, _ci);
- return -1;
- }
-
- return 0;
-}
-
-static struct msgb *handle_audit_endpoint(struct mgcp_parse_data *p)
-{
- if (p->found != 0)
- return create_err_response(NULL, 500, "AUEP", p->trans);
- else
- return create_ok_response(p->endp, 200, "AUEP", p->trans);
-}
-
-static int parse_conn_mode(const char *msg, struct mgcp_endpoint *endp)
-{
- int ret = 0;
- if (strcmp(msg, "recvonly") == 0)
- endp->conn_mode = MGCP_CONN_RECV_ONLY;
- else if (strcmp(msg, "sendrecv") == 0)
- endp->conn_mode = MGCP_CONN_RECV_SEND;
- else if (strcmp(msg, "sendonly") == 0)
- endp->conn_mode = MGCP_CONN_SEND_ONLY;
- else if (strcmp(msg, "loopback") == 0)
- endp->conn_mode = MGCP_CONN_LOOPBACK;
- else {
- LOGP(DMGCP, LOGL_ERROR, "Unknown connection mode: '%s'\n", msg);
- ret = -1;
- }
-
- endp->net_end.output_enabled =
- endp->conn_mode & MGCP_CONN_SEND_ONLY ? 1 : 0;
- endp->bts_end.output_enabled =
- endp->conn_mode & MGCP_CONN_RECV_ONLY ? 1 : 0;
-
- return ret;
-}
-
-static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_rtp_end *end,
- struct mgcp_port_range *range,
- int (*alloc)(struct mgcp_endpoint *endp, int port))
-{
- int i;
-
- if (range->mode == PORT_ALLOC_STATIC) {
- end->local_alloc = PORT_ALLOC_STATIC;
- return 0;
- }
-
- /* attempt to find a port */
- for (i = 0; i < 200; ++i) {
- int rc;
-
- if (range->last_port >= range->range_end)
- range->last_port = range->range_start;
-
- rc = alloc(endp, range->last_port);
-
- range->last_port += 2;
- if (rc == 0) {
- end->local_alloc = PORT_ALLOC_DYNAMIC;
- return 0;
- }
-
- }
-
- LOGP(DMGCP, LOGL_ERROR, "Allocating a RTP/RTCP port failed 200 times 0x%x.\n",
- ENDPOINT_NUMBER(endp));
- return -1;
-}
-
-static int allocate_ports(struct mgcp_endpoint *endp)
-{
- if (allocate_port(endp, &endp->net_end, &endp->cfg->net_ports,
- mgcp_bind_net_rtp_port) != 0)
- return -1;
-
- if (allocate_port(endp, &endp->bts_end, &endp->cfg->bts_ports,
- mgcp_bind_bts_rtp_port) != 0) {
- mgcp_rtp_end_reset(&endp->net_end);
- return -1;
- }
-
- if (endp->cfg->transcoder_ip && endp->tcfg->trunk_type == MGCP_TRUNK_VIRTUAL) {
- if (allocate_port(endp, &endp->trans_net,
- &endp->cfg->transcoder_ports,
- mgcp_bind_trans_net_rtp_port) != 0) {
- mgcp_rtp_end_reset(&endp->net_end);
- mgcp_rtp_end_reset(&endp->bts_end);
- return -1;
- }
-
- if (allocate_port(endp, &endp->trans_bts,
- &endp->cfg->transcoder_ports,
- mgcp_bind_trans_bts_rtp_port) != 0) {
- mgcp_rtp_end_reset(&endp->net_end);
- mgcp_rtp_end_reset(&endp->bts_end);
- mgcp_rtp_end_reset(&endp->trans_net);
- return -1;
- }
-
- /* remember that we have set up transcoding */
- endp->type = MGCP_RTP_TRANSCODED;
- }
-
- return 0;
-}
-
-/* Set the LCO from a string (see RFC 3435).
- * The string is stored in the 'string' field. A NULL string is handled excatly
- * like an empty string, the 'string' field is never NULL after this function
- * has been called. */
-static void set_local_cx_options(void *ctx, struct mgcp_lco *lco,
- const char *options)
-{
- char *p_opt, *a_opt;
- char codec[9];
-
- talloc_free(lco->string);
- talloc_free(lco->codec);
- lco->codec = NULL;
- lco->pkt_period_min = lco->pkt_period_max = 0;
- lco->string = talloc_strdup(ctx, options ? options : "");
-
- p_opt = strstr(lco->string, "p:");
- if (p_opt && sscanf(p_opt, "p:%d-%d",
- &lco->pkt_period_min, &lco->pkt_period_max) == 1)
- lco->pkt_period_max = lco->pkt_period_min;
-
- a_opt = strstr(lco->string, "a:");
- if (a_opt && sscanf(a_opt, "a:%8[^,]", codec) == 1)
- lco->codec = talloc_strdup(ctx, codec);
-}
-
-void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
- struct mgcp_rtp_end *rtp)
-{
- struct mgcp_trunk_config *tcfg = endp->tcfg;
-
- int patch_ssrc = expect_ssrc_change && tcfg->force_constant_ssrc;
-
- rtp->force_aligned_timing = tcfg->force_aligned_timing;
- rtp->force_constant_ssrc = patch_ssrc ? 1 : 0;
-
- LOGP(DMGCP, LOGL_DEBUG,
- "Configuring RTP endpoint: local port %d%s%s\n",
- ntohs(rtp->rtp_port),
- rtp->force_aligned_timing ? ", force constant timing" : "",
- rtp->force_constant_ssrc ? ", force constant ssrc" : "");
-}
-
-uint32_t mgcp_rtp_packet_duration(struct mgcp_endpoint *endp,
- struct mgcp_rtp_end *rtp)
-{
- int f = 0;
-
- /* Get the number of frames per channel and packet */
- if (rtp->frames_per_packet)
- f = rtp->frames_per_packet;
- else if (rtp->packet_duration_ms && rtp->codec.frame_duration_num) {
- int den = 1000 * rtp->codec.frame_duration_num;
- f = (rtp->packet_duration_ms * rtp->codec.frame_duration_den + den/2)
- / den;
- }
-
- return rtp->codec.rate * f * rtp->codec.frame_duration_num / rtp->codec.frame_duration_den;
-}
-
-static int mgcp_parse_osmux_cid(const char *line)
-{
- int osmux_cid;
-
- if (sscanf(line + 2, "Osmux: %u", &osmux_cid) != 1)
- return -1;
-
- if (osmux_cid > OSMUX_CID_MAX) {
- LOGP(DMGCP, LOGL_ERROR, "Osmux ID too large: %u > %u\n",
- osmux_cid, OSMUX_CID_MAX);
- return -1;
- }
- LOGP(DMGCP, LOGL_DEBUG, "bsc-nat offered Osmux CID %u\n", osmux_cid);
-
- return osmux_cid;
-}
-
-static int mgcp_osmux_setup(struct mgcp_endpoint *endp, const char *line)
-{
- if (!endp->cfg->osmux_init) {
- if (osmux_init(OSMUX_ROLE_BSC, endp->cfg) < 0) {
- LOGP(DMGCP, LOGL_ERROR, "Cannot init OSMUX\n");
- return -1;
- }
- LOGP(DMGCP, LOGL_NOTICE, "OSMUX socket has been set up\n");
- }
-
- return mgcp_parse_osmux_cid(line);
-}
-
-static struct msgb *handle_create_con(struct mgcp_parse_data *p)
-{
- struct mgcp_trunk_config *tcfg;
- struct mgcp_endpoint *endp = p->endp;
- int error_code = 400;
-
- const char *local_options = NULL;
- const char *callid = NULL;
- const char *mode = NULL;
- char *line;
- int have_sdp = 0, osmux_cid = -1;
-
- if (p->found != 0)
- return create_err_response(NULL, 510, "CRCX", p->trans);
-
- /* parse CallID C: and LocalParameters L: */
- for_each_line(line, p->save) {
- if (!mgcp_check_param(endp, line))
- continue;
-
- switch (line[0]) {
- case 'L':
- local_options = (const char *) line + 3;
- break;
- case 'C':
- callid = (const char *) line + 3;
- break;
- case 'M':
- mode = (const char *) line + 3;
- break;
- case 'X':
- /* Osmux is not enabled in this bsc, ignore it so the
- * bsc-nat knows that we don't want to use Osmux.
- */
- if (!p->endp->cfg->osmux)
- break;
-
- if (strncmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0)
- osmux_cid = mgcp_osmux_setup(endp, line);
- break;
- case '\0':
- have_sdp = 1;
- goto mgcp_header_done;
- default:
- LOGP(DMGCP, LOGL_NOTICE, "Unhandled option: '%c'/%d on 0x%x\n",
- *line, *line, ENDPOINT_NUMBER(endp));
- break;
- }
- }
-
-mgcp_header_done:
- tcfg = p->endp->tcfg;
-
- /* Check required data */
- if (!callid || !mode) {
- LOGP(DMGCP, LOGL_ERROR, "Missing callid and mode in CRCX on 0x%x\n",
- ENDPOINT_NUMBER(endp));
- return create_err_response(endp, 400, "CRCX", p->trans);
- }
-
- if (endp->allocated) {
- if (tcfg->force_realloc) {
- LOGP(DMGCP, LOGL_NOTICE, "Endpoint 0x%x already allocated. Forcing realloc.\n",
- ENDPOINT_NUMBER(endp));
- mgcp_release_endp(endp);
- if (p->cfg->realloc_cb)
- p->cfg->realloc_cb(tcfg, ENDPOINT_NUMBER(endp));
- } else {
- LOGP(DMGCP, LOGL_ERROR, "Endpoint is already used. 0x%x\n",
- ENDPOINT_NUMBER(endp));
- return create_err_response(endp, 400, "CRCX", p->trans);
- }
- }
-
- /* copy some parameters */
- endp->callid = talloc_strdup(tcfg->endpoints, callid);
-
- set_local_cx_options(endp->tcfg->endpoints, &endp->local_options,
- local_options);
-
- if (parse_conn_mode(mode, endp) != 0) {
- error_code = 517;
- goto error2;
- }
-
- /* initialize */
- endp->net_end.rtp_port = endp->net_end.rtcp_port = endp->bts_end.rtp_port = endp->bts_end.rtcp_port = 0;
- mgcp_rtp_end_config(endp, 0, &endp->net_end);
- mgcp_rtp_end_config(endp, 0, &endp->bts_end);
-
- /* set to zero until we get the info */
- memset(&endp->net_end.addr, 0, sizeof(endp->net_end.addr));
-
- /* bind to the port now */
- if (allocate_ports(endp) != 0)
- goto error2;
-
- /* assign a local call identifier or fail */
- endp->ci = generate_call_id(p->cfg);
- if (endp->ci == CI_UNUSED)
- goto error2;
-
- /* Annotate Osmux circuit ID and set it to negotiating state until this
- * is fully set up from the dummy load.
- */
- endp->osmux.state = OSMUX_STATE_DISABLED;
- if (osmux_cid >= 0) {
- endp->osmux.cid = osmux_cid;
- endp->osmux.state = OSMUX_STATE_NEGOTIATING;
- } else if (endp->cfg->osmux == OSMUX_USAGE_ONLY) {
- LOGP(DMGCP, LOGL_ERROR,
- "Osmux only and no osmux offered on 0x%x\n", ENDPOINT_NUMBER(endp));
- goto error2;
- }
-
- endp->allocated = 1;
-
- /* set up RTP media parameters */
- mgcp_set_audio_info(p->cfg, &endp->bts_end.codec, tcfg->audio_payload, tcfg->audio_name);
- endp->bts_end.fmtp_extra = talloc_strdup(tcfg->endpoints,
- tcfg->audio_fmtp_extra);
- if (have_sdp)
- mgcp_parse_sdp_data(endp, &endp->net_end, p);
- else if (endp->local_options.codec)
- mgcp_set_audio_info(p->cfg, &endp->net_end.codec,
- PTYPE_UNDEFINED, endp->local_options.codec);
-
- if (p->cfg->bts_force_ptime) {
- endp->bts_end.packet_duration_ms = p->cfg->bts_force_ptime;
- endp->bts_end.force_output_ptime = 1;
- }
-
- if (setup_rtp_processing(endp) != 0)
- goto error2;
-
- /* policy CB */
- if (p->cfg->policy_cb) {
- int rc;
- rc = p->cfg->policy_cb(tcfg, ENDPOINT_NUMBER(endp),
- MGCP_ENDP_CRCX, p->trans);
- switch (rc) {
- case MGCP_POLICY_REJECT:
- LOGP(DMGCP, LOGL_NOTICE, "CRCX rejected by policy on 0x%x\n",
- ENDPOINT_NUMBER(endp));
- mgcp_release_endp(endp);
- return create_err_response(endp, 400, "CRCX", p->trans);
- break;
- case MGCP_POLICY_DEFER:
- /* stop processing */
- create_transcoder(endp);
- return NULL;
- break;
- case MGCP_POLICY_CONT:
- /* just continue */
- break;
- }
- }
-
- LOGP(DMGCP, LOGL_DEBUG, "Creating endpoint on: 0x%x CI: %u port: %u/%u\n",
- ENDPOINT_NUMBER(endp), endp->ci,
- endp->net_end.local_port, endp->bts_end.local_port);
- if (p->cfg->change_cb)
- p->cfg->change_cb(tcfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_CRCX);
-
- if (endp->conn_mode & MGCP_CONN_RECV_ONLY && tcfg->keepalive_interval != 0) {
- send_dummy(endp);
- }
-
- create_transcoder(endp);
- return create_response_with_sdp(endp, "CRCX", p->trans);
-error2:
- mgcp_release_endp(endp);
- LOGP(DMGCP, LOGL_NOTICE, "Resource error on 0x%x\n", ENDPOINT_NUMBER(endp));
- return create_err_response(endp, error_code, "CRCX", p->trans);
-}
-
-static struct msgb *handle_modify_con(struct mgcp_parse_data *p)
-{
- struct mgcp_endpoint *endp = p->endp;
- int error_code = 500;
- int silent = 0;
- int have_sdp = 0;
- char *line;
- const char *local_options = NULL;
-
- if (p->found != 0)
- return create_err_response(NULL, 510, "MDCX", p->trans);
-
- if (endp->ci == CI_UNUSED) {
- LOGP(DMGCP, LOGL_ERROR, "Endpoint is not "
- "holding a connection. 0x%x\n", ENDPOINT_NUMBER(endp));
- return create_err_response(endp, 400, "MDCX", p->trans);
- }
-
- for_each_line(line, p->save) {
- if (!mgcp_check_param(endp, line))
- continue;
-
- switch (line[0]) {
- case 'C': {
- if (verify_call_id(endp, line + 3) != 0)
- goto error3;
- break;
- }
- case 'I': {
- if (verify_ci(endp, line + 3) != 0)
- goto error3;
- break;
- }
- case 'L':
- local_options = (const char *) line + 3;
- break;
- case 'M':
- if (parse_conn_mode(line + 3, endp) != 0) {
- error_code = 517;
- goto error3;
- }
- endp->orig_mode = endp->conn_mode;
- break;
- case 'Z':
- silent = strcmp("noanswer", line + 3) == 0;
- break;
- case '\0':
- /* SDP file begins */
- have_sdp = 1;
- mgcp_parse_sdp_data(endp, &endp->net_end, p);
- /* This will exhaust p->save, so the loop will
- * terminate next time.
- */
- break;
- default:
- LOGP(DMGCP, LOGL_NOTICE, "Unhandled MGCP option: '%c'/%d on 0x%x\n",
- line[0], line[0], ENDPOINT_NUMBER(endp));
- break;
- }
- }
-
- set_local_cx_options(endp->tcfg->endpoints, &endp->local_options,
- local_options);
-
- if (!have_sdp && endp->local_options.codec)
- mgcp_set_audio_info(p->cfg, &endp->net_end.codec,
- PTYPE_UNDEFINED, endp->local_options.codec);
-
- if (setup_rtp_processing(endp) != 0)
- goto error3;
-
- /* policy CB */
- if (p->cfg->policy_cb) {
- int rc;
- rc = p->cfg->policy_cb(endp->tcfg, ENDPOINT_NUMBER(endp),
- MGCP_ENDP_MDCX, p->trans);
- switch (rc) {
- case MGCP_POLICY_REJECT:
- LOGP(DMGCP, LOGL_NOTICE, "MDCX rejected by policy on 0x%x\n",
- ENDPOINT_NUMBER(endp));
- if (silent)
- goto out_silent;
- return create_err_response(endp, 400, "MDCX", p->trans);
- break;
- case MGCP_POLICY_DEFER:
- /* stop processing */
- return NULL;
- break;
- case MGCP_POLICY_CONT:
- /* just continue */
- break;
- }
- }
-
- mgcp_rtp_end_config(endp, 1, &endp->net_end);
- mgcp_rtp_end_config(endp, 1, &endp->bts_end);
-
- /* modify */
- LOGP(DMGCP, LOGL_DEBUG, "Modified endpoint on: 0x%x Server: %s:%u\n",
- ENDPOINT_NUMBER(endp), inet_ntoa(endp->net_end.addr), ntohs(endp->net_end.rtp_port));
- if (p->cfg->change_cb)
- p->cfg->change_cb(endp->tcfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_MDCX);
-
- if (endp->conn_mode & MGCP_CONN_RECV_ONLY &&
- endp->tcfg->keepalive_interval != 0)
- send_dummy(endp);
-
- if (silent)
- goto out_silent;
-
- return create_response_with_sdp(endp, "MDCX", p->trans);
-
-error3:
- return create_err_response(endp, error_code, "MDCX", p->trans);
-
-
-out_silent:
- return NULL;
-}
-
-static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
-{
- struct mgcp_endpoint *endp = p->endp;
- int error_code = 400;
- int silent = 0;
- char *line;
- char stats[1048];
-
- if (p->found != 0)
- return create_err_response(NULL, error_code, "DLCX", p->trans);
-
- if (!p->endp->allocated) {
- LOGP(DMGCP, LOGL_ERROR, "Endpoint is not used. 0x%x\n",
- ENDPOINT_NUMBER(endp));
- return create_err_response(endp, 400, "DLCX", p->trans);
- }
-
- for_each_line(line, p->save) {
- if (!mgcp_check_param(endp, line))
- continue;
-
- switch (line[0]) {
- case 'C':
- if (verify_call_id(endp, line + 3) != 0)
- goto error3;
- break;
- case 'I':
- if (verify_ci(endp, line + 3) != 0)
- goto error3;
- break;
- case 'Z':
- silent = strcmp("noanswer", line + 3) == 0;
- break;
- default:
- LOGP(DMGCP, LOGL_NOTICE, "Unhandled option: '%c'/%d on 0x%x\n",
- line[0], line[0], ENDPOINT_NUMBER(endp));
- break;
- }
- }
-
- /* policy CB */
- if (p->cfg->policy_cb) {
- int rc;
- rc = p->cfg->policy_cb(endp->tcfg, ENDPOINT_NUMBER(endp),
- MGCP_ENDP_DLCX, p->trans);
- switch (rc) {
- case MGCP_POLICY_REJECT:
- LOGP(DMGCP, LOGL_NOTICE, "DLCX rejected by policy on 0x%x\n",
- ENDPOINT_NUMBER(endp));
- if (silent)
- goto out_silent;
- return create_err_response(endp, 400, "DLCX", p->trans);
- break;
- case MGCP_POLICY_DEFER:
- /* stop processing */
- delete_transcoder(endp);
- return NULL;
- break;
- case MGCP_POLICY_CONT:
- /* just continue */
- break;
- }
- }
-
- /* free the connection */
- LOGP(DMGCP, LOGL_DEBUG, "Deleted endpoint on: 0x%x Server: %s:%u\n",
- ENDPOINT_NUMBER(endp), inet_ntoa(endp->net_end.addr), ntohs(endp->net_end.rtp_port));
-
- /* save the statistics of the current call */
- mgcp_format_stats(endp, stats, sizeof(stats));
-
- delete_transcoder(endp);
- mgcp_release_endp(endp);
- if (p->cfg->change_cb)
- p->cfg->change_cb(endp->tcfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_DLCX);
-
- if (silent)
- goto out_silent;
- return create_ok_resp_with_param(endp, 250, "DLCX", p->trans, stats);
-
-error3:
- return create_err_response(endp, error_code, "DLCX", p->trans);
-
-out_silent:
- return NULL;
-}
-
-static struct msgb *handle_rsip(struct mgcp_parse_data *p)
-{
- if (p->found != 0) {
- LOGP(DMGCP, LOGL_ERROR, "Failed to find the endpoint.\n");
- return NULL;
- }
-
- if (p->cfg->reset_cb)
- p->cfg->reset_cb(p->endp->tcfg);
- return NULL;
-}
-
-static char extract_tone(const char *line)
-{
- const char *str = strstr(line, "D/");
- if (!str)
- return CHAR_MAX;
-
- return str[2];
-}
-
-/*
- * This can request like DTMF detection and forward, fax detection... it
- * can also request when the notification should be send and such. We don't
- * do this right now.
- */
-static struct msgb *handle_noti_req(struct mgcp_parse_data *p)
-{
- int res = 0;
- char *line;
- char tone = CHAR_MAX;
-
- if (p->found != 0)
- return create_err_response(NULL, 400, "RQNT", p->trans);
-
- for_each_line(line, p->save) {
- switch (line[0]) {
- case 'S':
- tone = extract_tone(line);
- break;
- }
- }
-
- /* we didn't see a signal request with a tone */
- if (tone == CHAR_MAX)
- return create_ok_response(p->endp, 200, "RQNT", p->trans);
-
- if (p->cfg->rqnt_cb)
- res = p->cfg->rqnt_cb(p->endp, tone);
-
- return res == 0 ?
- create_ok_response(p->endp, 200, "RQNT", p->trans) :
- create_err_response(p->endp, res, "RQNT", p->trans);
-}
-
-static void mgcp_keepalive_timer_cb(void *_tcfg)
-{
- struct mgcp_trunk_config *tcfg = _tcfg;
- int i;
- LOGP(DMGCP, LOGL_DEBUG, "Triggered trunk %d keepalive timer.\n",
- tcfg->trunk_nr);
-
- if (tcfg->keepalive_interval <= 0)
- return;
-
- for (i = 1; i < tcfg->number_endpoints; ++i) {
- struct mgcp_endpoint *endp = &tcfg->endpoints[i];
- if (endp->conn_mode == MGCP_CONN_RECV_ONLY)
- send_dummy(endp);
- }
-
- LOGP(DMGCP, LOGL_DEBUG, "Rescheduling trunk %d keepalive timer.\n",
- tcfg->trunk_nr);
- osmo_timer_schedule(&tcfg->keepalive_timer, tcfg->keepalive_interval, 0);
-}
-
-void mgcp_trunk_set_keepalive(struct mgcp_trunk_config *tcfg, int interval)
-{
- tcfg->keepalive_interval = interval;
- osmo_timer_setup(&tcfg->keepalive_timer, mgcp_keepalive_timer_cb, tcfg);
-
- if (interval <= 0)
- osmo_timer_del(&tcfg->keepalive_timer);
- else
- osmo_timer_schedule(&tcfg->keepalive_timer,
- tcfg->keepalive_interval, 0);
-}
-
-struct mgcp_config *mgcp_config_alloc(void)
-{
- struct mgcp_config *cfg;
-
- cfg = talloc_zero(NULL, struct mgcp_config);
- if (!cfg) {
- LOGP(DMGCP, LOGL_FATAL, "Failed to allocate config.\n");
- return NULL;
- }
-
- cfg->source_port = 2427;
- cfg->source_addr = talloc_strdup(cfg, "0.0.0.0");
- cfg->osmux_addr = talloc_strdup(cfg, "0.0.0.0");
-
- cfg->transcoder_remote_base = 4000;
-
- cfg->bts_ports.base_port = RTP_PORT_DEFAULT;
- cfg->net_ports.base_port = RTP_PORT_NET_DEFAULT;
-
- cfg->rtp_processing_cb = &mgcp_rtp_processing_default;
- cfg->setup_rtp_processing_cb = &mgcp_setup_rtp_processing_default;
-
- cfg->get_net_downlink_format_cb = &mgcp_get_net_downlink_format_default;
-
- /* default trunk handling */
- cfg->trunk.cfg = cfg;
- cfg->trunk.trunk_nr = 0;
- cfg->trunk.trunk_type = MGCP_TRUNK_VIRTUAL;
- cfg->trunk.audio_name = talloc_strdup(cfg, "AMR/8000");
- cfg->trunk.audio_payload = 126;
- cfg->trunk.audio_send_ptime = 1;
- cfg->trunk.audio_send_name = 1;
- cfg->trunk.omit_rtcp = 0;
- mgcp_trunk_set_keepalive(&cfg->trunk, MGCP_KEEPALIVE_ONCE);
-
- INIT_LLIST_HEAD(&cfg->trunks);
-
- return cfg;
-}
-
-struct mgcp_trunk_config *mgcp_trunk_alloc(struct mgcp_config *cfg, int nr)
-{
- struct mgcp_trunk_config *trunk;
-
- trunk = talloc_zero(cfg, struct mgcp_trunk_config);
- if (!trunk) {
- LOGP(DMGCP, LOGL_ERROR, "Failed to allocate.\n");
- return NULL;
- }
-
- trunk->cfg = cfg;
- trunk->trunk_type = MGCP_TRUNK_E1;
- trunk->trunk_nr = nr;
- trunk->audio_name = talloc_strdup(cfg, "AMR/8000");
- trunk->audio_payload = 126;
- trunk->audio_send_ptime = 1;
- trunk->audio_send_name = 1;
- trunk->number_endpoints = 33;
- trunk->omit_rtcp = 0;
- mgcp_trunk_set_keepalive(trunk, MGCP_KEEPALIVE_ONCE);
- llist_add_tail(&trunk->entry, &cfg->trunks);
- return trunk;
-}
-
-struct mgcp_trunk_config *mgcp_trunk_num(struct mgcp_config *cfg, int index)
-{
- struct mgcp_trunk_config *trunk;
-
- llist_for_each_entry(trunk, &cfg->trunks, entry)
- if (trunk->trunk_nr == index)
- return trunk;
-
- return NULL;
-}
-
-static void mgcp_rtp_codec_reset(struct mgcp_rtp_codec *codec)
-{
- codec->payload_type = -1;
- talloc_free(codec->subtype_name);
- codec->subtype_name = NULL;
- talloc_free(codec->audio_name);
- codec->audio_name = NULL;
- codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM;
- codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN;
- codec->rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE;
- codec->channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS;
-}
-
-static void mgcp_rtp_end_reset(struct mgcp_rtp_end *end)
-{
- if (end->local_alloc == PORT_ALLOC_DYNAMIC) {
- mgcp_free_rtp_port(end);
- end->local_port = 0;
- }
-
- end->packets = 0;
- end->octets = 0;
- end->dropped_packets = 0;
- memset(&end->addr, 0, sizeof(end->addr));
- end->rtp_port = end->rtcp_port = 0;
- end->local_alloc = -1;
- talloc_free(end->fmtp_extra);
- end->fmtp_extra = NULL;
- talloc_free(end->rtp_process_data);
- end->rtp_process_data = NULL;
-
- /* Set default values */
- end->frames_per_packet = 0; /* unknown */
- end->packet_duration_ms = DEFAULT_RTP_AUDIO_PACKET_DURATION_MS;
- end->output_enabled = 0;
-
- mgcp_rtp_codec_reset(&end->codec);
- mgcp_rtp_codec_reset(&end->alt_codec);
-}
-
-static void mgcp_rtp_end_init(struct mgcp_rtp_end *end)
-{
- mgcp_rtp_end_reset(end);
- end->rtp.fd = -1;
- end->rtcp.fd = -1;
-}
-
-int mgcp_endpoints_allocate(struct mgcp_trunk_config *tcfg)
-{
- int i;
-
- /* Initialize all endpoints */
- tcfg->endpoints = _talloc_zero_array(tcfg->cfg,
- sizeof(struct mgcp_endpoint),
- tcfg->number_endpoints, "endpoints");
- if (!tcfg->endpoints)
- return -1;
-
- for (i = 0; i < tcfg->number_endpoints; ++i) {
- tcfg->endpoints[i].osmux.allocated_cid = -1;
- tcfg->endpoints[i].ci = CI_UNUSED;
- tcfg->endpoints[i].cfg = tcfg->cfg;
- tcfg->endpoints[i].tcfg = tcfg;
- mgcp_rtp_end_init(&tcfg->endpoints[i].net_end);
- mgcp_rtp_end_init(&tcfg->endpoints[i].bts_end);
- mgcp_rtp_end_init(&tcfg->endpoints[i].trans_net);
- mgcp_rtp_end_init(&tcfg->endpoints[i].trans_bts);
- }
-
- return 0;
-}
-
-void mgcp_release_endp(struct mgcp_endpoint *endp)
-{
- LOGP(DMGCP, LOGL_DEBUG, "Releasing endpoint on: 0x%x\n", ENDPOINT_NUMBER(endp));
- endp->ci = CI_UNUSED;
- endp->allocated = 0;
-
- talloc_free(endp->callid);
- endp->callid = NULL;
-
- talloc_free(endp->local_options.string);
- endp->local_options.string = NULL;
- talloc_free(endp->local_options.codec);
- endp->local_options.codec = NULL;
-
- mgcp_rtp_end_reset(&endp->bts_end);
- mgcp_rtp_end_reset(&endp->net_end);
- mgcp_rtp_end_reset(&endp->trans_net);
- mgcp_rtp_end_reset(&endp->trans_bts);
- endp->type = MGCP_RTP_DEFAULT;
-
- memset(&endp->net_state, 0, sizeof(endp->net_state));
- memset(&endp->bts_state, 0, sizeof(endp->bts_state));
-
- endp->conn_mode = endp->orig_mode = MGCP_CONN_NONE;
-
- if (endp->osmux.state == OSMUX_STATE_ENABLED)
- osmux_disable_endpoint(endp);
-
- /* release the circuit ID if it had been allocated */
- osmux_release_cid(endp);
-
- memset(&endp->taps, 0, sizeof(endp->taps));
-}
-
-void mgcp_initialize_endp(struct mgcp_endpoint *endp)
-{
- return mgcp_release_endp(endp);
-}
-
-static int send_trans(struct mgcp_config *cfg, const char *buf, int len)
-{
- struct sockaddr_in addr;
-
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_addr = cfg->transcoder_in;
- addr.sin_port = htons(2427);
- return sendto(cfg->gw_fd.bfd.fd, buf, len, 0,
- (struct sockaddr *) &addr, sizeof(addr));
-}
-
-static void send_msg(struct mgcp_endpoint *endp, int endpoint, int port,
- const char *msg, const char *mode)
-{
- char buf[2096];
- int len;
- int nchars;
-
- /* hardcoded to AMR right now, we do not know the real type at this point */
- len = snprintf(buf, sizeof(buf),
- "%s 42 %x@mgw MGCP 1.0\r\n"
- "C: 4256\r\n"
- "M: %s\r\n"
- "\r\n",
- msg, endpoint, mode);
-
- if (len < 0)
- return;
-
- nchars = write_response_sdp(endp, buf + len, sizeof(buf) + len - 1, NULL);
- if (nchars < 0)
- return;
-
- len += nchars;
-
- buf[sizeof(buf) - 1] = '\0';
-
- send_trans(endp->cfg, buf, len);
-}
-
-static void send_dlcx(struct mgcp_endpoint *endp, int endpoint)
-{
- char buf[2096];
- int len;
-
- len = snprintf(buf, sizeof(buf),
- "DLCX 43 %x@mgw MGCP 1.0\r\n"
- "C: 4256\r\n"
- , endpoint);
-
- if (len < 0)
- return;
-
- buf[sizeof(buf) - 1] = '\0';
-
- send_trans(endp->cfg, buf, len);
-}
-
-static int send_agent(struct mgcp_config *cfg, const char *buf, int len)
-{
- return write(cfg->gw_fd.bfd.fd, buf, len);
-}
-
-int mgcp_send_reset_all(struct mgcp_config *cfg)
-{
- static const char mgcp_reset[] = {
- "RSIP 1 *@mgw MGCP 1.0\r\n"
- };
-
- return send_agent(cfg, mgcp_reset, sizeof mgcp_reset -1);
-}
-
-int mgcp_send_reset_ep(struct mgcp_endpoint *endp, int endpoint)
-{
- char buf[128];
- int len;
-
- len = snprintf(buf, sizeof(buf),
- "RSIP 39 %x@mgw MGCP 1.0\r\n"
- , endpoint);
- if (len < 0)
- return len;
-
- buf[sizeof(buf) - 1] = '\0';
-
- return send_agent(endp->cfg, buf, len);
-}
-
-static int setup_rtp_processing(struct mgcp_endpoint *endp)
-{
- int rc = 0;
- struct mgcp_config *cfg = endp->cfg;
-
- if (endp->type != MGCP_RTP_DEFAULT)
- return 0;
-
- if (endp->conn_mode == MGCP_CONN_LOOPBACK)
- return 0;
-
- if (endp->conn_mode & MGCP_CONN_SEND_ONLY)
- rc |= cfg->setup_rtp_processing_cb(endp, &endp->net_end, &endp->bts_end);
- else
- rc |= cfg->setup_rtp_processing_cb(endp, &endp->net_end, NULL);
-
- if (endp->conn_mode & MGCP_CONN_RECV_ONLY)
- rc |= cfg->setup_rtp_processing_cb(endp, &endp->bts_end, &endp->net_end);
- else
- rc |= cfg->setup_rtp_processing_cb(endp, &endp->bts_end, NULL);
- return rc;
-}
-
-static void create_transcoder(struct mgcp_endpoint *endp)
-{
- int port;
- int in_endp = ENDPOINT_NUMBER(endp);
- int out_endp = endp_back_channel(in_endp);
-
- if (endp->type != MGCP_RTP_TRANSCODED)
- return;
-
- send_msg(endp, in_endp, endp->trans_bts.local_port, "CRCX", "sendrecv");
- send_msg(endp, in_endp, endp->trans_bts.local_port, "MDCX", "sendrecv");
- send_msg(endp, out_endp, endp->trans_net.local_port, "CRCX", "sendrecv");
- send_msg(endp, out_endp, endp->trans_net.local_port, "MDCX", "sendrecv");
-
- port = rtp_calculate_port(in_endp, endp->cfg->transcoder_remote_base);
- endp->trans_bts.rtp_port = htons(port);
- endp->trans_bts.rtcp_port = htons(port + 1);
-
- port = rtp_calculate_port(out_endp, endp->cfg->transcoder_remote_base);
- endp->trans_net.rtp_port = htons(port);
- endp->trans_net.rtcp_port = htons(port + 1);
-}
-
-static void delete_transcoder(struct mgcp_endpoint *endp)
-{
- int in_endp = ENDPOINT_NUMBER(endp);
- int out_endp = endp_back_channel(in_endp);
-
- if (endp->type != MGCP_RTP_TRANSCODED)
- return;
-
- send_dlcx(endp, in_endp);
- send_dlcx(endp, out_endp);
-}
-
-int mgcp_reset_transcoder(struct mgcp_config *cfg)
-{
- if (!cfg->transcoder_ip)
- return 0;
-
- static const char mgcp_reset[] = {
- "RSIP 1 13@mgw MGCP 1.0\r\n"
- };
-
- return send_trans(cfg, mgcp_reset, sizeof mgcp_reset -1);
-}
-
-void mgcp_format_stats(struct mgcp_endpoint *endp, char *msg, size_t size)
-{
- uint32_t expected, jitter;
- int ploss;
- int nchars;
- mgcp_state_calc_loss(&endp->net_state, &endp->net_end,
- &expected, &ploss);
- jitter = mgcp_state_calc_jitter(&endp->net_state);
-
- nchars = snprintf(msg, size,
- "\r\nP: PS=%u, OS=%u, PR=%u, OR=%u, PL=%d, JI=%u",
- endp->bts_end.packets, endp->bts_end.octets,
- endp->net_end.packets, endp->net_end.octets,
- ploss, jitter);
- if (nchars < 0 || nchars >= size)
- goto truncate;
-
- msg += nchars;
- size -= nchars;
-
- /* Error Counter */
- nchars = snprintf(msg, size,
- "\r\nX-Osmo-CP: EC TIS=%u, TOS=%u, TIR=%u, TOR=%u",
- endp->net_state.in_stream.err_ts_counter,
- endp->net_state.out_stream.err_ts_counter,
- endp->bts_state.in_stream.err_ts_counter,
- endp->bts_state.out_stream.err_ts_counter);
- if (nchars < 0 || nchars >= size)
- goto truncate;
-
- msg += nchars;
- size -= nchars;
-
- if (endp->osmux.state == OSMUX_STATE_ENABLED) {
- snprintf(msg, size,
- "\r\nX-Osmux-ST: CR=%u, BR=%u",
- endp->osmux.stats.chunks,
- endp->osmux.stats.octets);
- }
-truncate:
- msg[size - 1] = '\0';
-}
-
-int mgcp_parse_stats(struct msgb *msg, uint32_t *ps, uint32_t *os,
- uint32_t *pr, uint32_t *_or, int *loss, uint32_t *jitter)
-{
- char *line, *save;
- int rc;
-
- /* initialize with bad values */
- *ps = *os = *pr = *_or = *jitter = UINT_MAX;
- *loss = INT_MAX;
-
-
- line = strtok_r((char *) msg->l2h, "\r\n", &save);
- if (!line)
- return -1;
-
- /* this can only parse the message that is created above... */
- for_each_non_empty_line(line, save) {
- switch (line[0]) {
- case 'P':
- rc = sscanf(line, "P: PS=%u, OS=%u, PR=%u, OR=%u, PL=%d, JI=%u",
- ps, os, pr, _or, loss, jitter);
- return rc == 6 ? 0 : -1;
- }
- }
-
- return -1;
-}
diff --git a/openbsc/src/libmgcp/mgcp_sdp.c b/openbsc/src/libmgcp/mgcp_sdp.c
deleted file mode 100644
index b64894496..000000000
--- a/openbsc/src/libmgcp/mgcp_sdp.c
+++ /dev/null
@@ -1,305 +0,0 @@
-/*
- * Some SDP file parsing...
- *
- * (C) 2009-2015 by Holger Hans Peter Freyther <zecke@selfish.org>
- * (C) 2009-2014 by On-Waves
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <openbsc/mgcp.h>
-#include <openbsc/mgcp_internal.h>
-
-#include <errno.h>
-
-struct sdp_rtp_map {
- /* the type */
- int payload_type;
- /* null, static or later dynamic codec name */
- char *codec_name;
- /* A pointer to the original line for later parsing */
- char *map_line;
-
- int rate;
- int channels;
-};
-
-int mgcp_set_audio_info(void *ctx, struct mgcp_rtp_codec *codec,
- int payload_type, const char *audio_name)
-{
- int rate = codec->rate;
- int channels = codec->channels;
- char audio_codec[64];
-
- talloc_free(codec->subtype_name);
- codec->subtype_name = NULL;
- talloc_free(codec->audio_name);
- codec->audio_name = NULL;
-
- if (payload_type != PTYPE_UNDEFINED)
- codec->payload_type = payload_type;
-
- if (!audio_name) {
- switch (payload_type) {
- case 0: audio_name = "PCMU/8000/1"; break;
- case 3: audio_name = "GSM/8000/1"; break;
- case 8: audio_name = "PCMA/8000/1"; break;
- case 18: audio_name = "G729/8000/1"; break;
- default:
- /* Payload type is unknown, don't change rate and
- * channels. */
- /* TODO: return value? */
- return 0;
- }
- }
-
- if (sscanf(audio_name, "%63[^/]/%d/%d",
- audio_codec, &rate, &channels) < 1)
- return -EINVAL;
-
- codec->rate = rate;
- codec->channels = channels;
- codec->subtype_name = talloc_strdup(ctx, audio_codec);
- codec->audio_name = talloc_strdup(ctx, audio_name);
-
- if (!strcmp(audio_codec, "G729")) {
- codec->frame_duration_num = 10;
- codec->frame_duration_den = 1000;
- } else {
- codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM;
- codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN;
- }
-
- if (payload_type < 0) {
- payload_type = 96;
- if (rate == 8000 && channels == 1) {
- if (!strcmp(audio_codec, "GSM"))
- payload_type = 3;
- else if (!strcmp(audio_codec, "PCMA"))
- payload_type = 8;
- else if (!strcmp(audio_codec, "PCMU"))
- payload_type = 0;
- else if (!strcmp(audio_codec, "G729"))
- payload_type = 18;
- }
-
- codec->payload_type = payload_type;
- }
-
- if (channels != 1)
- LOGP(DMGCP, LOGL_NOTICE,
- "Channels != 1 in SDP: '%s'\n", audio_name);
-
- return 0;
-}
-
-void codecs_initialize(void *ctx, struct sdp_rtp_map *codecs, int used)
-{
- int i;
-
- for (i = 0; i < used; ++i) {
- switch (codecs[i].payload_type) {
- case 0:
- codecs[i].codec_name = "PCMU";
- codecs[i].rate = 8000;
- codecs[i].channels = 1;
- break;
- case 3:
- codecs[i].codec_name = "GSM";
- codecs[i].rate = 8000;
- codecs[i].channels = 1;
- break;
- case 8:
- codecs[i].codec_name = "PCMA";
- codecs[i].rate = 8000;
- codecs[i].channels = 1;
- break;
- case 18:
- codecs[i].codec_name = "G729";
- codecs[i].rate = 8000;
- codecs[i].channels = 1;
- break;
- }
- }
-}
-
-void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used, int payload, char *audio_name)
-{
- int i;
-
- for (i = 0; i < used; ++i) {
- char audio_codec[64];
- int rate = -1;
- int channels = -1;
- if (codecs[i].payload_type != payload)
- continue;
- if (sscanf(audio_name, "%63[^/]/%d/%d",
- audio_codec, &rate, &channels) < 1) {
- LOGP(DMGCP, LOGL_ERROR, "Failed to parse '%s'\n", audio_name);
- continue;
- }
-
- codecs[i].map_line = talloc_strdup(ctx, audio_name);
- codecs[i].codec_name = talloc_strdup(ctx, audio_codec);
- codecs[i].rate = rate;
- codecs[i].channels = channels;
- return;
- }
-
- LOGP(DMGCP, LOGL_ERROR, "Unconfigured PT(%d) with %s\n", payload, audio_name);
-}
-
-int is_codec_compatible(struct mgcp_endpoint *endp, struct sdp_rtp_map *codec)
-{
- char *bts_codec;
- char audio_codec[64];
-
- if (!codec->codec_name)
- return 0;
-
- /*
- * GSM, GSM/8000 and GSM/8000/1 should all be compatible.. let's go
- * by name first.
- */
- bts_codec = endp->tcfg->audio_name;
- if (sscanf(bts_codec, "%63[^/]/%*d/%*d", audio_codec) < 1)
- return 0;
-
- return strcasecmp(audio_codec, codec->codec_name) == 0;
-}
-
-int mgcp_parse_sdp_data(struct mgcp_endpoint *endp, struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p)
-{
- struct sdp_rtp_map codecs[10];
- int codecs_used = 0;
- char *line;
- int maxptime = -1;
- int i;
- int codecs_assigned = 0;
- void *tmp_ctx = talloc_new(NULL);
-
- memset(&codecs, 0, sizeof(codecs));
-
- for_each_line(line, p->save) {
- switch (line[0]) {
- case 'o':
- case 's':
- case 't':
- case 'v':
- /* skip these SDP attributes */
- break;
- case 'a': {
- int payload;
- int ptime, ptime2 = 0;
- char audio_name[64];
-
-
- if (sscanf(line, "a=rtpmap:%d %63s",
- &payload, audio_name) == 2) {
- codecs_update(tmp_ctx, codecs, codecs_used, payload, audio_name);
- } else if (sscanf(line, "a=ptime:%d-%d",
- &ptime, &ptime2) >= 1) {
- if (ptime2 > 0 && ptime2 != ptime)
- rtp->packet_duration_ms = 0;
- else
- rtp->packet_duration_ms = ptime;
- } else if (sscanf(line, "a=maxptime:%d", &ptime2) == 1) {
- maxptime = ptime2;
- }
- break;
- }
- case 'm': {
- int port, rc;
-
- rc = sscanf(line, "m=audio %d RTP/AVP %d %d %d %d %d %d %d %d %d %d",
- &port,
- &codecs[0].payload_type,
- &codecs[1].payload_type,
- &codecs[2].payload_type,
- &codecs[3].payload_type,
- &codecs[4].payload_type,
- &codecs[5].payload_type,
- &codecs[6].payload_type,
- &codecs[7].payload_type,
- &codecs[8].payload_type,
- &codecs[9].payload_type);
- if (rc >= 2) {
- rtp->rtp_port = htons(port);
- rtp->rtcp_port = htons(port + 1);
- codecs_used = rc - 1;
- codecs_initialize(tmp_ctx, codecs, codecs_used);
- }
- break;
- }
- case 'c': {
- char ipv4[16];
-
- if (sscanf(line, "c=IN IP4 %15s", ipv4) == 1) {
- inet_aton(ipv4, &rtp->addr);
- }
- break;
- }
- default:
- if (p->endp)
- LOGP(DMGCP, LOGL_NOTICE,
- "Unhandled SDP option: '%c'/%d on 0x%x\n",
- line[0], line[0], ENDPOINT_NUMBER(p->endp));
- else
- LOGP(DMGCP, LOGL_NOTICE,
- "Unhandled SDP option: '%c'/%d\n",
- line[0], line[0]);
- break;
- }
- }
-
- /* Now select the primary and alt_codec */
- for (i = 0; i < codecs_used && codecs_assigned < 2; ++i) {
- struct mgcp_rtp_codec *codec = codecs_assigned == 0 ?
- &rtp->codec : &rtp->alt_codec;
-
- if (endp->tcfg->no_audio_transcoding &&
- !is_codec_compatible(endp, &codecs[i])) {
- LOGP(DMGCP, LOGL_NOTICE, "Skipping codec %s\n",
- codecs[i].codec_name);
- continue;
- }
-
- mgcp_set_audio_info(p->cfg, codec,
- codecs[i].payload_type,
- codecs[i].map_line);
- codecs_assigned += 1;
- }
-
- if (codecs_assigned > 0) {
- /* TODO/XXX: Store this per codec and derive it on use */
- if (maxptime >= 0 && maxptime * rtp->codec.frame_duration_den >
- rtp->codec.frame_duration_num * 1500) {
- /* more than 1 frame */
- rtp->packet_duration_ms = 0;
- }
-
- LOGP(DMGCP, LOGL_NOTICE,
- "Got media info via SDP: port %d, payload %d (%s), "
- "duration %d, addr %s\n",
- ntohs(rtp->rtp_port), rtp->codec.payload_type,
- rtp->codec.subtype_name ? rtp->codec.subtype_name : "unknown",
- rtp->packet_duration_ms, inet_ntoa(rtp->addr));
- }
-
- talloc_free(tmp_ctx);
- return codecs_assigned > 0;
-}
-
diff --git a/openbsc/src/libmgcp/mgcp_transcode.c b/openbsc/src/libmgcp/mgcp_transcode.c
deleted file mode 100644
index f31e7aefb..000000000
--- a/openbsc/src/libmgcp/mgcp_transcode.c
+++ /dev/null
@@ -1,612 +0,0 @@
-/*
- * (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 <stdlib.h>
-#include <string.h>
-#include <errno.h>
-
-
-#include "g711common.h"
-
-#include <openbsc/debug.h>
-#include <openbsc/mgcp.h>
-#include <openbsc/mgcp_internal.h>
-#include <openbsc/mgcp_transcode.h>
-
-#include <osmocom/core/talloc.h>
-#include <osmocom/netif/rtp.h>
-
-int mgcp_transcoding_get_frame_size(void *state_, int nsamples, int dst)
-{
- struct mgcp_process_rtp_state *state = state_;
- if (dst)
- return (nsamples >= 0 ?
- nsamples / state->dst_samples_per_frame :
- 1) * state->dst_frame_size;
- else
- return (nsamples >= 0 ?
- nsamples / state->src_samples_per_frame :
- 1) * state->src_frame_size;
-}
-
-static enum audio_format get_audio_format(const struct mgcp_rtp_codec *codec)
-{
- if (codec->subtype_name) {
- if (!strcasecmp("GSM", codec->subtype_name))
- return AF_GSM;
- if (!strcasecmp("PCMA", codec->subtype_name))
- return AF_PCMA;
- if (!strcasecmp("PCMU", codec->subtype_name))
- return AF_PCMU;
-#ifdef HAVE_BCG729
- if (!strcasecmp("G729", codec->subtype_name))
- return AF_G729;
-#endif
- if (!strcasecmp("L16", codec->subtype_name))
- return AF_L16;
- }
-
- switch (codec->payload_type) {
- case 0 /* PCMU */:
- return AF_PCMU;
- case 3 /* GSM */:
- return AF_GSM;
- case 8 /* PCMA */:
- return AF_PCMA;
-#ifdef HAVE_BCG729
- case 18 /* G.729 */:
- return AF_G729;
-#endif
- case 11 /* L16 */:
- return AF_L16;
- default:
- return AF_INVALID;
- }
-}
-
-static void l16_encode(short *sample, unsigned char *buf, size_t n)
-{
- for (; n > 0; --n, ++sample, buf += 2) {
- buf[0] = sample[0] >> 8;
- buf[1] = sample[0] & 0xff;
- }
-}
-
-static void l16_decode(unsigned char *buf, short *sample, size_t n)
-{
- for (; n > 0; --n, ++sample, buf += 2)
- sample[0] = ((short)buf[0] << 8) | buf[1];
-}
-
-static void alaw_encode(short *sample, unsigned char *buf, size_t n)
-{
- for (; n > 0; --n)
- *(buf++) = s16_to_alaw(*(sample++));
-}
-
-static void alaw_decode(unsigned char *buf, short *sample, size_t n)
-{
- for (; n > 0; --n)
- *(sample++) = alaw_to_s16(*(buf++));
-}
-
-static void ulaw_encode(short *sample, unsigned char *buf, size_t n)
-{
- for (; n > 0; --n)
- *(buf++) = s16_to_ulaw(*(sample++));
-}
-
-static void ulaw_decode(unsigned char *buf, short *sample, size_t n)
-{
- for (; n > 0; --n)
- *(sample++) = ulaw_to_s16(*(buf++));
-}
-
-static int processing_state_destructor(struct mgcp_process_rtp_state *state)
-{
- switch (state->src_fmt) {
- case AF_GSM:
- if (state->src.gsm_handle)
- gsm_destroy(state->src.gsm_handle);
- break;
-#ifdef HAVE_BCG729
- case AF_G729:
- if (state->src.g729_dec)
- closeBcg729DecoderChannel(state->src.g729_dec);
- break;
-#endif
- default:
- break;
- }
- switch (state->dst_fmt) {
- case AF_GSM:
- if (state->dst.gsm_handle)
- gsm_destroy(state->dst.gsm_handle);
- break;
-#ifdef HAVE_BCG729
- case AF_G729:
- if (state->dst.g729_enc)
- closeBcg729EncoderChannel(state->dst.g729_enc);
- break;
-#endif
- default:
- break;
- }
- return 0;
-}
-
-int mgcp_transcoding_setup(struct mgcp_endpoint *endp,
- struct mgcp_rtp_end *dst_end,
- struct mgcp_rtp_end *src_end)
-{
- struct mgcp_process_rtp_state *state;
- enum audio_format src_fmt, dst_fmt;
- const struct mgcp_rtp_codec *dst_codec = &dst_end->codec;
-
- /* cleanup first */
- if (dst_end->rtp_process_data) {
- talloc_free(dst_end->rtp_process_data);
- dst_end->rtp_process_data = NULL;
- }
-
- if (!src_end)
- return 0;
-
- const struct mgcp_rtp_codec *src_codec = &src_end->codec;
-
- if (endp->tcfg->no_audio_transcoding) {
- LOGP(DMGCP, LOGL_NOTICE,
- "Transcoding disabled on endpoint 0x%x\n",
- ENDPOINT_NUMBER(endp));
- return 0;
- }
-
- src_fmt = get_audio_format(src_codec);
- dst_fmt = get_audio_format(dst_codec);
-
- LOGP(DMGCP, LOGL_ERROR,
- "Checking transcoding: %s (%d) -> %s (%d)\n",
- src_codec->subtype_name, src_codec->payload_type,
- dst_codec->subtype_name, dst_codec->payload_type);
-
- if (src_fmt == AF_INVALID || dst_fmt == AF_INVALID) {
- if (!src_codec->subtype_name || !dst_codec->subtype_name)
- /* Not enough info, do nothing */
- return 0;
-
- if (strcasecmp(src_codec->subtype_name, dst_codec->subtype_name) == 0)
- /* Nothing to do */
- return 0;
-
- LOGP(DMGCP, LOGL_ERROR,
- "Cannot transcode: %s codec not supported (%s -> %s).\n",
- src_fmt != AF_INVALID ? "destination" : "source",
- src_codec->audio_name, dst_codec->audio_name);
- return -EINVAL;
- }
-
- if (src_codec->rate && dst_codec->rate && src_codec->rate != dst_codec->rate) {
- LOGP(DMGCP, LOGL_ERROR,
- "Cannot transcode: rate conversion (%d -> %d) not supported.\n",
- src_codec->rate, dst_codec->rate);
- return -EINVAL;
- }
-
- state = talloc_zero(endp->tcfg->cfg, struct mgcp_process_rtp_state);
- talloc_set_destructor(state, processing_state_destructor);
- dst_end->rtp_process_data = state;
-
- state->src_fmt = src_fmt;
-
- switch (state->src_fmt) {
- case AF_L16:
- case AF_S16:
- state->src_frame_size = 80 * sizeof(short);
- state->src_samples_per_frame = 80;
- break;
- case AF_GSM:
- state->src_frame_size = sizeof(gsm_frame);
- state->src_samples_per_frame = 160;
- state->src.gsm_handle = gsm_create();
- if (!state->src.gsm_handle) {
- LOGP(DMGCP, LOGL_ERROR,
- "Failed to initialize GSM decoder.\n");
- return -EINVAL;
- }
- break;
-#ifdef HAVE_BCG729
- case AF_G729:
- state->src_frame_size = 10;
- state->src_samples_per_frame = 80;
- state->src.g729_dec = initBcg729DecoderChannel();
- if (!state->src.g729_dec) {
- LOGP(DMGCP, LOGL_ERROR,
- "Failed to initialize G.729 decoder.\n");
- return -EINVAL;
- }
- break;
-#endif
- case AF_PCMU:
- case AF_PCMA:
- state->src_frame_size = 80;
- state->src_samples_per_frame = 80;
- break;
- default:
- break;
- }
-
- state->dst_fmt = dst_fmt;
-
- switch (state->dst_fmt) {
- case AF_L16:
- case AF_S16:
- state->dst_frame_size = 80*sizeof(short);
- state->dst_samples_per_frame = 80;
- break;
- case AF_GSM:
- state->dst_frame_size = sizeof(gsm_frame);
- state->dst_samples_per_frame = 160;
- state->dst.gsm_handle = gsm_create();
- if (!state->dst.gsm_handle) {
- LOGP(DMGCP, LOGL_ERROR,
- "Failed to initialize GSM encoder.\n");
- return -EINVAL;
- }
- break;
-#ifdef HAVE_BCG729
- case AF_G729:
- state->dst_frame_size = 10;
- state->dst_samples_per_frame = 80;
- state->dst.g729_enc = initBcg729EncoderChannel();
- if (!state->dst.g729_enc) {
- LOGP(DMGCP, LOGL_ERROR,
- "Failed to initialize G.729 decoder.\n");
- return -EINVAL;
- }
- break;
-#endif
- case AF_PCMU:
- case AF_PCMA:
- state->dst_frame_size = 80;
- state->dst_samples_per_frame = 80;
- break;
- default:
- break;
- }
-
- if (dst_end->force_output_ptime)
- state->dst_packet_duration = mgcp_rtp_packet_duration(endp, dst_end);
-
- LOGP(DMGCP, LOGL_INFO,
- "Initialized RTP processing on: 0x%x "
- "conv: %d (%d, %d, %s) -> %d (%d, %d, %s)\n",
- ENDPOINT_NUMBER(endp),
- src_fmt, src_codec->payload_type, src_codec->rate, src_end->fmtp_extra,
- dst_fmt, dst_codec->payload_type, dst_codec->rate, dst_end->fmtp_extra);
-
- return 0;
-}
-
-void mgcp_transcoding_net_downlink_format(struct mgcp_endpoint *endp,
- int *payload_type,
- const char**audio_name,
- const char**fmtp_extra)
-{
- struct mgcp_process_rtp_state *state = endp->net_end.rtp_process_data;
- struct mgcp_rtp_codec *net_codec = &endp->net_end.codec;
- struct mgcp_rtp_codec *bts_codec = &endp->bts_end.codec;
-
- if (!state || net_codec->payload_type < 0) {
- *payload_type = bts_codec->payload_type;
- *audio_name = bts_codec->audio_name;
- *fmtp_extra = endp->bts_end.fmtp_extra;
- return;
- }
-
- *payload_type = net_codec->payload_type;
- *audio_name = net_codec->audio_name;
- *fmtp_extra = endp->net_end.fmtp_extra;
-}
-
-static int decode_audio(struct mgcp_process_rtp_state *state,
- uint8_t **src, size_t *nbytes)
-{
- while (*nbytes >= state->src_frame_size) {
- if (state->sample_cnt + state->src_samples_per_frame > ARRAY_SIZE(state->samples)) {
- LOGP(DMGCP, LOGL_ERROR,
- "Sample buffer too small: %zu > %zu.\n",
- state->sample_cnt + state->src_samples_per_frame,
- ARRAY_SIZE(state->samples));
- return -ENOSPC;
- }
- switch (state->src_fmt) {
- case AF_GSM:
- if (gsm_decode(state->src.gsm_handle,
- (gsm_byte *)*src, state->samples + state->sample_cnt) < 0) {
- LOGP(DMGCP, LOGL_ERROR,
- "Failed to decode GSM.\n");
- return -EINVAL;
- }
- break;
-#ifdef HAVE_BCG729
- case AF_G729:
- bcg729Decoder(state->src.g729_dec, *src, 0, state->samples + state->sample_cnt);
- break;
-#endif
- case AF_PCMU:
- ulaw_decode(*src, state->samples + state->sample_cnt,
- state->src_samples_per_frame);
- break;
- case AF_PCMA:
- alaw_decode(*src, state->samples + state->sample_cnt,
- state->src_samples_per_frame);
- break;
- case AF_S16:
- memmove(state->samples + state->sample_cnt, *src,
- state->src_frame_size);
- break;
- case AF_L16:
- l16_decode(*src, state->samples + state->sample_cnt,
- state->src_samples_per_frame);
- break;
- default:
- break;
- }
- *src += state->src_frame_size;
- *nbytes -= state->src_frame_size;
- state->sample_cnt += state->src_samples_per_frame;
- }
- return 0;
-}
-
-static int encode_audio(struct mgcp_process_rtp_state *state,
- uint8_t *dst, size_t buf_size, size_t max_samples)
-{
- int nbytes = 0;
- size_t nsamples = 0;
- /* Encode samples into dst */
- while (nsamples + state->dst_samples_per_frame <= max_samples) {
- if (nbytes + state->dst_frame_size > buf_size) {
- if (nbytes > 0)
- break;
-
- /* Not even one frame fits into the buffer */
- LOGP(DMGCP, LOGL_INFO,
- "Encoding (RTP) buffer too small: %zu > %zu.\n",
- nbytes + state->dst_frame_size, buf_size);
- return -ENOSPC;
- }
- switch (state->dst_fmt) {
- case AF_GSM:
- gsm_encode(state->dst.gsm_handle,
- state->samples + state->sample_offs, dst);
- break;
-#ifdef HAVE_BCG729
- case AF_G729:
- bcg729Encoder(state->dst.g729_enc,
- state->samples + state->sample_offs, dst);
- break;
-#endif
- case AF_PCMU:
- ulaw_encode(state->samples + state->sample_offs, dst,
- state->src_samples_per_frame);
- break;
- case AF_PCMA:
- alaw_encode(state->samples + state->sample_offs, dst,
- state->src_samples_per_frame);
- break;
- case AF_S16:
- memmove(dst, state->samples + state->sample_offs,
- state->dst_frame_size);
- break;
- case AF_L16:
- l16_encode(state->samples + state->sample_offs, dst,
- state->src_samples_per_frame);
- break;
- default:
- break;
- }
- dst += state->dst_frame_size;
- nbytes += state->dst_frame_size;
- state->sample_offs += state->dst_samples_per_frame;
- nsamples += state->dst_samples_per_frame;
- }
- state->sample_cnt -= nsamples;
- return nbytes;
-}
-
-static struct mgcp_rtp_end *source_for_dest(struct mgcp_endpoint *endp,
- struct mgcp_rtp_end *dst_end)
-{
- if (&endp->bts_end == dst_end)
- return &endp->net_end;
- else if (&endp->net_end == dst_end)
- return &endp->bts_end;
- OSMO_ASSERT(0);
-}
-
-/*
- * With some modems we get offered multiple codecs
- * and we have selected one of them. It might not
- * be the right one and we need to detect this with
- * the first audio packets. One difficulty is that
- * we patch the rtp payload type in place, so we
- * need to discuss this.
- */
-struct mgcp_process_rtp_state *check_transcode_state(
- struct mgcp_endpoint *endp,
- struct mgcp_rtp_end *dst_end,
- struct rtp_hdr *rtp_hdr)
-{
- struct mgcp_rtp_end *src_end;
-
- /* Only deal with messages from net to bts */
- if (&endp->bts_end != dst_end)
- goto done;
-
- src_end = source_for_dest(endp, dst_end);
-
- /* Already patched */
- if (rtp_hdr->payload_type == dst_end->codec.payload_type)
- goto done;
- /* The payload we expect */
- if (rtp_hdr->payload_type == src_end->codec.payload_type)
- goto done;
- /* The matching alternate payload type? Then switch */
- if (rtp_hdr->payload_type == src_end->alt_codec.payload_type) {
- struct mgcp_config *cfg = endp->cfg;
- struct mgcp_rtp_codec tmp_codec = src_end->alt_codec;
- src_end->alt_codec = src_end->codec;
- src_end->codec = tmp_codec;
- cfg->setup_rtp_processing_cb(endp, &endp->net_end, &endp->bts_end);
- cfg->setup_rtp_processing_cb(endp, &endp->bts_end, &endp->net_end);
- }
-
-done:
- return dst_end->rtp_process_data;
-}
-
-int mgcp_transcoding_process_rtp(struct mgcp_endpoint *endp,
- struct mgcp_rtp_end *dst_end,
- char *data, int *len, int buf_size)
-{
- struct mgcp_process_rtp_state *state;
- const size_t rtp_hdr_size = sizeof(struct rtp_hdr);
- struct rtp_hdr *rtp_hdr = (struct rtp_hdr *) data;
- char *payload_data = (char *) &rtp_hdr->data[0];
- int payload_len = *len - rtp_hdr_size;
- uint8_t *src = (uint8_t *)payload_data;
- uint8_t *dst = (uint8_t *)payload_data;
- size_t nbytes = payload_len;
- size_t nsamples;
- size_t max_samples;
- uint32_t ts_no;
- int rc;
-
- state = check_transcode_state(endp, dst_end, rtp_hdr);
- if (!state)
- return 0;
-
- if (state->src_fmt == state->dst_fmt) {
- if (!state->dst_packet_duration)
- return 0;
-
- /* TODO: repackage without transcoding */
- }
-
- /* If the remaining samples do not fit into a fixed ptime,
- * a) discard them, if the next packet is much later
- * b) add silence and * send it, if the current packet is not
- * yet too late
- * c) append the sample data, if the timestamp matches exactly
- */
-
- /* TODO: check payload type (-> G.711 comfort noise) */
-
- if (payload_len > 0) {
- ts_no = ntohl(rtp_hdr->timestamp);
- if (!state->is_running) {
- state->next_seq = ntohs(rtp_hdr->sequence);
- state->next_time = ts_no;
- state->is_running = 1;
- }
-
-
- if (state->sample_cnt > 0) {
- int32_t delta = ts_no - state->next_time;
- /* TODO: check sequence? reordering? packet loss? */
-
- if (delta > state->sample_cnt) {
- /* There is a time gap between the last packet
- * and the current one. Just discard the
- * partial data that is left in the buffer.
- * TODO: This can be improved by adding silence
- * instead if the delta is small enough.
- */
- LOGP(DMGCP, LOGL_NOTICE,
- "0x%x dropping sample buffer due delta=%d sample_cnt=%zu\n",
- ENDPOINT_NUMBER(endp), delta, state->sample_cnt);
- state->sample_cnt = 0;
- state->next_time = ts_no;
- } else if (delta < 0) {
- LOGP(DMGCP, LOGL_NOTICE,
- "RTP time jumps backwards, delta = %d, "
- "discarding buffered samples\n",
- delta);
- state->sample_cnt = 0;
- state->sample_offs = 0;
- return -EAGAIN;
- }
-
- /* Make sure the samples start without offset */
- if (state->sample_offs && state->sample_cnt)
- memmove(&state->samples[0],
- &state->samples[state->sample_offs],
- state->sample_cnt *
- sizeof(state->samples[0]));
- }
-
- state->sample_offs = 0;
-
- /* Append decoded audio to samples */
- decode_audio(state, &src, &nbytes);
-
- if (nbytes > 0)
- LOGP(DMGCP, LOGL_NOTICE,
- "Skipped audio frame in RTP packet: %zu octets\n",
- nbytes);
- } else
- ts_no = state->next_time;
-
- if (state->sample_cnt < state->dst_packet_duration)
- return -EAGAIN;
-
- max_samples =
- state->dst_packet_duration ?
- state->dst_packet_duration : state->sample_cnt;
-
- nsamples = state->sample_cnt;
-
- rc = encode_audio(state, dst, buf_size, max_samples);
- /*
- * There were no samples to encode?
- * TODO: how does this work for comfort noise?
- */
- if (rc == 0)
- return -ENOMSG;
- /* Any other error during the encoding */
- if (rc < 0)
- return rc;
-
- nsamples -= state->sample_cnt;
-
- *len = rtp_hdr_size + rc;
- rtp_hdr->sequence = htons(state->next_seq);
- rtp_hdr->timestamp = htonl(ts_no);
-
- state->next_seq += 1;
- state->next_time = ts_no + nsamples;
-
- /*
- * XXX: At this point we should always have consumed
- * samples. So doing OSMO_ASSERT(nsamples > 0) and returning
- * rtp_hdr_size should be fine.
- */
- return nsamples ? rtp_hdr_size : 0;
-}
diff --git a/openbsc/src/libmgcp/mgcp_vty.c b/openbsc/src/libmgcp/mgcp_vty.c
deleted file mode 100644
index 7d4b2da84..000000000
--- a/openbsc/src/libmgcp/mgcp_vty.c
+++ /dev/null
@@ -1,1543 +0,0 @@
-/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
-/* The protocol implementation */
-
-/*
- * (C) 2009-2014 by Holger Hans Peter Freyther <zecke@selfish.org>
- * (C) 2009-2011 by On-Waves
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-
-#include <osmocom/core/talloc.h>
-
-#include <openbsc/mgcp.h>
-#include <openbsc/mgcp_internal.h>
-#include <openbsc/vty.h>
-
-#include <string.h>
-
-#define RTCP_OMIT_STR "Drop RTCP packets in both directions\n"
-#define RTP_PATCH_STR "Modify RTP packet header in both directions\n"
-#define RTP_KEEPALIVE_STR "Send dummy UDP packet to net RTP destination\n"
-
-static struct mgcp_config *g_cfg = NULL;
-
-static struct mgcp_trunk_config *find_trunk(struct mgcp_config *cfg, int nr)
-{
- struct mgcp_trunk_config *trunk;
-
- if (nr == 0)
- trunk = &cfg->trunk;
- else
- trunk = mgcp_trunk_num(cfg, nr);
-
- return trunk;
-}
-
-/*
- * vty code for mgcp below
- */
-struct cmd_node mgcp_node = {
- MGCP_NODE,
- "%s(config-mgcp)# ",
- 1,
-};
-
-struct cmd_node trunk_node = {
- TRUNK_NODE,
- "%s(config-mgcp-trunk)# ",
- 1,
-};
-
-static int config_write_mgcp(struct vty *vty)
-{
- vty_out(vty, "mgcp%s", VTY_NEWLINE);
- if (g_cfg->local_ip)
- vty_out(vty, " local ip %s%s", g_cfg->local_ip, VTY_NEWLINE);
- if (g_cfg->bts_ip && strlen(g_cfg->bts_ip) != 0)
- vty_out(vty, " bts ip %s%s", g_cfg->bts_ip, VTY_NEWLINE);
- vty_out(vty, " bind ip %s%s", g_cfg->source_addr, VTY_NEWLINE);
- vty_out(vty, " bind port %u%s", g_cfg->source_port, VTY_NEWLINE);
-
- if (g_cfg->bts_ports.mode == PORT_ALLOC_STATIC)
- vty_out(vty, " rtp bts-base %u%s", g_cfg->bts_ports.base_port, VTY_NEWLINE);
- else
- vty_out(vty, " rtp bts-range %u %u%s",
- g_cfg->bts_ports.range_start, g_cfg->bts_ports.range_end, VTY_NEWLINE);
- if (g_cfg->bts_ports.bind_addr)
- vty_out(vty, " rtp bts-bind-ip %s%s", g_cfg->bts_ports.bind_addr, VTY_NEWLINE);
-
- if (g_cfg->net_ports.mode == PORT_ALLOC_STATIC)
- vty_out(vty, " rtp net-base %u%s", g_cfg->net_ports.base_port, VTY_NEWLINE);
- else
- vty_out(vty, " rtp net-range %u %u%s",
- g_cfg->net_ports.range_start, g_cfg->net_ports.range_end, VTY_NEWLINE);
- if (g_cfg->net_ports.bind_addr)
- vty_out(vty, " rtp net-bind-ip %s%s", g_cfg->net_ports.bind_addr, VTY_NEWLINE);
-
- vty_out(vty, " rtp ip-dscp %d%s", g_cfg->endp_dscp, VTY_NEWLINE);
- if (g_cfg->trunk.keepalive_interval == MGCP_KEEPALIVE_ONCE)
- vty_out(vty, " rtp keep-alive once%s", VTY_NEWLINE);
- else if (g_cfg->trunk.keepalive_interval)
- vty_out(vty, " rtp keep-alive %d%s",
- g_cfg->trunk.keepalive_interval, VTY_NEWLINE);
- else
- vty_out(vty, " no rtp keep-alive%s", VTY_NEWLINE);
-
- if (g_cfg->trunk.omit_rtcp)
- vty_out(vty, " rtcp-omit%s", VTY_NEWLINE);
- else
- vty_out(vty, " no rtcp-omit%s", VTY_NEWLINE);
- if (g_cfg->trunk.force_constant_ssrc || g_cfg->trunk.force_aligned_timing) {
- vty_out(vty, " %srtp-patch ssrc%s",
- g_cfg->trunk.force_constant_ssrc ? "" : "no ", VTY_NEWLINE);
- vty_out(vty, " %srtp-patch timestamp%s",
- g_cfg->trunk.force_aligned_timing ? "" : "no ", VTY_NEWLINE);
- } else
- vty_out(vty, " no rtp-patch%s", VTY_NEWLINE);
- if (g_cfg->trunk.audio_payload != -1)
- vty_out(vty, " sdp audio-payload number %d%s",
- g_cfg->trunk.audio_payload, VTY_NEWLINE);
- if (g_cfg->trunk.audio_name)
- vty_out(vty, " sdp audio-payload name %s%s",
- g_cfg->trunk.audio_name, VTY_NEWLINE);
- if (g_cfg->trunk.audio_fmtp_extra)
- vty_out(vty, " sdp audio fmtp-extra %s%s",
- g_cfg->trunk.audio_fmtp_extra, VTY_NEWLINE);
- vty_out(vty, " %ssdp audio-payload send-ptime%s",
- g_cfg->trunk.audio_send_ptime ? "" : "no ", VTY_NEWLINE);
- vty_out(vty, " %ssdp audio-payload send-name%s",
- g_cfg->trunk.audio_send_name ? "" : "no ", VTY_NEWLINE);
- vty_out(vty, " loop %u%s", !!g_cfg->trunk.audio_loop, VTY_NEWLINE);
- vty_out(vty, " number endpoints %u%s", g_cfg->trunk.number_endpoints - 1, VTY_NEWLINE);
- vty_out(vty, " %sallow-transcoding%s",
- g_cfg->trunk.no_audio_transcoding ? "no " : "", VTY_NEWLINE);
- if (g_cfg->call_agent_addr)
- vty_out(vty, " call-agent ip %s%s", g_cfg->call_agent_addr, VTY_NEWLINE);
- if (g_cfg->transcoder_ip)
- vty_out(vty, " transcoder-mgw %s%s", g_cfg->transcoder_ip, VTY_NEWLINE);
-
- if (g_cfg->transcoder_ports.mode == PORT_ALLOC_STATIC)
- vty_out(vty, " rtp transcoder-base %u%s", g_cfg->transcoder_ports.base_port, VTY_NEWLINE);
- else
- vty_out(vty, " rtp transcoder-range %u %u%s",
- g_cfg->transcoder_ports.range_start, g_cfg->transcoder_ports.range_end, VTY_NEWLINE);
- if (g_cfg->bts_force_ptime > 0)
- vty_out(vty, " rtp force-ptime %d%s", g_cfg->bts_force_ptime, VTY_NEWLINE);
- vty_out(vty, " transcoder-remote-base %u%s", g_cfg->transcoder_remote_base, VTY_NEWLINE);
-
- switch (g_cfg->osmux) {
- case OSMUX_USAGE_ON:
- vty_out(vty, " osmux on%s", VTY_NEWLINE);
- break;
- case OSMUX_USAGE_ONLY:
- vty_out(vty, " osmux only%s", VTY_NEWLINE);
- break;
- case OSMUX_USAGE_OFF:
- default:
- vty_out(vty, " osmux off%s", VTY_NEWLINE);
- break;
- }
- if (g_cfg->osmux) {
- vty_out(vty, " osmux bind-ip %s%s",
- g_cfg->osmux_addr, VTY_NEWLINE);
- vty_out(vty, " osmux batch-factor %d%s",
- g_cfg->osmux_batch, VTY_NEWLINE);
- vty_out(vty, " osmux batch-size %u%s",
- g_cfg->osmux_batch_size, VTY_NEWLINE);
- vty_out(vty, " osmux port %u%s",
- g_cfg->osmux_port, VTY_NEWLINE);
- vty_out(vty, " osmux dummy %s%s",
- g_cfg->osmux_dummy ? "on" : "off", VTY_NEWLINE);
- }
- return CMD_SUCCESS;
-}
-
-static void dump_rtp_end(const char *end_name, struct vty *vty,
- struct mgcp_rtp_state *state, struct mgcp_rtp_end *end)
-{
- struct mgcp_rtp_codec *codec = &end->codec;
-
- vty_out(vty,
- " %s%s"
- " Timestamp Errs: %d->%d%s"
- " Dropped Packets: %d%s"
- " Payload Type: %d Rate: %u Channels: %d %s"
- " Frame Duration: %u Frame Denominator: %u%s"
- " FPP: %d Packet Duration: %u%s"
- " FMTP-Extra: %s Audio-Name: %s Sub-Type: %s%s"
- " Output-Enabled: %d Force-PTIME: %d%s",
- end_name, VTY_NEWLINE,
- state->in_stream.err_ts_counter,
- state->out_stream.err_ts_counter, VTY_NEWLINE,
- end->dropped_packets, VTY_NEWLINE,
- codec->payload_type, codec->rate, codec->channels, VTY_NEWLINE,
- codec->frame_duration_num, codec->frame_duration_den, VTY_NEWLINE,
- end->frames_per_packet, end->packet_duration_ms, VTY_NEWLINE,
- end->fmtp_extra, codec->audio_name, codec->subtype_name, VTY_NEWLINE,
- end->output_enabled, end->force_output_ptime, VTY_NEWLINE);
-}
-
-static void dump_trunk(struct vty *vty, struct mgcp_trunk_config *cfg, int verbose)
-{
- int i;
-
- vty_out(vty, "%s trunk nr %d with %d endpoints:%s",
- cfg->trunk_type == MGCP_TRUNK_VIRTUAL ? "Virtual" : "E1",
- cfg->trunk_nr, cfg->number_endpoints - 1, VTY_NEWLINE);
-
- if (!cfg->endpoints) {
- vty_out(vty, "No endpoints allocated yet.%s", VTY_NEWLINE);
- return;
- }
-
- for (i = 1; i < cfg->number_endpoints; ++i) {
- struct mgcp_endpoint *endp = &cfg->endpoints[i];
- vty_out(vty,
- " Endpoint 0x%.2x: CI: %d net: %u/%u bts: %u/%u on %s "
- "traffic received bts: %u remote: %u transcoder: %u/%u%s",
- i, endp->ci,
- ntohs(endp->net_end.rtp_port), ntohs(endp->net_end.rtcp_port),
- ntohs(endp->bts_end.rtp_port), ntohs(endp->bts_end.rtcp_port),
- inet_ntoa(endp->bts_end.addr),
- endp->bts_end.packets, endp->net_end.packets,
- endp->trans_net.packets, endp->trans_bts.packets,
- VTY_NEWLINE);
-
- if (verbose && endp->allocated) {
- dump_rtp_end("Net->BTS", vty, &endp->bts_state, &endp->bts_end);
- dump_rtp_end("BTS->Net", vty, &endp->net_state, &endp->net_end);
- }
- }
-}
-
-DEFUN(show_mcgp, show_mgcp_cmd,
- "show mgcp [stats]",
- SHOW_STR
- "Display information about the MGCP Media Gateway\n"
- "Include Statistics\n")
-{
- struct mgcp_trunk_config *trunk;
- int show_stats = argc >= 1;
-
- dump_trunk(vty, &g_cfg->trunk, show_stats);
-
- llist_for_each_entry(trunk, &g_cfg->trunks, entry)
- dump_trunk(vty, trunk, show_stats);
-
- if (g_cfg->osmux)
- vty_out(vty, "Osmux used CID: %d%s", osmux_used_cid(), VTY_NEWLINE);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp,
- cfg_mgcp_cmd,
- "mgcp",
- "Configure the MGCP")
-{
- vty->node = MGCP_NODE;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_local_ip,
- cfg_mgcp_local_ip_cmd,
- "local ip A.B.C.D",
- "Local options for the SDP record\n"
- IP_STR
- "IPv4 Address to use in SDP record\n")
-{
- osmo_talloc_replace_string(g_cfg, &g_cfg->local_ip, argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_bts_ip,
- cfg_mgcp_bts_ip_cmd,
- "bts ip A.B.C.D",
- "BTS Audio source/destination options\n"
- IP_STR
- "IPv4 Address of the BTS\n")
-{
- osmo_talloc_replace_string(g_cfg, &g_cfg->bts_ip, argv[0]);
- inet_aton(g_cfg->bts_ip, &g_cfg->bts_in);
- return CMD_SUCCESS;
-}
-
-#define BIND_STR "Listen/Bind related socket option\n"
-DEFUN(cfg_mgcp_bind_ip,
- cfg_mgcp_bind_ip_cmd,
- "bind ip A.B.C.D",
- BIND_STR
- IP_STR
- "IPv4 Address to bind to\n")
-{
- osmo_talloc_replace_string(g_cfg, &g_cfg->source_addr, argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_bind_port,
- cfg_mgcp_bind_port_cmd,
- "bind port <0-65534>",
- BIND_STR
- "Port information\n"
- "UDP port to listen for MGCP messages\n")
-{
- unsigned int port = atoi(argv[0]);
- g_cfg->source_port = port;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_bind_early,
- cfg_mgcp_bind_early_cmd,
- "bind early (0|1)",
- BIND_STR
- "Bind local ports on start up\n"
- "Bind on demand\n" "Bind on startup\n")
-{
- vty_out(vty, "bind early is deprecated, remove it from the config.\n");
- return CMD_WARNING;
-}
-
-static void parse_base(struct mgcp_port_range *range, const char **argv)
-{
- unsigned int port = atoi(argv[0]);
- range->mode = PORT_ALLOC_STATIC;
- range->base_port = port;
-}
-
-static void parse_range(struct mgcp_port_range *range, const char **argv)
-{
- range->mode = PORT_ALLOC_DYNAMIC;
- range->range_start = atoi(argv[0]);
- range->range_end = atoi(argv[1]);
- range->last_port = g_cfg->bts_ports.range_start;
-}
-
-
-#define RTP_STR "RTP configuration\n"
-#define BTS_START_STR "First UDP port allocated for the BTS side\n"
-#define NET_START_STR "First UDP port allocated for the NET side\n"
-#define UDP_PORT_STR "UDP Port number\n"
-DEFUN(cfg_mgcp_rtp_bts_base_port,
- cfg_mgcp_rtp_bts_base_port_cmd,
- "rtp bts-base <0-65534>",
- RTP_STR
- BTS_START_STR
- UDP_PORT_STR)
-{
- parse_base(&g_cfg->bts_ports, argv);
- return CMD_SUCCESS;
-}
-
-#define RANGE_START_STR "Start of the range of ports\n"
-#define RANGE_END_STR "End of the range of ports\n"
-DEFUN(cfg_mgcp_rtp_bts_range,
- cfg_mgcp_rtp_bts_range_cmd,
- "rtp bts-range <0-65534> <0-65534>",
- RTP_STR "Range of ports to use for the BTS side\n"
- RANGE_START_STR RANGE_END_STR)
-{
- parse_range(&g_cfg->bts_ports, argv);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_rtp_net_range,
- cfg_mgcp_rtp_net_range_cmd,
- "rtp net-range <0-65534> <0-65534>",
- RTP_STR "Range of ports to use for the NET side\n"
- RANGE_START_STR RANGE_END_STR)
-{
- parse_range(&g_cfg->net_ports, argv);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_rtp_net_base_port,
- cfg_mgcp_rtp_net_base_port_cmd,
- "rtp net-base <0-65534>",
- RTP_STR NET_START_STR UDP_PORT_STR)
-{
- parse_base(&g_cfg->net_ports, argv);
- return CMD_SUCCESS;
-}
-
-ALIAS_DEPRECATED(cfg_mgcp_rtp_bts_base_port, cfg_mgcp_rtp_base_port_cmd,
- "rtp base <0-65534>",
- RTP_STR BTS_START_STR UDP_PORT_STR)
-
-DEFUN(cfg_mgcp_rtp_transcoder_range,
- cfg_mgcp_rtp_transcoder_range_cmd,
- "rtp transcoder-range <0-65534> <0-65534>",
- RTP_STR "Range of ports to use for the Transcoder\n"
- RANGE_START_STR RANGE_END_STR)
-{
- parse_range(&g_cfg->transcoder_ports, argv);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_rtp_transcoder_base,
- cfg_mgcp_rtp_transcoder_base_cmd,
- "rtp transcoder-base <0-65534>",
- RTP_STR "First UDP port allocated for the Transcoder side\n"
- UDP_PORT_STR)
-{
- parse_base(&g_cfg->transcoder_ports, argv);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_rtp_bts_bind_ip,
- cfg_mgcp_rtp_bts_bind_ip_cmd,
- "rtp bts-bind-ip A.B.C.D",
- RTP_STR "Bind endpoints facing the BTS\n" "Address to bind to\n")
-{
- osmo_talloc_replace_string(g_cfg, &g_cfg->bts_ports.bind_addr, argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_rtp_no_bts_bind_ip,
- cfg_mgcp_rtp_no_bts_bind_ip_cmd,
- "no rtp bts-bind-ip",
- NO_STR RTP_STR "Bind endpoints facing the BTS\n" "Address to bind to\n")
-{
- talloc_free(g_cfg->bts_ports.bind_addr);
- g_cfg->bts_ports.bind_addr = NULL;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_rtp_net_bind_ip,
- cfg_mgcp_rtp_net_bind_ip_cmd,
- "rtp net-bind-ip A.B.C.D",
- RTP_STR "Bind endpoints facing the Network\n" "Address to bind to\n")
-{
- osmo_talloc_replace_string(g_cfg, &g_cfg->net_ports.bind_addr, argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_rtp_no_net_bind_ip,
- cfg_mgcp_rtp_no_net_bind_ip_cmd,
- "no rtp net-bind-ip",
- NO_STR RTP_STR "Bind endpoints facing the Network\n" "Address to bind to\n")
-{
- talloc_free(g_cfg->net_ports.bind_addr);
- g_cfg->net_ports.bind_addr = NULL;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_rtp_ip_dscp,
- cfg_mgcp_rtp_ip_dscp_cmd,
- "rtp ip-dscp <0-255>",
- RTP_STR
- "Apply IP_TOS to the audio stream (including Osmux)\n" "The DSCP value\n")
-{
- int dscp = atoi(argv[0]);
- g_cfg->endp_dscp = dscp;
- return CMD_SUCCESS;
-}
-
-ALIAS_DEPRECATED(cfg_mgcp_rtp_ip_dscp, cfg_mgcp_rtp_ip_tos_cmd,
- "rtp ip-tos <0-255>",
- RTP_STR
- "Apply IP_TOS to the audio stream\n" "The DSCP value\n")
-
-#define FORCE_PTIME_STR "Force a fixed ptime for packets sent to the BTS"
-DEFUN(cfg_mgcp_rtp_force_ptime,
- cfg_mgcp_rtp_force_ptime_cmd,
- "rtp force-ptime (10|20|40)",
- RTP_STR FORCE_PTIME_STR
- "The required ptime (packet duration) in ms\n"
- "10 ms\n20 ms\n40 ms\n")
-{
- g_cfg->bts_force_ptime = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_no_rtp_force_ptime,
- cfg_mgcp_no_rtp_force_ptime_cmd,
- "no rtp force-ptime",
- NO_STR RTP_STR FORCE_PTIME_STR)
-{
- g_cfg->bts_force_ptime = 0;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_sdp_fmtp_extra,
- cfg_mgcp_sdp_fmtp_extra_cmd,
- "sdp audio fmtp-extra .NAME",
- "Add extra fmtp for the SDP file\n" "Audio\n" "Fmtp-extra\n"
- "Extra Information\n")
-{
- char *txt = argv_concat(argv, argc, 0);
- if (!txt)
- return CMD_WARNING;
-
- osmo_talloc_replace_string(g_cfg, &g_cfg->trunk.audio_fmtp_extra, txt);
- talloc_free(txt);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_allow_transcoding,
- cfg_mgcp_allow_transcoding_cmd,
- "allow-transcoding",
- "Allow transcoding\n")
-{
- g_cfg->trunk.no_audio_transcoding = 0;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_no_allow_transcoding,
- cfg_mgcp_no_allow_transcoding_cmd,
- "no allow-transcoding",
- NO_STR "Allow transcoding\n")
-{
- g_cfg->trunk.no_audio_transcoding = 1;
- return CMD_SUCCESS;
-}
-
-#define SDP_STR "SDP File related options\n"
-#define AUDIO_STR "Audio payload options\n"
-DEFUN(cfg_mgcp_sdp_payload_number,
- cfg_mgcp_sdp_payload_number_cmd,
- "sdp audio-payload number <0-255>",
- SDP_STR AUDIO_STR
- "Number\n" "Payload number\n")
-{
- unsigned int payload = atoi(argv[0]);
- g_cfg->trunk.audio_payload = payload;
- return CMD_SUCCESS;
-}
-
-ALIAS_DEPRECATED(cfg_mgcp_sdp_payload_number, cfg_mgcp_sdp_payload_number_cmd_old,
- "sdp audio payload number <0-255>",
- SDP_STR AUDIO_STR AUDIO_STR "Number\n" "Payload number\n")
-
-
-DEFUN(cfg_mgcp_sdp_payload_name,
- cfg_mgcp_sdp_payload_name_cmd,
- "sdp audio-payload name NAME",
- SDP_STR AUDIO_STR "Name\n" "Payload name\n")
-{
- osmo_talloc_replace_string(g_cfg, &g_cfg->trunk.audio_name, argv[0]);
- return CMD_SUCCESS;
-}
-
-ALIAS_DEPRECATED(cfg_mgcp_sdp_payload_name, cfg_mgcp_sdp_payload_name_cmd_old,
- "sdp audio payload name NAME",
- SDP_STR AUDIO_STR AUDIO_STR "Name\n" "Payload name\n")
-
-DEFUN(cfg_mgcp_sdp_payload_send_ptime,
- cfg_mgcp_sdp_payload_send_ptime_cmd,
- "sdp audio-payload send-ptime",
- SDP_STR AUDIO_STR
- "Send SDP ptime (packet duration) attribute\n")
-{
- g_cfg->trunk.audio_send_ptime = 1;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_no_sdp_payload_send_ptime,
- cfg_mgcp_no_sdp_payload_send_ptime_cmd,
- "no sdp audio-payload send-ptime",
- NO_STR SDP_STR AUDIO_STR
- "Send SDP ptime (packet duration) attribute\n")
-{
- g_cfg->trunk.audio_send_ptime = 0;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_sdp_payload_send_name,
- cfg_mgcp_sdp_payload_send_name_cmd,
- "sdp audio-payload send-name",
- SDP_STR AUDIO_STR
- "Send SDP rtpmap with the audio name\n")
-{
- g_cfg->trunk.audio_send_name = 1;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_no_sdp_payload_send_name,
- cfg_mgcp_no_sdp_payload_send_name_cmd,
- "no sdp audio-payload send-name",
- NO_STR SDP_STR AUDIO_STR
- "Send SDP rtpmap with the audio name\n")
-{
- g_cfg->trunk.audio_send_name = 0;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_loop,
- cfg_mgcp_loop_cmd,
- "loop (0|1)",
- "Loop audio for all endpoints on main trunk\n"
- "Don't Loop\n" "Loop\n")
-{
- if (g_cfg->osmux) {
- vty_out(vty, "Cannot use `loop' with `osmux'.%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
- g_cfg->trunk.audio_loop = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_number_endp,
- cfg_mgcp_number_endp_cmd,
- "number endpoints <0-65534>",
- "Number options\n" "Endpoints available\n" "Number endpoints\n")
-{
- /* + 1 as we start counting at one */
- g_cfg->trunk.number_endpoints = atoi(argv[0]) + 1;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_omit_rtcp,
- cfg_mgcp_omit_rtcp_cmd,
- "rtcp-omit",
- RTCP_OMIT_STR)
-{
- g_cfg->trunk.omit_rtcp = 1;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_no_omit_rtcp,
- cfg_mgcp_no_omit_rtcp_cmd,
- "no rtcp-omit",
- NO_STR RTCP_OMIT_STR)
-{
- g_cfg->trunk.omit_rtcp = 0;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_patch_rtp_ssrc,
- cfg_mgcp_patch_rtp_ssrc_cmd,
- "rtp-patch ssrc",
- RTP_PATCH_STR
- "Force a fixed SSRC\n"
- )
-{
- g_cfg->trunk.force_constant_ssrc = 1;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_no_patch_rtp_ssrc,
- cfg_mgcp_no_patch_rtp_ssrc_cmd,
- "no rtp-patch ssrc",
- NO_STR RTP_PATCH_STR
- "Force a fixed SSRC\n"
- )
-{
- g_cfg->trunk.force_constant_ssrc = 0;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_patch_rtp_ts,
- cfg_mgcp_patch_rtp_ts_cmd,
- "rtp-patch timestamp",
- RTP_PATCH_STR
- "Adjust RTP timestamp\n"
- )
-{
- g_cfg->trunk.force_aligned_timing = 1;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_no_patch_rtp_ts,
- cfg_mgcp_no_patch_rtp_ts_cmd,
- "no rtp-patch timestamp",
- NO_STR RTP_PATCH_STR
- "Adjust RTP timestamp\n"
- )
-{
- g_cfg->trunk.force_aligned_timing = 0;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_no_patch_rtp,
- cfg_mgcp_no_patch_rtp_cmd,
- "no rtp-patch",
- NO_STR RTP_PATCH_STR)
-{
- g_cfg->trunk.force_constant_ssrc = 0;
- g_cfg->trunk.force_aligned_timing = 0;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_rtp_keepalive,
- cfg_mgcp_rtp_keepalive_cmd,
- "rtp keep-alive <1-120>",
- RTP_STR RTP_KEEPALIVE_STR
- "Keep alive interval in secs\n"
- )
-{
- mgcp_trunk_set_keepalive(&g_cfg->trunk, atoi(argv[0]));
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_rtp_keepalive_once,
- cfg_mgcp_rtp_keepalive_once_cmd,
- "rtp keep-alive once",
- RTP_STR RTP_KEEPALIVE_STR
- "Send dummy packet only once after CRCX/MDCX\n"
- )
-{
- mgcp_trunk_set_keepalive(&g_cfg->trunk, MGCP_KEEPALIVE_ONCE);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_no_rtp_keepalive,
- cfg_mgcp_no_rtp_keepalive_cmd,
- "no rtp keep-alive",
- NO_STR RTP_STR RTP_KEEPALIVE_STR
- )
-{
- mgcp_trunk_set_keepalive(&g_cfg->trunk, 0);
- return CMD_SUCCESS;
-}
-
-
-
-#define CALL_AGENT_STR "Callagent information\n"
-DEFUN(cfg_mgcp_agent_addr,
- cfg_mgcp_agent_addr_cmd,
- "call-agent ip A.B.C.D",
- CALL_AGENT_STR IP_STR
- "IPv4 Address of the callagent\n")
-{
- osmo_talloc_replace_string(g_cfg, &g_cfg->call_agent_addr, argv[0]);
- return CMD_SUCCESS;
-}
-
-ALIAS_DEPRECATED(cfg_mgcp_agent_addr, cfg_mgcp_agent_addr_cmd_old,
- "call agent ip A.B.C.D",
- CALL_AGENT_STR CALL_AGENT_STR IP_STR
- "IPv4 Address of the callagent\n")
-
-
-DEFUN(cfg_mgcp_transcoder,
- cfg_mgcp_transcoder_cmd,
- "transcoder-mgw A.B.C.D",
- "Use a MGW to detranscoder RTP\n"
- "The IP address of the MGW")
-{
- osmo_talloc_replace_string(g_cfg, &g_cfg->transcoder_ip, argv[0]);
- inet_aton(g_cfg->transcoder_ip, &g_cfg->transcoder_in);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_no_transcoder,
- cfg_mgcp_no_transcoder_cmd,
- "no transcoder-mgw",
- NO_STR "Disable the transcoding\n")
-{
- if (g_cfg->transcoder_ip) {
- LOGP(DMGCP, LOGL_NOTICE, "Disabling transcoding on future calls.\n");
- talloc_free(g_cfg->transcoder_ip);
- g_cfg->transcoder_ip = NULL;
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_transcoder_remote_base,
- cfg_mgcp_transcoder_remote_base_cmd,
- "transcoder-remote-base <0-65534>",
- "Set the base port for the transcoder\n" "The RTP base port on the transcoder")
-{
- g_cfg->transcoder_remote_base = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_trunk, cfg_mgcp_trunk_cmd,
- "trunk <1-64>",
- "Configure a SS7 trunk\n" "Trunk Nr\n")
-{
- struct mgcp_trunk_config *trunk;
- int index = atoi(argv[0]);
-
- trunk = mgcp_trunk_num(g_cfg, index);
- if (!trunk)
- trunk = mgcp_trunk_alloc(g_cfg, index);
-
- if (!trunk) {
- vty_out(vty, "%%Unable to allocate trunk %u.%s",
- index, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- vty->node = TRUNK_NODE;
- vty->index = trunk;
- return CMD_SUCCESS;
-}
-
-static int config_write_trunk(struct vty *vty)
-{
- struct mgcp_trunk_config *trunk;
-
- llist_for_each_entry(trunk, &g_cfg->trunks, entry) {
- vty_out(vty, " trunk %d%s", trunk->trunk_nr, VTY_NEWLINE);
- vty_out(vty, " sdp audio-payload number %d%s",
- trunk->audio_payload, VTY_NEWLINE);
- vty_out(vty, " sdp audio-payload name %s%s",
- trunk->audio_name, VTY_NEWLINE);
- vty_out(vty, " %ssdp audio-payload send-ptime%s",
- trunk->audio_send_ptime ? "" : "no ", VTY_NEWLINE);
- vty_out(vty, " %ssdp audio-payload send-name%s",
- trunk->audio_send_name ? "" : "no ", VTY_NEWLINE);
-
- if (trunk->keepalive_interval == MGCP_KEEPALIVE_ONCE)
- vty_out(vty, " rtp keep-alive once%s", VTY_NEWLINE);
- else if (trunk->keepalive_interval)
- vty_out(vty, " rtp keep-alive %d%s",
- trunk->keepalive_interval, VTY_NEWLINE);
- else
- vty_out(vty, " no rtp keep-alive%s", VTY_NEWLINE);
-
- vty_out(vty, " loop %d%s",
- trunk->audio_loop, VTY_NEWLINE);
- if (trunk->omit_rtcp)
- vty_out(vty, " rtcp-omit%s", VTY_NEWLINE);
- else
- vty_out(vty, " no rtcp-omit%s", VTY_NEWLINE);
- if (trunk->force_constant_ssrc || trunk->force_aligned_timing) {
- vty_out(vty, " %srtp-patch ssrc%s",
- trunk->force_constant_ssrc ? "" : "no ", VTY_NEWLINE);
- vty_out(vty, " %srtp-patch timestamp%s",
- trunk->force_aligned_timing ? "" : "no ", VTY_NEWLINE);
- } else
- vty_out(vty, " no rtp-patch%s", VTY_NEWLINE);
- if (trunk->audio_fmtp_extra)
- vty_out(vty, " sdp audio fmtp-extra %s%s",
- trunk->audio_fmtp_extra, VTY_NEWLINE);
- vty_out(vty, " %sallow-transcoding%s",
- trunk->no_audio_transcoding ? "no " : "", VTY_NEWLINE);
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_trunk_sdp_fmtp_extra,
- cfg_trunk_sdp_fmtp_extra_cmd,
- "sdp audio fmtp-extra .NAME",
- "Add extra fmtp for the SDP file\n" "Audio\n" "Fmtp-extra\n"
- "Extra Information\n")
-{
- struct mgcp_trunk_config *trunk = vty->index;
- char *txt = argv_concat(argv, argc, 0);
- if (!txt)
- return CMD_WARNING;
-
- osmo_talloc_replace_string(g_cfg, &trunk->audio_fmtp_extra, txt);
- talloc_free(txt);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_trunk_payload_number,
- cfg_trunk_payload_number_cmd,
- "sdp audio-payload number <0-255>",
- SDP_STR AUDIO_STR "Number\n" "Payload Number\n")
-{
- struct mgcp_trunk_config *trunk = vty->index;
- unsigned int payload = atoi(argv[0]);
-
- trunk->audio_payload = payload;
- return CMD_SUCCESS;
-}
-
-ALIAS_DEPRECATED(cfg_trunk_payload_number, cfg_trunk_payload_number_cmd_old,
- "sdp audio payload number <0-255>",
- SDP_STR AUDIO_STR AUDIO_STR "Number\n" "Payload Number\n")
-
-DEFUN(cfg_trunk_payload_name,
- cfg_trunk_payload_name_cmd,
- "sdp audio-payload name NAME",
- SDP_STR AUDIO_STR "Payload\n" "Payload Name\n")
-{
- struct mgcp_trunk_config *trunk = vty->index;
-
- osmo_talloc_replace_string(g_cfg, &trunk->audio_name, argv[0]);
- return CMD_SUCCESS;
-}
-
-ALIAS_DEPRECATED(cfg_trunk_payload_name, cfg_trunk_payload_name_cmd_old,
- "sdp audio payload name NAME",
- SDP_STR AUDIO_STR AUDIO_STR "Payload\n" "Payload Name\n")
-
-
-DEFUN(cfg_trunk_loop,
- cfg_trunk_loop_cmd,
- "loop (0|1)",
- "Loop audio for all endpoints on this trunk\n"
- "Don't Loop\n" "Loop\n")
-{
- struct mgcp_trunk_config *trunk = vty->index;
-
- if (g_cfg->osmux) {
- vty_out(vty, "Cannot use `loop' with `osmux'.%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
- trunk->audio_loop = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_trunk_sdp_payload_send_ptime,
- cfg_trunk_sdp_payload_send_ptime_cmd,
- "sdp audio-payload send-ptime",
- SDP_STR AUDIO_STR
- "Send SDP ptime (packet duration) attribute\n")
-{
- struct mgcp_trunk_config *trunk = vty->index;
- trunk->audio_send_ptime = 1;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_trunk_no_sdp_payload_send_ptime,
- cfg_trunk_no_sdp_payload_send_ptime_cmd,
- "no sdp audio-payload send-ptime",
- NO_STR SDP_STR AUDIO_STR
- "Send SDP ptime (packet duration) attribute\n")
-{
- struct mgcp_trunk_config *trunk = vty->index;
- trunk->audio_send_ptime = 0;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_trunk_sdp_payload_send_name,
- cfg_trunk_sdp_payload_send_name_cmd,
- "sdp audio-payload send-name",
- SDP_STR AUDIO_STR
- "Send SDP rtpmap with the audio name\n")
-{
- struct mgcp_trunk_config *trunk = vty->index;
- trunk->audio_send_name = 1;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_trunk_no_sdp_payload_send_name,
- cfg_trunk_no_sdp_payload_send_name_cmd,
- "no sdp audio-payload send-name",
- NO_STR SDP_STR AUDIO_STR
- "Send SDP rtpmap with the audio name\n")
-{
- struct mgcp_trunk_config *trunk = vty->index;
- trunk->audio_send_name = 0;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_trunk_omit_rtcp,
- cfg_trunk_omit_rtcp_cmd,
- "rtcp-omit",
- RTCP_OMIT_STR)
-{
- struct mgcp_trunk_config *trunk = vty->index;
- trunk->omit_rtcp = 1;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_trunk_no_omit_rtcp,
- cfg_trunk_no_omit_rtcp_cmd,
- "no rtcp-omit",
- NO_STR RTCP_OMIT_STR)
-{
- struct mgcp_trunk_config *trunk = vty->index;
- trunk->omit_rtcp = 0;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_trunk_patch_rtp_ssrc,
- cfg_trunk_patch_rtp_ssrc_cmd,
- "rtp-patch ssrc",
- RTP_PATCH_STR
- "Force a fixed SSRC\n"
- )
-{
- struct mgcp_trunk_config *trunk = vty->index;
- trunk->force_constant_ssrc = 1;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_trunk_no_patch_rtp_ssrc,
- cfg_trunk_no_patch_rtp_ssrc_cmd,
- "no rtp-patch ssrc",
- NO_STR RTP_PATCH_STR
- "Force a fixed SSRC\n"
- )
-{
- struct mgcp_trunk_config *trunk = vty->index;
- trunk->force_constant_ssrc = 0;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_trunk_patch_rtp_ts,
- cfg_trunk_patch_rtp_ts_cmd,
- "rtp-patch timestamp",
- RTP_PATCH_STR
- "Adjust RTP timestamp\n"
- )
-{
- struct mgcp_trunk_config *trunk = vty->index;
- trunk->force_aligned_timing = 1;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_trunk_no_patch_rtp_ts,
- cfg_trunk_no_patch_rtp_ts_cmd,
- "no rtp-patch timestamp",
- NO_STR RTP_PATCH_STR
- "Adjust RTP timestamp\n"
- )
-{
- struct mgcp_trunk_config *trunk = vty->index;
- trunk->force_aligned_timing = 0;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_trunk_no_patch_rtp,
- cfg_trunk_no_patch_rtp_cmd,
- "no rtp-patch",
- NO_STR RTP_PATCH_STR)
-{
- struct mgcp_trunk_config *trunk = vty->index;
- trunk->force_constant_ssrc = 0;
- trunk->force_aligned_timing = 0;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_trunk_rtp_keepalive,
- cfg_trunk_rtp_keepalive_cmd,
- "rtp keep-alive <1-120>",
- RTP_STR RTP_KEEPALIVE_STR
- "Keep-alive interval in secs\n"
- )
-{
- struct mgcp_trunk_config *trunk = vty->index;
- mgcp_trunk_set_keepalive(trunk, atoi(argv[0]));
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_trunk_rtp_keepalive_once,
- cfg_trunk_rtp_keepalive_once_cmd,
- "rtp keep-alive once",
- RTP_STR RTP_KEEPALIVE_STR
- "Send dummy packet only once after CRCX/MDCX\n"
- )
-{
- struct mgcp_trunk_config *trunk = vty->index;
- mgcp_trunk_set_keepalive(trunk, MGCP_KEEPALIVE_ONCE);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_trunk_no_rtp_keepalive,
- cfg_trunk_no_rtp_keepalive_cmd,
- "no rtp keep-alive",
- NO_STR RTP_STR RTP_KEEPALIVE_STR
- )
-{
- struct mgcp_trunk_config *trunk = vty->index;
- mgcp_trunk_set_keepalive(trunk, 0);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_trunk_allow_transcoding,
- cfg_trunk_allow_transcoding_cmd,
- "allow-transcoding",
- "Allow transcoding\n")
-{
- struct mgcp_trunk_config *trunk = vty->index;
- trunk->no_audio_transcoding = 0;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_trunk_no_allow_transcoding,
- cfg_trunk_no_allow_transcoding_cmd,
- "no allow-transcoding",
- NO_STR "Allow transcoding\n")
-{
- struct mgcp_trunk_config *trunk = vty->index;
- trunk->no_audio_transcoding = 1;
- return CMD_SUCCESS;
-}
-
-DEFUN(loop_endp,
- loop_endp_cmd,
- "loop-endpoint <0-64> NAME (0|1)",
- "Loop a given endpoint\n" "Trunk number\n"
- "The name in hex of the endpoint\n" "Disable the loop\n" "Enable the loop\n")
-{
- struct mgcp_trunk_config *trunk;
- struct mgcp_endpoint *endp;
-
- trunk = find_trunk(g_cfg, atoi(argv[0]));
- if (!trunk) {
- vty_out(vty, "%%Trunk %d not found in the config.%s",
- atoi(argv[0]), VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- if (!trunk->endpoints) {
- vty_out(vty, "%%Trunk %d has no endpoints allocated.%s",
- trunk->trunk_nr, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- int endp_no = strtoul(argv[1], NULL, 16);
- if (endp_no < 1 || endp_no >= trunk->number_endpoints) {
- vty_out(vty, "Loopback number %s/%d is invalid.%s",
- argv[1], endp_no, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
-
- endp = &trunk->endpoints[endp_no];
- int loop = atoi(argv[2]);
-
- if (loop)
- endp->conn_mode = MGCP_CONN_LOOPBACK;
- else
- endp->conn_mode = endp->orig_mode;
-
- /* Handle it like a MDCX, switch on SSRC patching if enabled */
- mgcp_rtp_end_config(endp, 1, &endp->bts_end);
- mgcp_rtp_end_config(endp, 1, &endp->net_end);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(tap_call,
- tap_call_cmd,
- "tap-call <0-64> ENDPOINT (bts-in|bts-out|net-in|net-out) A.B.C.D <0-65534>",
- "Forward data on endpoint to a different system\n" "Trunk number\n"
- "The endpoint in hex\n"
- "Forward the data coming from the bts\n"
- "Forward the data coming from the bts leaving to the network\n"
- "Forward the data coming from the net\n"
- "Forward the data coming from the net leaving to the bts\n"
- "destination IP of the data\n" "destination port\n")
-{
- struct mgcp_rtp_tap *tap;
- struct mgcp_trunk_config *trunk;
- struct mgcp_endpoint *endp;
- int port = 0;
-
- trunk = find_trunk(g_cfg, atoi(argv[0]));
- if (!trunk) {
- vty_out(vty, "%%Trunk %d not found in the config.%s",
- atoi(argv[0]), VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- if (!trunk->endpoints) {
- vty_out(vty, "%%Trunk %d has no endpoints allocated.%s",
- trunk->trunk_nr, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- int endp_no = strtoul(argv[1], NULL, 16);
- if (endp_no < 1 || endp_no >= trunk->number_endpoints) {
- vty_out(vty, "Endpoint number %s/%d is invalid.%s",
- argv[1], endp_no, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- endp = &trunk->endpoints[endp_no];
-
- if (strcmp(argv[2], "bts-in") == 0) {
- port = MGCP_TAP_BTS_IN;
- } else if (strcmp(argv[2], "bts-out") == 0) {
- port = MGCP_TAP_BTS_OUT;
- } else if (strcmp(argv[2], "net-in") == 0) {
- port = MGCP_TAP_NET_IN;
- } else if (strcmp(argv[2], "net-out") == 0) {
- port = MGCP_TAP_NET_OUT;
- } else {
- vty_out(vty, "Unknown mode... tricked vty?%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- tap = &endp->taps[port];
- memset(&tap->forward, 0, sizeof(tap->forward));
- inet_aton(argv[3], &tap->forward.sin_addr);
- tap->forward.sin_port = htons(atoi(argv[4]));
- tap->enabled = 1;
- return CMD_SUCCESS;
-}
-
-DEFUN(free_endp, free_endp_cmd,
- "free-endpoint <0-64> NUMBER",
- "Free the given endpoint\n" "Trunk number\n"
- "Endpoint number in hex.\n")
-{
- struct mgcp_trunk_config *trunk;
- struct mgcp_endpoint *endp;
-
- trunk = find_trunk(g_cfg, atoi(argv[0]));
- if (!trunk) {
- vty_out(vty, "%%Trunk %d not found in the config.%s",
- atoi(argv[0]), VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- if (!trunk->endpoints) {
- vty_out(vty, "%%Trunk %d has no endpoints allocated.%s",
- trunk->trunk_nr, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- int endp_no = strtoul(argv[1], NULL, 16);
- if (endp_no < 1 || endp_no >= trunk->number_endpoints) {
- vty_out(vty, "Endpoint number %s/%d is invalid.%s",
- argv[1], endp_no, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- endp = &trunk->endpoints[endp_no];
- mgcp_release_endp(endp);
- return CMD_SUCCESS;
-}
-
-DEFUN(reset_endp, reset_endp_cmd,
- "reset-endpoint <0-64> NUMBER",
- "Reset the given endpoint\n" "Trunk number\n"
- "Endpoint number in hex.\n")
-{
- struct mgcp_trunk_config *trunk;
- struct mgcp_endpoint *endp;
- int endp_no, rc;
-
- trunk = find_trunk(g_cfg, atoi(argv[0]));
- if (!trunk) {
- vty_out(vty, "%%Trunk %d not found in the config.%s",
- atoi(argv[0]), VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- if (!trunk->endpoints) {
- vty_out(vty, "%%Trunk %d has no endpoints allocated.%s",
- trunk->trunk_nr, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- endp_no = strtoul(argv[1], NULL, 16);
- if (endp_no < 1 || endp_no >= trunk->number_endpoints) {
- vty_out(vty, "Endpoint number %s/%d is invalid.%s",
- argv[1], endp_no, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- endp = &trunk->endpoints[endp_no];
- rc = mgcp_send_reset_ep(endp, ENDPOINT_NUMBER(endp));
- if (rc < 0) {
- vty_out(vty, "Error %d sending reset.%s", rc, VTY_NEWLINE);
- return CMD_WARNING;
- }
- return CMD_SUCCESS;
-}
-
-DEFUN(reset_all_endp, reset_all_endp_cmd,
- "reset-all-endpoints",
- "Reset all endpoints\n")
-{
- int rc;
-
- rc = mgcp_send_reset_all(g_cfg);
- if (rc < 0) {
- vty_out(vty, "Error %d during endpoint reset.%s",
- rc, VTY_NEWLINE);
- return CMD_WARNING;
- }
- return CMD_SUCCESS;
-}
-
-#define OSMUX_STR "RTP multiplexing\n"
-DEFUN(cfg_mgcp_osmux,
- cfg_mgcp_osmux_cmd,
- "osmux (on|off|only)",
- OSMUX_STR "Enable OSMUX\n" "Disable OSMUX\n" "Only use OSMUX\n")
-{
- if (strcmp(argv[0], "off") == 0) {
- g_cfg->osmux = OSMUX_USAGE_OFF;
- return CMD_SUCCESS;
- }
-
- if (strcmp(argv[0], "on") == 0)
- g_cfg->osmux = OSMUX_USAGE_ON;
- else if (strcmp(argv[0], "only") == 0)
- g_cfg->osmux = OSMUX_USAGE_ONLY;
-
- if (g_cfg->trunk.audio_loop) {
- vty_out(vty, "Cannot use `loop' with `osmux'.%s",
- VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_osmux_ip,
- cfg_mgcp_osmux_ip_cmd,
- "osmux bind-ip A.B.C.D",
- OSMUX_STR IP_STR "IPv4 Address to bind to\n")
-{
- osmo_talloc_replace_string(g_cfg, &g_cfg->osmux_addr, argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_osmux_batch_factor,
- cfg_mgcp_osmux_batch_factor_cmd,
- "osmux batch-factor <1-8>",
- OSMUX_STR "Batching factor\n" "Number of messages in the batch\n")
-{
- g_cfg->osmux_batch = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_osmux_batch_size,
- cfg_mgcp_osmux_batch_size_cmd,
- "osmux batch-size <1-65535>",
- OSMUX_STR "batch size\n" "Batch size in bytes\n")
-{
- g_cfg->osmux_batch_size = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_osmux_port,
- cfg_mgcp_osmux_port_cmd,
- "osmux port <1-65535>",
- OSMUX_STR "port\n" "UDP port\n")
-{
- g_cfg->osmux_port = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_mgcp_osmux_dummy,
- cfg_mgcp_osmux_dummy_cmd,
- "osmux dummy (on|off)",
- OSMUX_STR "Dummy padding\n" "Enable dummy padding\n" "Disable dummy padding\n")
-{
- if (strcmp(argv[0], "on") == 0)
- g_cfg->osmux_dummy = 1;
- else if (strcmp(argv[0], "off") == 0)
- g_cfg->osmux_dummy = 0;
-
- return CMD_SUCCESS;
-}
-
-int mgcp_vty_init(void)
-{
- install_element_ve(&show_mgcp_cmd);
- install_element(ENABLE_NODE, &loop_endp_cmd);
- install_element(ENABLE_NODE, &tap_call_cmd);
- install_element(ENABLE_NODE, &free_endp_cmd);
- install_element(ENABLE_NODE, &reset_endp_cmd);
- install_element(ENABLE_NODE, &reset_all_endp_cmd);
-
- install_element(CONFIG_NODE, &cfg_mgcp_cmd);
- install_node(&mgcp_node, config_write_mgcp);
-
- vty_install_default(MGCP_NODE);
- install_element(MGCP_NODE, &cfg_mgcp_local_ip_cmd);
- install_element(MGCP_NODE, &cfg_mgcp_bts_ip_cmd);
- install_element(MGCP_NODE, &cfg_mgcp_bind_ip_cmd);
- install_element(MGCP_NODE, &cfg_mgcp_bind_port_cmd);
- install_element(MGCP_NODE, &cfg_mgcp_bind_early_cmd);
- install_element(MGCP_NODE, &cfg_mgcp_rtp_base_port_cmd);
- install_element(MGCP_NODE, &cfg_mgcp_rtp_bts_base_port_cmd);
- install_element(MGCP_NODE, &cfg_mgcp_rtp_net_base_port_cmd);
- install_element(MGCP_NODE, &cfg_mgcp_rtp_bts_range_cmd);
- install_element(MGCP_NODE, &cfg_mgcp_rtp_bts_bind_ip_cmd);
- install_element(MGCP_NODE, &cfg_mgcp_rtp_no_bts_bind_ip_cmd);
- install_element(MGCP_NODE, &cfg_mgcp_rtp_net_range_cmd);
- install_element(MGCP_NODE, &cfg_mgcp_rtp_net_bind_ip_cmd);
- install_element(MGCP_NODE, &cfg_mgcp_rtp_no_net_bind_ip_cmd);
- install_element(MGCP_NODE, &cfg_mgcp_rtp_transcoder_range_cmd);
- install_element(MGCP_NODE, &cfg_mgcp_rtp_transcoder_base_cmd);
- install_element(MGCP_NODE, &cfg_mgcp_rtp_ip_dscp_cmd);
- install_element(MGCP_NODE, &cfg_mgcp_rtp_ip_tos_cmd);
- install_element(MGCP_NODE, &cfg_mgcp_rtp_force_ptime_cmd);
- install_element(MGCP_NODE, &cfg_mgcp_no_rtp_force_ptime_cmd);
- install_element(MGCP_NODE, &cfg_mgcp_rtp_keepalive_cmd);
- install_element(MGCP_NODE, &cfg_mgcp_rtp_keepalive_once_cmd);
- install_element(MGCP_NODE, &cfg_mgcp_no_rtp_keepalive_cmd);
- install_element(MGCP_NODE, &cfg_mgcp_agent_addr_cmd);
- install_element(MGCP_NODE, &cfg_mgcp_agent_addr_cmd_old);
- install_element(MGCP_NODE, &cfg_mgcp_transcoder_cmd);
- install_element(MGCP_NODE, &cfg_mgcp_no_transcoder_cmd);
- install_element(MGCP_NODE, &cfg_mgcp_transcoder_remote_base_cmd);
- install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_number_cmd);
- install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_name_cmd);
- install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_number_cmd_old);
- install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_name_cmd_old);
- install_element(MGCP_NODE, &cfg_mgcp_loop_cmd);
- install_element(MGCP_NODE, &cfg_mgcp_number_endp_cmd);
- install_element(MGCP_NODE, &cfg_mgcp_omit_rtcp_cmd);
- install_element(MGCP_NODE, &cfg_mgcp_no_omit_rtcp_cmd);
- install_element(MGCP_NODE, &cfg_mgcp_patch_rtp_ssrc_cmd);
- install_element(MGCP_NODE, &cfg_mgcp_no_patch_rtp_ssrc_cmd);
- install_element(MGCP_NODE, &cfg_mgcp_patch_rtp_ts_cmd);
- install_element(MGCP_NODE, &cfg_mgcp_no_patch_rtp_ts_cmd);
- install_element(MGCP_NODE, &cfg_mgcp_no_patch_rtp_cmd);
- install_element(MGCP_NODE, &cfg_mgcp_sdp_fmtp_extra_cmd);
- install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_send_ptime_cmd);
- install_element(MGCP_NODE, &cfg_mgcp_no_sdp_payload_send_ptime_cmd);
- install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_send_name_cmd);
- install_element(MGCP_NODE, &cfg_mgcp_no_sdp_payload_send_name_cmd);
- install_element(MGCP_NODE, &cfg_mgcp_osmux_cmd);
- install_element(MGCP_NODE, &cfg_mgcp_osmux_ip_cmd);
- install_element(MGCP_NODE, &cfg_mgcp_osmux_batch_factor_cmd);
- install_element(MGCP_NODE, &cfg_mgcp_osmux_batch_size_cmd);
- install_element(MGCP_NODE, &cfg_mgcp_osmux_port_cmd);
- install_element(MGCP_NODE, &cfg_mgcp_osmux_dummy_cmd);
- install_element(MGCP_NODE, &cfg_mgcp_allow_transcoding_cmd);
- install_element(MGCP_NODE, &cfg_mgcp_no_allow_transcoding_cmd);
-
-
- install_element(MGCP_NODE, &cfg_mgcp_trunk_cmd);
- install_node(&trunk_node, config_write_trunk);
- vty_install_default(TRUNK_NODE);
- install_element(TRUNK_NODE, &cfg_trunk_rtp_keepalive_cmd);
- install_element(TRUNK_NODE, &cfg_trunk_rtp_keepalive_once_cmd);
- install_element(TRUNK_NODE, &cfg_trunk_no_rtp_keepalive_cmd);
- install_element(TRUNK_NODE, &cfg_trunk_payload_number_cmd);
- install_element(TRUNK_NODE, &cfg_trunk_payload_name_cmd);
- install_element(TRUNK_NODE, &cfg_trunk_payload_number_cmd_old);
- install_element(TRUNK_NODE, &cfg_trunk_payload_name_cmd_old);
- install_element(TRUNK_NODE, &cfg_trunk_loop_cmd);
- install_element(TRUNK_NODE, &cfg_trunk_omit_rtcp_cmd);
- install_element(TRUNK_NODE, &cfg_trunk_no_omit_rtcp_cmd);
- install_element(TRUNK_NODE, &cfg_trunk_patch_rtp_ssrc_cmd);
- install_element(TRUNK_NODE, &cfg_trunk_no_patch_rtp_ssrc_cmd);
- install_element(TRUNK_NODE, &cfg_trunk_patch_rtp_ts_cmd);
- install_element(TRUNK_NODE, &cfg_trunk_no_patch_rtp_ts_cmd);
- install_element(TRUNK_NODE, &cfg_trunk_no_patch_rtp_cmd);
- install_element(TRUNK_NODE, &cfg_trunk_sdp_fmtp_extra_cmd);
- install_element(TRUNK_NODE, &cfg_trunk_sdp_payload_send_ptime_cmd);
- install_element(TRUNK_NODE, &cfg_trunk_no_sdp_payload_send_ptime_cmd);
- install_element(TRUNK_NODE, &cfg_trunk_sdp_payload_send_name_cmd);
- install_element(TRUNK_NODE, &cfg_trunk_no_sdp_payload_send_name_cmd);
- install_element(TRUNK_NODE, &cfg_trunk_allow_transcoding_cmd);
- install_element(TRUNK_NODE, &cfg_trunk_no_allow_transcoding_cmd);
-
- return 0;
-}
-
-static int allocate_trunk(struct mgcp_trunk_config *trunk)
-{
- int i;
- struct mgcp_config *cfg = trunk->cfg;
-
- if (mgcp_endpoints_allocate(trunk) != 0) {
- LOGP(DMGCP, LOGL_ERROR,
- "Failed to allocate %d endpoints on trunk %d.\n",
- trunk->number_endpoints, trunk->trunk_nr);
- return -1;
- }
-
- /* early bind */
- for (i = 1; i < trunk->number_endpoints; ++i) {
- struct mgcp_endpoint *endp = &trunk->endpoints[i];
-
- if (cfg->bts_ports.mode == PORT_ALLOC_STATIC) {
- cfg->last_bts_port += 2;
- if (mgcp_bind_bts_rtp_port(endp, cfg->last_bts_port) != 0) {
- LOGP(DMGCP, LOGL_FATAL,
- "Failed to bind: %d\n", cfg->last_bts_port);
- return -1;
- }
- endp->bts_end.local_alloc = PORT_ALLOC_STATIC;
- }
-
- if (cfg->net_ports.mode == PORT_ALLOC_STATIC) {
- cfg->last_net_port += 2;
- if (mgcp_bind_net_rtp_port(endp, cfg->last_net_port) != 0) {
- LOGP(DMGCP, LOGL_FATAL,
- "Failed to bind: %d\n", cfg->last_net_port);
- return -1;
- }
- endp->net_end.local_alloc = PORT_ALLOC_STATIC;
- }
-
- if (trunk->trunk_type == MGCP_TRUNK_VIRTUAL &&
- cfg->transcoder_ip && cfg->transcoder_ports.mode == PORT_ALLOC_STATIC) {
- int rtp_port;
-
- /* network side */
- rtp_port = rtp_calculate_port(ENDPOINT_NUMBER(endp),
- cfg->transcoder_ports.base_port);
- if (mgcp_bind_trans_net_rtp_port(endp, rtp_port) != 0) {
- LOGP(DMGCP, LOGL_FATAL, "Failed to bind: %d\n", rtp_port);
- return -1;
- }
- endp->trans_net.local_alloc = PORT_ALLOC_STATIC;
-
- /* bts side */
- rtp_port = rtp_calculate_port(endp_back_channel(ENDPOINT_NUMBER(endp)),
- cfg->transcoder_ports.base_port);
- if (mgcp_bind_trans_bts_rtp_port(endp, rtp_port) != 0) {
- LOGP(DMGCP, LOGL_FATAL, "Failed to bind: %d\n", rtp_port);
- return -1;
- }
- endp->trans_bts.local_alloc = PORT_ALLOC_STATIC;
- }
- }
-
- return 0;
-}
-
-int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg,
- enum mgcp_role role)
-{
- int rc;
- struct mgcp_trunk_config *trunk;
-
- cfg->osmux_port = OSMUX_PORT;
- cfg->osmux_batch = 4;
- cfg->osmux_batch_size = OSMUX_BATCH_DEFAULT_MAX;
-
- g_cfg = cfg;
- rc = vty_read_config_file(config_file, NULL);
- if (rc < 0) {
- fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
- return rc;
- }
-
-
- if (!g_cfg->bts_ip)
- fprintf(stderr, "No BTS ip address specified. This will allow everyone to connect.\n");
-
- if (!g_cfg->source_addr) {
- fprintf(stderr, "You need to specify a bind address.\n");
- return -1;
- }
-
- /* initialize the last ports */
- g_cfg->last_bts_port = rtp_calculate_port(0, g_cfg->bts_ports.base_port);
- g_cfg->last_net_port = rtp_calculate_port(0, g_cfg->net_ports.base_port);
-
- if (allocate_trunk(&g_cfg->trunk) != 0) {
- LOGP(DMGCP, LOGL_ERROR, "Failed to initialize the virtual trunk.\n");
- return -1;
- }
-
- llist_for_each_entry(trunk, &g_cfg->trunks, entry) {
- if (allocate_trunk(trunk) != 0) {
- LOGP(DMGCP, LOGL_ERROR,
- "Failed to initialize E1 trunk %d.\n", trunk->trunk_nr);
- return -1;
- }
- }
- cfg->role = role;
-
- return 0;
-}
-
diff --git a/openbsc/src/libmsc/Makefile.am b/openbsc/src/libmsc/Makefile.am
deleted file mode 100644
index 9d966dbc1..000000000
--- a/openbsc/src/libmsc/Makefile.am
+++ /dev/null
@@ -1,58 +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) \
- $(NULL)
-
-noinst_HEADERS = \
- meas_feed.h \
- $(NULL)
-
-noinst_LIBRARIES = \
- libmsc.a \
- $(NULL)
-
-libmsc_a_SOURCES = \
- auth.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 \
- rrlp.c \
- silent_call.c \
- sms_queue.c \
- token_auth.c \
- ussd.c \
- vty_interface_layer3.c \
- transaction.c \
- osmo_msc.c \
- ctrl_commands.c \
- meas_feed.c \
- $(NULL)
-
-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/openbsc/src/libmsc/auth.c b/openbsc/src/libmsc/auth.c
deleted file mode 100644
index 19def1ec1..000000000
--- a/openbsc/src/libmsc/auth.c
+++ /dev/null
@@ -1,157 +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 }
-};
-
-static int
-_use_xor(struct gsm_auth_info *ainfo, struct gsm_auth_tuple *atuple)
-{
- int i, l = ainfo->a3a8_ki_len;
-
- if ((l > A38_XOR_MAX_KEY_LEN) || (l < A38_XOR_MIN_KEY_LEN)) {
- LOGP(DMM, LOGL_ERROR, "Invalid XOR key (len=%d) %s\n",
- ainfo->a3a8_ki_len,
- osmo_hexdump(ainfo->a3a8_ki, ainfo->a3a8_ki_len));
- return -1;
- }
-
- for (i=0; i<4; i++)
- atuple->vec.sres[i] = atuple->vec.rand[i] ^ ainfo->a3a8_ki[i];
- for (i=4; i<12; i++)
- atuple->vec.kc[i-4] = atuple->vec.rand[i] ^ ainfo->a3a8_ki[i];
-
- return 0;
-}
-
-static int
-_use_comp128_v1(struct gsm_auth_info *ainfo, struct gsm_auth_tuple *atuple)
-{
- if (ainfo->a3a8_ki_len != A38_COMP128_KEY_LEN) {
- LOGP(DMM, LOGL_ERROR, "Invalid COMP128v1 key (len=%d) %s\n",
- ainfo->a3a8_ki_len,
- osmo_hexdump(ainfo->a3a8_ki, ainfo->a3a8_ki_len));
- return -1;
- }
-
- comp128(ainfo->a3a8_ki, atuple->vec.rand, atuple->vec.sres, atuple->vec.kc);
-
- return 0;
-}
-
-/* Return values
- * -1 -> Internal error
- * 0 -> Not available
- * 1 -> Tuple returned, need to do auth, then enable cipher
- * 2 -> Tuple returned, need to enable cipher
- */
-int auth_get_tuple_for_subscr(struct gsm_auth_tuple *atuple,
- struct gsm_subscriber *subscr, int key_seq)
-{
- struct gsm_auth_info ainfo;
- int rc;
-
- /* Get subscriber info (if any) */
- rc = db_get_authinfo_for_subscr(&ainfo, subscr);
- if (rc < 0) {
- LOGP(DMM, LOGL_NOTICE,
- "No retrievable Ki for subscriber %s, skipping auth\n",
- subscr_name(subscr));
- return rc == -ENOENT ? AUTH_NOT_AVAIL : AUTH_ERROR;
- }
-
- /* If possible, re-use the last tuple and skip auth */
- rc = db_get_lastauthtuple_for_subscr(atuple, subscr);
- if ((rc == 0) &&
- (key_seq != GSM_KEY_SEQ_INVAL) &&
- (key_seq == atuple->key_seq) &&
- (atuple->use_count < 3))
- {
- atuple->use_count++;
- db_sync_lastauthtuple_for_subscr(atuple, subscr);
- DEBUGP(DMM, "Auth tuple use < 3, just doing ciphering\n");
- return AUTH_DO_CIPH;
- }
-
- /* Generate a new one */
- if (rc != 0) {
- /* If db_get_lastauthtuple_for_subscr() returned nothing, make
- * sure the atuple memory is initialized to zero and thus start
- * off with key_seq = 0. */
- memset(atuple, 0, sizeof(*atuple));
- } else {
- /* If db_get_lastauthtuple_for_subscr() returned a previous
- * tuple, use the next key_seq. */
- atuple->key_seq = (atuple->key_seq + 1) % 7;
- }
- atuple->use_count = 1;
-
- if (RAND_bytes(atuple->vec.rand, sizeof(atuple->vec.rand)) != 1) {
- LOGP(DMM, LOGL_NOTICE, "RAND_bytes failed, can't generate new auth tuple\n");
- return AUTH_ERROR;
- }
-
- switch (ainfo.auth_algo) {
- case AUTH_ALGO_NONE:
- DEBUGP(DMM, "No authentication for subscriber\n");
- return AUTH_NOT_AVAIL;
-
- case AUTH_ALGO_XOR:
- if (_use_xor(&ainfo, atuple))
- return AUTH_NOT_AVAIL;
- break;
-
- case AUTH_ALGO_COMP128v1:
- if (_use_comp128_v1(&ainfo, atuple))
- return AUTH_NOT_AVAIL;
- break;
-
- default:
- DEBUGP(DMM, "Unsupported auth type algo_id=%d\n",
- ainfo.auth_algo);
- return AUTH_NOT_AVAIL;
- }
-
- db_sync_lastauthtuple_for_subscr(atuple, subscr);
-
- DEBUGP(DMM, "Need to do authentication and ciphering\n");
- return AUTH_DO_AUTH_THEN_CIPH;
-}
-
diff --git a/openbsc/src/libmsc/ctrl_commands.c b/openbsc/src/libmsc/ctrl_commands.c
deleted file mode 100644
index c99dde44c..000000000
--- a/openbsc/src/libmsc/ctrl_commands.c
+++ /dev/null
@@ -1,212 +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 <stdbool.h>
-
-static bool alg_supported(const char *alg)
-{
- /*
- * TODO: share this with the vty_interface and extend to all
- * algorithms supported by libosmocore now. Make it table based
- * as well.
- */
- if (strcasecmp(alg, "none") == 0)
- return true;
- if (strcasecmp(alg, "xor") == 0)
- return true;
- if (strcasecmp(alg, "comp128v1") == 0)
- return true;
- return false;
-}
-
-static int verify_subscriber_modify(struct ctrl_cmd *cmd, const char *value, void *d)
-{
- char *tmp, *imsi, *msisdn, *alg, *ki, *saveptr = NULL;
- int rc = 0;
-
- tmp = talloc_strdup(cmd, value);
- if (!tmp)
- return 1;
-
- imsi = strtok_r(tmp, ",", &saveptr);
- msisdn = strtok_r(NULL, ",", &saveptr);
- alg = strtok_r(NULL, ",", &saveptr);
- ki = strtok_r(NULL, ",", &saveptr);
-
- if (!imsi || !msisdn)
- rc = 1;
- else if (strlen(imsi) > GSM23003_IMSI_MAX_DIGITS)
- rc = 1;
- else if (strlen(msisdn) >= GSM_EXTENSION_LENGTH)
- rc = 1;
- else if (alg) {
- if (!alg_supported(alg))
- rc = 1;
- else if (strcasecmp(alg, "none") != 0 && !ki)
- rc = 1;
- }
-
- talloc_free(tmp);
- return rc;
-}
-
-static int set_subscriber_modify(struct ctrl_cmd *cmd, void *data)
-{
- struct gsm_network *net = cmd->node;
- char *tmp, *imsi, *msisdn, *alg, *ki, *saveptr = NULL;
- struct gsm_subscriber* subscr;
- int rc;
-
- tmp = talloc_strdup(cmd, cmd->value);
- if (!tmp)
- return 1;
-
- imsi = strtok_r(tmp, ",", &saveptr);
- msisdn = strtok_r(NULL, ",", &saveptr);
- alg = strtok_r(NULL, ",", &saveptr);
- ki = strtok_r(NULL, ",", &saveptr);
-
- subscr = subscr_get_by_imsi(net->subscr_group, imsi);
- if (!subscr)
- subscr = subscr_create_subscriber(net->subscr_group, imsi);
- if (!subscr)
- goto fail;
-
- subscr->authorized = 1;
- osmo_strlcpy(subscr->extension, msisdn, sizeof(subscr->extension));
-
- /* put it back to the db */
- rc = db_sync_subscriber(subscr);
- db_subscriber_update(subscr);
-
- /* handle optional ciphering */
- if (alg) {
- if (strcasecmp(alg, "none") == 0)
- db_sync_authinfo_for_subscr(NULL, subscr);
- else {
- struct gsm_auth_info ainfo = { 0, };
- /* the verify should make sure that this is okay */
- OSMO_ASSERT(alg);
- OSMO_ASSERT(ki);
-
- if (strcasecmp(alg, "xor") == 0)
- ainfo.auth_algo = AUTH_ALGO_XOR;
- else if (strcasecmp(alg, "comp128v1") == 0)
- ainfo.auth_algo = AUTH_ALGO_COMP128v1;
-
- rc = osmo_hexparse(ki, ainfo.a3a8_ki, sizeof(ainfo.a3a8_ki));
- if (rc < 0) {
- subscr_put(subscr);
- talloc_free(tmp);
- cmd->reply = "Failed to parse KI";
- return CTRL_CMD_ERROR;
- }
-
- ainfo.a3a8_ki_len = rc;
- db_sync_authinfo_for_subscr(&ainfo, subscr);
- rc = 0;
- }
- db_sync_lastauthtuple_for_subscr(NULL, subscr);
- }
- subscr_put(subscr);
-
- talloc_free(tmp);
-
- if (rc != 0) {
- cmd->reply = "Failed to store the record in the DB";
- return CTRL_CMD_ERROR;
- }
-
- cmd->reply = "OK";
- return CTRL_CMD_REPLY;
-
-fail:
- talloc_free(tmp);
- cmd->reply = "Failed to create subscriber";
- return CTRL_CMD_ERROR;
-}
-
-CTRL_CMD_DEFINE_WO(subscriber_modify, "subscriber-modify-v1");
-
-static int set_subscriber_delete(struct ctrl_cmd *cmd, void *data)
-{
- int was_used = 0;
- int rc;
- struct gsm_subscriber *subscr;
- struct gsm_network *net = cmd->node;
-
- subscr = subscr_get_by_imsi(net->subscr_group, cmd->value);
- if (!subscr) {
- cmd->reply = "Failed to find subscriber";
- return CTRL_CMD_ERROR;
- }
-
- if (subscr->use_count != 1) {
- LOGP(DCTRL, LOGL_NOTICE, "Going to remove active subscriber.\n");
- was_used = 1;
- }
-
- rc = db_subscriber_delete(subscr);
- subscr_put(subscr);
-
- if (rc != 0) {
- cmd->reply = "Failed to remove subscriber";
- return CTRL_CMD_ERROR;
- }
-
- cmd->reply = was_used ? "Removed active subscriber" : "Removed";
- return CTRL_CMD_REPLY;
-}
-CTRL_CMD_DEFINE_WO_NOVRF(subscriber_delete, "subscriber-delete-v1");
-
-static void list_cb(struct gsm_subscriber *subscr, void *d)
-{
- char **data = (char **) d;
- *data = talloc_asprintf_append(*data, "%s,%s\n",
- subscr->imsi, subscr->extension);
-}
-
-static int get_subscriber_list(struct ctrl_cmd *cmd, void *d)
-{
- cmd->reply = talloc_strdup(cmd, "");
-
- db_subscriber_list_active(list_cb, &cmd->reply);
- printf("%s\n", cmd->reply);
- return CTRL_CMD_REPLY;
-}
-CTRL_CMD_DEFINE_RO(subscriber_list, "subscriber-list-active-v1");
-
-int msc_ctrl_cmds_install(void)
-{
- int rc = 0;
-
- rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_subscriber_modify);
- rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_subscriber_delete);
- rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_subscriber_list);
- return rc;
-}
diff --git a/openbsc/src/libmsc/db.c b/openbsc/src/libmsc/db.c
deleted file mode 100644
index 5fe2a3c6b..000000000
--- a/openbsc/src/libmsc/db.c
+++ /dev/null
@@ -1,1752 +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 <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>
-
-/* Semi-Private-Interface (SPI) for the subscriber code */
-void subscr_direct_free(struct gsm_subscriber *subscr);
-
-static char *db_basename = NULL;
-static char *db_dirname = NULL;
-static dbi_conn conn;
-
-#define SCHEMA_REVISION "4"
-
-enum {
- SCHEMA_META,
- INSERT_META,
- SCHEMA_SUBSCRIBER,
- SCHEMA_AUTH,
- SCHEMA_EQUIPMENT,
- SCHEMA_EQUIPMENT_WATCH,
- SCHEMA_SMS,
- SCHEMA_VLR,
- SCHEMA_APDU,
- SCHEMA_COUNTERS,
- SCHEMA_RATE,
- SCHEMA_AUTHKEY,
- SCHEMA_AUTHLAST,
-};
-
-static const char *create_stmts[] = {
- [SCHEMA_META] = "CREATE TABLE IF NOT EXISTS Meta ("
- "id INTEGER PRIMARY KEY AUTOINCREMENT, "
- "key TEXT UNIQUE NOT NULL, "
- "value TEXT NOT NULL"
- ")",
- [INSERT_META] = "INSERT OR IGNORE INTO Meta "
- "(key, value) "
- "VALUES "
- "('revision', " SCHEMA_REVISION ")",
- [SCHEMA_SUBSCRIBER] = "CREATE TABLE IF NOT EXISTS Subscriber ("
- "id INTEGER PRIMARY KEY AUTOINCREMENT, "
- "created TIMESTAMP NOT NULL, "
- "updated TIMESTAMP NOT NULL, "
- "imsi NUMERIC UNIQUE NOT NULL, "
- "name TEXT, "
- "extension TEXT UNIQUE, "
- "authorized INTEGER NOT NULL DEFAULT 0, "
- "tmsi TEXT UNIQUE, "
- "lac INTEGER NOT NULL DEFAULT 0, "
- "expire_lu TIMESTAMP DEFAULT NULL"
- ")",
- [SCHEMA_AUTH] = "CREATE TABLE IF NOT EXISTS AuthToken ("
- "id INTEGER PRIMARY KEY AUTOINCREMENT, "
- "subscriber_id INTEGER UNIQUE NOT NULL, "
- "created TIMESTAMP NOT NULL, "
- "token TEXT UNIQUE NOT NULL"
- ")",
- [SCHEMA_EQUIPMENT] = "CREATE TABLE IF NOT EXISTS Equipment ("
- "id INTEGER PRIMARY KEY AUTOINCREMENT, "
- "created TIMESTAMP NOT NULL, "
- "updated TIMESTAMP NOT NULL, "
- "name TEXT, "
- "classmark1 NUMERIC, "
- "classmark2 BLOB, "
- "classmark3 BLOB, "
- "imei NUMERIC UNIQUE NOT NULL"
- ")",
- [SCHEMA_EQUIPMENT_WATCH] = "CREATE TABLE IF NOT EXISTS EquipmentWatch ("
- "id INTEGER PRIMARY KEY AUTOINCREMENT, "
- "created TIMESTAMP NOT NULL, "
- "updated TIMESTAMP NOT NULL, "
- "subscriber_id NUMERIC NOT NULL, "
- "equipment_id NUMERIC NOT NULL, "
- "UNIQUE (subscriber_id, equipment_id) "
- ")",
- [SCHEMA_SMS] = "CREATE TABLE IF NOT EXISTS SMS ("
- /* metadata, not part of sms */
- "id INTEGER PRIMARY KEY AUTOINCREMENT, "
- "created TIMESTAMP NOT NULL, "
- "sent TIMESTAMP, "
- "deliver_attempts INTEGER NOT NULL DEFAULT 0, "
- /* data directly copied/derived from SMS */
- "valid_until TIMESTAMP, "
- "reply_path_req INTEGER NOT NULL, "
- "status_rep_req INTEGER NOT NULL, "
- "protocol_id INTEGER NOT NULL, "
- "data_coding_scheme INTEGER NOT NULL, "
- "ud_hdr_ind INTEGER NOT NULL, "
- "src_addr TEXT NOT NULL, "
- "src_ton INTEGER NOT NULL, "
- "src_npi INTEGER NOT NULL, "
- "dest_addr TEXT NOT NULL, "
- "dest_ton INTEGER NOT NULL, "
- "dest_npi INTEGER NOT NULL, "
- "user_data BLOB, " /* TP-UD */
- /* additional data, interpreted from SMS */
- "header BLOB, " /* UD Header */
- "text TEXT " /* decoded UD after UDH */
- ")",
- [SCHEMA_VLR] = "CREATE TABLE IF NOT EXISTS VLR ("
- "id INTEGER PRIMARY KEY AUTOINCREMENT, "
- "created TIMESTAMP NOT NULL, "
- "updated TIMESTAMP NOT NULL, "
- "subscriber_id NUMERIC UNIQUE NOT NULL, "
- "last_bts NUMERIC NOT NULL "
- ")",
- [SCHEMA_APDU] = "CREATE TABLE IF NOT EXISTS ApduBlobs ("
- "id INTEGER PRIMARY KEY AUTOINCREMENT, "
- "created TIMESTAMP NOT NULL, "
- "apdu_id_flags INTEGER NOT NULL, "
- "subscriber_id INTEGER NOT NULL, "
- "apdu BLOB "
- ")",
- [SCHEMA_COUNTERS] = "CREATE TABLE IF NOT EXISTS Counters ("
- "id INTEGER PRIMARY KEY AUTOINCREMENT, "
- "timestamp TIMESTAMP NOT NULL, "
- "value INTEGER NOT NULL, "
- "name TEXT NOT NULL "
- ")",
- [SCHEMA_RATE] = "CREATE TABLE IF NOT EXISTS RateCounters ("
- "id INTEGER PRIMARY KEY AUTOINCREMENT, "
- "timestamp TIMESTAMP NOT NULL, "
- "value INTEGER NOT NULL, "
- "name TEXT NOT NULL, "
- "idx INTEGER NOT NULL "
- ")",
- [SCHEMA_AUTHKEY] = "CREATE TABLE IF NOT EXISTS AuthKeys ("
- "subscriber_id INTEGER PRIMARY KEY, "
- "algorithm_id INTEGER NOT NULL, "
- "a3a8_ki BLOB "
- ")",
- [SCHEMA_AUTHLAST] = "CREATE TABLE IF NOT EXISTS AuthLastTuples ("
- "subscriber_id INTEGER PRIMARY KEY, "
- "issued TIMESTAMP NOT NULL, "
- "use_count INTEGER NOT NULL DEFAULT 0, "
- "key_seq INTEGER NOT NULL, "
- "rand BLOB NOT NULL, "
- "sres BLOB NOT NULL, "
- "kc BLOB NOT NULL "
- ")",
-};
-
-void db_error_func(dbi_conn conn, void *data)
-{
- const char *msg;
- dbi_conn_error(conn, &msg);
- LOGP(DDB, LOGL_ERROR, "DBI: %s\n", msg);
- osmo_log_backtrace(DDB, LOGL_ERROR);
-}
-
-static int update_db_revision_2(void)
-{
- dbi_result result;
-
- result = dbi_conn_query(conn,
- "ALTER TABLE Subscriber "
- "ADD COLUMN expire_lu "
- "TIMESTAMP DEFAULT NULL");
- if (!result) {
- LOGP(DDB, LOGL_ERROR,
- "Failed to alter table Subscriber (upgrade from rev 2).\n");
- return -EINVAL;
- }
- dbi_result_free(result);
-
- result = dbi_conn_query(conn,
- "UPDATE Meta "
- "SET value = '3' "
- "WHERE key = 'revision'");
- if (!result) {
- LOGP(DDB, LOGL_ERROR,
- "Failed to update DB schema revision (upgrade from rev 2).\n");
- return -EINVAL;
- }
- dbi_result_free(result);
-
- return 0;
-}
-
-/**
- * Copied from the normal sms_from_result_v3 to avoid having
- * to make sure that the real routine will remain backward
- * compatible.
- */
-static struct gsm_sms *sms_from_result_v3(dbi_result result)
-{
- struct gsm_sms *sms = sms_alloc();
- long long unsigned int sender_id;
- struct gsm_subscriber *sender;
- const char *text, *daddr;
- const unsigned char *user_data;
- char buf[32];
-
- if (!sms)
- return NULL;
-
- sms->id = dbi_result_get_ulonglong(result, "id");
-
- sender_id = dbi_result_get_ulonglong(result, "sender_id");
- snprintf(buf, sizeof(buf), "%llu", sender_id);
- sender = db_get_subscriber(GSM_SUBSCRIBER_ID, buf);
- OSMO_ASSERT(sender);
- osmo_strlcpy(sms->src.addr, sender->extension, sizeof(sms->src.addr));
- subscr_direct_free(sender);
- sender = NULL;
-
- sms->reply_path_req = dbi_result_get_ulonglong(result, "reply_path_req");
- sms->status_rep_req = dbi_result_get_ulonglong(result, "status_rep_req");
- sms->ud_hdr_ind = dbi_result_get_ulonglong(result, "ud_hdr_ind");
- sms->protocol_id = dbi_result_get_ulonglong(result, "protocol_id");
- sms->data_coding_scheme = dbi_result_get_ulonglong(result,
- "data_coding_scheme");
-
- daddr = dbi_result_get_string(result, "dest_addr");
- if (daddr)
- 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;
-}
-
-struct gsm_subscriber *db_create_subscriber(const char *imsi, uint64_t smin,
- uint64_t smax, bool alloc_exten)
-{
- dbi_result result;
- struct gsm_subscriber *subscr;
-
- /* Is this subscriber known in the db? */
- subscr = db_get_subscriber(GSM_SUBSCRIBER_IMSI, imsi);
- if (subscr) {
- subscr_put(subscr);
- return NULL;
- }
-
- subscr = subscr_alloc();
- if (!subscr)
- return NULL;
- subscr->flags |= GSM_SUBSCRIBER_FIRST_CONTACT;
- result = dbi_conn_queryf(conn,
- "INSERT INTO Subscriber "
- "(imsi, created, updated) "
- "VALUES "
- "(%s, datetime('now'), datetime('now')) ",
- imsi
- );
- if (!result) {
- LOGP(DDB, LOGL_ERROR, "Failed to create Subscriber by IMSI.\n");
- subscr_put(subscr);
- return NULL;
- }
- subscr->id = dbi_conn_sequence_last(conn, NULL);
- osmo_strlcpy(subscr->imsi, imsi, sizeof(subscr->imsi));
- dbi_result_free(result);
- LOGP(DDB, LOGL_INFO, "New Subscriber: ID %llu, IMSI %s\n", subscr->id, subscr->imsi);
- if (alloc_exten)
- db_subscriber_alloc_exten(subscr, smin, smax);
- return subscr;
-}
-
-osmo_static_assert(sizeof(unsigned char) == sizeof(struct gsm48_classmark1), classmark1_size);
-
-static int get_equipment_by_subscr(struct gsm_subscriber *subscr)
-{
- dbi_result result;
- const char *string;
- unsigned char cm1;
- const unsigned char *cm2, *cm3;
- struct gsm_equipment *equip = &subscr->equipment;
-
- result = dbi_conn_queryf(conn,
- "SELECT Equipment.* "
- "FROM Equipment JOIN EquipmentWatch ON "
- "EquipmentWatch.equipment_id=Equipment.id "
- "WHERE EquipmentWatch.subscriber_id = %llu "
- "ORDER BY EquipmentWatch.updated DESC", subscr->id);
- if (!result)
- return -EIO;
-
- if (!dbi_result_next_row(result)) {
- dbi_result_free(result);
- return -ENOENT;
- }
-
- equip->id = dbi_result_get_ulonglong(result, "id");
-
- string = dbi_result_get_string(result, "imei");
- if (string)
- osmo_strlcpy(equip->imei, string, sizeof(equip->imei));
-
- string = dbi_result_get_string(result, "classmark1");
- if (string) {
- cm1 = atoi(string) & 0xff;
- memcpy(&equip->classmark1, &cm1, sizeof(equip->classmark1));
- }
-
- equip->classmark2_len = dbi_result_get_field_length(result, "classmark2");
- cm2 = dbi_result_get_binary(result, "classmark2");
- if (equip->classmark2_len > sizeof(equip->classmark2))
- equip->classmark2_len = sizeof(equip->classmark2);
- if (cm2)
- memcpy(equip->classmark2, cm2, equip->classmark2_len);
-
- equip->classmark3_len = dbi_result_get_field_length(result, "classmark3");
- cm3 = dbi_result_get_binary(result, "classmark3");
- if (equip->classmark3_len > sizeof(equip->classmark3))
- equip->classmark3_len = sizeof(equip->classmark3);
- if (cm3)
- memcpy(equip->classmark3, cm3, equip->classmark3_len);
-
- dbi_result_free(result);
-
- return 0;
-}
-
-int db_get_authinfo_for_subscr(struct gsm_auth_info *ainfo,
- struct gsm_subscriber *subscr)
-{
- dbi_result result;
- const unsigned char *a3a8_ki;
-
- result = dbi_conn_queryf(conn,
- "SELECT * FROM AuthKeys WHERE subscriber_id=%llu",
- subscr->id);
- if (!result)
- return -EIO;
-
- if (!dbi_result_next_row(result)) {
- dbi_result_free(result);
- return -ENOENT;
- }
-
- ainfo->auth_algo = dbi_result_get_ulonglong(result, "algorithm_id");
- ainfo->a3a8_ki_len = dbi_result_get_field_length(result, "a3a8_ki");
- a3a8_ki = dbi_result_get_binary(result, "a3a8_ki");
- if (ainfo->a3a8_ki_len > sizeof(ainfo->a3a8_ki))
- ainfo->a3a8_ki_len = sizeof(ainfo->a3a8_ki);
- memcpy(ainfo->a3a8_ki, a3a8_ki, ainfo->a3a8_ki_len);
-
- dbi_result_free(result);
-
- return 0;
-}
-
-int db_sync_authinfo_for_subscr(struct gsm_auth_info *ainfo,
- struct gsm_subscriber *subscr)
-{
- dbi_result result;
- struct gsm_auth_info ainfo_old;
- int rc, upd;
- unsigned char *ki_str;
-
- /* Deletion ? */
- if (ainfo == NULL) {
- result = dbi_conn_queryf(conn,
- "DELETE FROM AuthKeys WHERE subscriber_id=%llu",
- subscr->id);
-
- if (!result)
- return -EIO;
-
- dbi_result_free(result);
-
- return 0;
- }
-
- /* Check if already existing */
- rc = db_get_authinfo_for_subscr(&ainfo_old, subscr);
- if (rc && rc != -ENOENT)
- return rc;
- upd = rc ? 0 : 1;
-
- /* Update / Insert */
- dbi_conn_quote_binary_copy(conn,
- ainfo->a3a8_ki, ainfo->a3a8_ki_len, &ki_str);
-
- if (!upd) {
- result = dbi_conn_queryf(conn,
- "INSERT INTO AuthKeys "
- "(subscriber_id, algorithm_id, a3a8_ki) "
- "VALUES (%llu, %u, %s)",
- subscr->id, ainfo->auth_algo, ki_str);
- } else {
- result = dbi_conn_queryf(conn,
- "UPDATE AuthKeys "
- "SET algorithm_id=%u, a3a8_ki=%s "
- "WHERE subscriber_id=%llu",
- ainfo->auth_algo, ki_str, subscr->id);
- }
-
- free(ki_str);
-
- if (!result)
- return -EIO;
-
- dbi_result_free(result);
-
- return 0;
-}
-
-int db_get_lastauthtuple_for_subscr(struct gsm_auth_tuple *atuple,
- struct gsm_subscriber *subscr)
-{
- dbi_result result;
- int len;
- const unsigned char *blob;
-
- result = dbi_conn_queryf(conn,
- "SELECT * FROM AuthLastTuples WHERE subscriber_id=%llu",
- subscr->id);
- if (!result)
- return -EIO;
-
- if (!dbi_result_next_row(result)) {
- dbi_result_free(result);
- return -ENOENT;
- }
-
- memset(atuple, 0, sizeof(*atuple));
-
- atuple->use_count = dbi_result_get_ulonglong(result, "use_count");
- atuple->key_seq = dbi_result_get_ulonglong(result, "key_seq");
-
- len = dbi_result_get_field_length(result, "rand");
- if (len != sizeof(atuple->vec.rand))
- goto err_size;
-
- blob = dbi_result_get_binary(result, "rand");
- memcpy(atuple->vec.rand, blob, len);
-
- len = dbi_result_get_field_length(result, "sres");
- if (len != sizeof(atuple->vec.sres))
- goto err_size;
-
- blob = dbi_result_get_binary(result, "sres");
- memcpy(atuple->vec.sres, blob, len);
-
- len = dbi_result_get_field_length(result, "kc");
- if (len != sizeof(atuple->vec.kc))
- goto err_size;
-
- blob = dbi_result_get_binary(result, "kc");
- memcpy(atuple->vec.kc, blob, len);
-
- dbi_result_free(result);
-
- return 0;
-
-err_size:
- dbi_result_free(result);
- return -EIO;
-}
-
-int db_sync_lastauthtuple_for_subscr(struct gsm_auth_tuple *atuple,
- struct gsm_subscriber *subscr)
-{
- dbi_result result;
- int rc, upd;
- struct gsm_auth_tuple atuple_old;
- unsigned char *rand_str, *sres_str, *kc_str;
-
- /* Deletion ? */
- if (atuple == NULL) {
- result = dbi_conn_queryf(conn,
- "DELETE FROM AuthLastTuples WHERE subscriber_id=%llu",
- subscr->id);
-
- if (!result)
- return -EIO;
-
- dbi_result_free(result);
-
- return 0;
- }
-
- /* Check if already existing */
- rc = db_get_lastauthtuple_for_subscr(&atuple_old, subscr);
- if (rc && rc != -ENOENT)
- return rc;
- upd = rc ? 0 : 1;
-
- /* Update / Insert */
- dbi_conn_quote_binary_copy(conn,
- atuple->vec.rand, sizeof(atuple->vec.rand), &rand_str);
- dbi_conn_quote_binary_copy(conn,
- atuple->vec.sres, sizeof(atuple->vec.sres), &sres_str);
- dbi_conn_quote_binary_copy(conn,
- atuple->vec.kc, sizeof(atuple->vec.kc), &kc_str);
-
- if (!upd) {
- result = dbi_conn_queryf(conn,
- "INSERT INTO AuthLastTuples "
- "(subscriber_id, issued, use_count, "
- "key_seq, rand, sres, kc) "
- "VALUES (%llu, datetime('now'), %u, "
- "%u, %s, %s, %s ) ",
- subscr->id, atuple->use_count, atuple->key_seq,
- rand_str, sres_str, kc_str);
- } else {
- char *issued = atuple->key_seq == atuple_old.key_seq ?
- "issued" : "datetime('now')";
- result = dbi_conn_queryf(conn,
- "UPDATE AuthLastTuples "
- "SET issued=%s, use_count=%u, "
- "key_seq=%u, rand=%s, sres=%s, kc=%s "
- "WHERE subscriber_id = %llu",
- issued, atuple->use_count, atuple->key_seq,
- rand_str, sres_str, kc_str, subscr->id);
- }
-
- free(rand_str);
- free(sres_str);
- free(kc_str);
-
- if (!result)
- return -EIO;
-
- dbi_result_free(result);
-
- return 0;
-}
-
-static void db_set_from_query(struct gsm_subscriber *subscr, dbi_conn result)
-{
- const char *string;
- string = dbi_result_get_string(result, "imsi");
- if (string)
- osmo_strlcpy(subscr->imsi, string, sizeof(subscr->imsi));
-
- string = dbi_result_get_string(result, "tmsi");
- if (string)
- subscr->tmsi = tmsi_from_string(string);
-
- string = dbi_result_get_string(result, "name");
- if (string)
- osmo_strlcpy(subscr->name, string, sizeof(subscr->name));
-
- string = dbi_result_get_string(result, "extension");
- if (string)
- osmo_strlcpy(subscr->extension, string, sizeof(subscr->extension));
-
- subscr->lac = dbi_result_get_ulonglong(result, "lac");
-
- if (!dbi_result_field_is_null(result, "expire_lu"))
- subscr->expire_lu = dbi_result_get_datetime(result, "expire_lu");
- else
- subscr->expire_lu = GSM_SUBSCRIBER_NO_EXPIRATION;
-
- subscr->authorized = dbi_result_get_ulonglong(result, "authorized");
-
-}
-
-#define BASE_QUERY "SELECT * FROM Subscriber "
-struct gsm_subscriber *db_get_subscriber(enum gsm_subscriber_field field,
- const char *id)
-{
- dbi_result result;
- char *quoted;
- struct gsm_subscriber *subscr;
-
- switch (field) {
- case GSM_SUBSCRIBER_IMSI:
- dbi_conn_quote_string_copy(conn, id, &quoted);
- result = dbi_conn_queryf(conn,
- BASE_QUERY
- "WHERE imsi = %s ",
- quoted
- );
- free(quoted);
- break;
- case GSM_SUBSCRIBER_TMSI:
- dbi_conn_quote_string_copy(conn, id, &quoted);
- result = dbi_conn_queryf(conn,
- BASE_QUERY
- "WHERE tmsi = %s ",
- quoted
- );
- free(quoted);
- break;
- case GSM_SUBSCRIBER_EXTENSION:
- dbi_conn_quote_string_copy(conn, id, &quoted);
- result = dbi_conn_queryf(conn,
- BASE_QUERY
- "WHERE extension = %s ",
- quoted
- );
- free(quoted);
- break;
- case GSM_SUBSCRIBER_ID:
- dbi_conn_quote_string_copy(conn, id, &quoted);
- result = dbi_conn_queryf(conn,
- BASE_QUERY
- "WHERE id = %s ", quoted);
- free(quoted);
- break;
- default:
- LOGP(DDB, LOGL_NOTICE, "Unknown query selector for Subscriber.\n");
- return NULL;
- }
- if (!result) {
- LOGP(DDB, LOGL_ERROR, "Failed to query Subscriber.\n");
- return NULL;
- }
- if (!dbi_result_next_row(result)) {
- DEBUGP(DDB, "Failed to find the Subscriber. '%u' '%s'\n",
- field, id);
- dbi_result_free(result);
- return NULL;
- }
-
- subscr = subscr_alloc();
- subscr->id = dbi_result_get_ulonglong(result, "id");
-
- db_set_from_query(subscr, result);
- DEBUGP(DDB, "Found Subscriber: ID %llu, IMSI %s, NAME '%s', TMSI %x, EXTEN '%s', LAC %hu, AUTH %u\n",
- subscr->id, subscr->imsi, subscr->name, subscr->tmsi, subscr->extension,
- subscr->lac, subscr->authorized);
- dbi_result_free(result);
-
- get_equipment_by_subscr(subscr);
-
- return subscr;
-}
-
-int db_subscriber_update(struct gsm_subscriber *subscr)
-{
- char buf[32];
- dbi_result result;
-
- /* Copy the id to a string as queryf with %llu is failing */
- sprintf(buf, "%llu", subscr->id);
- result = dbi_conn_queryf(conn,
- BASE_QUERY
- "WHERE id = %s", buf);
-
- if (!result) {
- LOGP(DDB, LOGL_ERROR, "Failed to query Subscriber: %llu\n", subscr->id);
- return -EIO;
- }
- if (!dbi_result_next_row(result)) {
- DEBUGP(DDB, "Failed to find the Subscriber. %llu\n",
- subscr->id);
- dbi_result_free(result);
- return -EIO;
- }
-
- db_set_from_query(subscr, result);
- dbi_result_free(result);
- get_equipment_by_subscr(subscr);
-
- return 0;
-}
-
-int db_sync_subscriber(struct gsm_subscriber *subscriber)
-{
- dbi_result result;
- char tmsi[14];
- char *q_tmsi, *q_name, *q_extension;
-
- dbi_conn_quote_string_copy(conn,
- subscriber->name, &q_name);
- if (subscriber->extension[0] != '\0')
- dbi_conn_quote_string_copy(conn,
- subscriber->extension, &q_extension);
- else
- q_extension = strdup("NULL");
-
- if (subscriber->tmsi != GSM_RESERVED_TMSI) {
- sprintf(tmsi, "%u", subscriber->tmsi);
- dbi_conn_quote_string_copy(conn,
- tmsi,
- &q_tmsi);
- } else
- q_tmsi = strdup("NULL");
-
- if (subscriber->expire_lu == GSM_SUBSCRIBER_NO_EXPIRATION) {
- result = dbi_conn_queryf(conn,
- "UPDATE Subscriber "
- "SET updated = datetime('now'), "
- "name = %s, "
- "extension = %s, "
- "authorized = %i, "
- "tmsi = %s, "
- "lac = %i, "
- "expire_lu = NULL "
- "WHERE imsi = %s ",
- q_name,
- q_extension,
- subscriber->authorized,
- q_tmsi,
- subscriber->lac,
- subscriber->imsi);
- } else {
- result = dbi_conn_queryf(conn,
- "UPDATE Subscriber "
- "SET updated = datetime('now'), "
- "name = %s, "
- "extension = %s, "
- "authorized = %i, "
- "tmsi = %s, "
- "lac = %i, "
- "expire_lu = datetime(%i, 'unixepoch') "
- "WHERE imsi = %s ",
- q_name,
- q_extension,
- subscriber->authorized,
- q_tmsi,
- subscriber->lac,
- (int) subscriber->expire_lu,
- subscriber->imsi);
- }
-
- free(q_tmsi);
- free(q_name);
- free(q_extension);
-
- if (!result) {
- LOGP(DDB, LOGL_ERROR, "Failed to update Subscriber (by IMSI).\n");
- return 1;
- }
-
- dbi_result_free(result);
-
- return 0;
-}
-
-int db_subscriber_delete(struct gsm_subscriber *subscr)
-{
- dbi_result result;
-
- result = dbi_conn_queryf(conn,
- "DELETE FROM AuthKeys WHERE subscriber_id=%llu",
- subscr->id);
- if (!result) {
- LOGP(DDB, LOGL_ERROR,
- "Failed to delete Authkeys for %llu\n", subscr->id);
- return -1;
- }
- dbi_result_free(result);
-
- result = dbi_conn_queryf(conn,
- "DELETE FROM AuthLastTuples WHERE subscriber_id=%llu",
- subscr->id);
- if (!result) {
- LOGP(DDB, LOGL_ERROR,
- "Failed to delete AuthLastTuples for %llu\n", subscr->id);
- return -1;
- }
- dbi_result_free(result);
-
- result = dbi_conn_queryf(conn,
- "DELETE FROM AuthToken WHERE subscriber_id=%llu",
- subscr->id);
- if (!result) {
- LOGP(DDB, LOGL_ERROR,
- "Failed to delete AuthToken for %llu\n", subscr->id);
- return -1;
- }
- dbi_result_free(result);
-
- result = dbi_conn_queryf(conn,
- "DELETE FROM EquipmentWatch WHERE subscriber_id=%llu",
- subscr->id);
- if (!result) {
- LOGP(DDB, LOGL_ERROR,
- "Failed to delete EquipmentWatch for %llu\n", subscr->id);
- return -1;
- }
- dbi_result_free(result);
-
- if (subscr->extension[0] != '\0') {
- result = dbi_conn_queryf(conn,
- "DELETE FROM SMS WHERE src_addr=%s OR dest_addr=%s",
- subscr->extension, subscr->extension);
- if (!result) {
- LOGP(DDB, LOGL_ERROR,
- "Failed to delete SMS for %llu\n", subscr->id);
- return -1;
- }
- dbi_result_free(result);
- }
-
- result = dbi_conn_queryf(conn,
- "DELETE FROM VLR WHERE subscriber_id=%llu",
- subscr->id);
- if (!result) {
- LOGP(DDB, LOGL_ERROR,
- "Failed to delete VLR for %llu\n", subscr->id);
- return -1;
- }
- dbi_result_free(result);
-
- result = dbi_conn_queryf(conn,
- "DELETE FROM ApduBlobs WHERE subscriber_id=%llu",
- subscr->id);
- if (!result) {
- LOGP(DDB, LOGL_ERROR,
- "Failed to delete ApduBlobs for %llu\n", subscr->id);
- return -1;
- }
- dbi_result_free(result);
-
- result = dbi_conn_queryf(conn,
- "DELETE FROM Subscriber WHERE id=%llu",
- subscr->id);
- if (!result) {
- LOGP(DDB, LOGL_ERROR,
- "Failed to delete Subscriber for %llu\n", subscr->id);
- return -1;
- }
- dbi_result_free(result);
-
- return 0;
-}
-
-/**
- * List all the authorized and non-expired subscribers. The callback will
- * be called one by one. The subscr argument is not fully initialize and
- * subscr_get/subscr_put must not be called. The passed in pointer will be
- * deleted after the callback by the database call.
- */
-int db_subscriber_list_active(void (*cb)(struct gsm_subscriber*,void*), void *closure)
-{
- dbi_result result;
-
- result = dbi_conn_query(conn,
- "SELECT * from Subscriber WHERE LAC != 0 AND authorized = 1");
- if (!result) {
- LOGP(DDB, LOGL_ERROR, "Failed to list active subscribers\n");
- return -1;
- }
-
- while (dbi_result_next_row(result)) {
- struct gsm_subscriber *subscr;
-
- subscr = subscr_alloc();
- subscr->id = dbi_result_get_ulonglong(result, "id");
- db_set_from_query(subscr, result);
- cb(subscr, closure);
- OSMO_ASSERT(subscr->use_count == 1);
- llist_del(&subscr->entry);
- talloc_free(subscr);
- }
-
- dbi_result_free(result);
- return 0;
-}
-
-int db_sync_equipment(struct gsm_equipment *equip)
-{
- dbi_result result;
- unsigned char *cm2, *cm3;
- char *q_imei;
- uint8_t classmark1;
-
- memcpy(&classmark1, &equip->classmark1, sizeof(classmark1));
- DEBUGP(DDB, "Sync Equipment IMEI=%s, classmark1=%02x",
- equip->imei, classmark1);
- if (equip->classmark2_len)
- DEBUGPC(DDB, ", classmark2=%s",
- osmo_hexdump(equip->classmark2, equip->classmark2_len));
- if (equip->classmark3_len)
- DEBUGPC(DDB, ", classmark3=%s",
- osmo_hexdump(equip->classmark3, equip->classmark3_len));
- DEBUGPC(DDB, "\n");
-
- dbi_conn_quote_binary_copy(conn, equip->classmark2,
- equip->classmark2_len, &cm2);
- dbi_conn_quote_binary_copy(conn, equip->classmark3,
- equip->classmark3_len, &cm3);
- dbi_conn_quote_string_copy(conn, equip->imei, &q_imei);
-
- result = dbi_conn_queryf(conn,
- "UPDATE Equipment SET "
- "updated = datetime('now'), "
- "classmark1 = %u, "
- "classmark2 = %s, "
- "classmark3 = %s "
- "WHERE imei = %s ",
- classmark1, cm2, cm3, q_imei);
-
- free(cm2);
- free(cm3);
- free(q_imei);
-
- if (!result) {
- LOGP(DDB, LOGL_ERROR, "Failed to update Equipment\n");
- return -EIO;
- }
-
- dbi_result_free(result);
- return 0;
-}
-
-int db_subscriber_expire(void *priv, void (*callback)(void *priv, long long unsigned int id))
-{
- dbi_result result;
-
- result = dbi_conn_query(conn,
- "SELECT id "
- "FROM Subscriber "
- "WHERE lac != 0 AND "
- "( expire_lu is NOT NULL "
- "AND expire_lu < datetime('now') ) "
- "LIMIT 1");
- if (!result) {
- LOGP(DDB, LOGL_ERROR, "Failed to get expired subscribers\n");
- return -EIO;
- }
-
- while (dbi_result_next_row(result))
- callback(priv, dbi_result_get_ulonglong(result, "id"));
-
- dbi_result_free(result);
- return 0;
-}
-
-int db_subscriber_alloc_tmsi(struct gsm_subscriber *subscriber)
-{
- dbi_result result = NULL;
- char tmsi[14];
- char *tmsi_quoted;
-
- for (;;) {
- if (RAND_bytes((uint8_t *) &subscriber->tmsi, sizeof(subscriber->tmsi)) != 1) {
- LOGP(DDB, LOGL_ERROR, "RAND_bytes failed\n");
- return 1;
- }
- if (subscriber->tmsi == GSM_RESERVED_TMSI)
- continue;
-
- sprintf(tmsi, "%u", subscriber->tmsi);
- dbi_conn_quote_string_copy(conn, tmsi, &tmsi_quoted);
- result = dbi_conn_queryf(conn,
- "SELECT * FROM Subscriber "
- "WHERE tmsi = %s ",
- tmsi_quoted);
-
- free(tmsi_quoted);
-
- if (!result) {
- LOGP(DDB, LOGL_ERROR, "Failed to query Subscriber "
- "while allocating new TMSI.\n");
- return 1;
- }
- if (dbi_result_get_numrows(result)) {
- dbi_result_free(result);
- continue;
- }
- if (!dbi_result_next_row(result)) {
- dbi_result_free(result);
- DEBUGP(DDB, "Allocated TMSI %u for IMSI %s.\n",
- subscriber->tmsi, subscriber->imsi);
- return db_sync_subscriber(subscriber);
- }
- dbi_result_free(result);
- }
- return 0;
-}
-
-int db_subscriber_alloc_exten(struct gsm_subscriber *subscriber, uint64_t smin,
- uint64_t smax)
-{
- dbi_result result = NULL;
- uint64_t try;
-
- for (;;) {
- try = (rand() % (smax - smin + 1) + smin);
- result = dbi_conn_queryf(conn,
- "SELECT * FROM Subscriber "
- "WHERE extension = %"PRIu64,
- try
- );
- if (!result) {
- LOGP(DDB, LOGL_ERROR, "Failed to query Subscriber "
- "while allocating new extension.\n");
- return 1;
- }
- if (dbi_result_get_numrows(result)){
- dbi_result_free(result);
- continue;
- }
- if (!dbi_result_next_row(result)) {
- dbi_result_free(result);
- break;
- }
- dbi_result_free(result);
- }
- sprintf(subscriber->extension, "%"PRIu64, try);
- DEBUGP(DDB, "Allocated extension %"PRIu64 " for IMSI %s.\n", try, subscriber->imsi);
- return db_sync_subscriber(subscriber);
-}
-/*
- * try to allocate a new unique token for this subscriber and return it
- * via a parameter. if the subscriber already has a token, return
- * an error.
- */
-
-int db_subscriber_alloc_token(struct gsm_subscriber *subscriber, uint32_t *token)
-{
- dbi_result result;
- uint32_t try;
-
- for (;;) {
- if (RAND_bytes((uint8_t *) &try, sizeof(try)) != 1) {
- LOGP(DDB, LOGL_ERROR, "RAND_bytes failed\n");
- return 1;
- }
- if (!try) /* 0 is an invalid token */
- continue;
- result = dbi_conn_queryf(conn,
- "SELECT * FROM AuthToken "
- "WHERE subscriber_id = %llu OR token = \"%08X\" ",
- subscriber->id, try);
- if (!result) {
- LOGP(DDB, LOGL_ERROR, "Failed to query AuthToken "
- "while allocating new token.\n");
- return 1;
- }
- if (dbi_result_get_numrows(result)) {
- dbi_result_free(result);
- continue;
- }
- if (!dbi_result_next_row(result)) {
- dbi_result_free(result);
- break;
- }
- dbi_result_free(result);
- }
- result = dbi_conn_queryf(conn,
- "INSERT INTO AuthToken "
- "(subscriber_id, created, token) "
- "VALUES "
- "(%llu, datetime('now'), \"%08X\") ",
- subscriber->id, try);
- if (!result) {
- LOGP(DDB, LOGL_ERROR, "Failed to create token %08X for "
- "IMSI %s.\n", try, subscriber->imsi);
- return 1;
- }
- dbi_result_free(result);
- *token = try;
- DEBUGP(DDB, "Allocated token %08X for IMSI %s.\n", try, subscriber->imsi);
-
- return 0;
-}
-
-int db_subscriber_assoc_imei(struct gsm_subscriber *subscriber, char imei[GSM23003_IMEISV_NUM_DIGITS])
-{
- unsigned long long equipment_id, watch_id;
- dbi_result result;
-
- osmo_strlcpy(subscriber->equipment.imei, imei, sizeof(subscriber->equipment.imei));
-
- result = dbi_conn_queryf(conn,
- "INSERT OR IGNORE INTO Equipment "
- "(imei, created, updated) "
- "VALUES "
- "(%s, datetime('now'), datetime('now')) ",
- imei);
- if (!result) {
- LOGP(DDB, LOGL_ERROR, "Failed to create Equipment by IMEI.\n");
- return 1;
- }
-
- equipment_id = 0;
- if (dbi_result_get_numrows_affected(result)) {
- equipment_id = dbi_conn_sequence_last(conn, NULL);
- }
- dbi_result_free(result);
-
- if (equipment_id)
- DEBUGP(DDB, "New Equipment: ID %llu, IMEI %s\n", equipment_id, imei);
- else {
- result = dbi_conn_queryf(conn,
- "SELECT id FROM Equipment "
- "WHERE imei = %s ",
- imei
- );
- if (!result) {
- LOGP(DDB, LOGL_ERROR, "Failed to query Equipment by IMEI.\n");
- return 1;
- }
- if (!dbi_result_next_row(result)) {
- LOGP(DDB, LOGL_ERROR, "Failed to find the Equipment.\n");
- dbi_result_free(result);
- return 1;
- }
- equipment_id = dbi_result_get_ulonglong(result, "id");
- dbi_result_free(result);
- }
-
- result = dbi_conn_queryf(conn,
- "INSERT OR IGNORE INTO EquipmentWatch "
- "(subscriber_id, equipment_id, created, updated) "
- "VALUES "
- "(%llu, %llu, datetime('now'), datetime('now')) ",
- subscriber->id, equipment_id);
- if (!result) {
- LOGP(DDB, LOGL_ERROR, "Failed to create EquipmentWatch.\n");
- return 1;
- }
-
- watch_id = 0;
- if (dbi_result_get_numrows_affected(result))
- watch_id = dbi_conn_sequence_last(conn, NULL);
-
- dbi_result_free(result);
- if (watch_id)
- DEBUGP(DDB, "New EquipmentWatch: ID %llu, IMSI %s, IMEI %s\n",
- equipment_id, subscriber->imsi, imei);
- else {
- result = dbi_conn_queryf(conn,
- "UPDATE EquipmentWatch "
- "SET updated = datetime('now') "
- "WHERE subscriber_id = %llu AND equipment_id = %llu ",
- subscriber->id, equipment_id);
- if (!result) {
- LOGP(DDB, LOGL_ERROR, "Failed to update EquipmentWatch.\n");
- return 1;
- }
- dbi_result_free(result);
- DEBUGP(DDB, "Updated EquipmentWatch: ID %llu, IMSI %s, IMEI %s\n",
- equipment_id, subscriber->imsi, imei);
- }
-
- return 0;
-}
-
-/* store an [unsent] SMS to the database */
-int db_sms_store(struct gsm_sms *sms)
-{
- dbi_result result;
- char *q_text, *q_daddr, *q_saddr;
- unsigned char *q_udata;
- char *validity_timestamp = "2222-2-2";
-
- /* FIXME: generate validity timestamp based on validity_minutes */
-
- dbi_conn_quote_string_copy(conn, (char *)sms->text, &q_text);
- dbi_conn_quote_string_copy(conn, (char *)sms->dst.addr, &q_daddr);
- dbi_conn_quote_string_copy(conn, (char *)sms->src.addr, &q_saddr);
- dbi_conn_quote_binary_copy(conn, sms->user_data, sms->user_data_len,
- &q_udata);
-
- /* FIXME: correct validity period */
- result = dbi_conn_queryf(conn,
- "INSERT INTO SMS "
- "(created, valid_until, "
- "reply_path_req, status_rep_req, protocol_id, "
- "data_coding_scheme, ud_hdr_ind, "
- "user_data, text, "
- "dest_addr, dest_ton, dest_npi, "
- "src_addr, src_ton, src_npi) VALUES "
- "(datetime('now'), %u, "
- "%u, %u, %u, "
- "%u, %u, "
- "%s, %s, "
- "%s, %u, %u, "
- "%s, %u, %u)",
- validity_timestamp,
- sms->reply_path_req, sms->status_rep_req, sms->protocol_id,
- sms->data_coding_scheme, sms->ud_hdr_ind,
- q_udata, q_text,
- q_daddr, sms->dst.ton, sms->dst.npi,
- q_saddr, sms->src.ton, sms->src.npi);
- free(q_text);
- free(q_udata);
- free(q_daddr);
- free(q_saddr);
-
- if (!result)
- return -EIO;
-
- dbi_result_free(result);
- return 0;
-}
-
-static struct gsm_sms *sms_from_result(struct gsm_network *net, dbi_result result)
-{
- struct gsm_sms *sms = sms_alloc();
- const char *text, *daddr, *saddr;
- const unsigned char *user_data;
-
- if (!sms)
- return NULL;
-
- sms->id = dbi_result_get_ulonglong(result, "id");
-
- /* FIXME: validity */
- /* FIXME: those should all be get_uchar, but sqlite3 is braindead */
- sms->reply_path_req = dbi_result_get_ulonglong(result, "reply_path_req");
- sms->status_rep_req = dbi_result_get_ulonglong(result, "status_rep_req");
- sms->ud_hdr_ind = dbi_result_get_ulonglong(result, "ud_hdr_ind");
- sms->protocol_id = dbi_result_get_ulonglong(result, "protocol_id");
- sms->data_coding_scheme = dbi_result_get_ulonglong(result,
- "data_coding_scheme");
- /* sms->msg_ref is temporary and not stored in DB */
-
- sms->dst.npi = dbi_result_get_ulonglong(result, "dest_npi");
- sms->dst.ton = dbi_result_get_ulonglong(result, "dest_ton");
- daddr = dbi_result_get_string(result, "dest_addr");
- if (daddr)
- osmo_strlcpy(sms->dst.addr, daddr, sizeof(sms->dst.addr));
- sms->receiver = subscr_get_by_extension(net->subscr_group, sms->dst.addr);
-
- sms->src.npi = dbi_result_get_ulonglong(result, "src_npi");
- sms->src.ton = dbi_result_get_ulonglong(result, "src_ton");
- saddr = dbi_result_get_string(result, "src_addr");
- if (saddr)
- 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;
-}
-
-/* retrieve the next unsent SMS with ID >= min_id */
-struct gsm_sms *db_sms_get_unsent(struct gsm_network *net, unsigned long long min_id)
-{
- dbi_result result;
- struct gsm_sms *sms;
-
- result = dbi_conn_queryf(conn,
- "SELECT SMS.* "
- "FROM SMS JOIN Subscriber ON "
- "SMS.dest_addr = Subscriber.extension "
- "WHERE SMS.id >= %llu AND SMS.sent IS NULL "
- "AND Subscriber.lac > 0 "
- "ORDER BY SMS.id LIMIT 1",
- min_id);
- if (!result)
- return NULL;
-
- if (!dbi_result_next_row(result)) {
- dbi_result_free(result);
- return NULL;
- }
-
- sms = sms_from_result(net, result);
-
- dbi_result_free(result);
-
- return sms;
-}
-
-struct gsm_sms *db_sms_get_unsent_by_subscr(struct gsm_network *net,
- unsigned long long min_subscr_id,
- unsigned int failed)
-{
- dbi_result result;
- struct gsm_sms *sms;
-
- result = dbi_conn_queryf(conn,
- "SELECT SMS.* "
- "FROM SMS JOIN Subscriber ON "
- "SMS.dest_addr = Subscriber.extension "
- "WHERE Subscriber.id >= %llu AND SMS.sent IS NULL "
- "AND Subscriber.lac > 0 AND SMS.deliver_attempts < %u "
- "ORDER BY Subscriber.id, SMS.id LIMIT 1",
- min_subscr_id, failed);
- if (!result)
- return NULL;
-
- if (!dbi_result_next_row(result)) {
- dbi_result_free(result);
- return NULL;
- }
-
- sms = sms_from_result(net, result);
-
- dbi_result_free(result);
-
- return sms;
-}
-
-/* retrieve the next unsent SMS for a given subscriber */
-struct gsm_sms *db_sms_get_unsent_for_subscr(struct gsm_subscriber *subscr)
-{
- dbi_result result;
- struct gsm_sms *sms;
-
- result = dbi_conn_queryf(conn,
- "SELECT SMS.* "
- "FROM SMS JOIN Subscriber ON "
- "SMS.dest_addr = Subscriber.extension "
- "WHERE Subscriber.id = %llu AND SMS.sent IS NULL "
- "AND Subscriber.lac > 0 "
- "ORDER BY SMS.id LIMIT 1",
- subscr->id);
- if (!result)
- return NULL;
-
- if (!dbi_result_next_row(result)) {
- dbi_result_free(result);
- return NULL;
- }
-
- sms = sms_from_result(subscr->group->net, result);
-
- dbi_result_free(result);
-
- return sms;
-}
-
-/* mark a given SMS as delivered */
-int db_sms_mark_delivered(struct gsm_sms *sms)
-{
- dbi_result result;
-
- result = dbi_conn_queryf(conn,
- "UPDATE SMS "
- "SET sent = datetime('now') "
- "WHERE id = %llu", sms->id);
- if (!result) {
- LOGP(DDB, LOGL_ERROR, "Failed to mark SMS %llu as sent.\n", sms->id);
- return 1;
- }
-
- dbi_result_free(result);
- return 0;
-}
-
-/* increase the number of attempted deliveries */
-int db_sms_inc_deliver_attempts(struct gsm_sms *sms)
-{
- dbi_result result;
-
- result = dbi_conn_queryf(conn,
- "UPDATE SMS "
- "SET deliver_attempts = deliver_attempts + 1 "
- "WHERE id = %llu", sms->id);
- if (!result) {
- LOGP(DDB, LOGL_ERROR, "Failed to inc deliver attempts for "
- "SMS %llu.\n", sms->id);
- return 1;
- }
-
- dbi_result_free(result);
- return 0;
-}
-
-int db_apdu_blob_store(struct gsm_subscriber *subscr,
- uint8_t apdu_id_flags, uint8_t len,
- uint8_t *apdu)
-{
- dbi_result result;
- unsigned char *q_apdu;
-
- dbi_conn_quote_binary_copy(conn, apdu, len, &q_apdu);
-
- result = dbi_conn_queryf(conn,
- "INSERT INTO ApduBlobs "
- "(created,subscriber_id,apdu_id_flags,apdu) VALUES "
- "(datetime('now'),%llu,%u,%s)",
- subscr->id, apdu_id_flags, q_apdu);
-
- free(q_apdu);
-
- if (!result)
- return -EIO;
-
- dbi_result_free(result);
- return 0;
-}
-
-int db_store_counter(struct osmo_counter *ctr)
-{
- dbi_result result;
- char *q_name;
-
- dbi_conn_quote_string_copy(conn, ctr->name, &q_name);
-
- result = dbi_conn_queryf(conn,
- "INSERT INTO Counters "
- "(timestamp,name,value) VALUES "
- "(datetime('now'),%s,%lu)", q_name, ctr->value);
-
- free(q_name);
-
- if (!result)
- return -EIO;
-
- dbi_result_free(result);
- return 0;
-}
-
-static int db_store_rate_ctr(struct rate_ctr_group *ctrg, unsigned int num,
- char *q_prefix)
-{
- dbi_result result;
- char *q_name;
-
- dbi_conn_quote_string_copy(conn, ctrg->desc->ctr_desc[num].name,
- &q_name);
-
- result = dbi_conn_queryf(conn,
- "Insert INTO RateCounters "
- "(timestamp,name,idx,value) VALUES "
- "(datetime('now'),%s.%s,%u,%"PRIu64")",
- q_prefix, q_name, ctrg->idx, ctrg->ctr[num].current);
-
- free(q_name);
-
- if (!result)
- return -EIO;
-
- dbi_result_free(result);
- return 0;
-}
-
-int db_store_rate_ctr_group(struct rate_ctr_group *ctrg)
-{
- unsigned int i;
- char *q_prefix;
-
- dbi_conn_quote_string_copy(conn, ctrg->desc->group_name_prefix, &q_prefix);
-
- for (i = 0; i < ctrg->desc->num_ctr; i++)
- db_store_rate_ctr(ctrg, i, q_prefix);
-
- free(q_prefix);
-
- return 0;
-}
diff --git a/openbsc/src/libmsc/gsm_04_08.c b/openbsc/src/libmsc/gsm_04_08.c
deleted file mode 100644
index 89108e466..000000000
--- a/openbsc/src/libmsc/gsm_04_08.c
+++ /dev/null
@@ -1,4047 +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-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 "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 <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 <assert.h>
-
-void *tall_locop_ctx;
-void *tall_authciphop_ctx;
-
-static int tch_rtp_signal(struct gsm_lchan *lchan, int signal);
-
-static int gsm0408_loc_upd_acc(struct gsm_subscriber_connection *conn);
-static int gsm48_tx_simple(struct gsm_subscriber_connection *conn,
- uint8_t pdisc, uint8_t msg_type);
-static void schedule_reject(struct gsm_subscriber_connection *conn);
-static void release_anchor(struct gsm_subscriber_connection *conn);
-
-struct gsm_lai {
- uint16_t mcc;
- uint16_t mnc;
- uint16_t lac;
-};
-
-static int apply_codec_restrictions(struct gsm_bts *bts,
- struct gsm_mncc_bearer_cap *bcap)
-{
- int i, j;
-
- /* remove unsupported speech versions from list */
- for (i = 0, j = 0; bcap->speech_ver[i] >= 0; i++) {
- if (bcap->speech_ver[i] == GSM48_BCAP_SV_FR)
- bcap->speech_ver[j++] = GSM48_BCAP_SV_FR;
- if (bcap->speech_ver[i] == GSM48_BCAP_SV_EFR && bts->codec.efr)
- bcap->speech_ver[j++] = GSM48_BCAP_SV_EFR;
- if (bcap->speech_ver[i] == GSM48_BCAP_SV_AMR_F && bts->codec.amr)
- bcap->speech_ver[j++] = GSM48_BCAP_SV_AMR_F;
- if (bcap->speech_ver[i] == GSM48_BCAP_SV_HR && bts->codec.hr)
- bcap->speech_ver[j++] = GSM48_BCAP_SV_HR;
- if (bcap->speech_ver[i] == GSM48_BCAP_SV_AMR_H && bts->codec.amr)
- bcap->speech_ver[j++] = GSM48_BCAP_SV_AMR_H;
- }
- bcap->speech_ver[j] = -1;
-
- return 0;
-}
-
-static uint32_t new_callref = 0x80000001;
-
-void cc_tx_to_mncc(struct gsm_network *net, struct msgb *msg)
-{
- net->mncc_recv(net, msg);
-}
-
-static int gsm48_conn_sendmsg(struct msgb *msg, struct gsm_subscriber_connection *conn,
- struct gsm_trans *trans)
-{
- struct gsm48_hdr *gh = (struct gsm48_hdr *) msg->data;
-
- /* if we get passed a transaction reference, do some common
- * work that the caller no longer has to do */
- if (trans) {
- gh->proto_discr = trans->protocol | (trans->transaction_id << 4);
- msg->lchan = trans->conn->lchan;
- }
-
- if (msg->lchan) {
- struct e1inp_sign_link *sign_link =
- msg->lchan->ts->trx->rsl_link;
-
- msg->dst = sign_link;
- if (gsm48_hdr_pdisc(gh) == GSM48_PDISC_CC)
- DEBUGP(DCC, "(bts %d trx %d ts %d ti %02x) "
- "Sending '%s' to MS.\n",
- sign_link->trx->bts->nr,
- sign_link->trx->nr, msg->lchan->ts->nr,
- gh->proto_discr & 0xf0,
- gsm48_cc_msg_name(gh->msg_type));
- else
- DEBUGP(DCC, "(bts %d trx %d ts %d pd %02x) "
- "Sending 0x%02x to MS.\n",
- sign_link->trx->bts->nr,
- sign_link->trx->nr, msg->lchan->ts->nr,
- gh->proto_discr, gh->msg_type);
- }
-
- return gsm0808_submit_dtap(conn, msg, 0, 0);
-}
-
-int gsm48_cc_tx_notify_ss(struct gsm_trans *trans, const char *message)
-{
- struct gsm48_hdr *gh;
- struct msgb *ss_notify;
-
- ss_notify = gsm0480_create_notifySS(message);
- if (!ss_notify)
- return -1;
-
- gsm0480_wrap_invoke(ss_notify, GSM0480_OP_CODE_NOTIFY_SS, 0);
- uint8_t *data = msgb_push(ss_notify, 1);
- data[0] = ss_notify->len - 1;
- gh = (struct gsm48_hdr *) msgb_push(ss_notify, sizeof(*gh));
- gh->msg_type = GSM48_MT_CC_FACILITY;
- return gsm48_conn_sendmsg(ss_notify, trans->conn, trans);
-}
-
-void release_security_operation(struct gsm_subscriber_connection *conn)
-{
- if (!conn->sec_operation)
- return;
-
- talloc_free(conn->sec_operation);
- conn->sec_operation = NULL;
- msc_release_connection(conn);
-}
-
-void allocate_security_operation(struct gsm_subscriber_connection *conn)
-{
- conn->sec_operation = talloc_zero(tall_authciphop_ctx,
- struct gsm_security_operation);
-}
-
-int gsm48_secure_channel(struct gsm_subscriber_connection *conn, int key_seq,
- gsm_cbfn *cb, void *cb_data)
-{
- struct gsm_network *net = conn->network;
- struct gsm_subscriber *subscr = conn->subscr;
- struct gsm_security_operation *op;
- struct gsm_auth_tuple atuple;
- int status = -1, rc;
-
- /* Check if we _can_ enable encryption. Cases where we can't:
- * - Encryption disabled in config
- * - Channel already secured (nothing to do)
- * - Subscriber equipment doesn't support configured encryption
- */
- if (!net->a5_encryption) {
- status = GSM_SECURITY_NOAVAIL;
- } else if (conn->lchan->encr.alg_id > RSL_ENC_ALG_A5(0)) {
- DEBUGP(DMM, "Requesting to secure an already secure channel");
- status = GSM_SECURITY_ALREADY;
- } else if (!ms_cm2_a5n_support(subscr->equipment.classmark2,
- net->a5_encryption)) {
- DEBUGP(DMM, "Subscriber equipment doesn't support requested encryption");
- status = GSM_SECURITY_NOAVAIL;
- }
-
- /* If not done yet, try to get info for this user */
- if (status < 0) {
- rc = auth_get_tuple_for_subscr(&atuple, subscr, key_seq);
- if (rc <= 0)
- status = GSM_SECURITY_NOAVAIL;
- }
-
- /* Are we done yet ? */
- if (status >= 0)
- return cb ?
- cb(GSM_HOOK_RR_SECURITY, status, NULL, conn, cb_data) :
- 0;
-
- /* Start an operation (can't have more than one pending !!!) */
- if (conn->sec_operation)
- return -EBUSY;
-
- allocate_security_operation(conn);
- op = conn->sec_operation;
- op->cb = cb;
- op->cb_data = cb_data;
- memcpy(&op->atuple, &atuple, sizeof(struct gsm_auth_tuple));
-
- /* FIXME: Should start a timer for completion ... */
-
- /* Then do whatever is needed ... */
- if (rc == AUTH_DO_AUTH_THEN_CIPH) {
- /* Start authentication */
- return gsm48_tx_mm_auth_req(conn, op->atuple.vec.rand, NULL,
- op->atuple.key_seq);
- } else if (rc == AUTH_DO_CIPH) {
- /* Start ciphering directly */
- return gsm0808_cipher_mode(conn, net->a5_encryption,
- op->atuple.vec.kc, 8, 0);
- }
-
- return -EINVAL; /* not reached */
-}
-
-static bool subscr_regexp_check(const struct gsm_network *net, const char *imsi)
-{
- if (!net->authorized_reg_str)
- return false;
-
- if (regexec(&net->authorized_regexp, imsi, 0, NULL, 0) != REG_NOMATCH)
- return true;
-
- return false;
-}
-
-static int authorize_subscriber(struct gsm_loc_updating_operation *loc,
- struct gsm_subscriber *subscriber)
-{
- if (!subscriber)
- return 0;
-
- /*
- * Do not send accept yet as more information should arrive. Some
- * phones will not send us the information and we will have to check
- * what we want to do with that.
- */
- if (loc && (loc->waiting_for_imsi || loc->waiting_for_imei))
- return 0;
-
- switch (subscriber->group->net->auth_policy) {
- case GSM_AUTH_POLICY_CLOSED:
- return subscriber->authorized;
- case GSM_AUTH_POLICY_REGEXP:
- if (subscriber->authorized)
- return 1;
- if (subscr_regexp_check(subscriber->group->net,
- subscriber->imsi))
- subscriber->authorized = 1;
- return subscriber->authorized;
- case GSM_AUTH_POLICY_TOKEN:
- if (subscriber->authorized)
- return subscriber->authorized;
- return (subscriber->flags & GSM_SUBSCRIBER_FIRST_CONTACT);
- case GSM_AUTH_POLICY_ACCEPT_ALL:
- return 1;
- default:
- return 0;
- }
-}
-
-static void _release_loc_updating_req(struct gsm_subscriber_connection *conn, int release)
-{
- if (!conn->loc_operation)
- return;
-
- /* No need to keep the connection up */
- release_anchor(conn);
-
- osmo_timer_del(&conn->loc_operation->updating_timer);
- talloc_free(conn->loc_operation);
- conn->loc_operation = NULL;
- if (release)
- msc_release_connection(conn);
-}
-
-static void loc_updating_failure(struct gsm_subscriber_connection *conn, int release)
-{
- if (!conn->loc_operation)
- return;
- LOGP(DMM, LOGL_ERROR, "Location Updating failed for %s\n",
- subscr_name(conn->subscr));
- rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_FAILED]);
- _release_loc_updating_req(conn, release);
-}
-
-static void loc_updating_success(struct gsm_subscriber_connection *conn, int release)
-{
- if (!conn->loc_operation)
- return;
- LOGP(DMM, LOGL_INFO, "Location Updating completed for %s\n",
- subscr_name(conn->subscr));
- rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_COMPLETED]);
- _release_loc_updating_req(conn, release);
-}
-
-static void allocate_loc_updating_req(struct gsm_subscriber_connection *conn)
-{
- if (conn->loc_operation)
- LOGP(DMM, LOGL_ERROR, "Connection already had operation.\n");
- loc_updating_failure(conn, 0);
-
- conn->loc_operation = talloc_zero(tall_locop_ctx,
- struct gsm_loc_updating_operation);
-}
-
-static int finish_lu(struct gsm_subscriber_connection *conn)
-{
- int rc = 0;
- int avoid_tmsi = conn->network->avoid_tmsi;
-
- /* We're all good */
- if (avoid_tmsi) {
- conn->subscr->tmsi = GSM_RESERVED_TMSI;
- db_sync_subscriber(conn->subscr);
- } else {
- db_subscriber_alloc_tmsi(conn->subscr);
- }
-
- rc = gsm0408_loc_upd_acc(conn);
- if (conn->network->send_mm_info) {
- /* send MM INFO with network name */
- rc = gsm48_tx_mm_info(conn);
- }
-
- /* call subscr_update after putting the loc_upd_acc
- * in the transmit queue, since S_SUBSCR_ATTACHED might
- * trigger further action like SMS delivery */
- subscr_update(conn->subscr, conn->bts,
- GSM_SUBSCRIBER_UPDATE_ATTACHED);
-
- /*
- * The gsm0408_loc_upd_acc sends a MI with the TMSI. The
- * MS needs to respond with a TMSI REALLOCATION COMPLETE
- * (even if the TMSI is the same).
- * If avoid_tmsi == true, we don't send a TMSI, we don't
- * expect a reply and Location Updating is done.
- */
- if (avoid_tmsi)
- loc_updating_success(conn, 1);
-
- return rc;
-}
-
-static int _gsm0408_authorize_sec_cb(unsigned int hooknum, unsigned int event,
- struct msgb *msg, void *data, void *param)
-{
- struct gsm_subscriber_connection *conn = data;
- int rc = 0;
-
- switch (event) {
- case GSM_SECURITY_AUTH_FAILED:
- loc_updating_failure(conn, 1);
- break;
-
- case GSM_SECURITY_ALREADY:
- LOGP(DMM, LOGL_ERROR, "We don't expect LOCATION "
- "UPDATING after CM SERVICE REQUEST\n");
- /* fall through */
-
- case GSM_SECURITY_NOAVAIL:
- case GSM_SECURITY_SUCCEEDED:
- rc = finish_lu(conn);
- break;
-
- default:
- rc = -EINVAL;
- };
-
- return rc;
-}
-
-static int gsm0408_authorize(struct gsm_subscriber_connection *conn, struct msgb *msg)
-{
- if (!conn->loc_operation)
- return 0;
-
- if (authorize_subscriber(conn->loc_operation, conn->subscr))
- return gsm48_secure_channel(conn,
- conn->loc_operation->key_seq,
- _gsm0408_authorize_sec_cb, NULL);
- return 0;
-}
-
-void gsm0408_clear_request(struct gsm_subscriber_connection *conn, uint32_t cause)
-{
- struct gsm_trans *trans, *temp;
-
- /* avoid someone issuing a clear */
- conn->in_release = 1;
-
- /*
- * Cancel any outstanding location updating request
- * operation taking place on the subscriber connection.
- */
- loc_updating_failure(conn, 0);
-
- /* We might need to cancel the paging response or such. */
- if (conn->sec_operation && conn->sec_operation->cb) {
- conn->sec_operation->cb(GSM_HOOK_RR_SECURITY, GSM_SECURITY_AUTH_FAILED,
- NULL, conn, conn->sec_operation->cb_data);
- }
-
- release_security_operation(conn);
- release_anchor(conn);
-
- /*
- * Free all transactions that are associated with the released
- * connection. The transaction code will inform the CC or SMS
- * facilities that will send the release indications. As part of
- * the CC REL_IND the remote leg might be released and this will
- * trigger the call to trans_free. This is something the llist
- * macro can not handle and we will need to re-iterate the list.
- *
- * TODO: Move the trans_list into the subscriber connection and
- * create a pending list for MT transactions. These exist before
- * we have a subscriber connection.
- */
-restart:
- llist_for_each_entry_safe(trans, temp, &conn->network->trans_list, entry) {
- if (trans->conn == conn) {
- trans_free(trans);
- goto restart;
- }
- }
-}
-
-void gsm0408_clear_all_trans(struct gsm_network *net, int protocol)
-{
- struct gsm_trans *trans, *temp;
-
- LOGP(DCC, LOGL_NOTICE, "Clearing all currently active transactions!!!\n");
-
- llist_for_each_entry_safe(trans, temp, &net->trans_list, entry) {
- if (trans->protocol == protocol) {
- trans->callref = 0;
- trans_free(trans);
- }
- }
-}
-
-/* Chapter 9.2.14 : Send LOCATION UPDATING REJECT */
-int gsm0408_loc_upd_rej(struct gsm_subscriber_connection *conn, uint8_t cause)
-{
- struct gsm_bts *bts = conn->bts;
- struct msgb *msg;
-
- msg = gsm48_create_loc_upd_rej(cause);
- if (!msg) {
- LOGP(DMM, LOGL_ERROR, "Failed to create msg for LOCATION UPDATING REJECT.\n");
- return -1;
- }
-
- msg->lchan = conn->lchan;
-
- LOGP(DMM, LOGL_INFO, "Subscriber %s: LOCATION UPDATING REJECT "
- "LAC=%u BTS=%u\n", subscr_name(conn->subscr),
- bts->location_area_code, bts->nr);
-
- return gsm48_conn_sendmsg(msg, conn, NULL);
-}
-
-/* Chapter 9.2.13 : Send LOCATION UPDATE ACCEPT */
-static int gsm0408_loc_upd_acc(struct gsm_subscriber_connection *conn)
-{
- struct 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;
-
- msg->lchan = conn->lchan;
-
- gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
- gh->proto_discr = GSM48_PDISC_MM;
- gh->msg_type = GSM48_MT_MM_LOC_UPD_ACCEPT;
-
- lai = (struct gsm48_loc_area_id *) msgb_put(msg, sizeof(*lai));
- gsm48_generate_lai(lai, conn->network->country_code,
- conn->network->network_code,
- conn->bts->location_area_code);
-
- if (conn->subscr->tmsi == GSM_RESERVED_TMSI) {
- uint8_t mi[10];
- int len;
- len = gsm48_generate_mid_from_imsi(mi, conn->subscr->imsi);
- mid = msgb_put(msg, len);
- memcpy(mid, mi, len);
- } else {
- mid = msgb_put(msg, GSM48_MID_TMSI_LEN);
- gsm48_generate_mid_from_tmsi(mid, conn->subscr->tmsi);
- }
-
- DEBUGP(DMM, "-> LOCATION UPDATE ACCEPT\n");
-
- 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;
-
- msg->lchan = conn->lchan;
-
- gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1);
- gh->proto_discr = GSM48_PDISC_MM;
- gh->msg_type = GSM48_MT_MM_ID_REQ;
- gh->data[0] = id_type;
-
- return gsm48_conn_sendmsg(msg, conn, NULL);
-}
-
-static struct gsm_subscriber *subscr_create(const struct gsm_network *net,
- const char *imsi)
-{
- if (!net->auto_create_subscr)
- return NULL;
-
- if (!subscr_regexp_check(net, imsi))
- return NULL;
-
- return subscr_create_subscriber(net->subscr_group, imsi);
-}
-
-/* Parse Chapter 9.2.11 Identity Response */
-static int mm_rx_id_resp(struct gsm_subscriber_connection *conn, struct msgb *msg)
-{
- struct gsm48_hdr *gh = msgb_l3(msg);
- struct gsm_network *net = conn->network;
- uint8_t mi_type = gh->data[1] & GSM_MI_TYPE_MASK;
- char mi_string[GSM48_MI_SIZE];
-
- gsm48_mi_to_string(mi_string, sizeof(mi_string), &gh->data[1], gh->data[0]);
- DEBUGP(DMM, "IDENTITY RESPONSE: MI(%s)=%s\n",
- gsm48_mi_type_name(mi_type), mi_string);
-
- osmo_signal_dispatch(SS_SUBSCR, S_SUBSCR_IDENTITY, gh->data);
-
- switch (mi_type) {
- case GSM_MI_TYPE_IMSI:
- /* look up subscriber based on IMSI, create if not found */
- if (!conn->subscr) {
- conn->subscr = subscr_get_by_imsi(net->subscr_group,
- mi_string);
- if (!conn->subscr)
- conn->subscr = subscr_create(net, mi_string);
- }
- if (!conn->subscr && conn->loc_operation) {
- gsm0408_loc_upd_rej(conn, net->reject_cause);
- loc_updating_failure(conn, 1);
- return 0;
- }
- if (conn->loc_operation)
- conn->loc_operation->waiting_for_imsi = 0;
- break;
- case GSM_MI_TYPE_IMEI:
- case GSM_MI_TYPE_IMEISV:
- /* update subscribe <-> IMEI mapping */
- if (conn->subscr) {
- db_subscriber_assoc_imei(conn->subscr, mi_string);
- db_sync_equipment(&conn->subscr->equipment);
- }
- if (conn->loc_operation)
- conn->loc_operation->waiting_for_imei = 0;
- break;
- }
-
- /* Check if we can let the mobile station enter */
- return gsm0408_authorize(conn, msg);
-}
-
-
-static void loc_upd_rej_cb(void *data)
-{
- struct gsm_subscriber_connection *conn = data;
-
- LOGP(DMM, LOGL_DEBUG, "Location Updating Request procedure timedout.\n");
- gsm0408_loc_upd_rej(conn, conn->network->reject_cause);
- loc_updating_failure(conn, 1);
-}
-
-static void schedule_reject(struct gsm_subscriber_connection *conn)
-{
- osmo_timer_setup(&conn->loc_operation->updating_timer, loc_upd_rej_cb,
- conn);
- osmo_timer_schedule(&conn->loc_operation->updating_timer, 5, 0);
-}
-
-static const struct value_string lupd_names[] = {
- { GSM48_LUPD_NORMAL, "NORMAL" },
- { GSM48_LUPD_PERIODIC, "PERIODIC" },
- { GSM48_LUPD_IMSI_ATT, "IMSI ATTACH" },
- { 0, NULL }
-};
-
-/* Chapter 9.2.15: Receive Location Updating Request */
-static int mm_rx_loc_upd_req(struct gsm_subscriber_connection *conn, struct msgb *msg)
-{
- struct gsm48_hdr *gh = msgb_l3(msg);
- struct gsm48_loc_upd_req *lu;
- struct gsm_subscriber *subscr = NULL;
- uint8_t mi_type;
- char mi_string[GSM48_MI_SIZE];
-
- lu = (struct gsm48_loc_upd_req *) gh->data;
-
- mi_type = lu->mi[0] & GSM_MI_TYPE_MASK;
-
- gsm48_mi_to_string(mi_string, sizeof(mi_string), lu->mi, lu->mi_len);
-
- DEBUGPC(DMM, "MI(%s)=%s type=%s ", gsm48_mi_type_name(mi_type),
- mi_string, get_value_string(lupd_names, lu->type));
-
- osmo_signal_dispatch(SS_SUBSCR, S_SUBSCR_IDENTITY, &lu->mi_len);
-
- switch (lu->type) {
- case GSM48_LUPD_NORMAL:
- rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_TYPE_NORMAL]);
- break;
- case GSM48_LUPD_IMSI_ATT:
- rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_TYPE_ATTACH]);
- break;
- case GSM48_LUPD_PERIODIC:
- rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_TYPE_PERIODIC]);
- break;
- }
-
- /*
- * Pseudo Spoof detection: Just drop a second/concurrent
- * location updating request.
- */
- if (conn->loc_operation) {
- DEBUGPC(DMM, "ignoring request due an existing one: %p.\n",
- conn->loc_operation);
- gsm0408_loc_upd_rej(conn, GSM48_REJECT_PROTOCOL_ERROR);
- return 0;
- }
-
- allocate_loc_updating_req(conn);
-
- conn->loc_operation->key_seq = lu->key_seq;
-
- switch (mi_type) {
- case GSM_MI_TYPE_IMSI:
- DEBUGPC(DMM, "\n");
- /* we always want the IMEI, too */
- mm_tx_identity_req(conn, GSM_MI_TYPE_IMEI);
- conn->loc_operation->waiting_for_imei = 1;
-
- /* look up subscriber based on IMSI, create if not found */
- subscr = subscr_get_by_imsi(conn->network->subscr_group, mi_string);
- if (!subscr)
- subscr = subscr_create(conn->network, mi_string);
- if (!subscr) {
- gsm0408_loc_upd_rej(conn, conn->network->reject_cause);
- loc_updating_failure(conn, 0); /* FIXME: set release == true? */
- return 0;
- }
- break;
- case GSM_MI_TYPE_TMSI:
- DEBUGPC(DMM, "\n");
- /* look up the subscriber based on TMSI, request IMSI if it fails */
- subscr = subscr_get_by_tmsi(conn->network->subscr_group,
- tmsi_from_string(mi_string));
- if (!subscr) {
- /* send IDENTITY REQUEST message to get IMSI */
- mm_tx_identity_req(conn, GSM_MI_TYPE_IMSI);
- conn->loc_operation->waiting_for_imsi = 1;
- }
- /* we always want the IMEI, too */
- mm_tx_identity_req(conn, GSM_MI_TYPE_IMEI);
- conn->loc_operation->waiting_for_imei = 1;
- break;
- case GSM_MI_TYPE_IMEI:
- case GSM_MI_TYPE_IMEISV:
- /* no sim card... FIXME: what to do ? */
- DEBUGPC(DMM, "unimplemented mobile identity type\n");
- break;
- default:
- DEBUGPC(DMM, "unknown mobile identity type\n");
- break;
- }
-
- /* schedule the reject timer */
- schedule_reject(conn);
-
- if (!subscr) {
- DEBUGPC(DRR, "<- Can't find any subscriber for this ID\n");
- /* FIXME: request id? close channel? */
- return -EINVAL;
- }
-
- conn->subscr = subscr;
- conn->subscr->equipment.classmark1 = lu->classmark1;
-
- /* check if we can let the subscriber into our network immediately
- * or if we need to wait for identity responses. */
- return gsm0408_authorize(conn, msg);
-}
-
-/* Turn int into semi-octet representation: 98 => 0x89 */
-static uint8_t bcdify(uint8_t value)
-{
- uint8_t ret;
-
- ret = value / 10;
- ret |= (value % 10) << 4;
-
- return ret;
-}
-
-
-/* Section 9.2.15a */
-int gsm48_tx_mm_info(struct gsm_subscriber_connection *conn)
-{
- struct msgb *msg = gsm48_msgb_alloc_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;
-
- msg->lchan = conn->lchan;
-
- gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
- gh->proto_discr = GSM48_PDISC_MM;
- gh->msg_type = GSM48_MT_MM_INFO;
-
- if (net->name_long) {
-#if 0
- name_len = strlen(net->name_long);
- /* 10.5.3.5a */
- ptr8 = msgb_put(msg, 3);
- ptr8[0] = GSM48_IE_NAME_LONG;
- ptr8[1] = name_len*2 +1;
- ptr8[2] = 0x90; /* UCS2, no spare bits, no CI */
-
- ptr16 = (uint16_t *) msgb_put(msg, name_len*2);
- for (i = 0; i < name_len; i++)
- ptr16[i] = htons(net->name_long[i]);
-
- /* FIXME: Use Cell Broadcast, not UCS-2, since
- * UCS-2 is only supported by later revisions of the spec */
-#endif
- name_len = (strlen(net->name_long)*7)/8;
- name_pad = (8 - strlen(net->name_long)*7)%8;
- if (name_pad > 0)
- name_len++;
- /* 10.5.3.5a */
- ptr8 = msgb_put(msg, 3);
- ptr8[0] = GSM48_IE_NAME_LONG;
- ptr8[1] = name_len +1;
- ptr8[2] = 0x80 | name_pad; /* Cell Broadcast DCS, no CI */
-
- ptr8 = msgb_put(msg, name_len);
- gsm_7bit_encode_n(ptr8, name_len, net->name_long, NULL);
-
- }
-
- if (net->name_short) {
-#if 0
- name_len = strlen(net->name_short);
- /* 10.5.3.5a */
- ptr8 = (uint8_t *) msgb_put(msg, 3);
- ptr8[0] = GSM48_IE_NAME_SHORT;
- ptr8[1] = name_len*2 + 1;
- ptr8[2] = 0x90; /* UCS2, no spare bits, no CI */
-
- ptr16 = (uint16_t *) msgb_put(msg, name_len*2);
- for (i = 0; i < name_len; i++)
- ptr16[i] = htons(net->name_short[i]);
-#endif
- name_len = (strlen(net->name_short)*7)/8;
- name_pad = (8 - strlen(net->name_short)*7)%8;
- if (name_pad > 0)
- name_len++;
- /* 10.5.3.5a */
- ptr8 = (uint8_t *) msgb_put(msg, 3);
- ptr8[0] = GSM48_IE_NAME_SHORT;
- ptr8[1] = name_len +1;
- ptr8[2] = 0x80 | name_pad; /* Cell Broadcast DCS, no CI */
-
- ptr8 = msgb_put(msg, name_len);
- gsm_7bit_encode_n(ptr8, name_len, net->name_short, NULL);
-
- }
-
- /* Section 10.5.3.9 */
- cur_t = time(NULL);
- gmt_time = gmtime(&cur_t);
-
- ptr8 = msgb_put(msg, 8);
- ptr8[0] = GSM48_IE_NET_TIME_TZ;
- ptr8[1] = bcdify(gmt_time->tm_year % 100);
- ptr8[2] = bcdify(gmt_time->tm_mon + 1);
- ptr8[3] = bcdify(gmt_time->tm_mday);
- ptr8[4] = bcdify(gmt_time->tm_hour);
- ptr8[5] = bcdify(gmt_time->tm_min);
- ptr8[6] = bcdify(gmt_time->tm_sec);
-
- if (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(rand, 16));
- if (autn)
- DEBUGP(DMM, " AUTH REQ (autn = %s)\n", osmo_hexdump(autn, 16));
-
- msg->lchan = conn->lchan;
- gh->proto_discr = GSM48_PDISC_MM;
- gh->msg_type = GSM48_MT_MM_AUTH_REQ;
-
- ar->key_seq = key_seq;
-
- /* 16 bytes RAND parameters */
- 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);
-}
-
-/*
- * At the 30C3 phones miss their periodic update
- * interval a lot and then remain unreachable. In case
- * we still know the TMSI we can just attach it again.
- */
-static void implit_attach(struct gsm_subscriber_connection *conn)
-{
- if (conn->subscr->lac != GSM_LAC_RESERVED_DETACHED)
- return;
-
- subscr_update(conn->subscr, conn->bts,
- GSM_SUBSCRIBER_UPDATE_ATTACHED);
-}
-
-
-static int _gsm48_rx_mm_serv_req_sec_cb(
- unsigned int hooknum, unsigned int event,
- struct msgb *msg, void *data, void *param)
-{
- struct gsm_subscriber_connection *conn = data;
- int rc = 0;
-
- /* auth failed or succeeded, the timer was stopped */
- conn->expire_timer_stopped = 1;
-
- switch (event) {
- case GSM_SECURITY_AUTH_FAILED:
- /* Nothing to do */
- break;
-
- case GSM_SECURITY_NOAVAIL:
- case GSM_SECURITY_ALREADY:
- rc = gsm48_tx_mm_serv_ack(conn);
- implit_attach(conn);
- break;
-
- case GSM_SECURITY_SUCCEEDED:
- /* nothing to do. CIPHER MODE COMMAND is
- * implicit CM SERV ACK */
- implit_attach(conn);
- break;
-
- default:
- rc = -EINVAL;
- };
-
- return rc;
-}
-
-/*
- * Handle CM Service Requests
- * a) Verify that the packet is long enough to contain the information
- * we require otherwsie reject with INCORRECT_MESSAGE
- * b) Try to parse the TMSI. If we do not have one reject
- * c) Check that we know the subscriber with the TMSI otherwise reject
- * with a HLR cause
- * d) Set the subscriber on the gsm_lchan and accept
- */
-static int gsm48_rx_mm_serv_req(struct gsm_subscriber_connection *conn, struct msgb *msg)
-{
- uint8_t mi_type;
- char mi_string[GSM48_MI_SIZE];
-
- struct gsm_network *network = conn->network;
- struct gsm_subscriber *subscr;
- struct gsm48_hdr *gh = msgb_l3(msg);
- struct gsm48_service_request *req =
- (struct gsm48_service_request *)gh->data;
- /* unfortunately in Phase1 the classmark2 length is variable */
- uint8_t classmark2_len = gh->data[1];
- uint8_t *classmark2 = gh->data+2;
- uint8_t mi_len = *(classmark2 + classmark2_len);
- uint8_t *mi = (classmark2 + classmark2_len + 1);
-
- DEBUGP(DMM, "<- CM SERVICE REQUEST ");
- if (msg->data_len < sizeof(struct gsm48_service_request*)) {
- DEBUGPC(DMM, "wrong sized message\n");
- return gsm48_tx_mm_serv_rej(conn,
- GSM48_REJECT_INCORRECT_MESSAGE);
- }
-
- if (msg->data_len < req->mi_len + 6) {
- DEBUGPC(DMM, "does not fit in packet\n");
- return gsm48_tx_mm_serv_rej(conn,
- GSM48_REJECT_INCORRECT_MESSAGE);
- }
-
- gsm48_mi_to_string(mi_string, sizeof(mi_string), mi, mi_len);
- mi_type = mi[0] & GSM_MI_TYPE_MASK;
-
- if (mi_type == GSM_MI_TYPE_IMSI) {
- DEBUGPC(DMM, "serv_type=0x%02x MI(%s)=%s\n",
- req->cm_service_type, gsm48_mi_type_name(mi_type),
- mi_string);
- subscr = subscr_get_by_imsi(network->subscr_group,
- mi_string);
- } else if (mi_type == GSM_MI_TYPE_TMSI) {
- DEBUGPC(DMM, "serv_type=0x%02x MI(%s)=%s\n",
- req->cm_service_type, gsm48_mi_type_name(mi_type),
- mi_string);
- subscr = subscr_get_by_tmsi(network->subscr_group,
- tmsi_from_string(mi_string));
- } else {
- DEBUGPC(DMM, "mi_type is not expected: %d\n", mi_type);
- return gsm48_tx_mm_serv_rej(conn,
- GSM48_REJECT_INCORRECT_MESSAGE);
- }
-
- osmo_signal_dispatch(SS_SUBSCR, S_SUBSCR_IDENTITY, (classmark2 + classmark2_len));
-
- if (is_siemens_bts(conn->bts))
- send_siemens_mrpci(msg->lchan, classmark2-1);
-
-
- /* FIXME: if we don't know the TMSI, inquire abit IMSI and allocate new TMSI */
- if (!subscr)
- return gsm48_tx_mm_serv_rej(conn,
- GSM48_REJECT_IMSI_UNKNOWN_IN_VLR);
-
- if (!conn->subscr)
- conn->subscr = subscr;
- else if (conn->subscr == subscr)
- subscr_put(subscr); /* lchan already has a ref, don't need another one */
- else {
- DEBUGP(DMM, "<- CM Channel already owned by someone else?\n");
- subscr_put(subscr);
- }
-
- subscr->equipment.classmark2_len = classmark2_len;
- memcpy(subscr->equipment.classmark2, classmark2, classmark2_len);
- db_sync_equipment(&subscr->equipment);
-
- /* we will send a MM message soon */
- conn->expire_timer_stopped = 1;
-
- return gsm48_secure_channel(conn, req->cipher_key_seq,
- _gsm48_rx_mm_serv_req_sec_cb, NULL);
-}
-
-static int gsm48_rx_mm_imsi_detach_ind(struct gsm_subscriber_connection *conn, struct msgb *msg)
-{
- struct gsm_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 gsm_subscriber *subscr = NULL;
-
- gsm48_mi_to_string(mi_string, sizeof(mi_string), idi->mi, idi->mi_len);
- DEBUGP(DMM, "IMSI DETACH INDICATION: MI(%s)=%s",
- gsm48_mi_type_name(mi_type), mi_string);
-
- rate_ctr_inc(&network->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_TYPE_DETACH]);
-
- switch (mi_type) {
- case GSM_MI_TYPE_TMSI:
- DEBUGPC(DMM, "\n");
- subscr = subscr_get_by_tmsi(network->subscr_group,
- tmsi_from_string(mi_string));
- break;
- case GSM_MI_TYPE_IMSI:
- DEBUGPC(DMM, "\n");
- subscr = subscr_get_by_imsi(network->subscr_group,
- mi_string);
- break;
- case GSM_MI_TYPE_IMEI:
- case GSM_MI_TYPE_IMEISV:
- /* no sim card... FIXME: what to do ? */
- DEBUGPC(DMM, ": unimplemented mobile identity type\n");
- break;
- default:
- DEBUGPC(DMM, ": unknown mobile identity type\n");
- break;
- }
-
- if (subscr) {
- subscr_update(subscr, conn->bts,
- GSM_SUBSCRIBER_UPDATE_DETACHED);
- DEBUGP(DMM, "Subscriber: %s\n", subscr_name(subscr));
-
- subscr->equipment.classmark1 = idi->classmark1;
- db_sync_equipment(&subscr->equipment);
-
- subscr_put(subscr);
- } else
- DEBUGP(DMM, "Unknown Subscriber ?!?\n");
-
- /* FIXME: iterate over all transactions and release them,
- * imagine an IMSI DETACH happening during an active call! */
-
- release_anchor(conn);
- return 0;
-}
-
-static int gsm48_rx_mm_status(struct msgb *msg)
-{
- struct gsm48_hdr *gh = msgb_l3(msg);
-
- DEBUGP(DMM, "MM STATUS (reject cause 0x%02x)\n", gh->data[0]);
-
- return 0;
-}
-
-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",
- subscr_name(conn->subscr), 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",
- subscr_name(conn->subscr), 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",
- subscr_name(conn->subscr),
- 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",
- subscr_name(conn->subscr), 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)
-{
- struct gsm_network *net = conn->network;
- uint8_t res[16];
- uint8_t res_len;
- int rc;
- bool is_r99;
-
- if (!conn->subscr) {
- LOGP(DMM, LOGL_ERROR,
- "MM AUTHENTICATION RESPONSE: invalid: no subscriber\n");
- gsm48_tx_mm_auth_rej(conn);
- release_security_operation(conn);
- 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) {
- gsm48_tx_mm_auth_rej(conn);
- release_security_operation(conn);
- return -EINVAL;
- }
-
- DEBUGP(DMM, "%s: MM %s AUTHENTICATION RESPONSE (%s = %s)\n",
- subscr_name(conn->subscr),
- is_r99 ? "R99" : "GSM", is_r99 ? "res" : "sres",
- osmo_hexdump_nospc(res, res_len));
-
- /* Future: vlr_sub_rx_auth_resp(conn->vsub, is_r99,
- * conn->via_ran == RAN_UTRAN_IU,
- * res, res_len);
- */
-
- if (res_len != 4) {
- LOGP(DMM, LOGL_ERROR,
- "%s: MM AUTHENTICATION RESPONSE:"
- " UMTS authentication not supported\n",
- subscr_name(conn->subscr));
- }
-
- /* Safety check */
- if (!conn->sec_operation) {
- DEBUGP(DMM, "No authentication/cipher operation in progress !!!\n");
- return -EIO;
- }
-
- /* Validate SRES */
- if (memcmp(conn->sec_operation->atuple.vec.sres, res, 4)) {
- int rc;
- gsm_cbfn *cb = conn->sec_operation->cb;
-
- DEBUGPC(DMM, "Invalid (expected %s)\n",
- osmo_hexdump(conn->sec_operation->atuple.vec.sres, 4));
-
- if (cb)
- cb(GSM_HOOK_RR_SECURITY, GSM_SECURITY_AUTH_FAILED,
- NULL, conn, conn->sec_operation->cb_data);
-
- rc = gsm48_tx_mm_auth_rej(conn);
- release_security_operation(conn);
- return rc;
- }
-
- DEBUGPC(DMM, "OK\n");
-
- /* Start ciphering */
- return gsm0808_cipher_mode(conn, net->a5_encryption,
- conn->sec_operation->atuple.vec.kc, 8, 0);
-}
-
-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;
- int rc;
-
- if (!conn->sec_operation) {
- DEBUGP(DMM, "%s: MM R99 AUTHENTICATION FAILURE:"
- " No authentication/cipher operation in progress\n",
- subscr_name(conn->subscr));
- return -EINVAL;
- }
-
- if (!conn->subscr) {
- LOGP(DMM, LOGL_ERROR,
- "MM R99 AUTHENTICATION FAILURE: invalid: no subscriber\n");
- gsm48_tx_mm_auth_rej(conn);
- release_security_operation(conn);
- return -EINVAL;
- }
-
- if (msgb_l3len(msg) < sizeof(*gh) + 1) {
- LOGP(DMM, LOGL_ERROR,
- "%s: MM R99 AUTHENTICATION FAILURE:"
- " l3 length invalid: %u\n",
- subscr_name(conn->subscr), msgb_l3len(msg));
- gsm48_tx_mm_auth_rej(conn);
- release_security_operation(conn);
- 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",
- subscr_name(conn->subscr), cause);
- rc = gsm48_tx_mm_auth_rej(conn);
- release_security_operation(conn);
- return rc;
- }
-
- /* 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",
- subscr_name(conn->subscr));
- gsm48_tx_mm_auth_rej(conn);
- release_security_operation(conn);
- 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",
- subscr_name(conn->subscr),
- GSM48_IE_AUTS, auts_tag, auts_len);
- gsm48_tx_mm_auth_rej(conn);
- release_security_operation(conn);
- 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",
- subscr_name(conn->subscr), msgb_l3len(msg));
- gsm48_tx_mm_auth_rej(conn);
- release_security_operation(conn);
- 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",
- subscr_name(conn->subscr), osmo_hexdump_nospc(auts, 14));
-
- /* Future: vlr_sub_rx_auth_fail(conn->vsub, auts); */
-
- LOGP(DMM, LOGL_ERROR, "%s: MM R99 AUTHENTICATION not supported\n",
- subscr_name(conn->subscr));
- rc = gsm48_tx_mm_auth_rej(conn);
- release_security_operation(conn);
- return rc;
-}
-
-/* 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:
- DEBUGP(DMM, "LOCATION UPDATING REQUEST: ");
- rc = mm_rx_loc_upd_req(conn, msg);
- break;
- case GSM48_MT_MM_ID_RESP:
- rc = mm_rx_id_resp(conn, msg);
- break;
- case GSM48_MT_MM_CM_SERV_REQ:
- rc = gsm48_rx_mm_serv_req(conn, msg);
- break;
- case GSM48_MT_MM_STATUS:
- rc = gsm48_rx_mm_status(msg);
- break;
- case GSM48_MT_MM_TMSI_REALL_COMPL:
- DEBUGP(DMM, "TMSI Reallocation Completed. Subscriber: %s\n",
- subscr_name(conn->subscr));
- loc_updating_success(conn, 1);
- break;
- case GSM48_MT_MM_IMSI_DETACH_IND:
- rc = gsm48_rx_mm_imsi_detach_ind(conn, msg);
- break;
- case GSM48_MT_MM_CM_REEST_REQ:
- DEBUGP(DMM, "CM REESTABLISH REQUEST: Not implemented\n");
- break;
- case GSM48_MT_MM_AUTH_RESP:
- rc = gsm48_rx_mm_auth_resp(conn, msg);
- break;
- 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;
-}
-
-/* Receive a PAGING RESPONSE message from the MS */
-static int gsm48_rx_rr_pag_resp(struct gsm_subscriber_connection *conn, struct msgb *msg)
-{
- struct gsm48_hdr *gh = msgb_l3(msg);
- struct gsm48_pag_resp *resp;
- uint8_t *classmark2_lv = gh->data + 1;
- uint8_t mi_type;
- char mi_string[GSM48_MI_SIZE];
- struct gsm_subscriber *subscr = NULL;
- struct bsc_subscr *bsub;
- uint32_t tmsi;
- int rc = 0;
-
- resp = (struct gsm48_pag_resp *) &gh->data[0];
- gsm48_paging_extract_mi(resp, msgb_l3len(msg) - sizeof(*gh),
- mi_string, &mi_type);
- DEBUGP(DRR, "PAGING RESPONSE: MI(%s)=%s\n",
- gsm48_mi_type_name(mi_type), mi_string);
-
- switch (mi_type) {
- case GSM_MI_TYPE_TMSI:
- tmsi = tmsi_from_string(mi_string);
- subscr = subscr_get_by_tmsi(conn->network->subscr_group, tmsi);
- break;
- case GSM_MI_TYPE_IMSI:
- subscr = subscr_get_by_imsi(conn->network->subscr_group,
- mi_string);
- break;
- }
-
- if (!subscr) {
- DEBUGP(DRR, "<- Can't find any subscriber for this ID\n");
- /* FIXME: request id? close channel? */
- return -EINVAL;
- }
-
- if (!conn->subscr) {
- conn->subscr = subscr;
- } else if (conn->subscr != subscr) {
- LOGP(DRR, LOGL_ERROR, "<- Channel already owned by someone else?\n");
- subscr_put(subscr);
- return -EINVAL;
- } else {
- DEBUGP(DRR, "<- Channel already owned by us\n");
- subscr_put(subscr);
- subscr = conn->subscr;
- }
-
- log_set_context(LOG_CTX_VLR_SUBSCR, subscr);
- DEBUGP(DRR, "<- Channel was requested by %s\n",
- subscr->name && strlen(subscr->name) ? subscr->name : subscr->imsi);
-
- subscr->equipment.classmark2_len = *classmark2_lv;
- memcpy(subscr->equipment.classmark2, classmark2_lv+1, *classmark2_lv);
- db_sync_equipment(&subscr->equipment);
-
- /* TODO MSC split -- creating a BSC subscriber directly from MSC data
- * structures in RAM. At some point the MSC will send a message to the
- * BSC instead. */
- bsub = bsc_subscr_find_or_create_by_imsi(conn->network->bsc_subscribers,
- subscr->imsi);
- bsub->tmsi = subscr->tmsi;
- bsub->lac = subscr->lac;
-
- /* We received a paging */
- conn->expire_timer_stopped = 1;
-
- rc = gsm48_handle_paging_resp(conn, msg, bsub);
- return rc;
-}
-
-static int gsm48_rx_rr_app_info(struct gsm_subscriber_connection *conn, struct msgb *msg)
-{
- struct gsm48_hdr *gh = msgb_l3(msg);
- uint8_t apdu_id_flags;
- uint8_t apdu_len;
- uint8_t *apdu_data;
-
- apdu_id_flags = gh->data[0];
- apdu_len = gh->data[1];
- apdu_data = gh->data+2;
-
- DEBUGP(DRR, "RX APPLICATION INFO id/flags=0x%02x apdu_len=%u apdu=%s\n",
- apdu_id_flags, apdu_len, osmo_hexdump(apdu_data, apdu_len));
-
- return db_apdu_blob_store(conn->subscr, apdu_id_flags, apdu_len, apdu_data);
-}
-
-/* Receive a GSM 04.08 Radio Resource (RR) message */
-static int gsm0408_rcv_rr(struct gsm_subscriber_connection *conn, struct msgb *msg)
-{
- struct gsm48_hdr *gh = msgb_l3(msg);
- int rc = 0;
-
- switch (gh->msg_type) {
- case GSM48_MT_RR_PAG_RESP:
- rc = gsm48_rx_rr_pag_resp(conn, msg);
- break;
- case GSM48_MT_RR_APP_INFO:
- rc = gsm48_rx_rr_app_info(conn, msg);
- break;
- default:
- LOGP(DRR, LOGL_NOTICE, "MSC: Unimplemented %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;
-
- msg->lchan = conn->lchan;
-
- DEBUGP(DRR, "TX APPLICATION INFO id=0x%02x, len=%u\n",
- apdu_id, apdu_len);
-
- gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 2 + apdu_len);
- gh->proto_discr = GSM48_PDISC_RR;
- gh->msg_type = GSM48_MT_RR_APP_INFO;
- gh->data[0] = apdu_id;
- gh->data[1] = apdu_len;
- memcpy(gh->data+2, apdu, apdu_len);
-
- return gsm48_conn_sendmsg(msg, conn, NULL);
-}
-
-/* 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));
-
- msg->lchan = conn->lchan;
-
- gh->proto_discr = pdisc;
- gh->msg_type = msg_type;
-
- return gsm48_conn_sendmsg(msg, conn, NULL);
-}
-
-static void gsm48_stop_cc_timer(struct gsm_trans *trans)
-{
- if (osmo_timer_pending(&trans->cc.timer)) {
- DEBUGP(DCC, "stopping pending timer T%x\n", trans->cc.Tcurrent);
- osmo_timer_del(&trans->cc.timer);
- trans->cc.Tcurrent = 0;
- }
-}
-
-static int mncc_recvmsg(struct gsm_network *net, struct gsm_trans *trans,
- int msg_type, struct gsm_mncc *mncc)
-{
- struct msgb *msg;
- unsigned char *data;
-
- if (trans)
- if (trans->conn && trans->conn->lchan)
- DEBUGP(DCC, "(bts %d trx %d ts %d ti %x sub %s) "
- "Sending '%s' to MNCC.\n",
- trans->conn->lchan->ts->trx->bts->nr,
- trans->conn->lchan->ts->trx->nr,
- trans->conn->lchan->ts->nr, trans->transaction_id,
- (trans->subscr)?(trans->subscr->extension):"-",
- get_mncc_name(msg_type));
- else
- DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) "
- "Sending '%s' to MNCC.\n",
- (trans->subscr)?(trans->subscr->extension):"-",
- get_mncc_name(msg_type));
- else
- DEBUGP(DCC, "(bts - trx - ts - ti -- sub -) "
- "Sending '%s' to MNCC.\n", get_mncc_name(msg_type));
-
- mncc->msg_type = msg_type;
-
- msg = msgb_alloc(sizeof(struct gsm_mncc), "MNCC");
- if (!msg)
- return -ENOMEM;
-
- data = msgb_put(msg, sizeof(struct gsm_mncc));
- memcpy(data, mncc, sizeof(struct gsm_mncc));
-
- cc_tx_to_mncc(net, msg);
-
- return 0;
-}
-
-int mncc_release_ind(struct gsm_network *net, struct gsm_trans *trans,
- uint32_t callref, int location, int value)
-{
- struct gsm_mncc rel;
-
- memset(&rel, 0, sizeof(rel));
- rel.callref = callref;
- mncc_set_cause(&rel, location, value);
- if (trans && trans->cc.state == GSM_CSTATE_RELEASE_REQ)
- return mncc_recvmsg(net, trans, MNCC_REL_CNF, &rel);
- return mncc_recvmsg(net, trans, MNCC_REL_IND, &rel);
-}
-
-/* Call Control Specific transaction release.
- * gets called by trans_free, DO NOT CALL YOURSELF! */
-void _gsm48_cc_trans_free(struct gsm_trans *trans)
-{
- gsm48_stop_cc_timer(trans);
-
- /* send release to L4, if callref still exists */
- if (trans->callref) {
- /* Ressource unavailable */
- mncc_release_ind(trans->net, trans, trans->callref,
- GSM48_CAUSE_LOC_PRN_S_LU,
- GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
- }
- if (trans->cc.state != GSM_CSTATE_NULL)
- new_cc_state(trans, GSM_CSTATE_NULL);
- if (trans->conn)
- trau_mux_unmap(&trans->conn->lchan->ts->e1_link, trans->callref);
-}
-
-static int gsm48_cc_tx_setup(struct gsm_trans *trans, void *arg);
-
-/* call-back from paging the B-end of the connection */
-static int setup_trig_pag_evt(unsigned int hooknum, unsigned int event,
- struct msgb *msg, void *_conn, void *_transt)
-{
- struct gsm_subscriber_connection *conn = _conn;
- struct gsm_trans *transt = _transt;
-
- OSMO_ASSERT(!transt->conn);
-
- /* check all tranactions (without lchan) for subscriber */
- switch (event) {
- case GSM_PAGING_SUCCEEDED:
- DEBUGP(DCC, "Paging subscr %s succeeded!\n", transt->subscr->extension);
- OSMO_ASSERT(conn);
- /* Assign lchan */
- transt->conn = conn;
- /* send SETUP request to called party */
- gsm48_cc_tx_setup(transt, &transt->cc.msg);
- break;
- case GSM_PAGING_EXPIRED:
- case GSM_PAGING_BUSY:
- DEBUGP(DCC, "Paging subscr %s expired!\n",
- transt->subscr->extension);
- /* Temporarily out of order */
- mncc_release_ind(transt->net, transt,
- transt->callref,
- GSM48_CAUSE_LOC_PRN_S_LU,
- GSM48_CC_CAUSE_DEST_OOO);
- transt->callref = 0;
- transt->paging_request = NULL;
- trans_free(transt);
- break;
- default:
- LOGP(DCC, LOGL_ERROR, "Unknown paging event %d\n", event);
- break;
- }
-
- transt->paging_request = NULL;
- return 0;
-}
-
-static int tch_recv_mncc(struct gsm_network *net, uint32_t callref, int enable);
-
-/* handle audio path for handover */
-static int switch_for_handover(struct gsm_lchan *old_lchan,
- struct gsm_lchan *new_lchan)
-{
- struct rtp_socket *old_rs, *new_rs, *other_rs;
-
- /* Ask the new socket to send to the already known port. */
- if (new_lchan->conn->mncc_rtp_bridge) {
- LOGP(DHO, LOGL_DEBUG, "Forwarding RTP\n");
- rsl_ipacc_mdcx(new_lchan,
- old_lchan->abis_ip.connect_ip,
- old_lchan->abis_ip.connect_port, 0);
- return 0;
- }
-
- if (ipacc_rtp_direct) {
- LOGP(DHO, LOGL_ERROR, "unable to handover in direct RTP mode\n");
- return 0;
- }
-
- /* RTP Proxy mode */
- new_rs = new_lchan->abis_ip.rtp_socket;
- old_rs = old_lchan->abis_ip.rtp_socket;
-
- if (!new_rs) {
- LOGP(DHO, LOGL_ERROR, "no RTP socket for new_lchan\n");
- return -EIO;
- }
-
- rsl_ipacc_mdcx_to_rtpsock(new_lchan);
-
- if (!old_rs) {
- LOGP(DHO, LOGL_ERROR, "no RTP socket for old_lchan\n");
- return -EIO;
- }
-
- /* copy rx_action and reference to other sock */
- new_rs->rx_action = old_rs->rx_action;
- new_rs->tx_action = old_rs->tx_action;
- new_rs->transmit = old_rs->transmit;
-
- switch (old_lchan->abis_ip.rtp_socket->rx_action) {
- case RTP_PROXY:
- other_rs = old_rs->proxy.other_sock;
- rtp_socket_proxy(new_rs, other_rs);
- /* delete reference to other end socket to prevent
- * rtp_socket_free() from removing the inverse reference */
- old_rs->proxy.other_sock = NULL;
- break;
- case RTP_RECV_UPSTREAM:
- new_rs->receive = old_rs->receive;
- break;
- case RTP_NONE:
- break;
- }
-
- return 0;
-}
-
-static void maybe_switch_for_handover(struct gsm_lchan *lchan)
-{
- struct gsm_lchan *old_lchan;
- old_lchan = bsc_handover_pending(lchan);
- if (old_lchan)
- switch_for_handover(old_lchan, lchan);
-}
-
-/* some other part of the code sends us a signal */
-static int handle_abisip_signal(unsigned int subsys, unsigned int signal,
- void *handler_data, void *signal_data)
-{
- struct gsm_lchan *lchan = signal_data;
- int rc;
- struct gsm_network *net;
- struct gsm_trans *trans;
-
- if (subsys != SS_ABISIP)
- return 0;
-
- /* RTP bridge handling */
- if (lchan->conn && lchan->conn->mncc_rtp_bridge)
- return tch_rtp_signal(lchan, signal);
-
- /* in case we use direct BTS-to-BTS RTP */
- if (ipacc_rtp_direct)
- return 0;
-
- switch (signal) {
- case S_ABISIP_CRCX_ACK:
- /* in case we don't use direct BTS-to-BTS RTP */
- /* the BTS has successfully bound a TCH to a local ip/port,
- * which means we can connect our UDP socket to it */
- if (lchan->abis_ip.rtp_socket) {
- rtp_socket_free(lchan->abis_ip.rtp_socket);
- lchan->abis_ip.rtp_socket = NULL;
- }
-
- lchan->abis_ip.rtp_socket = rtp_socket_create();
- if (!lchan->abis_ip.rtp_socket)
- return -EIO;
-
- rc = rtp_socket_connect(lchan->abis_ip.rtp_socket,
- lchan->abis_ip.bound_ip,
- lchan->abis_ip.bound_port);
- if (rc < 0)
- return -EIO;
-
- /* check if any transactions on this lchan still have
- * a tch_recv_mncc request pending */
- net = lchan->ts->trx->bts->network;
- llist_for_each_entry(trans, &net->trans_list, entry) {
- if (trans->conn && trans->conn->lchan == lchan && trans->tch_recv) {
- DEBUGP(DCC, "pending tch_recv_mncc request\n");
- tch_recv_mncc(net, trans->callref, 1);
- }
- }
-
- /*
- * TODO: this appears to be too early? Why not until after
- * the handover detect or the handover complete?
- *
- * Do we have a handover pending for this new lchan? In that
- * case re-route the audio from the old channel to the new one.
- */
- maybe_switch_for_handover(lchan);
- break;
- case S_ABISIP_DLCX_IND:
- /* the BTS tells us a RTP stream has been disconnected */
- if (lchan->abis_ip.rtp_socket) {
- rtp_socket_free(lchan->abis_ip.rtp_socket);
- lchan->abis_ip.rtp_socket = NULL;
- }
-
- break;
- }
-
- return 0;
-}
-
-/* map two ipaccess RTP streams onto each other */
-static int tch_map(struct gsm_lchan *lchan, struct gsm_lchan *remote_lchan)
-{
- struct gsm_bts *bts = lchan->ts->trx->bts;
- struct gsm_bts *remote_bts = remote_lchan->ts->trx->bts;
- enum gsm_chan_t lt = lchan->type, rt = remote_lchan->type;
- enum gsm48_chan_mode lm = lchan->tch_mode, rm = remote_lchan->tch_mode;
- int rc;
-
- DEBUGP(DCC, "Setting up TCH map between (bts=%u,trx=%u,ts=%u,%s) and "
- "(bts=%u,trx=%u,ts=%u,%s)\n",
- bts->nr, lchan->ts->trx->nr, lchan->ts->nr,
- get_value_string(gsm_chan_t_names, lt),
- remote_bts->nr, remote_lchan->ts->trx->nr, remote_lchan->ts->nr,
- get_value_string(gsm_chan_t_names, rt));
-
- if (bts->type != remote_bts->type) {
- LOGP(DCC, LOGL_ERROR, "Cannot switch calls between different BTS types yet\n");
- return -EINVAL;
- }
-
- if (lt != rt) {
- LOGP(DCC, LOGL_ERROR, "Cannot patch through call with different"
- " channel types: local = %s, remote = %s\n",
- get_value_string(gsm_chan_t_names, lt),
- get_value_string(gsm_chan_t_names, rt));
- return -EBADSLT;
- }
-
- if (lm != rm) {
- LOGP(DCC, LOGL_ERROR, "Cannot patch through call with different"
- " channel modes: local = %s, remote = %s\n",
- get_value_string(gsm48_chan_mode_names, lm),
- get_value_string(gsm48_chan_mode_names, rm));
- return -EMEDIUMTYPE;
- }
-
- // todo: map between different bts types
- switch (bts->type) {
- case GSM_BTS_TYPE_NANOBTS:
- case GSM_BTS_TYPE_OSMOBTS:
- if (!ipacc_rtp_direct) {
- if (!lchan->abis_ip.rtp_socket) {
- LOGP(DHO, LOGL_ERROR, "no RTP socket for "
- "lchan\n");
- return -EIO;
- }
- if (!remote_lchan->abis_ip.rtp_socket) {
- LOGP(DHO, LOGL_ERROR, "no RTP socket for "
- "remote_lchan\n");
- return -EIO;
- }
-
- /* connect the TCH's to our RTP proxy */
- rc = rsl_ipacc_mdcx_to_rtpsock(lchan);
- if (rc < 0)
- return rc;
- rc = rsl_ipacc_mdcx_to_rtpsock(remote_lchan);
- if (rc < 0)
- return rc;
- /* connect them with each other */
- rtp_socket_proxy(lchan->abis_ip.rtp_socket,
- remote_lchan->abis_ip.rtp_socket);
- } else {
- /* directly connect TCH RTP streams to each other */
- rc = rsl_ipacc_mdcx(lchan, remote_lchan->abis_ip.bound_ip,
- remote_lchan->abis_ip.bound_port,
- remote_lchan->abis_ip.rtp_payload2);
- if (rc < 0)
- return rc;
- rc = rsl_ipacc_mdcx(remote_lchan, lchan->abis_ip.bound_ip,
- lchan->abis_ip.bound_port,
- lchan->abis_ip.rtp_payload2);
- }
- break;
- case GSM_BTS_TYPE_BS11:
- case GSM_BTS_TYPE_RBS2000:
- case GSM_BTS_TYPE_NOKIA_SITE:
- trau_mux_map_lchan(lchan, remote_lchan);
- break;
- default:
- LOGP(DCC, LOGL_ERROR, "Unknown BTS type %u\n", bts->type);
- return -EINVAL;
- }
-
- return 0;
-}
-
-/* bridge channels of two transactions */
-static int tch_bridge(struct gsm_network *net, 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->subscr);
-
- /* through-connect channel */
- return tch_map(trans1->conn->lchan, trans2->conn->lchan);
-}
-
-/* enable receive of channels to MNCC upqueue */
-static int tch_recv_mncc(struct gsm_network *net, uint32_t callref, int enable)
-{
- struct gsm_trans *trans;
- struct gsm_lchan *lchan;
- struct gsm_bts *bts;
- int rc;
-
- /* Find callref */
- trans = trans_find_by_callref(net, callref);
- if (!trans)
- return -EIO;
- if (!trans->conn)
- return 0;
-
- log_set_context(LOG_CTX_VLR_SUBSCR, trans->subscr);
- lchan = trans->conn->lchan;
- bts = lchan->ts->trx->bts;
-
- /* store receive state */
- trans->tch_recv = enable;
-
- switch (bts->type) {
- case GSM_BTS_TYPE_NANOBTS:
- case GSM_BTS_TYPE_OSMOBTS:
- if (ipacc_rtp_direct) {
- LOGP(DCC, LOGL_ERROR, "Error: RTP proxy is disabled\n");
- return -EINVAL;
- }
- /* In case, we don't have a RTP socket to the BTS yet, the BTS
- * will not be connected to our RTP proxy and the socket will
- * not be assigned to the application interface. This method
- * will be called again, once the audio socket is created and
- * connected. */
- if (!lchan->abis_ip.rtp_socket) {
- DEBUGP(DCC, "queue tch_recv_mncc request (%d)\n", enable);
- return 0;
- }
- if (enable) {
- /* connect the TCH's to our RTP proxy */
- rc = rsl_ipacc_mdcx_to_rtpsock(lchan);
- if (rc < 0)
- return rc;
- /* assign socket to application interface */
- rtp_socket_upstream(lchan->abis_ip.rtp_socket,
- net, callref);
- } else
- rtp_socket_upstream(lchan->abis_ip.rtp_socket,
- net, 0);
- break;
- case GSM_BTS_TYPE_BS11:
- case GSM_BTS_TYPE_RBS2000:
- case GSM_BTS_TYPE_NOKIA_SITE:
- /* In case we don't have a TCH with correct mode, the TRAU muxer
- * will not be asigned to the application interface. This is
- * performed by switch_trau_mux() after successful handover or
- * assignment. */
- if (lchan->tch_mode == GSM48_CMODE_SIGN) {
- DEBUGP(DCC, "queue tch_recv_mncc request (%d)\n", enable);
- return 0;
- }
- if (enable)
- return trau_recv_lchan(lchan, callref);
- return trau_mux_unmap(NULL, callref);
- break;
- default:
- LOGP(DCC, LOGL_ERROR, "Unknown BTS type %u\n", bts->type);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int gsm48_cc_rx_status_enq(struct gsm_trans *trans, struct msgb *msg)
-{
- DEBUGP(DCC, "-> STATUS ENQ\n");
- return gsm48_cc_tx_status(trans, msg);
-}
-
-static int gsm48_cc_tx_release(struct gsm_trans *trans, void *arg);
-static int gsm48_cc_tx_disconnect(struct gsm_trans *trans, void *arg);
-
-static void gsm48_cc_timeout(void *arg)
-{
- struct gsm_trans *trans = arg;
- int disconnect = 0, release = 0;
- int mo_cause = GSM48_CC_CAUSE_RECOVERY_TIMER;
- int mo_location = GSM48_CAUSE_LOC_USER;
- int l4_cause = GSM48_CC_CAUSE_NORMAL_UNSPEC;
- int l4_location = GSM48_CAUSE_LOC_PRN_S_LU;
- struct gsm_mncc mo_rel, l4_rel;
-
- memset(&mo_rel, 0, sizeof(struct gsm_mncc));
- mo_rel.callref = trans->callref;
- memset(&l4_rel, 0, sizeof(struct gsm_mncc));
- l4_rel.callref = trans->callref;
-
- switch(trans->cc.Tcurrent) {
- case 0x303:
- release = 1;
- l4_cause = GSM48_CC_CAUSE_USER_NOTRESPOND;
- break;
- case 0x310:
- disconnect = 1;
- l4_cause = GSM48_CC_CAUSE_USER_NOTRESPOND;
- break;
- case 0x313:
- disconnect = 1;
- /* unknown, did not find it in the specs */
- break;
- case 0x301:
- disconnect = 1;
- l4_cause = GSM48_CC_CAUSE_USER_NOTRESPOND;
- break;
- case 0x308:
- if (!trans->cc.T308_second) {
- /* restart T308 a second time */
- gsm48_cc_tx_release(trans, &trans->cc.msg);
- trans->cc.T308_second = 1;
- break; /* stay in release state */
- }
- trans_free(trans);
- return;
-// release = 1;
-// l4_cause = 14;
-// break;
- case 0x306:
- release = 1;
- mo_cause = trans->cc.msg.cause.value;
- mo_location = trans->cc.msg.cause.location;
- break;
- case 0x323:
- disconnect = 1;
- break;
- default:
- release = 1;
- }
-
- if (release && trans->callref) {
- /* process release towards layer 4 */
- mncc_release_ind(trans->net, trans, trans->callref,
- l4_location, l4_cause);
- trans->callref = 0;
- }
-
- if (disconnect && trans->callref) {
- /* process disconnect towards layer 4 */
- mncc_set_cause(&l4_rel, l4_location, l4_cause);
- mncc_recvmsg(trans->net, trans, MNCC_DISC_IND, &l4_rel);
- }
-
- /* process disconnect towards mobile station */
- if (disconnect || release) {
- mncc_set_cause(&mo_rel, mo_location, mo_cause);
- mo_rel.cause.diag[0] = ((trans->cc.Tcurrent & 0xf00) >> 8) + '0';
- mo_rel.cause.diag[1] = ((trans->cc.Tcurrent & 0x0f0) >> 4) + '0';
- mo_rel.cause.diag[2] = (trans->cc.Tcurrent & 0x00f) + '0';
- mo_rel.cause.diag_len = 3;
-
- if (disconnect)
- gsm48_cc_tx_disconnect(trans, &mo_rel);
- if (release)
- gsm48_cc_tx_release(trans, &mo_rel);
- }
-
-}
-
-/* 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;
- setup.lchan_type = trans->conn->lchan->type;
- tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0);
- /* emergency setup is identified by msg_type */
- if (msg_type == GSM48_MT_CC_EMERG_SETUP)
- setup.emergency = 1;
-
- /* use subscriber as calling party number */
- setup.fields |= MNCC_F_CALLING;
- osmo_strlcpy(setup.calling.number, trans->subscr->extension,
- sizeof(setup.calling.number));
- osmo_strlcpy(setup.imsi, trans->subscr->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);
- apply_codec_restrictions(trans->conn->bts, &setup.bearer_cap);
- }
- /* facility */
- if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) {
- setup.fields |= MNCC_F_FACILITY;
- gsm48_decode_facility(&setup.facility,
- TLVP_VAL(&tp, GSM48_IE_FACILITY)-1);
- }
- /* called party bcd number */
- if (TLVP_PRESENT(&tp, GSM48_IE_CALLED_BCD)) {
- setup.fields |= MNCC_F_CALLED;
- gsm48_decode_called(&setup.called,
- TLVP_VAL(&tp, GSM48_IE_CALLED_BCD)-1);
- }
- /* user-user */
- if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) {
- setup.fields |= MNCC_F_USERUSER;
- gsm48_decode_useruser(&setup.useruser,
- TLVP_VAL(&tp, GSM48_IE_USER_USER)-1);
- }
- /* ss-version */
- if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) {
- setup.fields |= MNCC_F_SSVERSION;
- gsm48_decode_ssversion(&setup.ssversion,
- TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1);
- }
- /* CLIR suppression */
- if (TLVP_PRESENT(&tp, GSM48_IE_CLIR_SUPP))
- setup.clir.sup = 1;
- /* CLIR invocation */
- if (TLVP_PRESENT(&tp, GSM48_IE_CLIR_INVOC))
- setup.clir.inv = 1;
- /* cc cap */
- if (TLVP_PRESENT(&tp, GSM48_IE_CC_CAP)) {
- setup.fields |= MNCC_F_CCCAP;
- gsm48_decode_cccap(&setup.cccap,
- TLVP_VAL(&tp, GSM48_IE_CC_CAP)-1);
- }
-
- new_cc_state(trans, GSM_CSTATE_INITIATED);
-
- LOGP(DCC, LOGL_INFO, "Subscriber %s (%s) sends SETUP to %s\n",
- subscr_name(trans->subscr), trans->subscr->extension,
- setup.called.number);
-
- 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->subscr,
- GSM48_PDISC_CC, 0);
- if (trans_id < 0) {
- /* no free transaction ID */
- rc = mncc_release_ind(trans->net, trans, trans->callref,
- GSM48_CAUSE_LOC_PRN_S_LU,
- GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
- trans->callref = 0;
- trans_free(trans);
- return rc;
- }
- trans->transaction_id = trans_id;
-
- gh->msg_type = GSM48_MT_CC_SETUP;
-
- gsm48_start_cc_timer(trans, 0x303, GSM48_T303);
-
- /* bearer capability */
- if (setup->fields & MNCC_F_BEARER_CAP)
- gsm48_encode_bearer_cap(msg, 0, &setup->bearer_cap);
- /* facility */
- if (setup->fields & MNCC_F_FACILITY)
- gsm48_encode_facility(msg, 0, &setup->facility);
- /* progress */
- if (setup->fields & MNCC_F_PROGRESS)
- gsm48_encode_progress(msg, 0, &setup->progress);
- /* calling party BCD number */
- if (setup->fields & MNCC_F_CALLING)
- gsm48_encode_calling(msg, &setup->calling);
- /* called party BCD number */
- if (setup->fields & MNCC_F_CALLED)
- gsm48_encode_called(msg, &setup->called);
- /* user-user */
- if (setup->fields & MNCC_F_USERUSER)
- gsm48_encode_useruser(msg, 0, &setup->useruser);
- /* redirecting party BCD number */
- if (setup->fields & MNCC_F_REDIRECTING)
- gsm48_encode_redirecting(msg, &setup->redirecting);
- /* signal */
- if (setup->fields & MNCC_F_SIGNAL)
- gsm48_encode_signal(msg, setup->signal);
-
- new_cc_state(trans, GSM_CSTATE_CALL_PRESENT);
-
- 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;
-
- gsm48_stop_cc_timer(trans);
- gsm48_start_cc_timer(trans, 0x310, GSM48_T310);
-
- memset(&call_conf, 0, sizeof(struct gsm_mncc));
- call_conf.callref = trans->callref;
- call_conf.lchan_type = trans->conn->lchan->type;
- tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0);
-#if 0
- /* repeat */
- if (TLVP_PRESENT(&tp, GSM48_IE_REPEAT_CIR))
- call_conf.repeat = 1;
- if (TLVP_PRESENT(&tp, GSM48_IE_REPEAT_SEQ))
- call_conf.repeat = 2;
-#endif
- /* bearer capability */
- if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) {
- call_conf.fields |= MNCC_F_BEARER_CAP;
- gsm48_decode_bearer_cap(&call_conf.bearer_cap,
- TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1);
- apply_codec_restrictions(trans->conn->bts, &call_conf.bearer_cap);
- }
- /* cause */
- if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) {
- call_conf.fields |= MNCC_F_CAUSE;
- gsm48_decode_cause(&call_conf.cause,
- TLVP_VAL(&tp, GSM48_IE_CAUSE)-1);
- }
- /* cc cap */
- if (TLVP_PRESENT(&tp, GSM48_IE_CC_CAP)) {
- call_conf.fields |= MNCC_F_CCCAP;
- gsm48_decode_cccap(&call_conf.cccap,
- TLVP_VAL(&tp, GSM48_IE_CC_CAP)-1);
- }
-
- /* IMSI of called subscriber */
- osmo_strlcpy(call_conf.imsi, trans->subscr->imsi,
- sizeof(call_conf.imsi));
-
- new_cc_state(trans, GSM_CSTATE_MO_TERM_CALL_CONF);
-
- return mncc_recvmsg(trans->net, trans, MNCC_CALL_CONF_IND,
- &call_conf);
-}
-
-static int gsm48_cc_tx_call_proc(struct gsm_trans *trans, void *arg)
-{
- struct gsm_mncc *proceeding = arg;
- struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC PROC");
- struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
-
- gh->msg_type = GSM48_MT_CC_CALL_PROC;
-
- new_cc_state(trans, GSM_CSTATE_MO_CALL_PROC);
-
- /* bearer capability */
- if (proceeding->fields & MNCC_F_BEARER_CAP)
- gsm48_encode_bearer_cap(msg, 0, &proceeding->bearer_cap);
- /* facility */
- if (proceeding->fields & MNCC_F_FACILITY)
- gsm48_encode_facility(msg, 0, &proceeding->facility);
- /* progress */
- if (proceeding->fields & MNCC_F_PROGRESS)
- gsm48_encode_progress(msg, 0, &proceeding->progress);
-
- return gsm48_conn_sendmsg(msg, trans->conn, trans);
-}
-
-static int gsm48_cc_rx_alerting(struct gsm_trans *trans, struct msgb *msg)
-{
- struct gsm48_hdr *gh = msgb_l3(msg);
- unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
- struct tlv_parsed tp;
- struct gsm_mncc alerting;
-
- gsm48_stop_cc_timer(trans);
- gsm48_start_cc_timer(trans, 0x301, GSM48_T301);
-
- memset(&alerting, 0, sizeof(struct gsm_mncc));
- alerting.callref = trans->callref;
- tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0);
- /* facility */
- if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) {
- alerting.fields |= MNCC_F_FACILITY;
- gsm48_decode_facility(&alerting.facility,
- TLVP_VAL(&tp, GSM48_IE_FACILITY)-1);
- }
-
- /* progress */
- if (TLVP_PRESENT(&tp, GSM48_IE_PROGR_IND)) {
- alerting.fields |= MNCC_F_PROGRESS;
- gsm48_decode_progress(&alerting.progress,
- TLVP_VAL(&tp, GSM48_IE_PROGR_IND)-1);
- }
- /* ss-version */
- if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) {
- alerting.fields |= MNCC_F_SSVERSION;
- gsm48_decode_ssversion(&alerting.ssversion,
- TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1);
- }
-
- new_cc_state(trans, GSM_CSTATE_CALL_RECEIVED);
-
- return mncc_recvmsg(trans->net, trans, MNCC_ALERT_IND,
- &alerting);
-}
-
-static int gsm48_cc_tx_alerting(struct gsm_trans *trans, void *arg)
-{
- struct gsm_mncc *alerting = arg;
- struct msgb *msg = gsm48_msgb_alloc_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->subscr->extension,
- sizeof(connect.connected.number));
- osmo_strlcpy(connect.imsi, trans->subscr->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);
- apply_codec_restrictions(trans->conn->bts, &modify.bearer_cap);
- }
-
- new_cc_state(trans, GSM_CSTATE_MO_ORIG_MODIFY);
-
- return mncc_recvmsg(trans->net, trans, MNCC_MODIFY_IND, &modify);
-}
-
-static int gsm48_cc_tx_modify(struct gsm_trans *trans, void *arg)
-{
- struct gsm_mncc *modify = arg;
- struct msgb *msg = gsm48_msgb_alloc_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);
- apply_codec_restrictions(trans->conn->bts, &modify.bearer_cap);
- }
-
- new_cc_state(trans, GSM_CSTATE_ACTIVE);
-
- return mncc_recvmsg(trans->net, trans, MNCC_MODIFY_CNF, &modify);
-}
-
-static int gsm48_cc_tx_modify_complete(struct gsm_trans *trans, void *arg)
-{
- struct gsm_mncc *modify = arg;
- struct msgb *msg = gsm48_msgb_alloc_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);
- apply_codec_restrictions(trans->conn->bts, &modify.bearer_cap);
- }
- /* cause */
- if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) {
- modify.fields |= MNCC_F_CAUSE;
- gsm48_decode_cause(&modify.cause,
- TLVP_VAL(&tp, GSM48_IE_CAUSE)-1);
- }
-
- new_cc_state(trans, GSM_CSTATE_ACTIVE);
-
- return mncc_recvmsg(trans->net, trans, MNCC_MODIFY_REJ, &modify);
-}
-
-static int gsm48_cc_tx_modify_reject(struct gsm_trans *trans, void *arg)
-{
- struct gsm_mncc *modify = arg;
- struct msgb *msg = gsm48_msgb_alloc_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 int _gsm48_lchan_modify(struct gsm_trans *trans, void *arg)
-{
- struct gsm_mncc *mode = arg;
- struct gsm_lchan *lchan = trans->conn->lchan;
-
- /*
- * We were forced to make an assignment a lot earlier and
- * we should avoid sending another assignment that might
- * even lead to a different kind of lchan (TCH/F vs. TCH/H).
- * In case of rtp-bridge it is too late to change things
- * here.
- */
- if (trans->conn->mncc_rtp_bridge && lchan->tch_mode != GSM48_CMODE_SIGN)
- return 0;
-
- return gsm0808_assign_req(trans->conn, mode->lchan_mode,
- trans->conn->lchan->type != GSM_LCHAN_TCH_H);
-}
-
-static void mncc_recv_rtp(struct gsm_network *net, uint32_t callref,
- int cmd, uint32_t addr, uint16_t port, uint32_t payload_type,
- uint32_t payload_msg_type)
-{
- uint8_t data[sizeof(struct gsm_mncc)];
- struct gsm_mncc_rtp *rtp;
-
- memset(&data, 0, sizeof(data));
- rtp = (struct gsm_mncc_rtp *) &data[0];
-
- rtp->callref = callref;
- rtp->msg_type = cmd;
- rtp->ip = addr;
- rtp->port = port;
- rtp->payload_type = payload_type;
- rtp->payload_msg_type = payload_msg_type;
- mncc_recvmsg(net, NULL, cmd, (struct gsm_mncc *)data);
-}
-
-static void mncc_recv_rtp_sock(struct gsm_network *net, struct gsm_trans *trans, int cmd)
-{
- struct gsm_lchan *lchan;
- int msg_type;
-
- lchan = trans->conn->lchan;
- switch (lchan->abis_ip.rtp_payload) {
- case RTP_PT_GSM_FULL:
- msg_type = GSM_TCHF_FRAME;
- break;
- case RTP_PT_GSM_EFR:
- msg_type = GSM_TCHF_FRAME_EFR;
- break;
- case RTP_PT_GSM_HALF:
- msg_type = GSM_TCHH_FRAME;
- break;
- case RTP_PT_AMR:
- msg_type = GSM_TCH_FRAME_AMR;
- break;
- default:
- LOGP(DMNCC, LOGL_ERROR, "%s unknown payload type %d\n",
- gsm_lchan_name(lchan), lchan->abis_ip.rtp_payload);
- msg_type = 0;
- break;
- }
-
- return mncc_recv_rtp(net, trans->callref, cmd,
- lchan->abis_ip.bound_ip,
- lchan->abis_ip.bound_port,
- lchan->abis_ip.rtp_payload,
- msg_type);
-}
-
-static void mncc_recv_rtp_err(struct gsm_network *net, uint32_t callref, int cmd)
-{
- return mncc_recv_rtp(net, callref, cmd, 0, 0, 0, 0);
-}
-
-static int tch_rtp_create(struct gsm_network *net, uint32_t callref)
-{
- struct gsm_bts *bts;
- struct gsm_lchan *lchan;
- struct gsm_trans *trans;
- enum gsm48_chan_mode m;
-
- /* 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->subscr);
- if (!trans->conn) {
- LOGP(DMNCC, LOGL_NOTICE, "RTP create for trans without conn\n");
- mncc_recv_rtp_err(net, callref, MNCC_RTP_CREATE);
- return 0;
- }
-
- lchan = trans->conn->lchan;
- bts = lchan->ts->trx->bts;
- if (!is_ipaccess_bts(bts)) {
- /*
- * I want this to be straight forward and have no audio flow
- * through the nitb/osmo-mss system. This currently means that
- * this will not work with BS11/Nokia type BTS. We would need
- * to have a trau<->rtp bridge for these but still preferable
- * in another process.
- */
- LOGP(DMNCC, LOGL_ERROR, "RTP create only works with IP systems\n");
- mncc_recv_rtp_err(net, callref, MNCC_RTP_CREATE);
- return -EINVAL;
- }
-
- trans->conn->mncc_rtp_bridge = 1;
- /*
- * *sigh* we need to pick a codec now. Pick the most generic one
- * right now and hope we could fix that later on. This is very
- * similiar to the routine above.
- * Fallback to the internal MNCC mode to select a route.
- */
- if (lchan->tch_mode == GSM48_CMODE_SIGN) {
- trans->conn->mncc_rtp_create_pending = 1;
- m = mncc_codec_for_mode(lchan->type);
- LOGP(DMNCC, LOGL_DEBUG, "RTP create: codec=%s, chan_type=%s\n",
- get_value_string(gsm48_chan_mode_names, m),
- get_value_string(gsm_chan_t_names, lchan->type));
- return gsm0808_assign_req(trans->conn, m,
- lchan->type != GSM_LCHAN_TCH_H);
- }
-
- mncc_recv_rtp_sock(trans->net, trans, MNCC_RTP_CREATE);
- return 0;
-}
-
-static int tch_rtp_connect(struct gsm_network *net, void *arg)
-{
- struct gsm_lchan *lchan;
- struct gsm_trans *trans;
- struct gsm_mncc_rtp *rtp = arg;
-
- /* Find callref */
- trans = trans_find_by_callref(net, rtp->callref);
- if (!trans) {
- LOGP(DMNCC, LOGL_ERROR, "RTP connect for non-existing trans\n");
- mncc_recv_rtp_err(net, rtp->callref, MNCC_RTP_CONNECT);
- return -EIO;
- }
- log_set_context(LOG_CTX_VLR_SUBSCR, trans->subscr);
- if (!trans->conn) {
- LOGP(DMNCC, LOGL_ERROR, "RTP connect for trans without conn\n");
- mncc_recv_rtp_err(net, rtp->callref, MNCC_RTP_CONNECT);
- return 0;
- }
-
- lchan = trans->conn->lchan;
- LOGP(DMNCC, LOGL_DEBUG, "RTP connect: codec=%s, chan_type=%s\n",
- get_value_string(gsm48_chan_mode_names,
- mncc_codec_for_mode(lchan->type)),
- get_value_string(gsm_chan_t_names, lchan->type));
-
- /* TODO: Check if payload_msg_type is compatible with what we have */
- if (rtp->payload_type != lchan->abis_ip.rtp_payload) {
- LOGP(DMNCC, LOGL_ERROR, "RTP connect with different RTP payload\n");
- mncc_recv_rtp_err(net, rtp->callref, MNCC_RTP_CONNECT);
- }
-
- /*
- * FIXME: payload2 can't be sent with MDCX as the osmo-bts code
- * complains about both rtp and rtp payload2 being present in the
- * same package!
- */
- trans->conn->mncc_rtp_connect_pending = 1;
- return rsl_ipacc_mdcx(lchan, rtp->ip, rtp->port, 0);
-}
-
-static int tch_rtp_signal(struct gsm_lchan *lchan, int signal)
-{
- struct gsm_network *net;
- struct gsm_trans *tmp, *trans = NULL;
-
- net = lchan->ts->trx->bts->network;
- llist_for_each_entry(tmp, &net->trans_list, entry) {
- if (!tmp->conn)
- continue;
- if (tmp->conn->lchan != lchan && tmp->conn->ho_lchan != lchan)
- continue;
- trans = tmp;
- break;
- }
-
- if (!trans) {
- LOGP(DMNCC, LOGL_ERROR, "%s IPA abis signal but no transaction.\n",
- gsm_lchan_name(lchan));
- return 0;
- }
-
- switch (signal) {
- case S_ABISIP_CRCX_ACK:
- if (lchan->conn->mncc_rtp_create_pending) {
- lchan->conn->mncc_rtp_create_pending = 0;
- LOGP(DMNCC, LOGL_NOTICE, "%s sending pending RTP create ind.\n",
- gsm_lchan_name(lchan));
- mncc_recv_rtp_sock(net, trans, MNCC_RTP_CREATE);
- }
- /*
- * TODO: this appears to be too early? Why not until after
- * the handover detect or the handover complete?
- */
- maybe_switch_for_handover(lchan);
- break;
- case S_ABISIP_MDCX_ACK:
- if (lchan->conn->mncc_rtp_connect_pending) {
- lchan->conn->mncc_rtp_connect_pending = 0;
- LOGP(DMNCC, LOGL_NOTICE, "%s sending pending RTP connect ind.\n",
- gsm_lchan_name(lchan));
- mncc_recv_rtp_sock(net, trans, MNCC_RTP_CONNECT);
- }
- break;
- }
-
- return 0;
-}
-
-
-static struct downstate {
- uint32_t states;
- int type;
- int (*rout) (struct gsm_trans *trans, void *arg);
-} downstatelist[] = {
- /* mobile originating call establishment */
- {SBIT(GSM_CSTATE_INITIATED), /* 5.2.1.2 */
- MNCC_CALL_PROC_REQ, gsm48_cc_tx_call_proc},
- {SBIT(GSM_CSTATE_INITIATED) | SBIT(GSM_CSTATE_MO_CALL_PROC), /* 5.2.1.2 | 5.2.1.5 */
- MNCC_ALERT_REQ, gsm48_cc_tx_alerting},
- {SBIT(GSM_CSTATE_INITIATED) | SBIT(GSM_CSTATE_MO_CALL_PROC) | SBIT(GSM_CSTATE_CALL_DELIVERED), /* 5.2.1.2 | 5.2.1.6 | 5.2.1.6 */
- MNCC_SETUP_RSP, gsm48_cc_tx_connect},
- {SBIT(GSM_CSTATE_MO_CALL_PROC), /* 5.2.1.4.2 */
- MNCC_PROGRESS_REQ, gsm48_cc_tx_progress},
- /* mobile terminating call establishment */
- {SBIT(GSM_CSTATE_NULL), /* 5.2.2.1 */
- MNCC_SETUP_REQ, gsm48_cc_tx_setup},
- {SBIT(GSM_CSTATE_CONNECT_REQUEST),
- MNCC_SETUP_COMPL_REQ, gsm48_cc_tx_connect_ack},
- /* signalling during call */
- {SBIT(GSM_CSTATE_ACTIVE),
- MNCC_NOTIFY_REQ, gsm48_cc_tx_notify},
- {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_RELEASE_REQ),
- MNCC_FACILITY_REQ, gsm48_cc_tx_facility},
- {ALL_STATES,
- MNCC_START_DTMF_RSP, gsm48_cc_tx_start_dtmf_ack},
- {ALL_STATES,
- MNCC_START_DTMF_REJ, gsm48_cc_tx_start_dtmf_rej},
- {ALL_STATES,
- MNCC_STOP_DTMF_RSP, gsm48_cc_tx_stop_dtmf_ack},
- {SBIT(GSM_CSTATE_ACTIVE),
- MNCC_HOLD_CNF, gsm48_cc_tx_hold_ack},
- {SBIT(GSM_CSTATE_ACTIVE),
- MNCC_HOLD_REJ, gsm48_cc_tx_hold_rej},
- {SBIT(GSM_CSTATE_ACTIVE),
- MNCC_RETRIEVE_CNF, gsm48_cc_tx_retrieve_ack},
- {SBIT(GSM_CSTATE_ACTIVE),
- MNCC_RETRIEVE_REJ, gsm48_cc_tx_retrieve_rej},
- {SBIT(GSM_CSTATE_ACTIVE),
- MNCC_MODIFY_REQ, gsm48_cc_tx_modify},
- {SBIT(GSM_CSTATE_MO_ORIG_MODIFY),
- MNCC_MODIFY_RSP, gsm48_cc_tx_modify_complete},
- {SBIT(GSM_CSTATE_MO_ORIG_MODIFY),
- MNCC_MODIFY_REJ, gsm48_cc_tx_modify_reject},
- {SBIT(GSM_CSTATE_ACTIVE),
- MNCC_USERINFO_REQ, gsm48_cc_tx_userinfo},
- /* clearing */
- {SBIT(GSM_CSTATE_INITIATED),
- MNCC_REJ_REQ, gsm48_cc_tx_release_compl},
- {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_DISCONNECT_IND) - SBIT(GSM_CSTATE_RELEASE_REQ) - SBIT(GSM_CSTATE_DISCONNECT_REQ), /* 5.4.4 */
- MNCC_DISC_REQ, gsm48_cc_tx_disconnect},
- {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_RELEASE_REQ), /* 5.4.3.2 */
- MNCC_REL_REQ, gsm48_cc_tx_release},
- /* special */
- {ALL_STATES,
- MNCC_LCHAN_MODIFY, _gsm48_lchan_modify},
-};
-
-#define DOWNSLLEN \
- (sizeof(downstatelist) / sizeof(struct downstate))
-
-
-int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg)
-{
- int i, rc = 0;
- struct gsm_trans *trans = NULL, *transt;
- struct gsm_subscriber_connection *conn = NULL;
- struct gsm_bts *bts = NULL;
- struct gsm_mncc *data = arg, rel;
-
- DEBUGP(DMNCC, "receive message %s\n", get_mncc_name(msg_type));
-
- /* handle special messages */
- switch(msg_type) {
- case MNCC_BRIDGE:
- rc = tch_bridge(net, arg);
- if (rc < 0)
- disconnect_bridge(net, arg, -rc);
- return rc;
- case MNCC_FRAME_DROP:
- return tch_recv_mncc(net, data->callref, 0);
- case MNCC_FRAME_RECV:
- return tch_recv_mncc(net, data->callref, 1);
- case MNCC_RTP_CREATE:
- return tch_rtp_create(net, data->callref);
- case MNCC_RTP_CONNECT:
- return tch_rtp_connect(net, arg);
- case MNCC_RTP_FREE:
- /* unused right now */
- return -EIO;
- case GSM_TCHF_FRAME:
- case GSM_TCHF_FRAME_EFR:
- case GSM_TCHH_FRAME:
- case GSM_TCH_FRAME_AMR:
- /* Find callref */
- trans = trans_find_by_callref(net, data->callref);
- if (!trans) {
- LOGP(DMNCC, LOGL_ERROR, "TCH frame for non-existing trans\n");
- return -EIO;
- }
- log_set_context(LOG_CTX_VLR_SUBSCR, trans->subscr);
- if (!trans->conn) {
- LOGP(DMNCC, LOGL_NOTICE, "TCH frame for trans without conn\n");
- return 0;
- }
- if (!trans->conn->lchan) {
- LOGP(DMNCC, LOGL_NOTICE, "TCH frame for trans without lchan\n");
- return 0;
- }
- if (trans->conn->lchan->type != GSM_LCHAN_TCH_F
- && trans->conn->lchan->type != GSM_LCHAN_TCH_H) {
- /* This should be LOGL_ERROR or NOTICE, but
- * unfortuantely it happens for a couple of frames at
- * the beginning of every RTP connection */
- LOGP(DMNCC, LOGL_DEBUG, "TCH frame for lchan != TCH_F/TCH_H\n");
- return 0;
- }
- bts = trans->conn->lchan->ts->trx->bts;
- switch (bts->type) {
- case GSM_BTS_TYPE_NANOBTS:
- case GSM_BTS_TYPE_OSMOBTS:
- if (!trans->conn->lchan->abis_ip.rtp_socket) {
- DEBUGP(DMNCC, "TCH frame to lchan without RTP connection\n");
- return 0;
- }
- return rtp_send_frame(trans->conn->lchan->abis_ip.rtp_socket, arg);
- case GSM_BTS_TYPE_BS11:
- case GSM_BTS_TYPE_RBS2000:
- case GSM_BTS_TYPE_NOKIA_SITE:
- return trau_send_frame(trans->conn->lchan, arg);
- default:
- LOGP(DCC, LOGL_ERROR, "Unknown BTS type %u\n", bts->type);
- }
- return -EINVAL;
- }
-
- memset(&rel, 0, sizeof(struct gsm_mncc));
- rel.callref = data->callref;
-
- /* Find callref */
- trans = trans_find_by_callref(net, data->callref);
-
- /* Callref unknown */
- if (!trans) {
- struct gsm_subscriber *subscr;
-
- if (msg_type != MNCC_SETUP_REQ) {
- DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) "
- "Received '%s' from MNCC with "
- "unknown callref %d\n", data->called.number,
- get_mncc_name(msg_type), data->callref);
- /* Invalid call reference */
- return mncc_release_ind(net, NULL, data->callref,
- GSM48_CAUSE_LOC_PRN_S_LU,
- GSM48_CC_CAUSE_INVAL_TRANS_ID);
- }
- if (!data->called.number[0] && !data->imsi[0]) {
- DEBUGP(DCC, "(bts - trx - ts - ti) "
- "Received '%s' from MNCC with "
- "no number or IMSI\n", get_mncc_name(msg_type));
- /* Invalid number */
- return mncc_release_ind(net, NULL, data->callref,
- GSM48_CAUSE_LOC_PRN_S_LU,
- GSM48_CC_CAUSE_INV_NR_FORMAT);
- }
- /* New transaction due to setup, find subscriber */
- if (data->called.number[0])
- subscr = subscr_get_by_extension(net->subscr_group,
- data->called.number);
- else
- subscr = subscr_get_by_imsi(net->subscr_group,
- data->imsi);
-
- /* update the subscriber we deal with */
- log_set_context(LOG_CTX_VLR_SUBSCR, subscr);
-
- /* If subscriber is not found */
- if (!subscr) {
- DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) "
- "Received '%s' from MNCC with "
- "unknown subscriber %s\n", data->called.number,
- get_mncc_name(msg_type), data->called.number);
- /* Unknown subscriber */
- return mncc_release_ind(net, NULL, data->callref,
- GSM48_CAUSE_LOC_PRN_S_LU,
- GSM48_CC_CAUSE_UNASSIGNED_NR);
- }
- /* If subscriber is not "attached" */
- if (!subscr->lac) {
- DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) "
- "Received '%s' from MNCC with "
- "detached subscriber %s\n", data->called.number,
- get_mncc_name(msg_type), data->called.number);
- subscr_put(subscr);
- /* Temporarily out of order */
- return mncc_release_ind(net, NULL, data->callref,
- GSM48_CAUSE_LOC_PRN_S_LU,
- GSM48_CC_CAUSE_DEST_OOO);
- }
- /* Create transaction */
- trans = trans_alloc(net, subscr, GSM48_PDISC_CC, 0xff, data->callref);
- if (!trans) {
- DEBUGP(DCC, "No memory for trans.\n");
- subscr_put(subscr);
- /* Ressource unavailable */
- mncc_release_ind(net, NULL, data->callref,
- GSM48_CAUSE_LOC_PRN_S_LU,
- GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
- return -ENOMEM;
- }
- /* Find lchan */
- conn = connection_for_subscr(subscr);
-
- /* If subscriber has no lchan */
- if (!conn) {
- /* find transaction with this subscriber already paging */
- llist_for_each_entry(transt, &net->trans_list, entry) {
- /* Transaction of our lchan? */
- if (transt == trans ||
- transt->subscr != subscr)
- continue;
- DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) "
- "Received '%s' from MNCC with "
- "unallocated channel, paging already "
- "started for lac %d.\n",
- data->called.number,
- get_mncc_name(msg_type), subscr->lac);
- subscr_put(subscr);
- trans_free(trans);
- return 0;
- }
- /* store setup informations until paging was successfull */
- memcpy(&trans->cc.msg, data, sizeof(struct gsm_mncc));
-
- /* Request a channel */
- trans->paging_request = subscr_request_channel(subscr,
- RSL_CHANNEED_TCH_F, setup_trig_pag_evt,
- trans);
- if (!trans->paging_request) {
- LOGP(DCC, LOGL_ERROR, "Failed to allocate paging token.\n");
- subscr_put(subscr);
- trans_free(trans);
- return 0;
- }
- subscr_put(subscr);
- return 0;
- }
- /* Assign lchan */
- trans->conn = conn;
- subscr_put(subscr);
- } else {
- /* update the subscriber we deal with */
- log_set_context(LOG_CTX_VLR_SUBSCR, trans->subscr);
- }
-
- if (trans->conn)
- conn = trans->conn;
-
- /* if paging did not respond yet */
- if (!conn) {
- DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) "
- "Received '%s' from MNCC in paging state\n",
- (trans->subscr)?(trans->subscr->extension):"-",
- get_mncc_name(msg_type));
- mncc_set_cause(&rel, GSM48_CAUSE_LOC_PRN_S_LU,
- GSM48_CC_CAUSE_NORM_CALL_CLEAR);
- if (msg_type == MNCC_REL_REQ)
- rc = mncc_recvmsg(net, trans, MNCC_REL_CNF, &rel);
- else
- rc = mncc_recvmsg(net, trans, MNCC_REL_IND, &rel);
- trans->callref = 0;
- trans_free(trans);
- return rc;
- }
-
- DEBUGP(DCC, "(bts %d trx %d ts %d ti %02x sub %s) "
- "Received '%s' from MNCC in state %d (%s)\n",
- conn->bts->nr, conn->lchan->ts->trx->nr, conn->lchan->ts->nr,
- trans->transaction_id,
- (trans->conn->subscr)?(trans->conn->subscr->extension):"-",
- get_mncc_name(msg_type), trans->cc.state,
- gsm48_cc_state_name(trans->cc.state));
-
- /* Find function for current state and message */
- for (i = 0; i < DOWNSLLEN; i++)
- if ((msg_type == downstatelist[i].type)
- && ((1 << trans->cc.state) & downstatelist[i].states))
- break;
- if (i == DOWNSLLEN) {
- DEBUGP(DCC, "Message unhandled at this state.\n");
- return 0;
- }
-
- rc = downstatelist[i].rout(trans, arg);
-
- return rc;
-}
-
-
-static struct datastate {
- uint32_t states;
- int type;
- int (*rout) (struct gsm_trans *trans, struct msgb *msg);
-} datastatelist[] = {
- /* mobile originating call establishment */
- {SBIT(GSM_CSTATE_NULL), /* 5.2.1.2 */
- GSM48_MT_CC_SETUP, gsm48_cc_rx_setup},
- {SBIT(GSM_CSTATE_NULL), /* 5.2.1.2 */
- GSM48_MT_CC_EMERG_SETUP, gsm48_cc_rx_setup},
- {SBIT(GSM_CSTATE_CONNECT_IND), /* 5.2.1.2 */
- GSM48_MT_CC_CONNECT_ACK, gsm48_cc_rx_connect_ack},
- /* mobile terminating call establishment */
- {SBIT(GSM_CSTATE_CALL_PRESENT), /* 5.2.2.3.2 */
- GSM48_MT_CC_CALL_CONF, gsm48_cc_rx_call_conf},
- {SBIT(GSM_CSTATE_CALL_PRESENT) | SBIT(GSM_CSTATE_MO_TERM_CALL_CONF), /* ???? | 5.2.2.3.2 */
- GSM48_MT_CC_ALERTING, gsm48_cc_rx_alerting},
- {SBIT(GSM_CSTATE_CALL_PRESENT) | SBIT(GSM_CSTATE_MO_TERM_CALL_CONF) | SBIT(GSM_CSTATE_CALL_RECEIVED), /* (5.2.2.6) | 5.2.2.6 | 5.2.2.6 */
- GSM48_MT_CC_CONNECT, gsm48_cc_rx_connect},
- /* signalling during call */
- {ALL_STATES - SBIT(GSM_CSTATE_NULL),
- GSM48_MT_CC_FACILITY, gsm48_cc_rx_facility},
- {SBIT(GSM_CSTATE_ACTIVE),
- GSM48_MT_CC_NOTIFY, gsm48_cc_rx_notify},
- {ALL_STATES,
- GSM48_MT_CC_START_DTMF, gsm48_cc_rx_start_dtmf},
- {ALL_STATES,
- GSM48_MT_CC_STOP_DTMF, gsm48_cc_rx_stop_dtmf},
- {ALL_STATES,
- GSM48_MT_CC_STATUS_ENQ, gsm48_cc_rx_status_enq},
- {SBIT(GSM_CSTATE_ACTIVE),
- GSM48_MT_CC_HOLD, gsm48_cc_rx_hold},
- {SBIT(GSM_CSTATE_ACTIVE),
- GSM48_MT_CC_RETR, gsm48_cc_rx_retrieve},
- {SBIT(GSM_CSTATE_ACTIVE),
- GSM48_MT_CC_MODIFY, gsm48_cc_rx_modify},
- {SBIT(GSM_CSTATE_MO_TERM_MODIFY),
- GSM48_MT_CC_MODIFY_COMPL, gsm48_cc_rx_modify_complete},
- {SBIT(GSM_CSTATE_MO_TERM_MODIFY),
- GSM48_MT_CC_MODIFY_REJECT, gsm48_cc_rx_modify_reject},
- {SBIT(GSM_CSTATE_ACTIVE),
- GSM48_MT_CC_USER_INFO, gsm48_cc_rx_userinfo},
- /* clearing */
- {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_RELEASE_REQ), /* 5.4.3.2 */
- GSM48_MT_CC_DISCONNECT, gsm48_cc_rx_disconnect},
- {ALL_STATES - SBIT(GSM_CSTATE_NULL), /* 5.4.4.1.2.2 */
- GSM48_MT_CC_RELEASE, gsm48_cc_rx_release},
- {ALL_STATES, /* 5.4.3.4 */
- GSM48_MT_CC_RELEASE_COMPL, gsm48_cc_rx_release_compl},
-};
-
-#define DATASLLEN \
- (sizeof(datastatelist) / sizeof(struct datastate))
-
-static int gsm0408_rcv_cc(struct gsm_subscriber_connection *conn, struct msgb *msg)
-{
- struct gsm48_hdr *gh = msgb_l3(msg);
- uint8_t msg_type = 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->subscr) {
- LOGP(DCC, LOGL_ERROR, "Invalid conn, no subscriber\n");
- return -EINVAL;
- }
-
- /* Find transaction */
- trans = trans_find_by_id(conn, GSM48_PDISC_CC, transaction_id);
-
- DEBUGP(DCC, "(bts %d trx %d ts %d ti %x sub %s) "
- "Received '%s' from MS in state %d (%s)\n",
- conn->bts->nr, conn->lchan->ts->trx->nr, conn->lchan->ts->nr,
- transaction_id, (conn->subscr)?(conn->subscr->extension):"-",
- gsm48_cc_msg_name(msg_type), trans?(trans->cc.state):0,
- gsm48_cc_state_name(trans?(trans->cc.state):0));
-
- /* Create transaction */
- if (!trans) {
- DEBUGP(DCC, "Unknown transaction ID %x, "
- "creating new trans.\n", transaction_id);
- /* Create transaction */
- trans = trans_alloc(conn->network, conn->subscr,
- GSM48_PDISC_CC,
- transaction_id, new_callref++);
- if (!trans) {
- DEBUGP(DCC, "No memory for trans.\n");
- rc = gsm48_tx_simple(conn,
- GSM48_PDISC_CC | (transaction_id << 4),
- GSM48_MT_CC_RELEASE_COMPL);
- return -ENOMEM;
- }
- /* Assign transaction */
- trans->conn = conn;
- }
-
- /* find function for current state and message */
- for (i = 0; i < DATASLLEN; i++)
- if ((msg_type == datastatelist[i].type)
- && ((1 << trans->cc.state) & datastatelist[i].states))
- break;
- if (i == DATASLLEN) {
- DEBUGP(DCC, "Message unhandled at this state.\n");
- return 0;
- }
-
- assert(trans->subscr);
-
- rc = datastatelist[i].rout(trans, msg);
-
- return rc;
-}
-
-/* Create a dummy to wait five seconds */
-static void release_anchor(struct gsm_subscriber_connection *conn)
-{
- if (!conn->anch_operation)
- return;
-
- osmo_timer_del(&conn->anch_operation->timeout);
- talloc_free(conn->anch_operation);
- conn->anch_operation = NULL;
-}
-
-static void anchor_timeout(void *_data)
-{
- struct gsm_subscriber_connection *con = _data;
-
- release_anchor(con);
- msc_release_connection(con);
-}
-
-int gsm0408_new_conn(struct gsm_subscriber_connection *conn)
-{
- conn->anch_operation = talloc_zero(conn, struct gsm_anchor_operation);
- if (!conn->anch_operation)
- return -1;
-
- osmo_timer_setup(&conn->anch_operation->timeout, anchor_timeout, conn);
- osmo_timer_schedule(&conn->anch_operation->timeout, 5, 0);
- return 0;
-}
-
-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_con_free(struct gsm_subscriber_connection *conn)
-{
- if (!conn)
- return;
-
- if (conn->subscr) {
- subscr_put(conn->subscr);
- conn->subscr = NULL;
- }
-
- llist_del(&conn->entry);
- talloc_free(conn);
-}
-
-/* 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, pdisc=%d\n", pdisc);
-#if 0
- if (silent_call_reroute(conn, msg))
- return silent_call_rx(conn, msg);
-#endif
-
- switch (pdisc) {
- case GSM48_PDISC_CC:
- release_anchor(conn);
- rc = gsm0408_rcv_cc(conn, msg);
- break;
- case GSM48_PDISC_MM:
- rc = gsm0408_rcv_mm(conn, msg);
- break;
- case GSM48_PDISC_RR:
- rc = gsm0408_rcv_rr(conn, msg);
- break;
- case GSM48_PDISC_SMS:
- release_anchor(conn);
- rc = gsm0411_rcv_sms(conn, msg);
- break;
- case GSM48_PDISC_MM_GPRS:
- case GSM48_PDISC_SM_GPRS:
- LOGP(DRLL, LOGL_NOTICE, "Unimplemented "
- "GSM 04.08 discriminator 0x%02x\n", pdisc);
- rc = -ENOTSUP;
- break;
- case GSM48_PDISC_NC_SS:
- release_anchor(conn);
- rc = handle_rcv_ussd(conn, msg);
- break;
- default:
- LOGP(DRLL, LOGL_NOTICE, "Unknown "
- "GSM 04.08 discriminator 0x%02x\n", pdisc);
- rc = -EINVAL;
- break;
- }
-
- return rc;
-}
-
-/*
- * This will be run by the linker when loading the DSO. We use it to
- * do system initialization, e.g. registration of signal handlers.
- */
-static __attribute__((constructor)) void on_dso_load_0408(void)
-{
- osmo_signal_register_handler(SS_ABISIP, handle_abisip_signal, NULL);
-}
diff --git a/openbsc/src/libmsc/gsm_04_11.c b/openbsc/src/libmsc/gsm_04_11.c
deleted file mode 100644
index aa2030f80..000000000
--- a/openbsc/src/libmsc/gsm_04_11.c
+++ /dev/null
@@ -1,1070 +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/bsc_api.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)
- subscr_put(sms->receiver);
-#ifdef BUILD_SMPP
- if (sms->smpp.esme)
- smpp_esme_put(sms->smpp.esme);
-#endif
-
- talloc_free(sms);
-}
-
-struct gsm_sms *sms_from_text(struct gsm_subscriber *receiver,
- struct gsm_subscriber *sender,
- int dcs, const char *text)
-{
- struct gsm_sms *sms = sms_alloc();
-
- if (!sms)
- return NULL;
-
- sms->receiver = subscr_get(receiver);
- osmo_strlcpy(sms->text, text, sizeof(sms->text));
-
- osmo_strlcpy(sms->src.addr, sender->extension, 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->extension, 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 gsm0808_submit_dtap(conn, msg, UM_SAPI_SMS, 1);
-}
-
-/* Prefix msg with a 04.08/04.11 CP header */
-static int gsm411_cp_sendmsg(struct msgb *msg, struct gsm_trans *trans,
- uint8_t msg_type)
-{
- struct gsm48_hdr *gh;
-
- gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh));
- /* Outgoing needs the highest bit set */
- gh->proto_discr = trans->protocol | (trans->transaction_id<<4);
- gh->msg_type = msg_type;
-
- DEBUGP(DLSMS, "sending CP message (trans=%x)\n", trans->transaction_id);
-
- return gsm411_sendmsg(trans->conn, msg);
-}
-
-/* mm_send: receive MMCCSMS sap message from SMC */
-static int gsm411_mm_send(struct gsm411_smc_inst *inst, int msg_type,
- struct msgb *msg, int cp_msg_type)
-{
- struct gsm_trans *trans =
- container_of(inst, struct gsm_trans, sms.smc_inst);
- int rc = 0;
-
- switch (msg_type) {
- case GSM411_MMSMS_EST_REQ:
- /* recycle msg */
- rc = gsm411_smc_recv(inst, GSM411_MMSMS_EST_CNF, msg, 0);
- msgb_free(msg); /* upper layer does not free msg */
- break;
- case GSM411_MMSMS_DATA_REQ:
- rc = gsm411_cp_sendmsg(msg, trans, cp_msg_type);
- break;
- case GSM411_MMSMS_REL_REQ:
- DEBUGP(DLSMS, "Got MMSMS_REL_REQ, destroying transaction.\n");
- msgb_free(msg);
- trans_free(trans);
- break;
- default:
- LOGP(DLSMS, LOGL_NOTICE, "Unhandled MMCCSMS msg 0x%x\n", msg_type);
- msgb_free(msg);
- rc = -EINVAL;
- }
-
- return rc;
-}
-
-/* mm_send: receive MNCCSMS sap message from SMR */
-int gsm411_mn_send(struct gsm411_smr_inst *inst, int msg_type,
- struct msgb *msg)
-{
- struct gsm_trans *trans =
- container_of(inst, struct gsm_trans, sms.smr_inst);
-
- /* forward to SMC */
- return gsm411_smc_send(&trans->sms.smc_inst, msg_type, msg);
-}
-
-static int gsm340_rx_sms_submit(struct msgb *msg, struct gsm_sms *gsms)
-{
- if (db_sms_store(gsms) != 0) {
- LOGP(DLSMS, LOGL_ERROR, "Failed to store SMS in Database\n");
- return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER;
- }
- /* dispatch a signal to tell higher level about it */
- send_signal(S_SMS_SUBMITTED, NULL, gsms, 0);
-
- return 0;
-}
-
-/* generate a TPDU address field compliant with 03.40 sec. 9.1.2.5 */
-static int gsm340_gen_oa_sub(uint8_t *oa, unsigned int oa_len,
- const struct gsm_sms_addr *src)
-{
- /* network specific, private numbering plan */
- return gsm340_gen_oa(oa, oa_len, src->ton, src->npi, src->addr);
-}
-
-/* generate a msgb containing an 03.40 9.2.2.1 SMS-DELIVER TPDU derived from
- * struct gsm_sms, returns total size of TPDU */
-static int gsm340_gen_sms_deliver_tpdu(struct msgb *msg, struct gsm_sms *sms)
-{
- uint8_t *smsp;
- uint8_t oa[12]; /* max len per 03.40 */
- uint8_t oa_len = 0;
- uint8_t octet_len;
- unsigned int old_msg_len = msg->len;
-
- /* generate first octet with masked bits */
- smsp = msgb_put(msg, 1);
- /* TP-MTI (message type indicator) */
- *smsp = GSM340_SMS_DELIVER_SC2MS;
- /* TP-MMS (more messages to send) */
- if (0 /* FIXME */)
- *smsp |= 0x04;
- /* TP-SRI(deliver)/SRR(submit) */
- if (sms->status_rep_req)
- *smsp |= 0x20;
- /* TP-UDHI (indicating TP-UD contains a header) */
- if (sms->ud_hdr_ind)
- *smsp |= 0x40;
-
- /* generate originator address */
- oa_len = gsm340_gen_oa_sub(oa, sizeof(oa), &sms->src);
- smsp = msgb_put(msg, oa_len);
- memcpy(smsp, oa, oa_len);
-
- /* generate TP-PID */
- smsp = msgb_put(msg, 1);
- *smsp = sms->protocol_id;
-
- /* generate TP-DCS */
- smsp = msgb_put(msg, 1);
- *smsp = sms->data_coding_scheme;
-
- /* generate TP-SCTS */
- smsp = msgb_put(msg, 7);
- gsm340_gen_scts(smsp, time(NULL));
-
- /* generate TP-UDL */
- smsp = msgb_put(msg, 1);
- *smsp = sms->user_data_len;
-
- /* generate TP-UD */
- switch (gsm338_get_sms_alphabet(sms->data_coding_scheme)) {
- case DCS_7BIT_DEFAULT:
- octet_len = sms->user_data_len*7/8;
- if (sms->user_data_len*7%8 != 0)
- octet_len++;
- /* Warning, user_data_len indicates the amount of septets
- * (characters), we need amount of octets occupied */
- smsp = msgb_put(msg, octet_len);
- memcpy(smsp, sms->user_data, octet_len);
- break;
- case DCS_UCS2:
- case DCS_8BIT_DATA:
- smsp = msgb_put(msg, sms->user_data_len);
- memcpy(smsp, sms->user_data, sms->user_data_len);
- break;
- default:
- LOGP(DLSMS, LOGL_NOTICE, "Unhandled Data Coding Scheme: 0x%02X\n",
- sms->data_coding_scheme);
- break;
- }
-
- return msg->len - old_msg_len;
-}
-
-int sms_route_mt_sms(struct gsm_subscriber_connection *conn, struct msgb *msg,
- struct gsm_sms *gsms, uint8_t sms_mti, 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.",
- subscr_name(conn->subscr), 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 = subscr_get_by_extension(conn->network->subscr_group,
- 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.",
- subscr_name(conn->subscr), 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->subscr->extension,
- 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",
- subscr_name(conn->subscr), sms_mti, sms_vpf, gsms->msg_ref,
- gsms->protocol_id, gsms->data_coding_scheme, gsms->dst.addr,
- gsms->user_data_len,
- sms_alphabet == DCS_7BIT_DEFAULT ? gsms->text :
- osmo_hexdump(gsms->user_data, gsms->user_data_len));
-
- gsms->validity_minutes = gsm340_validity_period(sms_vpf, sms_vp);
-
- /* FIXME: This looks very wrong */
- send_signal(0, NULL, gsms, 0);
-
- rc = sms_route_mt_sms(conn, msg, gsms, sms_mti, 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",
- subscr_name(trans->conn->subscr), cause_len, cause,
- get_value_string(gsm411_rp_cause_strs, cause));
-
- if (!sms) {
- LOGP(DLSMS, LOGL_ERROR,
- "RX RP-ERR, but no sms in transaction?!?\n");
- return -EINVAL;
-#if 0
- return gsm411_send_rp_error(trans, rph->msg_ref,
- GSM411_RP_CAUSE_PROTOCOL_ERR);
-#endif
- }
-
- if (cause == GSM411_RP_CAUSE_MT_MEM_EXCEEDED) {
- /* MS has not enough memory to store the message. We need
- * to store this in our database and wait for a SMMA message */
- /* FIXME */
- send_signal(S_SMS_MEM_EXCEEDED, trans, sms, 0);
- 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->subscr)
- return -EIO;
- /* FIXME: send some error message */
-
- DEBUGP(DLSMS, "receiving data (trans_id=%x)\n", transaction_id);
- trans = trans_find_by_id(conn, GSM48_PDISC_SMS, transaction_id);
-
- /*
- * A transaction we created but don't know about?
- */
- if (!trans && (transaction_id & 0x8) == 0) {
- LOGP(DLSMS, LOGL_ERROR, "trans_id=%x allocated by us but known "
- "to us anymore. We are ignoring it, maybe a CP-ERROR "
- "from a MS?\n",
- transaction_id);
- return -EINVAL;
- }
-
- if (!trans) {
- DEBUGP(DLSMS, " -> (new transaction)\n");
- trans = trans_alloc(conn->network, conn->subscr,
- GSM48_PDISC_SMS,
- transaction_id, new_callref++);
- if (!trans) {
- DEBUGP(DLSMS, " -> No memory for trans\n");
- /* FIXME: send some error message */
- return -ENOMEM;
- }
- gsm411_smc_init(&trans->sms.smc_inst, 0, 1,
- gsm411_mn_recv, gsm411_mm_send);
- gsm411_smr_init(&trans->sms.smr_inst, 0, 1,
- gsm411_rl_recv, gsm411_mn_send);
-
- trans->conn = conn;
-
- new_trans = 1;
- }
-
- /* 5.4: For MO, if a CP-DATA is received for a new
- * transaction, equals reception of an implicit
- * last CP-ACK for previous transaction */
- if (trans->sms.smc_inst.cp_state == GSM411_CPS_IDLE
- && msg_type == GSM411_MT_CP_DATA) {
- int i;
- struct gsm_trans *ptrans;
-
- /* Scan through all remote initiated transactions */
- for (i=8; i<15; i++) {
- if (i == transaction_id)
- continue;
-
- ptrans = trans_find_by_id(conn, GSM48_PDISC_SMS, i);
- if (!ptrans)
- continue;
-
- DEBUGP(DLSMS, "Implicit CP-ACK for trans_id=%x\n", i);
-
- /* Finish it for good */
- trans_free(ptrans);
- }
- }
-
- gsm411_smc_recv(&trans->sms.smc_inst,
- (new_trans) ? GSM411_MMSMS_EST_IND : GSM411_MMSMS_DATA_IND,
- msg, msg_type);
-
- return rc;
-}
-
-/* Take a SMS in gsm_sms structure and send it through an already
- * existing lchan. We also assume that the caller ensured this lchan already
- * has a SAPI3 RLL connection! */
-int gsm411_send_sms(struct gsm_subscriber_connection *conn, struct gsm_sms *sms)
-{
- struct msgb *msg = gsm411_msgb_alloc();
- struct gsm_trans *trans;
- uint8_t *data, *rp_ud_len;
- uint8_t msg_ref = sms_next_rp_msg_ref(&conn->next_rp_ref);
- int transaction_id;
- int rc;
-
- transaction_id =
- trans_assign_trans_id(conn->network, conn->subscr,
- GSM48_PDISC_SMS, 0);
- if (transaction_id == -1) {
- LOGP(DLSMS, LOGL_ERROR, "No available transaction ids\n");
- send_signal(S_SMS_UNKNOWN_ERROR, NULL, sms, 0);
- sms_free(sms);
- msgb_free(msg);
- return -EBUSY;
- }
-
- DEBUGP(DLSMS, "%s()\n", __func__);
-
- /* FIXME: allocate transaction with message reference */
- trans = trans_alloc(conn->network, conn->subscr,
- GSM48_PDISC_SMS,
- transaction_id, new_callref++);
- if (!trans) {
- LOGP(DLSMS, LOGL_ERROR, "No memory for trans\n");
- send_signal(S_SMS_UNKNOWN_ERROR, NULL, sms, 0);
- sms_free(sms);
- msgb_free(msg);
- /* FIXME: send some error message */
- return -ENOMEM;
- }
- gsm411_smc_init(&trans->sms.smc_inst, sms->id, 1,
- gsm411_mn_recv, gsm411_mm_send);
- gsm411_smr_init(&trans->sms.smr_inst, sms->id, 1,
- gsm411_rl_recv, gsm411_mn_send);
- trans->sms.sms = sms;
-
- trans->conn = conn;
-
- /* Hardcode SMSC Originating Address for now */
- data = (uint8_t *)msgb_put(msg, 8);
- data[0] = 0x07; /* originator length == 7 */
- data[1] = 0x91; /* type of number: international, ISDN */
- data[2] = 0x44; /* 447785016005 */
- data[3] = 0x77;
- data[4] = 0x58;
- data[5] = 0x10;
- data[6] = 0x06;
- data[7] = 0x50;
-
- /* Hardcoded Destination Address */
- data = (uint8_t *)msgb_put(msg, 1);
- data[0] = 0; /* destination length == 0 */
-
- /* obtain a pointer for the rp_ud_len, so we can fill it later */
- rp_ud_len = (uint8_t *)msgb_put(msg, 1);
-
- /* generate the 03.40 SMS-DELIVER TPDU */
- rc = gsm340_gen_sms_deliver_tpdu(msg, sms);
- if (rc < 0) {
- send_signal(S_SMS_UNKNOWN_ERROR, trans, sms, 0);
- sms_free(sms);
- trans->sms.sms = NULL;
- trans_free(trans);
- msgb_free(msg);
- return rc;
- }
-
- *rp_ud_len = rc;
-
- DEBUGP(DLSMS, "TX: SMS DELIVER\n");
-
- 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 gsm_subscriber *subscr,
- struct gsm_sms *sms)
-{
- struct gsm_subscriber_connection *conn;
- void *res;
-
- /* check if we already have an open lchan to the subscriber.
- * if yes, send the SMS this way */
- conn = connection_for_subscr(subscr);
- if (conn) {
- LOGP(DLSMS, LOGL_DEBUG, "Sending SMS via already open connection %p to %s\n",
- conn, subscr_name(subscr));
- 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",
- subscr_name(subscr));
- res = subscr_request_channel(subscr, RSL_CHANNEED_SDCCH,
- paging_cb_send_sms, sms);
- if (!res) {
- send_signal(S_SMS_UNKNOWN_ERROR, NULL, sms, GSM_PAGING_BUSY);
- sms_free(sms);
- }
- return 0;
-}
-
-void _gsm411_sms_trans_free(struct gsm_trans *trans)
-{
- /* cleanup SMS instance */
- gsm411_smr_clear(&trans->sms.smr_inst);
- trans->sms.smr_inst.rl_recv = NULL;
- trans->sms.smr_inst.mn_send = NULL;
-
- gsm411_smc_clear(&trans->sms.smc_inst);
- trans->sms.smc_inst.mn_recv = NULL;
- trans->sms.smc_inst.mm_send = NULL;
-
- if (trans->sms.sms) {
- LOGP(DLSMS, LOGL_ERROR, "Transaction contains SMS.\n");
- send_signal(S_SMS_UNKNOWN_ERROR, trans, trans->sms.sms, 0);
- sms_free(trans->sms.sms);
- trans->sms.sms = NULL;
- }
-}
-
-void gsm411_sapi_n_reject(struct gsm_subscriber_connection *conn)
-{
- struct gsm_network *net;
- struct gsm_trans *trans, *tmp;
-
- net = conn->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/openbsc/src/libmsc/gsm_04_80.c b/openbsc/src/libmsc/gsm_04_80.c
deleted file mode 100644
index 479d6fbd2..000000000
--- a/openbsc/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/bsc_api.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 gsm0808_submit_dtap(conn, msg, 0, 0);
-}
-
-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 gsm0808_submit_dtap(conn, msg, 0, 0);
-}
-
-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 gsm0808_submit_dtap(conn, msg, 0, 0);
-}
-
-int msc_send_ussd_release_complete(struct gsm_subscriber_connection *conn)
-{
- struct msgb *msg = gsm0480_create_ussd_release_complete();
- if (!msg)
- return -1;
- return gsm0808_submit_dtap(conn, msg, 0, 0);
-}
diff --git a/openbsc/src/libmsc/gsm_subscriber.c b/openbsc/src/libmsc/gsm_subscriber.c
deleted file mode 100644
index 1a03cf76e..000000000
--- a/openbsc/src/libmsc/gsm_subscriber.c
+++ /dev/null
@@ -1,422 +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 <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>
-
-#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>
-
-void *tall_sub_req_ctx;
-
-extern struct llist_head *subscr_bsc_active_subscribers(void);
-
-int gsm48_secure_channel(struct gsm_subscriber_connection *conn, int key_seq,
- gsm_cbfn *cb, void *cb_data);
-
-
-/*
- * Struct for pending channel requests. This is managed in the
- * llist_head requests of each subscriber. The reference counting
- * should work in such a way that a subscriber with a pending request
- * remains in memory.
- */
-struct subscr_request {
- struct llist_head entry;
-
- /* the callback data */
- gsm_cbfn *cbfn;
- void *param;
-};
-
-static struct gsm_subscriber *get_subscriber(struct gsm_subscriber_group *sgrp,
- int type, const char *ident)
-{
- struct gsm_subscriber *subscr = db_get_subscriber(type, ident);
- if (subscr)
- subscr->group = sgrp;
- return subscr;
-}
-
-/*
- * We got the channel assigned and can now hand this channel
- * over to one of our callbacks.
- */
-static int subscr_paging_dispatch(unsigned int hooknum, unsigned int event,
- struct msgb *msg, void *data, void *param)
-{
- struct subscr_request *request, *tmp;
- struct gsm_subscriber_connection *conn = data;
- struct gsm_subscriber *subscr = param;
- struct paging_signal_data sig_data;
- struct bsc_subscr *bsub;
- struct gsm_network *net;
-
- OSMO_ASSERT(subscr && subscr->is_paging);
- net = subscr->group->net;
-
- /*
- * Stop paging on all other BTS. E.g. if this is
- * the first timeout on a BTS then the others will
- * timeout soon as well. Let's just stop everything
- * and forget we wanted to page.
- */
-
- /* TODO MSC split -- creating a BSC subscriber directly from MSC data
- * structures in RAM. At some point the MSC will send a message to the
- * BSC instead. */
- bsub = bsc_subscr_find_or_create_by_imsi(net->bsc_subscribers,
- subscr->imsi);
- bsub->tmsi = subscr->tmsi;
- bsub->lac = subscr->lac;
- paging_request_stop(&net->bts_list, NULL, bsub, NULL, NULL);
- bsc_subscr_put(bsub);
-
- /* Inform parts of the system we don't know */
- sig_data.subscr = subscr;
- sig_data.bts = conn ? conn->bts : NULL;
- sig_data.conn = conn;
- sig_data.paging_result = event;
- osmo_signal_dispatch(
- SS_PAGING,
- event == GSM_PAGING_SUCCEEDED ?
- S_PAGING_SUCCEEDED : S_PAGING_EXPIRED,
- &sig_data
- );
-
- llist_for_each_entry_safe(request, tmp, &subscr->requests, entry) {
- llist_del(&request->entry);
- request->cbfn(hooknum, event, msg, data, request->param);
- talloc_free(request);
- }
-
- /* balanced with the moment we start paging */
- subscr->is_paging = 0;
- subscr_put(subscr);
- return 0;
-}
-
-static int subscr_paging_sec_cb(unsigned int hooknum, unsigned int event,
- struct msgb *msg, void *data, void *param)
-{
- int rc;
-
- switch (event) {
- case GSM_SECURITY_AUTH_FAILED:
- /* Dispatch as paging failure */
- rc = subscr_paging_dispatch(
- GSM_HOOK_RR_PAGING, GSM_PAGING_EXPIRED,
- msg, data, param);
- break;
-
- case GSM_SECURITY_NOAVAIL:
- case GSM_SECURITY_SUCCEEDED:
- /* Dispatch as paging failure */
- rc = subscr_paging_dispatch(
- GSM_HOOK_RR_PAGING, GSM_PAGING_SUCCEEDED,
- msg, data, param);
- break;
-
- default:
- rc = -EINVAL;
- }
-
- return rc;
-}
-
-static int subscr_paging_cb(unsigned int hooknum, unsigned int event,
- struct msgb *msg, void *data, void *param)
-{
- struct gsm_subscriber_connection *conn = data;
- struct gsm48_hdr *gh;
- struct gsm48_pag_resp *pr;
-
- /* Other cases mean problem, dispatch direclty */
- if (event != GSM_PAGING_SUCCEEDED)
- return subscr_paging_dispatch(hooknum, event, msg, data, param);
-
- /* Get paging response */
- gh = msgb_l3(msg);
- pr = (struct gsm48_pag_resp *)gh->data;
-
- /* We _really_ have a channel, secure it now ! */
- return gsm48_secure_channel(conn, pr->key_seq, subscr_paging_sec_cb, param);
-}
-
-struct subscr_request *subscr_request_channel(struct gsm_subscriber *subscr,
- int channel_type, gsm_cbfn *cbfn, void *param)
-{
- int rc;
- struct subscr_request *request;
- struct bsc_subscr *bsub;
- struct gsm_network *net = subscr->group->net;
-
- /* Start paging.. we know it is async so we can do it before */
- if (!subscr->is_paging) {
- LOGP(DMM, LOGL_DEBUG, "Subscriber %s not paged yet.\n",
- subscr_name(subscr));
- /* TODO MSC split -- creating a BSC subscriber directly from
- * MSC data structures in RAM. At some point the MSC will send
- * a message to the BSC instead. */
- bsub = bsc_subscr_find_or_create_by_imsi(net->bsc_subscribers,
- subscr->imsi);
- bsub->tmsi = subscr->tmsi;
- bsub->lac = subscr->lac;
- rc = paging_request(net, bsub, channel_type, subscr_paging_cb,
- subscr);
- bsc_subscr_put(bsub);
- if (rc <= 0) {
- LOGP(DMM, LOGL_ERROR, "Subscriber %s paging failed: %d\n",
- subscr_name(subscr), rc);
- return NULL;
- }
- /* reduced on the first paging callback */
- subscr_get(subscr);
- subscr->is_paging = 1;
- }
-
- /* TODO: Stop paging in case of memory allocation failure */
- request = talloc_zero(subscr, struct subscr_request);
- if (!request)
- return NULL;
-
- request->cbfn = cbfn;
- request->param = param;
- llist_add_tail(&request->entry, &subscr->requests);
- return request;
-}
-
-void subscr_remove_request(struct subscr_request *request)
-{
- llist_del(&request->entry);
- talloc_free(request);
-}
-
-struct gsm_subscriber *subscr_create_subscriber(struct gsm_subscriber_group *sgrp,
- const char *imsi)
-{
- struct gsm_subscriber *subscr = db_create_subscriber(imsi,
- sgrp->net->ext_min,
- sgrp->net->ext_max,
- sgrp->net->auto_assign_exten);
- if (subscr)
- subscr->group = sgrp;
- return subscr;
-}
-
-struct gsm_subscriber *subscr_get_by_tmsi(struct gsm_subscriber_group *sgrp,
- uint32_t tmsi)
-{
- char tmsi_string[14];
- struct gsm_subscriber *subscr;
-
- /* we might have a record in memory already */
- llist_for_each_entry(subscr, subscr_bsc_active_subscribers(), entry) {
- if (tmsi == subscr->tmsi)
- return subscr_get(subscr);
- }
-
- sprintf(tmsi_string, "%u", tmsi);
- return get_subscriber(sgrp, GSM_SUBSCRIBER_TMSI, tmsi_string);
-}
-
-struct gsm_subscriber *subscr_get_by_imsi(struct gsm_subscriber_group *sgrp,
- const char *imsi)
-{
- struct gsm_subscriber *subscr;
-
- llist_for_each_entry(subscr, subscr_bsc_active_subscribers(), entry) {
- if (strcmp(subscr->imsi, imsi) == 0)
- return subscr_get(subscr);
- }
-
- return get_subscriber(sgrp, GSM_SUBSCRIBER_IMSI, imsi);
-}
-
-struct gsm_subscriber *subscr_get_by_extension(struct gsm_subscriber_group *sgrp,
- const char *ext)
-{
- struct gsm_subscriber *subscr;
-
- llist_for_each_entry(subscr, subscr_bsc_active_subscribers(), entry) {
- if (strcmp(subscr->extension, ext) == 0)
- return subscr_get(subscr);
- }
-
- return get_subscriber(sgrp, GSM_SUBSCRIBER_EXTENSION, ext);
-}
-
-struct gsm_subscriber *subscr_get_by_id(struct gsm_subscriber_group *sgrp,
- unsigned long long id)
-{
- struct gsm_subscriber *subscr;
- char buf[32];
- sprintf(buf, "%llu", id);
-
- llist_for_each_entry(subscr, subscr_bsc_active_subscribers(), entry) {
- if (subscr->id == id)
- return subscr_get(subscr);
- }
-
- return get_subscriber(sgrp, GSM_SUBSCRIBER_ID, buf);
-}
-
-int subscr_update_expire_lu(struct gsm_subscriber *s, struct gsm_bts *bts)
-{
- int rc;
-
- if (!s) {
- LOGP(DMM, LOGL_ERROR, "LU Expiration but NULL subscriber\n");
- return -1;
- }
- if (!bts) {
- LOGP(DMM, LOGL_ERROR, "%s: LU Expiration but NULL bts\n",
- subscr_name(s));
- return -1;
- }
-
- /* Table 10.5.33: The T3212 timeout value field is coded as the
- * binary representation of the timeout value for
- * periodic updating in decihours. Mark the subscriber as
- * inactive if it missed two consecutive location updates.
- * Timeout is twice the t3212 value plus one minute */
-
- /* Is expiration handling enabled? */
- if (bts->si_common.chan_desc.t3212 == 0)
- s->expire_lu = GSM_SUBSCRIBER_NO_EXPIRATION;
- else
- s->expire_lu = time(NULL) +
- (bts->si_common.chan_desc.t3212 * 60 * 6 * 2) + 60;
-
- rc = db_sync_subscriber(s);
- db_subscriber_update(s);
- return rc;
-}
-
-int subscr_update(struct gsm_subscriber *s, struct gsm_bts *bts, int reason)
-{
- int rc;
-
- /* FIXME: Migrate pending requests from one BSC to another */
- switch (reason) {
- case GSM_SUBSCRIBER_UPDATE_ATTACHED:
- s->group = bts->network->subscr_group;
- /* Indicate "attached to LAC" */
- s->lac = bts->location_area_code;
-
- LOGP(DMM, LOGL_INFO, "Subscriber %s ATTACHED LAC=%u\n",
- subscr_name(s), s->lac);
-
- /*
- * The below will set a new expire_lu but as a side-effect
- * the new lac will be saved in the database.
- */
- rc = subscr_update_expire_lu(s, bts);
- osmo_signal_dispatch(SS_SUBSCR, S_SUBSCR_ATTACHED, s);
- break;
- case GSM_SUBSCRIBER_UPDATE_DETACHED:
- /* Only detach if we are currently in this area */
- if (bts->location_area_code == s->lac)
- s->lac = GSM_LAC_RESERVED_DETACHED;
- LOGP(DMM, LOGL_INFO, "Subscriber %s DETACHED\n", subscr_name(s));
- rc = db_sync_subscriber(s);
- db_subscriber_update(s);
- osmo_signal_dispatch(SS_SUBSCR, S_SUBSCR_DETACHED, s);
- break;
- default:
- fprintf(stderr, "subscr_update with unknown reason: %d\n",
- reason);
- rc = db_sync_subscriber(s);
- db_subscriber_update(s);
- break;
- };
-
- return rc;
-}
-
-void subscr_update_from_db(struct gsm_subscriber *sub)
-{
- db_subscriber_update(sub);
-}
-
-static void subscr_expire_callback(void *data, long long unsigned int id)
-{
- struct gsm_network *net = data;
- struct gsm_subscriber *s = subscr_get_by_id(net->subscr_group, id);
- struct gsm_subscriber_connection *conn = connection_for_subscr(s);
-
- /*
- * The subscriber is active and the phone stopped the timer. As
- * we don't want to periodically update the database for active
- * subscribers we will just do it when the subscriber was selected
- * for expiration. This way on the next around another subscriber
- * will be selected.
- */
- if (conn && conn->expire_timer_stopped) {
- LOGP(DMM, LOGL_DEBUG, "Not expiring subscriber %s (ID %llu)\n",
- subscr_name(s), id);
- subscr_update_expire_lu(s, conn->bts);
- subscr_put(s);
- return;
- }
-
-
- LOGP(DMM, LOGL_NOTICE, "Expiring inactive subscriber %s (ID %llu)\n",
- subscr_name(s), id);
- s->lac = GSM_LAC_RESERVED_DETACHED;
- db_sync_subscriber(s);
-
- subscr_put(s);
-}
-
-void subscr_expire(struct gsm_subscriber_group *sgrp)
-{
- db_subscriber_expire(sgrp->net, subscr_expire_callback);
-}
-
-struct gsm_subscriber_connection *connection_for_subscr(struct gsm_subscriber *subscr)
-{
- /* FIXME: replace this with a backpointer in gsm_subscriber? */
- struct gsm_network *net = subscr->group->net;
- struct gsm_subscriber_connection *conn;
-
- llist_for_each_entry(conn, &net->subscr_conns, entry) {
- if (conn->subscr == subscr)
- return conn;
- }
-
- return NULL;
-}
diff --git a/openbsc/src/libmsc/meas_feed.c b/openbsc/src/libmsc/meas_feed.c
deleted file mode 100644
index 3ddcdc39c..000000000
--- a/openbsc/src/libmsc/meas_feed.c
+++ /dev/null
@@ -1,167 +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 "meas_feed.h"
-
-struct meas_feed_state {
- struct osmo_wqueue wqueue;
- char scenario[31+1];
- char *dst_host;
- uint16_t dst_port;
-};
-
-
-static struct meas_feed_state g_mfs;
-
-static int process_meas_rep(struct gsm_meas_rep *mr)
-{
- struct msgb *msg;
- struct meas_feed_meas *mfm;
- struct gsm_subscriber *subscr;
-
- /* ignore measurements as long as we don't know who it is */
- if (!mr->lchan || !mr->lchan->conn || !mr->lchan->conn->subscr)
- return 0;
-
- subscr = mr->lchan->conn->subscr;
-
- msg = msgb_alloc(sizeof(struct meas_feed_meas), "Meas. Feed");
- if (!msg)
- return 0;
-
- /* fill in the header */
- mfm = (struct meas_feed_meas *) msgb_put(msg, sizeof(*mfm));
- mfm->hdr.msg_type = MEAS_FEED_MEAS;
- mfm->hdr.version = MEAS_FEED_VERSION;
-
- /* fill in MEAS_FEED_MEAS specific header */
- osmo_strlcpy(mfm->imsi, subscr->imsi, sizeof(mfm->imsi));
- osmo_strlcpy(mfm->name, subscr->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/openbsc/src/libmsc/meas_feed.h b/openbsc/src/libmsc/meas_feed.h
deleted file mode 100644
index 782a9616c..000000000
--- a/openbsc/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/openbsc/src/libmsc/mncc.c b/openbsc/src/libmsc/mncc.c
deleted file mode 100644
index 8110eadca..000000000
--- a/openbsc/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/openbsc/src/libmsc/mncc_builtin.c b/openbsc/src/libmsc/mncc_builtin.c
deleted file mode 100644
index 067cc92f8..000000000
--- a/openbsc/src/libmsc/mncc_builtin.c
+++ /dev/null
@@ -1,426 +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;
-}
-
-uint8_t mncc_codec_for_mode(int lchan_type)
-{
- /* FIXME: check codec capabilities of the phone */
-
- if (lchan_type != GSM_LCHAN_TCH_H)
- return mncc_int.def_codec[0];
- else
- return mncc_int.def_codec[1];
-}
-
-static uint8_t determine_lchan_mode(struct gsm_mncc *setup)
-{
- return mncc_codec_for_mode(setup->lchan_type);
-}
-
-/* on incoming call, look up database and send setup to remote subscr. */
-static int mncc_setup_ind(struct gsm_call *call, int msg_type,
- struct gsm_mncc *setup)
-{
- struct gsm_mncc mncc;
- struct gsm_call *remote;
-
- memset(&mncc, 0, sizeof(struct gsm_mncc));
- mncc.callref = call->callref;
-
- /* already have remote call */
- if (call->remote_ref)
- return 0;
-
- /* transfer mode 1 would be packet mode, which was never specified */
- if (setup->bearer_cap.mode != 0) {
- LOGP(DMNCC, LOGL_NOTICE, "(call %x) We don't support "
- "packet mode\n", call->callref);
- mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU,
- GSM48_CC_CAUSE_BEARER_CA_UNAVAIL);
- goto out_reject;
- }
-
- /* we currently only do speech */
- if (setup->bearer_cap.transfer != GSM_MNCC_BCAP_SPEECH) {
- LOGP(DMNCC, LOGL_NOTICE, "(call %x) We only support "
- "voice calls\n", call->callref);
- mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU,
- GSM48_CC_CAUSE_BEARER_CA_UNAVAIL);
- goto out_reject;
- }
-
- /* create remote call */
- if (!(remote = talloc_zero(tall_call_ctx, struct gsm_call))) {
- mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU,
- GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
- goto out_reject;
- }
- llist_add_tail(&remote->entry, &call_list);
- remote->net = call->net;
- remote->callref = new_callref++;
- DEBUGP(DMNCC, "(call %x) Creating new remote instance %x.\n",
- call->callref, remote->callref);
-
- /* link remote call */
- call->remote_ref = remote->callref;
- remote->remote_ref = call->callref;
-
- /* send call proceeding */
- memset(&mncc, 0, sizeof(struct gsm_mncc));
- mncc.callref = call->callref;
- DEBUGP(DMNCC, "(call %x) Accepting call.\n", call->callref);
- mncc_tx_to_cc(call->net, MNCC_CALL_PROC_REQ, &mncc);
-
- /* modify mode */
- memset(&mncc, 0, sizeof(struct gsm_mncc));
- mncc.callref = call->callref;
- mncc.lchan_mode = determine_lchan_mode(setup);
- DEBUGP(DMNCC, "(call %x) Modify channel mode: %s\n", call->callref,
- get_value_string(gsm48_chan_mode_names, mncc.lchan_mode));
- 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);
-
- /* in direct mode, we always have to bridge the channels */
- if (ipacc_rtp_direct)
- return mncc_tx_to_cc(call->net, MNCC_BRIDGE, &bridge);
-
- /* 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;
-}
-
-/* receiving a (speech) traffic frame from the BSC code */
-static int mncc_rcv_data(struct gsm_call *call, int msg_type,
- struct gsm_data_frame *dfr)
-{
- struct gsm_trans *remote_trans;
-
- remote_trans = trans_find_by_callref(call->net, call->remote_ref);
-
- /* this shouldn't really happen */
- if (!remote_trans || !remote_trans->conn) {
- LOGP(DMNCC, LOGL_ERROR, "No transaction or transaction without lchan?!?\n");
- return -EIO;
- }
-
- /* RTP socket of remote end has meanwhile died */
- if (!remote_trans->conn->lchan->abis_ip.rtp_socket)
- return -EIO;
-
- return rtp_send_frame(remote_trans->conn->lchan->abis_ip.rtp_socket, dfr);
-}
-
-
-/* Internal MNCC handler input function (from CC -> MNCC -> here) */
-int int_mncc_recv(struct gsm_network *net, struct msgb *msg)
-{
- void *arg = msgb_data(msg);
- struct gsm_mncc *data = arg;
- int msg_type = data->msg_type;
- int callref;
- struct gsm_call *call = NULL, *callt;
- int rc = 0;
-
- /* Special messages */
- switch(msg_type) {
- }
-
- /* find callref */
- callref = data->callref;
- llist_for_each_entry(callt, &call_list, entry) {
- if (callt->callref == callref) {
- call = callt;
- break;
- }
- }
-
- /* create callref, if setup is received */
- if (!call) {
- if (msg_type != MNCC_SETUP_IND)
- goto out_free; /* drop */
- /* create call */
- if (!(call = talloc_zero(tall_call_ctx, struct gsm_call))) {
- struct gsm_mncc rel;
-
- memset(&rel, 0, sizeof(struct gsm_mncc));
- rel.callref = callref;
- mncc_set_cause(&rel, GSM48_CAUSE_LOC_PRN_S_LU,
- GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
- mncc_tx_to_cc(net, MNCC_REL_REQ, &rel);
- goto out_free;
- }
- llist_add_tail(&call->entry, &call_list);
- call->net = net;
- call->callref = callref;
- DEBUGP(DMNCC, "(call %x) Call created.\n", call->callref);
- }
-
- if (mncc_is_data_frame(msg_type)) {
- rc = mncc_rcv_data(call, msg_type, arg);
- goto out_free;
- }
-
- DEBUGP(DMNCC, "(call %x) Received message %s\n", call->callref,
- get_mncc_name(msg_type));
-
- switch(msg_type) {
- case MNCC_SETUP_IND:
- rc = mncc_setup_ind(call, msg_type, arg);
- break;
- case MNCC_SETUP_CNF:
- rc = mncc_setup_cnf(call, msg_type, arg);
- break;
- case MNCC_SETUP_COMPL_IND:
- break;
- case MNCC_CALL_CONF_IND:
- /* we now need to MODIFY the channel */
- data->lchan_mode = determine_lchan_mode(data);
- mncc_tx_to_cc(call->net, MNCC_LCHAN_MODIFY, data);
- break;
- case MNCC_ALERT_IND:
- rc = mncc_alert_ind(call, msg_type, arg);
- break;
- case MNCC_NOTIFY_IND:
- rc = mncc_notify_ind(call, msg_type, arg);
- break;
- case MNCC_DISC_IND:
- rc = mncc_disc_ind(call, msg_type, arg);
- break;
- case MNCC_REL_IND:
- case MNCC_REJ_IND:
- rc = mncc_rel_ind(call, msg_type, arg);
- break;
- case MNCC_REL_CNF:
- rc = mncc_rel_cnf(call, msg_type, arg);
- break;
- case MNCC_FACILITY_IND:
- break;
- case MNCC_START_DTMF_IND:
- 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/openbsc/src/libmsc/mncc_sock.c b/openbsc/src/libmsc/mncc_sock.c
deleted file mode 100644
index 0efe3a156..000000000
--- a/openbsc/src/libmsc/mncc_sock.c
+++ /dev/null
@@ -1,318 +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);
- hello->lchan_type_offset = offsetof(struct gsm_mncc, lchan_type);
-
- msgb_enqueue(&mncc->net->upqueue, msg);
- mncc->conn_bfd.when |= BSC_FD_WRITE;
-}
-
-/* accept a new connection */
-static int mncc_sock_accept(struct osmo_fd *bfd, unsigned int flags)
-{
- struct mncc_sock_state *state = (struct mncc_sock_state *)bfd->data;
- struct osmo_fd *conn_bfd = &state->conn_bfd;
- struct sockaddr_un un_addr;
- socklen_t len;
- int rc;
-
- len = sizeof(un_addr);
- rc = accept(bfd->fd, (struct sockaddr *) &un_addr, &len);
- if (rc < 0) {
- LOGP(DMNCC, LOGL_ERROR, "Failed to accept a new connection\n");
- return -1;
- }
-
- if (conn_bfd->fd >= 0) {
- LOGP(DMNCC, LOGL_NOTICE, "MNCC app connects but we already have "
- "another active connection ?!?\n");
- /* We already have one MNCC app connected, this is all we support */
- state->listen_bfd.when &= ~BSC_FD_READ;
- close(rc);
- return 0;
- }
-
- conn_bfd->fd = rc;
- conn_bfd->when = BSC_FD_READ;
- conn_bfd->cb = mncc_sock_cb;
- conn_bfd->data = state;
-
- if (osmo_fd_register(conn_bfd) != 0) {
- LOGP(DMNCC, LOGL_ERROR, "Failed to register new connection fd\n");
- close(conn_bfd->fd);
- conn_bfd->fd = -1;
- return -1;
- }
-
- LOGP(DMNCC, LOGL_NOTICE, "MNCC Socket has connection with external "
- "call control application\n");
-
- queue_hello(state);
- return 0;
-}
-
-
-int mncc_sock_init(struct gsm_network *net, 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/openbsc/src/libmsc/osmo_msc.c b/openbsc/src/libmsc/osmo_msc.c
deleted file mode 100644
index 2389980d3..000000000
--- a/openbsc/src/libmsc/osmo_msc.c
+++ /dev/null
@@ -1,177 +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/bsc_api.h>
-#include <openbsc/debug.h>
-#include <openbsc/transaction.h>
-#include <openbsc/db.h>
-
-#include <openbsc/gsm_04_11.h>
-
-static void msc_sapi_n_reject(struct gsm_subscriber_connection *conn, int dlci)
-{
- int sapi = dlci & 0x7;
-
- if (sapi == UM_SAPI_SMS)
- gsm411_sapi_n_reject(conn);
-}
-
-static int msc_clear_request(struct gsm_subscriber_connection *conn, uint32_t cause)
-{
- gsm0408_clear_request(conn, cause);
- return 1;
-}
-
-static int msc_compl_l3(struct gsm_subscriber_connection *conn, struct msgb *msg,
- uint16_t chosen_channel)
-{
- gsm0408_new_conn(conn);
- gsm0408_dispatch(conn, msg);
-
- /*
- * 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 BSC_API_CONN_POL_ACCEPT;
- if (conn->loc_operation || conn->sec_operation || conn->anch_operation)
- return BSC_API_CONN_POL_ACCEPT;
- if (trans_has_conn(conn))
- return BSC_API_CONN_POL_ACCEPT;
-
- LOGP(DRR, LOGL_INFO, "MSC Complete L3: Rejecting connection.\n");
- return BSC_API_CONN_POL_REJECT;
-}
-
-static void msc_dtap(struct gsm_subscriber_connection *conn, uint8_t link_id, struct msgb *msg)
-{
- gsm0408_dispatch(conn, msg);
-}
-
-static void msc_assign_compl(struct gsm_subscriber_connection *conn,
- uint8_t rr_cause, uint8_t chosen_channel,
- uint8_t encr_alg_id, uint8_t speec)
-{
- LOGP(DRR, LOGL_DEBUG, "MSC assign complete (do nothing).\n");
-}
-
-static void msc_assign_fail(struct gsm_subscriber_connection *conn,
- uint8_t cause, uint8_t *rr_cause)
-{
- LOGP(DRR, LOGL_DEBUG, "MSC assign failure (do nothing).\n");
-}
-
-static void msc_classmark_chg(struct gsm_subscriber_connection *conn,
- const uint8_t *cm2, uint8_t cm2_len,
- const uint8_t *cm3, uint8_t cm3_len)
-{
- struct gsm_subscriber *subscr = conn->subscr;
-
- if (subscr) {
- subscr->equipment.classmark2_len = cm2_len;
- memcpy(subscr->equipment.classmark2, cm2, cm2_len);
- if (cm3) {
- subscr->equipment.classmark3_len = cm3_len;
- memcpy(subscr->equipment.classmark3, cm3, cm3_len);
- }
- db_sync_equipment(&subscr->equipment);
- }
-}
-
-static void msc_ciph_m_compl(struct gsm_subscriber_connection *conn,
- struct msgb *msg, uint8_t alg_id)
-{
- gsm_cbfn *cb;
-
- DEBUGP(DRR, "CIPHERING MODE COMPLETE\n");
-
- /* Safety check */
- if (!conn->sec_operation) {
- DEBUGP(DRR, "No authentication/cipher operation in progress !!!\n");
- return;
- }
-
- /* FIXME: check for MI (if any) */
-
- /* Call back whatever was in progress (if anything) ... */
- cb = conn->sec_operation->cb;
- if (cb) {
- cb(GSM_HOOK_RR_SECURITY, GSM_SECURITY_SUCCEEDED,
- NULL, conn, conn->sec_operation->cb_data);
-
- }
-
- /* Complete the operation */
- release_security_operation(conn);
-}
-
-
-
-static struct bsc_api msc_handler = {
- .sapi_n_reject = msc_sapi_n_reject,
- .compl_l3 = msc_compl_l3,
- .dtap = msc_dtap,
- .clear_request = msc_clear_request,
- .assign_compl = msc_assign_compl,
- .assign_fail = msc_assign_fail,
- .classmark_chg = msc_classmark_chg,
- .cipher_mode_compl = msc_ciph_m_compl,
-};
-
-struct bsc_api *msc_bsc_api() {
- return &msc_handler;
-}
-
-/* lchan release handling */
-void msc_release_connection(struct gsm_subscriber_connection *conn)
-{
- /* skip when we are in release, e.g. due an error */
- if (conn->in_release)
- return;
-
- /* skip releasing of silent calls as they have no transaction */
- if (conn->silent_call)
- return;
-
- /* check if there is a pending operation */
- if (conn->loc_operation || conn->sec_operation || conn->anch_operation)
- return;
-
- if (trans_has_conn(conn))
- return;
-
- /* no more connections, asking to release the channel */
-
- /*
- * We had stopped the LU expire timer T3212. Now we are about
- * to send the MS back to the idle state and this should lead
- * to restarting the timer. Set the new expiration time.
- */
- if (conn->expire_timer_stopped)
- subscr_update_expire_lu(conn->subscr, conn->bts);
-
- conn->in_release = 1;
- gsm0808_clear(conn);
- msc_subscr_con_free(conn);
-}
diff --git a/openbsc/src/libmsc/rrlp.c b/openbsc/src/libmsc/rrlp.c
deleted file mode 100644
index e695daac7..000000000
--- a/openbsc/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 gsm_subscriber *subscr;
- struct gsm_subscriber_connection *conn;
-
- switch (signal) {
- case S_SUBSCR_ATTACHED:
- /* A subscriber has attached. */
- subscr = signal_data;
- conn = connection_for_subscr(subscr);
- if (!conn)
- break;
- send_rrlp_req(conn);
- break;
- }
- return 0;
-}
-
-static int paging_sig_cb(unsigned int subsys, unsigned int signal,
- void *handler_data, void *signal_data)
-{
- struct paging_signal_data *psig_data = signal_data;
-
- switch (signal) {
- case S_PAGING_SUCCEEDED:
- /* A subscriber has attached. */
- send_rrlp_req(psig_data->conn);
- break;
- case S_PAGING_EXPIRED:
- break;
- }
- return 0;
-}
-
-void on_dso_load_rrlp(void)
-{
- osmo_signal_register_handler(SS_SUBSCR, subscr_sig_cb, NULL);
- osmo_signal_register_handler(SS_PAGING, paging_sig_cb, NULL);
-}
diff --git a/openbsc/src/libmsc/silent_call.c b/openbsc/src/libmsc/silent_call.c
deleted file mode 100644
index 590d01bbf..000000000
--- a/openbsc/src/libmsc/silent_call.c
+++ /dev/null
@@ -1,152 +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:
- DEBUGPC(DLSMS, "success, using Timeslot %u on ARFCN %u\n",
- conn->lchan->ts->nr, conn->lchan->ts->trx->arfcn);
- conn->silent_call = 1;
- /* increment lchan reference count */
- osmo_signal_dispatch(SS_SCALL, S_SCALL_SUCCESS, &sigdata);
- break;
- case GSM_PAGING_EXPIRED:
- case GSM_PAGING_BUSY:
- case GSM_PAGING_OOM:
- DEBUGP(DLSMS, "expired\n");
- osmo_signal_dispatch(SS_SCALL, S_SCALL_EXPIRED, &sigdata);
- break;
- default:
- rc = -EINVAL;
- break;
- }
-
- return rc;
-}
-
-#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 gsm_subscriber *subscr, void *data, int type)
-{
- struct subscr_request *req;
-
- req = subscr_request_channel(subscr, type, paging_cb_silent, data);
- return req != NULL;
-}
-
-/* end a silent call with a given subscriber */
-int gsm_silent_call_stop(struct gsm_subscriber *subscr)
-{
- struct gsm_subscriber_connection *conn;
-
- conn = connection_for_subscr(subscr);
- if (!conn)
- return -EINVAL;
-
- /* did we actually establish a silent call for this guy? */
- if (!conn->silent_call)
- return -EINVAL;
-
- DEBUGPC(DLSMS, "Stopping silent call using Timeslot %u on ARFCN %u\n",
- conn->lchan->ts->nr, conn->lchan->ts->trx->arfcn);
-
- conn->silent_call = 0;
- msc_release_connection(conn);
-
- return 0;
-}
diff --git a/openbsc/src/libmsc/smpp_openbsc.c b/openbsc/src/libmsc/smpp_openbsc.c
deleted file mode 100644
index f94968a3f..000000000
--- a/openbsc/src/libmsc/smpp_openbsc.c
+++ /dev/null
@@ -1,758 +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 "smpp_smsc.h"
-
-/*! \brief find gsm_subscriber for a given SMPP NPI/TON/Address */
-static struct gsm_subscriber *subscr_by_dst(struct gsm_network *net,
- uint8_t npi, uint8_t ton, const char *addr)
-{
- struct gsm_subscriber *subscr = NULL;
-
- switch (npi) {
- case NPI_Land_Mobile_E212:
- subscr = subscr_get_by_imsi(net->subscr_group, addr);
- break;
- case NPI_ISDN_E163_E164:
- case NPI_Private:
- subscr = subscr_get_by_extension(net->subscr_group, addr);
- break;
- default:
- LOGP(DSMPP, LOGL_NOTICE, "Unsupported NPI: %u\n", npi);
- break;
- }
-
- /* tag the context in case we know it */
- log_set_context(LOG_CTX_VLR_SUBSCR, subscr);
- return subscr;
-}
-
-/*! \brief find a TLV with given tag in list of libsmpp34 TLVs */
-static struct tlv_t *find_tlv(struct tlv_t *head, uint16_t tag)
-{
- struct tlv_t *t;
-
- for (t = head; t != NULL; t = t->next) {
- if (t->tag == tag)
- return t;
- }
- return NULL;
-}
-
-/*! \brief convert from submit_sm_t to gsm_sms */
-static int submit_to_sms(struct gsm_sms **psms, struct gsm_network *net,
- const struct submit_sm_t *submit)
-{
- struct gsm_subscriber *dest;
- struct gsm_sms *sms;
- struct tlv_t *t;
- const uint8_t *sms_msg;
- unsigned int sms_msg_len;
- int mode;
-
- dest = subscr_by_dst(net, submit->dest_addr_npi,
- submit->dest_addr_ton,
- (const char *)submit->destination_addr);
- if (!dest) {
- LOGP(DLSMS, LOGL_NOTICE, "SMPP SUBMIT-SM for unknown subscriber: "
- "%s (NPI=%u)\n", submit->destination_addr,
- submit->dest_addr_npi);
- return ESME_RINVDSTADR;
- }
-
- t = find_tlv(submit->tlv, TLVID_message_payload);
- if (t) {
- if (submit->sm_length) {
- /* ERROR: we cannot have both! */
- LOGP(DLSMS, LOGL_ERROR, "SMPP Cannot have payload in "
- "TLV _and_ in the header\n");
- subscr_put(dest);
- return ESME_ROPTPARNOTALLWD;
- }
- sms_msg = t->value.octet;
- sms_msg_len = t->length;
- } else if (submit->sm_length > 0 && submit->sm_length < 255) {
- sms_msg = submit->short_message;
- sms_msg_len = submit->sm_length;
- } else {
- LOGP(DLSMS, LOGL_ERROR,
- "SMPP neither message payload nor valid sm_length.\n");
- subscr_put(dest);
- return ESME_RINVPARLEN;
- }
-
- sms = sms_alloc();
- sms->source = SMS_SOURCE_SMPP;
- sms->smpp.sequence_nr = submit->sequence_number;
-
- /* fill in the destination address */
- sms->receiver = dest;
- sms->dst.ton = submit->dest_addr_ton;
- sms->dst.npi = submit->dest_addr_npi;
- osmo_strlcpy(sms->dst.addr, dest->extension, 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 gsm_subscriber *subscr,
- uint8_t smpp_avail_status)
-{
- struct osmo_esme *esme;
-
- llist_for_each_entry(esme, &smsc->esme_list, list) {
- /* we currently send an alert notification to each ESME that is
- * connected, and do not require a (non-existant) delivery
- * pending flag to be set before, FIXME: make this VTY
- * configurable */
- if (esme->acl && esme->acl->deliver_src_imsi) {
- smpp_tx_alert(esme, TON_Subscriber_Number,
- NPI_Land_Mobile_E212,
- subscr->imsi, smpp_avail_status);
- } else {
- smpp_tx_alert(esme, TON_Network_Specific,
- NPI_ISDN_E163_E164,
- subscr->extension, 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->subscr) {
- /* SMMA without a subscriber? strange... */
- LOGP(DLSMS, LOGL_NOTICE, "SMMA without subscriber?\n");
- break;
- }
-
- /* There's no real 1:1 match for SMMA in SMPP. However,
- * an ALERT NOTIFICATION seems to be the most logical
- * choice */
- alert_all_esme(smsc, sig_sms->trans->subscr, 0);
- break;
- }
-
- return rc;
-}
-
-/*! \brief signal handler for subscriber related signals */
-static int smpp_subscr_cb(unsigned int subsys, unsigned int signal,
- void *handler_data, void *signal_data)
-{
- struct gsm_subscriber *subscr = signal_data;
- struct smsc *smsc = handler_data;
- uint8_t smpp_avail_status;
-
- /* determine the smpp_avail_status depending on attach/detach */
- switch (signal) {
- case S_SUBSCR_ATTACHED:
- smpp_avail_status = 0;
- break;
- case S_SUBSCR_DETACHED:
- smpp_avail_status = 2;
- break;
- default:
- return 0;
- }
-
- alert_all_esme(smsc, subscr, smpp_avail_status);
-
- return 0;
-}
-
-/* GSM 03.38 6.2.1 Character expanding (no decode!) */
-static int gsm_7bit_expand(char *text, const uint8_t *user_data, uint8_t septet_l, uint8_t ud_hdr_ind)
-{
- int i = 0;
- int shift = 0;
- uint8_t c;
-
- /* skip the user data header */
- if (ud_hdr_ind) {
- /* get user data header length + 1 (for the 'user data header length'-field) */
- shift = ((user_data[0] + 1) * 8) / 7;
- if ((((user_data[0] + 1) * 8) % 7) != 0)
- shift++;
- septet_l = septet_l - shift;
- }
-
- for (i = 0; i < septet_l; i++) {
- c =
- ((user_data[((i + shift) * 7 + 7) >> 3] <<
- (7 - (((i + shift) * 7 + 7) & 7))) |
- (user_data[((i + shift) * 7) >> 3] >>
- (((i + shift) * 7) & 7))) & 0x7f;
-
- *(text++) = c;
- }
-
- *text = '\0';
-
- return i;
-}
-
-
-/* FIXME: libsmpp34 helpers, they should be part of libsmpp34! */
-void append_tlv(tlv_t **req_tlv, uint16_t tag,
- const uint8_t *data, uint16_t len)
-{
- tlv_t tlv;
-
- memset(&tlv, 0, sizeof(tlv));
- tlv.tag = tag;
- tlv.length = len;
- memcpy(tlv.value.octet, data, tlv.length);
- build_tlv(req_tlv, &tlv);
-}
-void append_tlv_u8(tlv_t **req_tlv, uint16_t tag, uint8_t val)
-{
- tlv_t tlv;
-
- memset(&tlv, 0, sizeof(tlv));
- tlv.tag = tag;
- tlv.length = 1;
- tlv.value.val08 = val;
- build_tlv(req_tlv, &tlv);
-}
-void append_tlv_u16(tlv_t **req_tlv, uint16_t tag, uint16_t val)
-{
- tlv_t tlv;
-
- memset(&tlv, 0, sizeof(tlv));
- tlv.tag = tag;
- tlv.length = 2;
- tlv.value.val16 = htons(val);
- build_tlv(req_tlv, &tlv);
-}
-
-/* Append the Osmocom vendor-specific additional TLVs to a SMPP msg */
-static void append_osmo_tlvs(tlv_t **req_tlv, const struct gsm_lchan *lchan)
-{
- int idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep),
- lchan->meas_rep_idx, 1);
- const struct gsm_meas_rep *mr = &lchan->meas_rep[idx];
- const struct gsm_meas_rep_unidir *ul_meas = &mr->ul;
- const struct gsm_meas_rep_unidir *dl_meas = &mr->dl;
-
- /* Osmocom vendor-specific SMPP34 extensions */
- append_tlv_u16(req_tlv, TLVID_osmo_arfcn, lchan->ts->trx->arfcn);
- if (mr->flags & MEAS_REP_F_MS_L1) {
- uint8_t ms_dbm;
- append_tlv_u8(req_tlv, TLVID_osmo_ta, mr->ms_l1.ta);
- ms_dbm = ms_pwr_dbm(lchan->ts->trx->bts->band, mr->ms_l1.pwr);
- append_tlv_u8(req_tlv, TLVID_osmo_ms_l1_txpwr, ms_dbm);
- } else if (mr->flags & MEAS_REP_F_MS_TO) /* 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->subscr) {
- struct gsm_subscriber *subscr = lchan->conn->subscr;
- size_t imei_len = strlen(subscr->equipment.imei);
- if (imei_len)
- append_tlv(req_tlv, TLVID_osmo_imei,
- (uint8_t *)subscr->equipment.imei, imei_len+1);
- }
-}
-
-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);
- subscr_put(cmd->subscr);
- 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->subscr);
- 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->subscr);
- 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 gsm_subscriber *subscr, 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->subscr = subscr_get(subscr);
-
- /* 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->subscr->imsi);
- } else {
- deliver.source_addr_ton = TON_Network_Specific;
- deliver.source_addr_npi = NPI_ISDN_E163_E164;
- snprintf((char *)deliver.source_addr,
- sizeof(deliver.source_addr), "%s",
- conn->subscr->extension);
- }
-
- deliver.dest_addr_ton = sms->dst.ton;
- deliver.dest_addr_npi = sms->dst.npi;
- memcpy(deliver.destination_addr, sms->dst.addr,
- sizeof(deliver.destination_addr));
-
- deliver.esm_class = 1; /* datagram mode */
- if (sms->ud_hdr_ind)
- deliver.esm_class |= 0x40;
- if (sms->reply_path_req)
- deliver.esm_class |= 0x80;
-
- deliver.protocol_id = sms->protocol_id;
- deliver.priority_flag = 0;
- deliver.registered_delivery = 0;
-
- /* Figure out SMPP DCS from TP-DCS */
- dcs = sms->data_coding_scheme;
- if (smpp_determine_scheme(dcs, &deliver.data_coding, &mode) == -1)
- return -1;
-
- /* Transparently pass on DCS via SMPP if requested */
- if (esme->acl && esme->acl->dcs_transparent)
- deliver.data_coding = dcs;
-
- if (mode == MODE_7BIT) {
- uint8_t *dst = deliver.short_message;
-
- /* SMPP has this strange notion of putting 7bit SMS in
- * an octet-aligned mode */
- if (sms->ud_hdr_ind) {
- /* length (bytes) of UDH inside UD */
- uint8_t udh_len = sms->user_data[0] + 1;
-
- /* copy over the UDH */
- memcpy(dst, sms->user_data, udh_len);
- dst += udh_len;
- deliver.sm_length = udh_len;
- }
- /* add decoded text */
- deliver.sm_length += gsm_7bit_expand((char *)dst, sms->user_data, sms->user_data_len, sms->ud_hdr_ind);
- } else {
- deliver.sm_length = sms->user_data_len;
- memcpy(deliver.short_message, sms->user_data, deliver.sm_length);
- deliver.sm_length = sms->user_data_len;
- memcpy(deliver.short_message, sms->user_data, deliver.sm_length);
- }
-
- if (esme->acl && esme->acl->osmocom_ext && conn->lchan)
- append_osmo_tlvs(&deliver.tlv, conn->lchan);
-
- ret = smpp_tx_deliver(esme, &deliver);
- if (ret < 0)
- return ret;
-
- return smpp_cmd_enqueue(esme, conn->subscr, 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/openbsc/src/libmsc/smpp_smsc.c b/openbsc/src/libmsc/smpp_smsc.c
deleted file mode 100644
index 48a119261..000000000
--- a/openbsc/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/openbsc/src/libmsc/smpp_smsc.h b/openbsc/src/libmsc/smpp_smsc.h
deleted file mode 100644
index d8e82e421..000000000
--- a/openbsc/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 gsm_subscriber *subscr;
- 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/openbsc/src/libmsc/smpp_utils.c b/openbsc/src/libmsc/smpp_utils.c
deleted file mode 100644
index d0850d8c1..000000000
--- a/openbsc/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/openbsc/src/libmsc/smpp_vty.c b/openbsc/src/libmsc/smpp_vty.c
deleted file mode 100644
index 13467f182..000000000
--- a/openbsc/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/openbsc/src/libmsc/sms_queue.c b/openbsc/src/libmsc/sms_queue.c
deleted file mode 100644
index dc7f6e8c6..000000000
--- a/openbsc/src/libmsc/sms_queue.c
+++ /dev/null
@@ -1,544 +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 <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 <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 gsm_subscriber *subscr;
- unsigned long long sms_id;
- int failed_attempts;
- int resend;
-};
-
-struct gsm_sms_queue {
- struct osmo_timer_list resend_pending;
- struct osmo_timer_list push_queue;
- struct gsm_network *network;
- int max_fail;
- int max_pending;
- int pending;
-
- struct llist_head pending_sms;
- unsigned long long last_subscr_id;
-};
-
-static int sms_subscr_cb(unsigned int, unsigned int, void *, void *);
-static int sms_sms_cb(unsigned int, unsigned int, void *, void *);
-
-static struct gsm_sms_pending *sms_find_pending(struct gsm_sms_queue *smsq,
- struct gsm_sms *sms)
-{
- struct gsm_sms_pending *pending;
-
- llist_for_each_entry(pending, &smsq->pending_sms, entry) {
- if (pending->sms_id == sms->id)
- return pending;
- }
-
- return NULL;
-}
-
-static int sms_is_in_pending(struct gsm_sms_queue *smsq, struct gsm_sms *sms)
-{
- return sms_find_pending(smsq, sms) != NULL;
-}
-
-static struct gsm_sms_pending *sms_subscriber_find_pending(
- struct gsm_sms_queue *smsq,
- struct gsm_subscriber *subscr)
-{
- struct gsm_sms_pending *pending;
-
- llist_for_each_entry(pending, &smsq->pending_sms, entry) {
- if (pending->subscr == subscr)
- return pending;
- }
-
- return NULL;
-}
-
-static int sms_subscriber_is_pending(struct gsm_sms_queue *smsq,
- struct gsm_subscriber *subscr)
-{
- return sms_subscriber_find_pending(smsq, subscr) != NULL;
-}
-
-static struct gsm_sms_pending *sms_pending_from(struct gsm_sms_queue *smsq,
- struct gsm_sms *sms)
-{
- struct gsm_sms_pending *pending;
-
- pending = talloc_zero(smsq, struct gsm_sms_pending);
- if (!pending)
- return NULL;
-
- pending->subscr = subscr_get(sms->receiver);
- pending->sms_id = sms->id;
- return pending;
-}
-
-static void sms_pending_free(struct gsm_sms_pending *pending)
-{
- subscr_put(pending->subscr);
- llist_del(&pending->entry);
- talloc_free(pending);
-}
-
-static void sms_pending_resend(struct gsm_sms_pending *pending)
-{
- struct gsm_sms_queue *smsq;
- LOGP(DLSMS, LOGL_DEBUG,
- "Scheduling resend of SMS %llu.\n", pending->sms_id);
-
- pending->resend = 1;
-
- smsq = pending->subscr->group->net->sms_queue;
- if (osmo_timer_pending(&smsq->resend_pending))
- return;
-
- osmo_timer_schedule(&smsq->resend_pending, 1, 0);
-}
-
-static void sms_pending_failed(struct gsm_sms_pending *pending, int paging_error)
-{
- struct gsm_sms_queue *smsq;
-
- LOGP(DLSMS, LOGL_NOTICE, "Sending SMS %llu failed %d times.\n",
- pending->sms_id, pending->failed_attempts);
-
- smsq = pending->subscr->group->net->sms_queue;
- if (++pending->failed_attempts < smsq->max_fail)
- return sms_pending_resend(pending);
-
- sms_pending_free(pending);
- smsq->pending -= 1;
- sms_queue_trigger(smsq);
-}
-
-/*
- * Resend all SMS that are scheduled for a resend. This is done to
- * avoid an immediate failure.
- */
-static void sms_resend_pending(void *_data)
-{
- struct gsm_sms_pending *pending, *tmp;
- struct gsm_sms_queue *smsq = _data;
-
- llist_for_each_entry_safe(pending, tmp, &smsq->pending_sms, entry) {
- struct gsm_sms *sms;
- if (!pending->resend)
- continue;
-
- sms = db_sms_get(smsq->network, pending->sms_id);
-
- /* the sms is gone? Move to the next */
- if (!sms) {
- sms_pending_free(pending);
- smsq->pending -= 1;
- sms_queue_trigger(smsq);
- } else {
- pending->resend = 0;
- gsm411_send_sms_subscr(sms->receiver, sms);
- }
- }
-}
-
-static struct gsm_sms *take_next_sms(struct gsm_sms_queue *smsq)
-{
- struct gsm_sms *sms;
-
- sms = db_sms_get_unsent_by_subscr(smsq->network, smsq->last_subscr_id, 10);
- if (sms) {
- smsq->last_subscr_id = sms->receiver->id + 1;
- return sms;
- }
-
- /* need to wrap around */
- smsq->last_subscr_id = 0;
- sms = db_sms_get_unsent_by_subscr(smsq->network,
- smsq->last_subscr_id, 10);
- if (sms)
- smsq->last_subscr_id = sms->receiver->id + 1;
- return sms;
-}
-
-/**
- * I will submit up to max_pending - pending SMS to the
- * subsystem.
- */
-static void sms_submit_pending(void *_data)
-{
- struct gsm_sms_queue *smsq = _data;
- int attempts = smsq->max_pending - smsq->pending;
- int initialized = 0;
- unsigned long long first_sub = 0;
- int attempted = 0, rounds = 0;
-
- LOGP(DLSMS, LOGL_DEBUG, "Attempting to send %d SMS\n", attempts);
-
- do {
- struct gsm_sms_pending *pending;
- struct gsm_sms *sms;
-
-
- sms = take_next_sms(smsq);
- if (!sms) {
- 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 gsm_subscriber *subscr)
-{
- struct gsm_sms_queue *smsq = subscr->group->net->sms_queue;
- struct gsm_sms_pending *pending;
- struct gsm_sms *sms;
-
- /* the subscriber should not be in the queue */
- OSMO_ASSERT(!sms_subscriber_is_pending(smsq, subscr));
-
- /* check for more messages for this subscriber */
- sms = db_sms_get_unsent_for_subscr(subscr);
- if (!sms)
- goto no_pending_sms;
-
- /* No sms should be scheduled right now */
- OSMO_ASSERT(!sms_is_in_pending(smsq, sms));
-
- /* Remember that we deliver this SMS and send it */
- pending = sms_pending_from(smsq, sms);
- if (!pending) {
- LOGP(DLSMS, LOGL_ERROR,
- "Failed to create pending SMS entry.\n");
- sms_free(sms);
- goto no_pending_sms;
- }
-
- smsq->pending += 1;
- llist_add_tail(&pending->entry, &smsq->pending_sms);
- gsm411_send_sms_subscr(sms->receiver, sms);
- return;
-
-no_pending_sms:
- /* Try to send the SMS to avoid the queue being stuck */
- sms_submit_pending(subscr->group->net->sms_queue);
-}
-
-/*
- * Kick off the queue again.
- */
-int sms_queue_trigger(struct gsm_sms_queue *smsq)
-{
- 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 gsm_subscriber *subscr)
-{
- struct gsm_sms *sms;
- struct gsm_sms_pending *pending;
- struct gsm_subscriber_connection *conn;
-
- /*
- * The code used to be very clever and tried to submit
- * a SMS during the Location Updating Request. This has
- * two issues:
- * 1.) The Phone might not be ready yet, e.g. the C155
- * will not respond to the Submit when it is booting.
- * 2.) The queue is already trying to submit SMS to the
- * user and by not responding to the paging request
- * we will set the LAC back to 0. We would have to
- * stop the paging and move things over.
- *
- * We need to be careful in what we try here.
- */
-
- /* check if we have pending requests */
- pending = sms_subscriber_find_pending(net->sms_queue, subscr);
- if (pending) {
- LOGP(DMSC, LOGL_NOTICE,
- "Pending paging while subscriber %llu attached.\n",
- subscr->id);
- return 0;
- }
-
- conn = connection_for_subscr(subscr);
- if (!conn)
- return -1;
-
- /* Now try to deliver any pending SMS to this sub */
- sms = db_sms_get_unsent_for_subscr(subscr);
- if (!sms)
- return -1;
- gsm411_send_sms(conn, sms);
- return 0;
-}
-
-static int sms_subscr_cb(unsigned int subsys, unsigned int signal,
- void *handler_data, void *signal_data)
-{
- struct gsm_subscriber *subscr = signal_data;
-
- if (signal != S_SUBSCR_ATTACHED)
- return 0;
-
- /* this is readyForSM */
- return sub_ready_for_sm(handler_data, subscr);
-}
-
-static int sms_sms_cb(unsigned int subsys, unsigned int signal,
- void *handler_data, void *signal_data)
-{
- struct gsm_network *network = handler_data;
- struct sms_signal_data *sig_sms = signal_data;
- struct gsm_sms_pending *pending;
- struct gsm_subscriber *subscr;
-
- /* We got a new SMS and maybe should launch the queue again. */
- if (signal == S_SMS_SUBMITTED || signal == S_SMS_SMMA) {
- /* TODO: For SMMA we might want to re-use the radio connection. */
- sms_queue_trigger(network->sms_queue);
- return 0;
- }
-
- if (!sig_sms->sms)
- return -1;
-
-
- /*
- * Find the entry of our queue. The SMS subsystem will submit
- * sms that are not in our control as we just have a channel
- * open anyway.
- */
- pending = sms_find_pending(network->sms_queue, sig_sms->sms);
- if (!pending)
- return 0;
-
- switch (signal) {
- case S_SMS_DELIVERED:
- /* Remember the subscriber and clear the pending entry */
- network->sms_queue->pending -= 1;
- subscr = subscr_get(pending->subscr);
- sms_pending_free(pending);
- /* Attempt to send another SMS to this subscriber */
- sms_send_next(subscr);
- subscr_put(subscr);
- break;
- case S_SMS_MEM_EXCEEDED:
- network->sms_queue->pending -= 1;
- sms_pending_free(pending);
- sms_queue_trigger(network->sms_queue);
- break;
- case S_SMS_UNKNOWN_ERROR:
- /*
- * There can be many reasons for this failure. E.g. the paging
- * timed out, the subscriber was not paged at all, or there was
- * a protocol error. The current strategy is to try sending the
- * next SMS for busy/oom and to retransmit when we have paged.
- *
- * When the paging expires three times we will disable the
- * subscriber. If we have some kind of other transmit error we
- * should flag the SMS as bad.
- */
- switch (sig_sms->paging_result) {
- case 0:
- /* BAD SMS? */
- db_sms_inc_deliver_attempts(sig_sms->sms);
- sms_pending_failed(pending, 0);
- break;
- case GSM_PAGING_EXPIRED:
- sms_pending_failed(pending, 1);
- break;
-
- case GSM_PAGING_OOM:
- case GSM_PAGING_BUSY:
- network->sms_queue->pending -= 1;
- sms_pending_free(pending);
- sms_queue_trigger(network->sms_queue);
- break;
- default:
- LOGP(DLSMS, LOGL_ERROR, "Unhandled result: %d\n",
- sig_sms->paging_result);
- }
- break;
- default:
- LOGP(DLSMS, LOGL_ERROR, "Unhandled result: %d\n",
- sig_sms->paging_result);
- }
-
- return 0;
-}
-
-/* VTY helper functions */
-int sms_queue_stats(struct gsm_sms_queue *smsq, struct vty *vty)
-{
- struct gsm_sms_pending *pending;
-
- vty_out(vty, "SMSqueue with max_pending: %d pending: %d%s",
- smsq->max_pending, smsq->pending, VTY_NEWLINE);
-
- llist_for_each_entry(pending, &smsq->pending_sms, entry)
- vty_out(vty, " SMS Pending for Subscriber: %llu SMS: %llu Failed: %d.%s",
- pending->subscr->id, pending->sms_id,
- pending->failed_attempts, VTY_NEWLINE);
- return 0;
-}
-
-int sms_queue_set_max_pending(struct gsm_sms_queue *smsq, int max_pending)
-{
- LOGP(DLSMS, LOGL_NOTICE, "SMSqueue old max: %d new: %d\n",
- smsq->max_pending, max_pending);
- smsq->max_pending = max_pending;
- return 0;
-}
-
-int sms_queue_set_max_failure(struct gsm_sms_queue *smsq, int max_fail)
-{
- LOGP(DLSMS, LOGL_NOTICE, "SMSqueue max failure old: %d new: %d\n",
- smsq->max_fail, max_fail);
- smsq->max_fail = max_fail;
- return 0;
-}
-
-int sms_queue_clear(struct gsm_sms_queue *smsq)
-{
- struct gsm_sms_pending *pending, *tmp;
-
- llist_for_each_entry_safe(pending, tmp, &smsq->pending_sms, entry) {
- LOGP(DLSMS, LOGL_NOTICE,
- "SMSqueue clearing for sub %llu\n", pending->subscr->id);
- sms_pending_free(pending);
- }
-
- smsq->pending = 0;
- return 0;
-}
diff --git a/openbsc/src/libmsc/token_auth.c b/openbsc/src/libmsc/token_auth.c
deleted file mode 100644
index 5af1e980b..000000000
--- a/openbsc/src/libmsc/token_auth.c
+++ /dev/null
@@ -1,160 +0,0 @@
-/* SMS based token authentication for ad-hoc GSM networks */
-
-/* (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 <stdio.h>
-#include <osmocom/core/talloc.h>
-#include <openbsc/signal.h>
-#include <openbsc/gsm_data.h>
-#include <openbsc/gsm_04_11.h>
-#include <openbsc/gsm_04_08.h>
-#include <openbsc/gsm_subscriber.h>
-#include <openbsc/chan_alloc.h>
-#include <openbsc/db.h>
-
-#define TOKEN_SMS_TEXT "HAR 2009 GSM. Register at http://har2009.gnumonks.org/ " \
- "Your IMSI is %s, auth token is %08X, phone no is %s."
-
-static char *build_sms_string(struct gsm_subscriber *subscr, uint32_t token)
-{
- char *sms_str;
- unsigned int len;
-
- len = strlen(subscr->imsi) + 8 + strlen(TOKEN_SMS_TEXT);
- sms_str = talloc_size(tall_bsc_ctx, len);
- if (!sms_str)
- return NULL;
-
- snprintf(sms_str, len, TOKEN_SMS_TEXT, subscr->imsi, token,
- subscr->extension);
- sms_str[len-1] = '\0';
-
- return sms_str;
-}
-
-static int token_subscr_cb(unsigned int subsys, unsigned int signal,
- void *handler_data, void *signal_data)
-{
- struct gsm_subscriber *subscr = signal_data;
- struct gsm_sms *sms;
- int rc = 0;
-
- if (signal != S_SUBSCR_ATTACHED)
- return 0;
-
- if (subscr->group->net->auth_policy != GSM_AUTH_POLICY_TOKEN)
- return 0;
-
- if (subscr->flags & GSM_SUBSCRIBER_FIRST_CONTACT) {
- struct gsm_subscriber *sender;
- uint32_t token;
- char *sms_str;
-
- /* we've seen this subscriber for the first time. */
- rc = db_subscriber_alloc_token(subscr, &token);
- if (rc != 0) {
- rc = -EIO;
- goto unauth;
- }
-
- sms_str = build_sms_string(subscr, token);
- if (!sms_str) {
- rc = -ENOMEM;
- goto unauth;
- }
-
-
- /* FIXME: don't use ID 1 static */
- sender = subscr_get_by_id(subscr->group, 1);
-
- sms = sms_from_text(subscr, sender, 0, sms_str);
-
- subscr_put(sender);
- talloc_free(sms_str);
- if (!sms) {
- rc = -ENOMEM;
- goto unauth;
- }
-
- rc = gsm411_send_sms_subscr(subscr, sms);
-
- /* FIXME: else, delete the subscirber from database */
-unauth:
-
- /* make sure we don't allow him in again unless he clicks the web UI */
- subscr->authorized = 0;
- db_sync_subscriber(subscr);
- if (rc) {
- struct gsm_subscriber_connection *conn = connection_for_subscr(subscr);
- if (conn) {
- uint8_t auth_rand[16];
- /* kick the subscriber off the network */
- gsm48_tx_mm_auth_req(conn, auth_rand, NULL, 0);
- gsm48_tx_mm_auth_rej(conn);
- /* FIXME: close the channel early ?*/
- //gsm48_send_rr_Release(lchan);
- }
- }
- }
-
- return rc;
-}
-
-static int token_sms_cb(unsigned int subsys, unsigned int signal,
- void *handler_data, void *signal_data)
-{
- struct sms_signal_data *sig = signal_data;
- struct gsm_sms *sms = sig->sms;;
- struct gsm_subscriber_connection *conn;
- uint8_t auth_rand[16];
-
-
- if (signal != S_SMS_DELIVERED)
- return 0;
-
-
- /* these are not the droids we've been looking for */
- if (!sms->receiver ||
- !(sms->receiver->flags & GSM_SUBSCRIBER_FIRST_CONTACT))
- return 0;
-
-
- if (sms->receiver->group->net->auth_policy != GSM_AUTH_POLICY_TOKEN)
- return 0;
-
-
- conn = connection_for_subscr(sms->receiver);
- if (conn) {
- /* kick the subscriber off the network */
- gsm48_tx_mm_auth_req(conn, auth_rand, NULL, 0);
- gsm48_tx_mm_auth_rej(conn);
- /* FIXME: close the channel early ?*/
- //gsm48_send_rr_Release(lchan);
- }
-
- return 0;
-}
-
-//static __attribute__((constructor)) void on_dso_load_token(void)
-void on_dso_load_token(void)
-{
- osmo_signal_register_handler(SS_SUBSCR, token_subscr_cb, NULL);
- osmo_signal_register_handler(SS_SMS, token_sms_cb, NULL);
-}
diff --git a/openbsc/src/libmsc/transaction.c b/openbsc/src/libmsc/transaction.c
deleted file mode 100644
index dba4bed17..000000000
--- a/openbsc/src/libmsc/transaction.c
+++ /dev/null
@@ -1,163 +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_subscriber.h>
-#include <openbsc/gsm_04_08.h>
-#include <openbsc/mncc.h>
-#include <openbsc/paging.h>
-#include <openbsc/osmo_msc.h>
-
-void *tall_trans_ctx;
-
-void _gsm48_cc_trans_free(struct gsm_trans *trans);
-
-struct gsm_trans *trans_find_by_id(struct gsm_subscriber_connection *conn,
- uint8_t proto, uint8_t trans_id)
-{
- struct gsm_trans *trans;
- struct gsm_network *net = conn->network;
- struct gsm_subscriber *subscr = conn->subscr;
-
- llist_for_each_entry(trans, &net->trans_list, entry) {
- if (trans->subscr == subscr &&
- trans->protocol == proto &&
- trans->transaction_id == trans_id)
- return trans;
- }
- return NULL;
-}
-
-struct gsm_trans *trans_find_by_callref(struct gsm_network *net,
- uint32_t callref)
-{
- struct gsm_trans *trans;
-
- llist_for_each_entry(trans, &net->trans_list, entry) {
- if (trans->callref == callref)
- return trans;
- }
- return NULL;
-}
-
-struct gsm_trans *trans_alloc(struct gsm_network *net,
- struct gsm_subscriber *subscr,
- uint8_t protocol, uint8_t trans_id,
- uint32_t callref)
-{
- struct gsm_trans *trans;
-
- DEBUGP(DCC, "subscr=%p, net=%p\n", subscr, net);
-
- trans = talloc_zero(tall_trans_ctx, struct gsm_trans);
- if (!trans)
- return NULL;
-
- trans->subscr = subscr;
- subscr_get(trans->subscr);
-
- trans->protocol = protocol;
- trans->transaction_id = trans_id;
- trans->callref = callref;
-
- trans->net = net;
- llist_add_tail(&trans->entry, &net->trans_list);
-
- return trans;
-}
-
-void trans_free(struct gsm_trans *trans)
-{
- switch (trans->protocol) {
- case GSM48_PDISC_CC:
- _gsm48_cc_trans_free(trans);
- break;
- case GSM48_PDISC_SMS:
- _gsm411_sms_trans_free(trans);
- break;
- }
-
- if (trans->paging_request) {
- subscr_remove_request(trans->paging_request);
- trans->paging_request = NULL;
- }
-
- if (trans->subscr) {
- subscr_put(trans->subscr);
- trans->subscr = NULL;
- }
-
- llist_del(&trans->entry);
-
- if (trans->conn)
- msc_release_connection(trans->conn);
-
- trans->conn = NULL;
- talloc_free(trans);
-}
-
-/* allocate an unused transaction ID for the given subscriber
- * in the given protocol using the ti_flag specified */
-int trans_assign_trans_id(struct gsm_network *net, struct gsm_subscriber *subscr,
- uint8_t protocol, uint8_t ti_flag)
-{
- struct gsm_trans *trans;
- unsigned int used_tid_bitmask = 0;
- int i, j, h;
-
- if (ti_flag)
- ti_flag = 0x8;
-
- /* generate bitmask of already-used TIDs for this (subscr,proto) */
- llist_for_each_entry(trans, &net->trans_list, entry) {
- if (trans->subscr != subscr ||
- trans->protocol != protocol ||
- trans->transaction_id == 0xff)
- continue;
- used_tid_bitmask |= (1 << trans->transaction_id);
- }
-
- /* find a new one, trying to go in a 'circular' pattern */
- for (h = 6; h > 0; h--)
- if (used_tid_bitmask & (1 << (h | ti_flag)))
- break;
- for (i = 0; i < 7; i++) {
- j = ((h + i) % 7) | ti_flag;
- if ((used_tid_bitmask & (1 << j)) == 0)
- return j;
- }
-
- return -1;
-}
-
-int trans_has_conn(const struct gsm_subscriber_connection *conn)
-{
- struct gsm_trans *trans;
-
- llist_for_each_entry(trans, &conn->network->trans_list, entry)
- if (trans->conn == conn)
- return 1;
-
- return 0;
-}
diff --git a/openbsc/src/libmsc/ussd.c b/openbsc/src/libmsc/ussd.c
deleted file mode 100644
index f12c1f281..000000000
--- a/openbsc/src/libmsc/ussd.c
+++ /dev/null
@@ -1,95 +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>
-
-/* 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;
-
- 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);
- msc_release_connection(conn);
- 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);
- msc_release_connection(conn);
- return rc;
- }
- /* Still assuming a Release-Complete and returning */
- return 0;
- }
-
- 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);
- }
-
- /* check if we can release it */
- msc_release_connection(conn);
- return rc;
-}
-
-/* A network-specific handler function */
-static int send_own_number(struct gsm_subscriber_connection *conn, const struct msgb *msg, const struct ss_request *req)
-{
- char *own_number = conn->subscr->extension;
- char response_string[GSM_EXTENSION_LENGTH + 20];
-
- /* Need trailing CR as EOT character */
- snprintf(response_string, sizeof(response_string), "Your extension is %s\r", own_number);
- return gsm0480_send_ussd_response(conn, msg, response_string, req);
-}
diff --git a/openbsc/src/libmsc/vty_interface_layer3.c b/openbsc/src/libmsc/vty_interface_layer3.c
deleted file mode 100644
index e50329104..000000000
--- a/openbsc/src/libmsc/vty_interface_layer3.c
+++ /dev/null
@@ -1,1210 +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 <stdbool.h>
-#include <inttypes.h>
-#include <time.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 <osmocom/vty/logging.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 gsm_subscriber *subscr)
-{
- int rc;
- int reqs;
- struct gsm_auth_info ainfo;
- struct gsm_auth_tuple atuple;
- struct llist_head *entry;
- char expire_time[200];
-
- vty_out(vty, " ID: %llu, Authorized: %d%s", subscr->id,
- subscr->authorized, VTY_NEWLINE);
- if (strlen(subscr->name))
- vty_out(vty, " Name: '%s'%s", subscr->name, VTY_NEWLINE);
- if (strlen(subscr->extension))
- vty_out(vty, " Extension: %s%s", subscr->extension,
- VTY_NEWLINE);
- vty_out(vty, " LAC: %d/0x%x%s",
- subscr->lac, subscr->lac, VTY_NEWLINE);
- vty_out(vty, " IMSI: %s%s", subscr->imsi, VTY_NEWLINE);
- if (subscr->tmsi != GSM_RESERVED_TMSI)
- vty_out(vty, " TMSI: %08X%s", subscr->tmsi,
- VTY_NEWLINE);
-
- rc = db_get_authinfo_for_subscr(&ainfo, subscr);
- if (!rc) {
- vty_out(vty, " A3A8 algorithm id: %d%s",
- ainfo.auth_algo, VTY_NEWLINE);
- vty_out(vty, " A3A8 Ki: %s%s",
- osmo_hexdump(ainfo.a3a8_ki, ainfo.a3a8_ki_len),
- VTY_NEWLINE);
- }
-
- rc = db_get_lastauthtuple_for_subscr(&atuple, subscr);
- if (!rc) {
- vty_out(vty, " A3A8 last tuple (used %d times):%s",
- atuple.use_count, VTY_NEWLINE);
- vty_out(vty, " seq # : %d%s",
- atuple.key_seq, VTY_NEWLINE);
- vty_out(vty, " RAND : %s%s",
- osmo_hexdump(atuple.vec.rand, sizeof(atuple.vec.rand)),
- VTY_NEWLINE);
- vty_out(vty, " SRES : %s%s",
- osmo_hexdump(atuple.vec.sres, sizeof(atuple.vec.sres)),
- VTY_NEWLINE);
- vty_out(vty, " Kc : %s%s",
- osmo_hexdump(atuple.vec.kc, sizeof(atuple.vec.kc)),
- VTY_NEWLINE);
- }
-
- /* print the expiration time of a subscriber */
- strftime(expire_time, sizeof(expire_time),
- "%a, %d %b %Y %T %z", localtime(&subscr->expire_lu));
- expire_time[sizeof(expire_time) - 1] = '\0';
- vty_out(vty, " Expiration Time: %s%s", expire_time, VTY_NEWLINE);
-
- reqs = 0;
- llist_for_each(entry, &subscr->requests)
- reqs += 1;
- vty_out(vty, " Paging: %s paging Requests: %d%s",
- subscr->is_paging ? "is" : "not", reqs, VTY_NEWLINE);
- vty_out(vty, " Use count: %u%s", subscr->use_count, VTY_NEWLINE);
-}
-
-
-/* Subscriber */
-DEFUN(show_subscr_cache,
- show_subscr_cache_cmd,
- "show subscriber cache",
- SHOW_STR "Show information about subscribers\n"
- "Display contents of subscriber cache\n")
-{
- struct gsm_subscriber *subscr;
-
- llist_for_each_entry(subscr, &active_subscribers, entry) {
- vty_out(vty, " Subscriber:%s", VTY_NEWLINE);
- subscr_dump_full_vty(vty, subscr);
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN(sms_send_pend,
- sms_send_pend_cmd,
- "sms send pending",
- "SMS related commands\n" "SMS Sending related commands\n"
- "Send all pending SMS")
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- struct gsm_sms *sms;
- int id = 0;
-
- while (1) {
- sms = db_sms_get_unsent_by_subscr(gsmnet, id, UINT_MAX);
- if (!sms)
- break;
-
- gsm411_send_sms_subscr(sms->receiver, sms);
-
- id = sms->receiver->id + 1;
- }
-
- return CMD_SUCCESS;
-}
-
-static int _send_sms_str(struct gsm_subscriber *receiver,
- struct gsm_subscriber *sender,
- char *str, uint8_t tp_pid)
-{
- struct gsm_sms *sms;
-
- sms = sms_from_text(receiver, sender, 0, str);
- sms->protocol_id = tp_pid;
-
- /* store in database for the queue */
- if (db_sms_store(sms) != 0) {
- LOGP(DLSMS, LOGL_ERROR, "Failed to store SMS in Database\n");
- sms_free(sms);
- return CMD_WARNING;
- }
- LOGP(DLSMS, LOGL_DEBUG, "SMS stored in DB\n");
-
- sms_free(sms);
- sms_queue_trigger(receiver->group->net->sms_queue);
- return CMD_SUCCESS;
-}
-
-static struct gsm_subscriber *get_subscr_by_argv(struct gsm_network *gsmnet,
- const char *type,
- const char *id)
-{
- if (!strcmp(type, "extension"))
- return subscr_get_by_extension(gsmnet->subscr_group, id);
- else if (!strcmp(type, "imsi"))
- return subscr_get_by_imsi(gsmnet->subscr_group, id);
- else if (!strcmp(type, "tmsi"))
- return subscr_get_by_tmsi(gsmnet->subscr_group, atoi(id));
- else if (!strcmp(type, "id"))
- return subscr_get_by_id(gsmnet->subscr_group, atoi(id));
-
- return NULL;
-}
-#define SUBSCR_TYPES "(extension|imsi|tmsi|id)"
-#define SUBSCR_HELP "Operations on a Subscriber\n" \
- "Identify subscriber by 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 gsm_subscriber *subscr =
- get_subscr_by_argv(gsmnet, argv[0], argv[1]);
-
- if (!subscr) {
- vty_out(vty, "%% No subscriber found for %s %s%s",
- argv[0], argv[1], VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- subscr_dump_full_vty(vty, subscr);
-
- subscr_put(subscr);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(subscriber_create,
- subscriber_create_cmd,
- "subscriber create imsi ID",
- "Operations on a Subscriber\n" \
- "Create new subscriber\n" \
- "Identify the subscriber by his IMSI\n" \
- "Identifier for the subscriber\n")
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- struct gsm_subscriber *subscr;
-
- subscr = subscr_get_by_imsi(gsmnet->subscr_group, argv[0]);
- if (subscr)
- db_sync_subscriber(subscr);
- else {
- subscr = subscr_create_subscriber(gsmnet->subscr_group, argv[0]);
-
- if (!subscr) {
- vty_out(vty, "%% No subscriber created for IMSI %s%s",
- argv[0], VTY_NEWLINE);
- return CMD_WARNING;
- }
- }
-
- /* Show info about the created subscriber. */
- subscr_dump_full_vty(vty, subscr);
-
- subscr_put(subscr);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(subscriber_send_pending_sms,
- subscriber_send_pending_sms_cmd,
- "subscriber " SUBSCR_TYPES " ID sms pending-send",
- SUBSCR_HELP "SMS Operations\n" "Send pending SMS\n")
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- struct gsm_subscriber *subscr;
- struct gsm_sms *sms;
-
- subscr = get_subscr_by_argv(gsmnet, argv[0], argv[1]);
- if (!subscr) {
- vty_out(vty, "%% No subscriber found for %s %s%s",
- argv[0], argv[1], VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- sms = db_sms_get_unsent_by_subscr(gsmnet, subscr->id, UINT_MAX);
- if (sms)
- gsm411_send_sms_subscr(sms->receiver, sms);
-
- subscr_put(subscr);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(subscriber_send_sms,
- subscriber_send_sms_cmd,
- "subscriber " SUBSCR_TYPES " ID sms sender " SUBSCR_TYPES " SENDER_ID send .LINE",
- SUBSCR_HELP "SMS Operations\n" SUBSCR_HELP "Send SMS\n" "Actual SMS Text\n")
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- struct gsm_subscriber *subscr = get_subscr_by_argv(gsmnet, argv[0], argv[1]);
- struct gsm_subscriber *sender = get_subscr_by_argv(gsmnet, argv[2], argv[3]);
- char *str;
- int rc;
-
- if (!subscr) {
- vty_out(vty, "%% No subscriber found for %s %s%s",
- argv[0], argv[1], VTY_NEWLINE);
- rc = CMD_WARNING;
- goto err;
- }
-
- if (!sender) {
- vty_out(vty, "%% No sender found for %s %s%s",
- argv[2], argv[3], VTY_NEWLINE);
- rc = CMD_WARNING;
- goto err;
- }
-
- str = argv_concat(argv, argc, 4);
- rc = _send_sms_str(subscr, sender, str, 0);
- talloc_free(str);
-
-err:
- if (sender)
- subscr_put(sender);
-
- if (subscr)
- subscr_put(subscr);
-
- return rc;
-}
-
-DEFUN(subscriber_silent_sms,
- subscriber_silent_sms_cmd,
-
- "subscriber " SUBSCR_TYPES " ID silent-sms sender " SUBSCR_TYPES " SENDER_ID send .LINE",
- SUBSCR_HELP "Silent SMS Operations\n" SUBSCR_HELP "Send SMS\n" "Actual SMS Text\n")
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- struct gsm_subscriber *subscr = get_subscr_by_argv(gsmnet, argv[0], argv[1]);
- struct gsm_subscriber *sender = get_subscr_by_argv(gsmnet, argv[2], argv[3]);
- char *str;
- int rc;
-
- if (!subscr) {
- vty_out(vty, "%% No subscriber found for %s %s%s",
- argv[0], argv[1], VTY_NEWLINE);
- rc = CMD_WARNING;
- goto err;
- }
-
- if (!sender) {
- vty_out(vty, "%% No sender found for %s %s%s",
- argv[2], argv[3], VTY_NEWLINE);
- rc = CMD_WARNING;
- goto err;
- }
-
- str = argv_concat(argv, argc, 4);
- rc = _send_sms_str(subscr, sender, str, 64);
- talloc_free(str);
-
-err:
- if (sender)
- subscr_put(sender);
-
- if (subscr)
- subscr_put(subscr);
-
- return rc;
-}
-
-#define CHAN_TYPES "(any|tch/f|tch/any|sdcch)"
-#define CHAN_TYPE_HELP \
- "Any channel\n" \
- "TCH/F channel\n" \
- "Any TCH channel\n" \
- "SDCCH channel\n"
-
-DEFUN(subscriber_silent_call_start,
- subscriber_silent_call_start_cmd,
- "subscriber " SUBSCR_TYPES " ID silent-call start (any|tch/f|tch/any|sdcch)",
- SUBSCR_HELP "Silent call operation\n" "Start silent call\n"
- CHAN_TYPE_HELP)
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- struct gsm_subscriber *subscr = get_subscr_by_argv(gsmnet, argv[0], argv[1]);
- int rc, type;
-
- if (!subscr) {
- vty_out(vty, "%% No subscriber found for %s %s%s",
- argv[0], argv[1], VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- if (!strcmp(argv[2], "tch/f"))
- type = RSL_CHANNEED_TCH_F;
- else if (!strcmp(argv[2], "tch/any"))
- type = RSL_CHANNEED_TCH_ForH;
- else if (!strcmp(argv[2], "sdcch"))
- type = RSL_CHANNEED_SDCCH;
- else
- type = RSL_CHANNEED_ANY; /* Defaults to ANY */
-
- rc = gsm_silent_call_start(subscr, vty, type);
- if (rc <= 0) {
- vty_out(vty, "%% Subscriber not attached%s",
- VTY_NEWLINE);
- subscr_put(subscr);
- return CMD_WARNING;
- }
-
- subscr_put(subscr);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(subscriber_silent_call_stop,
- subscriber_silent_call_stop_cmd,
- "subscriber " SUBSCR_TYPES " ID silent-call stop",
- SUBSCR_HELP "Silent call operation\n" "Stop silent call\n"
- CHAN_TYPE_HELP)
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- struct gsm_subscriber *subscr = get_subscr_by_argv(gsmnet, argv[0], argv[1]);
- int rc;
-
- if (!subscr) {
- vty_out(vty, "%% No subscriber found for %s %s%s",
- argv[0], argv[1], VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- rc = gsm_silent_call_stop(subscr);
- if (rc < 0) {
- subscr_put(subscr);
- return CMD_WARNING;
- }
-
- subscr_put(subscr);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(subscriber_ussd_notify,
- subscriber_ussd_notify_cmd,
- "subscriber " SUBSCR_TYPES " ID ussd-notify (0|1|2) .TEXT",
- SUBSCR_HELP "Send a USSD notify to the subscriber\n"
- "Alerting Level 0\n"
- "Alerting Level 1\n"
- "Alerting Level 2\n"
- "Text of USSD message to send\n")
-{
- char *text;
- struct gsm_subscriber_connection *conn;
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- struct gsm_subscriber *subscr = get_subscr_by_argv(gsmnet, argv[0], argv[1]);
- int level;
-
- if (!subscr) {
- vty_out(vty, "%% No subscriber found for %s %s%s",
- argv[0], argv[1], VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- level = atoi(argv[2]);
- text = argv_concat(argv, argc, 3);
- if (!text) {
- subscr_put(subscr);
- return CMD_WARNING;
- }
-
- conn = connection_for_subscr(subscr);
- if (!conn) {
- vty_out(vty, "%% An active connection is required for %s %s%s",
- argv[0], argv[1], VTY_NEWLINE);
- subscr_put(subscr);
- talloc_free(text);
- return CMD_WARNING;
- }
-
- msc_send_ussd_notify(conn, level, text);
- msc_send_ussd_release_complete(conn);
-
- subscr_put(subscr);
- talloc_free(text);
- return CMD_SUCCESS;
-}
-
-DEFUN(ena_subscr_delete,
- ena_subscr_delete_cmd,
- "subscriber " SUBSCR_TYPES " ID delete",
- SUBSCR_HELP "Delete subscriber in HLR\n")
-{
- int rc;
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- struct gsm_subscriber *subscr =
- get_subscr_by_argv(gsmnet, argv[0], argv[1]);
-
- if (!subscr) {
- vty_out(vty, "%% No subscriber found for %s %s%s",
- argv[0], argv[1], VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- if (subscr->use_count != 1) {
- vty_out(vty, "Removing active subscriber%s", VTY_NEWLINE);
- }
-
- rc = db_subscriber_delete(subscr);
- subscr_put(subscr);
-
- if (rc != 0) {
- vty_out(vty, "Failed to remove subscriber%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN(ena_subscr_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 gsm_subscriber *subscr =
- get_subscr_by_argv(gsmnet, argv[0], argv[1]);
-
- if (!subscr) {
- vty_out(vty, "%% No subscriber found for %s %s%s",
- argv[0], argv[1], VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- subscr->expire_lu = time(0);
- db_sync_subscriber(subscr);
- subscr_put(subscr);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(ena_subscr_authorized,
- ena_subscr_authorized_cmd,
- "subscriber " SUBSCR_TYPES " ID authorized (0|1)",
- SUBSCR_HELP "(De-)Authorize subscriber in HLR\n"
- "Subscriber should NOT be authorized\n"
- "Subscriber should be authorized\n")
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- struct gsm_subscriber *subscr =
- get_subscr_by_argv(gsmnet, argv[0], argv[1]);
-
- if (!subscr) {
- vty_out(vty, "%% No subscriber found for %s %s%s",
- argv[0], argv[1], VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- subscr->authorized = atoi(argv[2]);
- db_sync_subscriber(subscr);
-
- subscr_put(subscr);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(ena_subscr_name,
- ena_subscr_name_cmd,
- "subscriber " SUBSCR_TYPES " ID name .NAME",
- SUBSCR_HELP "Set the name of the subscriber\n"
- "Name of the Subscriber\n")
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- struct gsm_subscriber *subscr =
- get_subscr_by_argv(gsmnet, argv[0], argv[1]);
- char *name;
-
- if (!subscr) {
- vty_out(vty, "%% No subscriber found for %s %s%s",
- argv[0], argv[1], VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- name = argv_concat(argv, argc, 2);
- if (!name) {
- subscr_put(subscr);
- return CMD_WARNING;
- }
-
- if (strlen(name) > sizeof(subscr->name)-1) {
- vty_out(vty,
- "%% NAME is too long, max. %zu characters are allowed%s",
- sizeof(subscr->name)-1, VTY_NEWLINE);
- subscr_put(subscr);
- return CMD_WARNING;
- }
-
- osmo_strlcpy(subscr->name, name, sizeof(subscr->name));
- talloc_free(name);
- db_sync_subscriber(subscr);
-
- subscr_put(subscr);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(ena_subscr_extension,
- ena_subscr_extension_cmd,
- "subscriber " SUBSCR_TYPES " ID extension EXTENSION",
- SUBSCR_HELP "Set the extension (phone number) of the subscriber\n"
- "Extension (phone number)\n")
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- struct gsm_subscriber *subscr =
- get_subscr_by_argv(gsmnet, argv[0], argv[1]);
- const char *ext = argv[2];
-
- if (!subscr) {
- vty_out(vty, "%% No subscriber found for %s %s%s",
- argv[0], argv[1], VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- if (strlen(ext) > sizeof(subscr->extension)-1) {
- vty_out(vty,
- "%% EXTENSION is too long, max. %zu characters are allowed%s",
- sizeof(subscr->extension)-1, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- osmo_strlcpy(subscr->extension, ext, sizeof(subscr->extension));
- db_sync_subscriber(subscr);
-
- subscr_put(subscr);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(ena_subscr_handover,
- ena_subscr_handover_cmd,
- "subscriber " SUBSCR_TYPES " ID handover BTS_NR",
- SUBSCR_HELP "Handover the active connection\n"
- "Number of the BTS to handover to\n")
-{
- int ret;
- struct gsm_subscriber_connection *conn;
- struct gsm_bts *bts;
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- struct gsm_subscriber *subscr =
- get_subscr_by_argv(gsmnet, argv[0], argv[1]);
-
- if (!subscr) {
- vty_out(vty, "%% No subscriber found for %s %s.%s",
- argv[0], argv[1], VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- conn = connection_for_subscr(subscr);
- if (!conn) {
- vty_out(vty, "%% No active connection for subscriber %s %s.%s",
- argv[0], argv[1], VTY_NEWLINE);
- subscr_put(subscr);
- return CMD_WARNING;
- }
-
- bts = gsm_bts_num(gsmnet, atoi(argv[2]));
- if (!bts) {
- vty_out(vty, "%% BTS with number(%d) could not be found.%s",
- atoi(argv[2]), VTY_NEWLINE);
- subscr_put(subscr);
- return CMD_WARNING;
- }
-
- /* now start the handover */
- ret = bsc_handover_start(conn->lchan, bts);
- if (ret != 0) {
- vty_out(vty, "%% Handover failed with errno %d.%s",
- ret, VTY_NEWLINE);
- } else {
- vty_out(vty, "%% Handover started from %s",
- gsm_lchan_name(conn->lchan));
- vty_out(vty, " to %s.%s", gsm_lchan_name(conn->ho_lchan),
- VTY_NEWLINE);
- }
-
- subscr_put(subscr);
- return CMD_SUCCESS;
-}
-
-#define A3A8_ALG_TYPES "(none|xor|comp128v1)"
-#define A3A8_ALG_HELP \
- "Use No A3A8 algorithm\n" \
- "Use XOR algorithm\n" \
- "Use COMP128v1 algorithm\n"
-
-DEFUN(ena_subscr_a3a8,
- ena_subscr_a3a8_cmd,
- "subscriber " SUBSCR_TYPES " ID a3a8 " A3A8_ALG_TYPES " [KI]",
- SUBSCR_HELP "Set a3a8 parameters for the subscriber\n"
- A3A8_ALG_HELP "Encryption Key Ki\n")
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- struct gsm_subscriber *subscr =
- get_subscr_by_argv(gsmnet, argv[0], argv[1]);
- const char *alg_str = argv[2];
- const char *ki_str = argc == 4 ? argv[3] : NULL;
- struct gsm_auth_info ainfo;
- int rc, minlen, maxlen;
-
- if (!subscr) {
- vty_out(vty, "%% No subscriber found for %s %s%s",
- argv[0], argv[1], VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- if (!strcasecmp(alg_str, "none")) {
- ainfo.auth_algo = AUTH_ALGO_NONE;
- minlen = maxlen = 0;
- } else if (!strcasecmp(alg_str, "xor")) {
- ainfo.auth_algo = AUTH_ALGO_XOR;
- minlen = A38_XOR_MIN_KEY_LEN;
- maxlen = A38_XOR_MAX_KEY_LEN;
- } else if (!strcasecmp(alg_str, "comp128v1")) {
- ainfo.auth_algo = AUTH_ALGO_COMP128v1;
- minlen = maxlen = A38_COMP128_KEY_LEN;
- } else {
- /* Unknown method */
- subscr_put(subscr);
- vty_out(vty, "%% Unknown auth method %s%s",
- alg_str, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- if (ki_str) {
- rc = osmo_hexparse(ki_str, ainfo.a3a8_ki, sizeof(ainfo.a3a8_ki));
- if ((rc > maxlen) || (rc < minlen)) {
- subscr_put(subscr);
- vty_out(vty, "%% Wrong Ki `%s'%s",
- ki_str, VTY_NEWLINE);
- return CMD_WARNING;
- }
- ainfo.a3a8_ki_len = rc;
- } else {
- ainfo.a3a8_ki_len = 0;
- if (minlen) {
- subscr_put(subscr);
- vty_out(vty, "%% Missing Ki argument%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
- }
-
- rc = db_sync_authinfo_for_subscr(
- ainfo.auth_algo == AUTH_ALGO_NONE ? NULL : &ainfo,
- subscr);
-
- /* the last tuple probably invalid with the new auth settings */
- db_sync_lastauthtuple_for_subscr(NULL, subscr);
- subscr_put(subscr);
-
- if (rc) {
- vty_out(vty, "%% Operation has failed%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
- return CMD_SUCCESS;
-}
-
-DEFUN(subscriber_purge,
- subscriber_purge_cmd,
- "subscriber purge-inactive",
- "Operations on a Subscriber\n" "Purge subscribers with a zero use count.\n")
-{
- struct gsm_network *net = gsmnet_from_vty(vty);
- int purged;
-
- purged = subscr_purge_inactive(net->subscr_group);
- vty_out(vty, "%d subscriber(s) were purged.%s", purged, VTY_NEWLINE);
- return CMD_SUCCESS;
-}
-
-DEFUN(subscriber_update,
- subscriber_update_cmd,
- "subscriber " SUBSCR_TYPES " ID update",
- SUBSCR_HELP "Update the subscriber data from the dabase.\n")
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- struct gsm_subscriber *subscr = get_subscr_by_argv(gsmnet, argv[0], argv[1]);
-
- if (!subscr) {
- vty_out(vty, "%% No subscriber found for %s %s%s",
- argv[0], argv[1], VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- subscr_update_from_db(subscr);
- subscr_put(subscr);
- return CMD_SUCCESS;
-}
-
-static int scall_cbfn(unsigned int subsys, unsigned int signal,
- void *handler_data, void *signal_data)
-{
- struct scall_signal_data *sigdata = signal_data;
- struct vty *vty = sigdata->data;
-
- switch (signal) {
- case S_SCALL_SUCCESS:
- vty_out(vty, "%% silent call on ARFCN %u timeslot %u%s",
- sigdata->conn->lchan->ts->trx->arfcn, sigdata->conn->lchan->ts->nr,
- VTY_NEWLINE);
- break;
- case S_SCALL_EXPIRED:
- vty_out(vty, "%% silent call expired paging%s", VTY_NEWLINE);
- break;
- }
- return 0;
-}
-
-DEFUN(show_stats,
- show_stats_cmd,
- "show statistics",
- SHOW_STR "Display network statistics\n")
-{
- struct gsm_network *net = gsmnet_from_vty(vty);
-
- openbsc_vty_print_statistics(vty, net);
- vty_out(vty, "Location Update : %lu attach, %lu normal, %lu periodic%s",
- 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 gsm_subscriber *vlr_subscr;
- struct bsc_subscr *bsc_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 = subscr_get_by_imsi(gsmnet->subscr_group, imsi);
- bsc_subscr = bsc_subscr_find_by_imsi(gsmnet->bsc_subscribers, imsi);
-
- if (!vlr_subscr && !bsc_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);
- log_set_filter_bsc_subscr(tgt, bsc_subscr);
- return CMD_SUCCESS;
-}
-
-static struct cmd_node nitb_node = {
- NITB_NODE,
- "%s(config-nitb)# ",
- 1,
-};
-
-DEFUN(cfg_nitb, cfg_nitb_cmd,
- "nitb", "Configure NITB options")
-{
- vty->node = NITB_NODE;
- return CMD_SUCCESS;
-}
-
-/* Note: limit on the parameter length is set by internal vty code limitations */
-DEFUN(cfg_nitb_subscr_random, cfg_nitb_subscr_random_cmd,
- "subscriber-create-on-demand random <1-9999999999> <2-9999999999>",
- "Set random parameters for a new record when a subscriber is first seen.\n"
- "Set random parameters for a new record when a subscriber is first seen.\n"
- "Minimum for subscriber extension\n""Maximum for subscriber extension\n")
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- uint64_t mi = atoll(argv[0]), ma = atoll(argv[1]);
- gsmnet->auto_create_subscr = true;
- gsmnet->auto_assign_exten = true;
- if (mi >= ma) {
- vty_out(vty, "Incorrect range: %s >= %s, expected MIN < MAX%s",
- argv[0], argv[1], VTY_NEWLINE);
- return CMD_WARNING;
- }
- gsmnet->ext_min = mi;
- gsmnet->ext_max = ma;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_nitb_subscr_create, cfg_nitb_subscr_create_cmd,
- "subscriber-create-on-demand [no-extension]",
- "Make a new record when a subscriber is first seen.\n"
- "Do not automatically assign extension to created subscribers\n")
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- gsmnet->auto_create_subscr = true;
- gsmnet->auto_assign_exten = argc ? false : true;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_nitb_no_subscr_create, cfg_nitb_no_subscr_create_cmd,
- "no subscriber-create-on-demand",
- NO_STR "Make a new record when a subscriber is first seen.\n")
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- gsmnet->auto_create_subscr = false;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_nitb_assign_tmsi, cfg_nitb_assign_tmsi_cmd,
- "assign-tmsi",
- "Assign TMSI during Location Updating.\n")
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- gsmnet->avoid_tmsi = 0;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_nitb_no_assign_tmsi, cfg_nitb_no_assign_tmsi_cmd,
- "no assign-tmsi",
- NO_STR "Assign TMSI during Location Updating.\n")
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
- gsmnet->avoid_tmsi = 1;
- return CMD_SUCCESS;
-}
-
-static int config_write_nitb(struct vty *vty)
-{
- struct gsm_network *gsmnet = gsmnet_from_vty(vty);
-
- vty_out(vty, "nitb%s", VTY_NEWLINE);
- if (!gsmnet->auto_create_subscr)
- vty_out(vty, " no subscriber-create-on-demand%s", VTY_NEWLINE);
- else
- vty_out(vty, " subscriber-create-on-demand%s%s",
- gsmnet->auto_assign_exten ? "" : " no-extension",
- VTY_NEWLINE);
-
- if (gsmnet->ext_min != GSM_MIN_EXTEN || gsmnet->ext_max != GSM_MAX_EXTEN)
- vty_out(vty, " subscriber-create-on-demand random %"PRIu64" %"
- PRIu64"%s", gsmnet->ext_min, gsmnet->ext_max,
- VTY_NEWLINE);
- vty_out(vty, " %sassign-tmsi%s",
- gsmnet->avoid_tmsi ? "no " : "", VTY_NEWLINE);
- return CMD_SUCCESS;
-}
-
-int bsc_vty_init_extra(void)
-{
- osmo_signal_register_handler(SS_SCALL, scall_cbfn, NULL);
-
- install_element_ve(&show_subscr_cmd);
- install_element_ve(&show_subscr_cache_cmd);
-
- install_element_ve(&sms_send_pend_cmd);
-
- install_element_ve(&subscriber_create_cmd);
- install_element_ve(&subscriber_send_sms_cmd);
- install_element_ve(&subscriber_silent_sms_cmd);
- install_element_ve(&subscriber_silent_call_start_cmd);
- install_element_ve(&subscriber_silent_call_stop_cmd);
- install_element_ve(&subscriber_ussd_notify_cmd);
- install_element_ve(&subscriber_update_cmd);
- install_element_ve(&show_stats_cmd);
- install_element_ve(&show_smsqueue_cmd);
- install_element_ve(&logging_fltr_imsi_cmd);
-
- install_element(ENABLE_NODE, &ena_subscr_delete_cmd);
- install_element(ENABLE_NODE, &ena_subscr_expire_cmd);
- install_element(ENABLE_NODE, &ena_subscr_name_cmd);
- install_element(ENABLE_NODE, &ena_subscr_extension_cmd);
- install_element(ENABLE_NODE, &ena_subscr_authorized_cmd);
- install_element(ENABLE_NODE, &ena_subscr_a3a8_cmd);
- install_element(ENABLE_NODE, &ena_subscr_handover_cmd);
- install_element(ENABLE_NODE, &subscriber_purge_cmd);
- install_element(ENABLE_NODE, &smsqueue_trigger_cmd);
- install_element(ENABLE_NODE, &smsqueue_max_cmd);
- install_element(ENABLE_NODE, &smsqueue_clear_cmd);
- install_element(ENABLE_NODE, &smsqueue_fail_cmd);
- install_element(ENABLE_NODE, &subscriber_send_pending_sms_cmd);
- install_element(ENABLE_NODE, &meas_feed_scenario_cmd);
-
- install_element(CONFIG_NODE, &cfg_mncc_int_cmd);
- install_node(&mncc_int_node, config_write_mncc_int);
- vty_install_default(MNCC_INT_NODE);
- install_element(MNCC_INT_NODE, &mnccint_def_codec_f_cmd);
- install_element(MNCC_INT_NODE, &mnccint_def_codec_h_cmd);
- install_element(MNCC_INT_NODE, &mnccint_meas_feed_cmd);
- install_element(MNCC_INT_NODE, &meas_feed_scenario_cmd);
-
- install_element(CFG_LOG_NODE, &log_level_sms_cmd);
- install_element(CFG_LOG_NODE, &logging_fltr_imsi_cmd);
-
-
- install_element(CONFIG_NODE, &cfg_nitb_cmd);
- install_node(&nitb_node, config_write_nitb);
- install_element(NITB_NODE, &cfg_nitb_subscr_create_cmd);
- install_element(NITB_NODE, &cfg_nitb_subscr_random_cmd);
- install_element(NITB_NODE, &cfg_nitb_no_subscr_create_cmd);
- install_element(NITB_NODE, &cfg_nitb_assign_tmsi_cmd);
- install_element(NITB_NODE, &cfg_nitb_no_assign_tmsi_cmd);
-
- return 0;
-}
diff --git a/openbsc/src/libtrau/Makefile.am b/openbsc/src/libtrau/Makefile.am
deleted file mode 100644
index 46becd6aa..000000000
--- a/openbsc/src/libtrau/Makefile.am
+++ /dev/null
@@ -1,31 +0,0 @@
-AM_CPPFLAGS = \
- $(all_includes) \
- -I$(top_srcdir)/include \
- -I$(top_builddir) \
- $(NULL)
-
-AM_CFLAGS = \
- -Wall \
- $(LIBOSMOCORE_CFLAGS) \
- $(LIBOSMOVTY_CFLAGS) \
- $(LIBOSMOABIS_CFLAGS) \
- $(LIBOSMONETIF_CFLAGS) \
- $(COVERAGE_CFLAGS) \
- $(NULL)
-
-AM_LDFLAGS = \
- $(LIBOSMOCORE_LIBS) \
- $(LIBOSMOGSM_LIBS) \
- $(LIBOSMOABIS_LIBS) \
- $(COVERAGE_LDFLAGS) \
- $(NULL)
-
-noinst_LIBRARIES = \
- libtrau.a \
- $(NULL)
-
-libtrau_a_SOURCES = \
- rtp_proxy.c \
- trau_mux.c \
- trau_upqueue.c \
- $(NULL)
diff --git a/openbsc/src/libtrau/rtp_proxy.c b/openbsc/src/libtrau/rtp_proxy.c
deleted file mode 100644
index 6b38ee5fe..000000000
--- a/openbsc/src/libtrau/rtp_proxy.c
+++ /dev/null
@@ -1,764 +0,0 @@
-/* RTP proxy handling for ip.access nanoBTS */
-
-/* (C) 2009-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 <errno.h>
-#include <unistd.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <sys/time.h> /* gettimeofday() */
-#include <unistd.h> /* get..() */
-#include <time.h> /* clock() */
-#include <sys/utsname.h> /* uname() */
-
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/utils.h>
-#include <openbsc/gsm_data.h>
-#include <osmocom/core/msgb.h>
-#include <osmocom/core/select.h>
-#include <openbsc/debug.h>
-#include <openbsc/rtp_proxy.h>
-#include <openbsc/mncc.h>
-#include <openbsc/trau_upqueue.h>
-
-#include <osmocom/netif/rtp.h>
-
-/* attempt to determine byte order */
-#include <sys/param.h>
-#include <limits.h>
-
-static LLIST_HEAD(rtp_sockets);
-
-/* should we mangle the CNAME inside SDES of RTCP packets? We disable
- * this by default, as it seems to be not needed */
-static int mangle_rtcp_cname = 0;
-
-enum rtp_bfd_priv {
- RTP_PRIV_NONE,
- RTP_PRIV_RTP,
- RTP_PRIV_RTCP
-};
-
-#define RTP_ALLOC_SIZE 1500
-
-#define RTCP_TYPE_SDES 202
-
-#define RTCP_IE_CNAME 1
-
-
-#define RTP_VERSION 2
-
-/* 33 for FR, all other codecs have smaller size */
-#define MAX_RTP_PAYLOAD_LEN 33
-
-/* decode an rtp frame and create a new buffer with payload */
-static int rtp_decode(struct msgb *msg, uint32_t callref, struct msgb **data)
-{
- struct msgb *new_msg;
- struct gsm_data_frame *frame;
- struct rtp_hdr *rtph = (struct rtp_hdr *)msg->data;
- struct rtp_x_hdr *rtpxh;
- uint8_t *payload, *payload_out;
- int payload_len;
- int msg_type;
- int x_len;
-
- if (msg->len < 12) {
- DEBUGPC(DLMUX, "received RTP frame too short (len = %d)\n",
- msg->len);
- return -EINVAL;
- }
- if (rtph->version != RTP_VERSION) {
- DEBUGPC(DLMUX, "received RTP version %d not supported.\n",
- rtph->version);
- return -EINVAL;
- }
- payload = msg->data + sizeof(struct rtp_hdr) + (rtph->csrc_count << 2);
- payload_len = msg->len - sizeof(struct rtp_hdr) - (rtph->csrc_count << 2);
- if (payload_len < 0) {
- DEBUGPC(DLMUX, "received RTP frame too short (len = %d, "
- "csrc count = %d)\n", msg->len, rtph->csrc_count);
- return -EINVAL;
- }
- if (rtph->extension) {
- if (payload_len < sizeof(struct rtp_x_hdr)) {
- DEBUGPC(DLMUX, "received RTP frame too short for "
- "extension header\n");
- return -EINVAL;
- }
- rtpxh = (struct rtp_x_hdr *)payload;
- x_len = ntohs(rtpxh->length) * 4 + sizeof(struct rtp_x_hdr);
- payload += x_len;
- payload_len -= x_len;
- if (payload_len < 0) {
- DEBUGPC(DLMUX, "received RTP frame too short, "
- "extension header exceeds frame length\n");
- return -EINVAL;
- }
- }
- if (rtph->padding) {
- if (payload_len < 1) {
- DEBUGPC(DLMUX, "received RTP frame too short for "
- "padding length\n");
- return -EINVAL;
- }
- payload_len -= payload[payload_len - 1];
- if (payload_len < 0) {
- DEBUGPC(DLMUX, "received RTP frame with padding "
- "greater than payload\n");
- return -EINVAL;
- }
- }
-
- switch (rtph->payload_type) {
- case RTP_PT_GSM_FULL:
- msg_type = GSM_TCHF_FRAME;
- if (payload_len != RTP_LEN_GSM_FULL) {
- DEBUGPC(DLMUX, "received RTP full rate frame with "
- "payload length != %d (len = %d)\n",
- RTP_LEN_GSM_FULL, payload_len);
- return -EINVAL;
- }
- break;
- case RTP_PT_GSM_EFR:
- msg_type = GSM_TCHF_FRAME_EFR;
- if (payload_len != RTP_LEN_GSM_EFR) {
- DEBUGPC(DLMUX, "received RTP extended full rate frame "
- "with payload length != %d (len = %d)\n",
- RTP_LEN_GSM_EFR, payload_len);
- return -EINVAL;
- }
- break;
- case RTP_PT_GSM_HALF:
- msg_type = GSM_TCHH_FRAME;
- if (payload_len != RTP_LEN_GSM_HALF) {
- DEBUGPC(DLMUX, "received RTP half rate frame with "
- "payload length != %d (len = %d)\n",
- RTP_LEN_GSM_HALF, payload_len);
- return -EINVAL;
- }
- break;
- case RTP_PT_AMR:
- msg_type = GSM_TCH_FRAME_AMR;
- break;
- default:
- DEBUGPC(DLMUX, "received RTP frame with unknown payload "
- "type %d\n", rtph->payload_type);
- return -EINVAL;
- }
-
- if (payload_len > MAX_RTP_PAYLOAD_LEN ||
- (rtph->payload_type == RTP_PT_AMR &&
- payload_len > MAX_RTP_PAYLOAD_LEN - 1)) {
- DEBUGPC(DLMUX, "RTP payload too large (%d octets)\n",
- payload_len);
- return -EINVAL;
- }
-
- /* always allocate for the maximum possible size to avoid
- * fragmentation */
- new_msg = msgb_alloc(sizeof(struct gsm_data_frame) +
- MAX_RTP_PAYLOAD_LEN+1, "GSM-DATA (TCH)");
-
- if (!new_msg)
- return -ENOMEM;
- frame = (struct gsm_data_frame *) msgb_put(new_msg, sizeof(struct gsm_data_frame));
- frame->msg_type = msg_type;
- frame->callref = callref;
- if (rtph->payload_type == RTP_PT_AMR) {
- /* for FR/HR/EFR the length is implicit. In AMR, we
- * need to make it explicit by using the first byte of
- * the data[] buffer as length byte */
- uint8_t *data0 = msgb_put(new_msg, 1);
- *data0 = payload_len;
- }
- payload_out = msgb_put(new_msg, payload_len);
- memcpy(payload_out, payload, payload_len);
-
- *data = new_msg;
- return 0;
-}
-
-/*! \brief encode and send a rtp frame
- * \param[in] rs RTP socket through which we shall send
- * \param[in] frame GSM RTP frame to be sent
- */
-int rtp_send_frame(struct rtp_socket *rs, struct gsm_data_frame *frame)
-{
- struct rtp_sub_socket *rss = &rs->rtp;
- struct msgb *msg;
- struct rtp_hdr *rtph;
- uint8_t *payload;
- int payload_type;
- int payload_len;
- int duration; /* in samples */
- int is_bfi = 0;
-
- if (rs->tx_action != RTP_SEND_DOWNSTREAM) {
- /* initialize sequences */
- rs->tx_action = RTP_SEND_DOWNSTREAM;
- rs->transmit.ssrc = rand();
- rs->transmit.sequence = random();
- rs->transmit.timestamp = random();
- }
-
- switch (frame->msg_type) {
- case GSM_TCHF_FRAME:
- payload_type = RTP_PT_GSM_FULL;
- payload_len = RTP_LEN_GSM_FULL;
- duration = RTP_GSM_DURATION;
- break;
- case GSM_TCHF_FRAME_EFR:
- payload_type = RTP_PT_GSM_EFR;
- payload_len = RTP_LEN_GSM_EFR;
- duration = RTP_GSM_DURATION;
- break;
- case GSM_TCHH_FRAME:
- payload_type = RTP_PT_GSM_HALF;
- payload_len = RTP_LEN_GSM_HALF;
- duration = RTP_GSM_DURATION;
- break;
- case GSM_TCH_FRAME_AMR:
- payload_type = RTP_PT_AMR;
- payload_len = frame->data[0];
- duration = RTP_GSM_DURATION;
- break;
- case GSM_BAD_FRAME:
- payload_type = 0;
- payload_len = 0;
- duration = RTP_GSM_DURATION;
- is_bfi = 1;
- break;
- default:
- DEBUGPC(DLMUX, "unsupported message type %d\n",
- frame->msg_type);
- return -EINVAL;
- }
-
- if (payload_len > MAX_RTP_PAYLOAD_LEN) {
- DEBUGPC(DLMUX, "RTP payload too large (%d octets)\n",
- payload_len);
- return -EINVAL;
- }
-
- if (is_bfi) {
- /* In case of a bad frame, just count and drop packet. */
- rs->transmit.timestamp += duration;
- rs->transmit.sequence++;
- return 0;
- }
-
- msg = msgb_alloc(sizeof(struct rtp_hdr) + payload_len, "RTP-GSM");
- if (!msg)
- return -ENOMEM;
- rtph = (struct rtp_hdr *) msgb_put(msg, sizeof(struct rtp_hdr));
- rtph->version = RTP_VERSION;
- rtph->padding = 0;
- rtph->extension = 0;
- rtph->csrc_count = 0;
- rtph->marker = 0;
- rtph->payload_type = payload_type;
- rtph->sequence = htons(rs->transmit.sequence++);
- rtph->timestamp = htonl(rs->transmit.timestamp);
- rs->transmit.timestamp += duration;
- rtph->ssrc = htonl(rs->transmit.ssrc);
-
- payload = msgb_put(msg, payload_len);
- if (frame->msg_type == GSM_TCH_FRAME_AMR)
- memcpy(payload, frame->data + 1, payload_len);
- else
- memcpy(payload, frame->data, payload_len);
- msgb_enqueue(&rss->tx_queue, msg);
- rss->bfd.when |= BSC_FD_WRITE;
-
- return 0;
-}
-
-/* iterate over all chunks in one RTCP message, look for CNAME IEs and
- * replace all of those with 'new_cname' */
-static int rtcp_sdes_cname_mangle(struct msgb *msg, struct rtcp_hdr *rh,
- uint16_t *rtcp_len, const char *new_cname)
-{
- uint8_t *rtcp_end;
- uint8_t *cur = (uint8_t *) rh;
- uint8_t tag, len = 0;
-
- rtcp_end = cur + *rtcp_len;
- /* move cur to end of RTP header */
- cur += sizeof(*rh);
-
- /* iterate over Chunks */
- while (cur+4 < rtcp_end) {
- /* skip four bytes SSRC/CSRC */
- cur += 4;
-
- /* iterate over IE's inside the chunk */
- while (cur+1 < rtcp_end) {
- tag = *cur++;
- if (tag == 0) {
- /* end of chunk, skip additional zero */
- while ((*cur++ == 0) && (cur < rtcp_end)) { }
- break;
- }
- len = *cur++;
-
- if (tag == RTCP_IE_CNAME) {
- /* we've found the CNAME, lets mangle it */
- if (len < strlen(new_cname)) {
- /* we need to make more space */
- int increase = strlen(new_cname) - len;
-
- msgb_push(msg, increase);
- memmove(cur+len+increase, cur+len,
- rtcp_end - (cur+len));
- /* FIXME: we have to respect RTCP
- * padding/alignment rules! */
- len += increase;
- *(cur-1) += increase;
- rtcp_end += increase;
- *rtcp_len += increase;
- }
- /* copy new CNAME into message */
- memcpy(cur, new_cname, strlen(new_cname));
- /* FIXME: zero the padding in case new CNAME
- * is smaller than old one !!! */
- }
- cur += len;
- }
- }
-
- return 0;
-}
-
-static int rtcp_mangle(struct msgb *msg, struct rtp_socket *rs)
-{
- struct rtp_sub_socket *rss = &rs->rtcp;
- struct rtcp_hdr *rtph;
- uint16_t old_len;
- int rc;
-
- if (!mangle_rtcp_cname)
- return 0;
-
- printf("RTCP\n");
- /* iterate over list of RTCP messages */
- rtph = (struct rtcp_hdr *)msg->data;
- while ((void *)rtph + sizeof(*rtph) <= (void *)msg->data + msg->len) {
- old_len = (ntohs(rtph->length) + 1) * 4;
- if ((void *)rtph + old_len > (void *)msg->data + msg->len) {
- DEBUGPC(DLMUX, "received RTCP packet too short for "
- "length element\n");
- return -EINVAL;
- }
- if (rtph->type == RTCP_TYPE_SDES) {
- char new_cname[255];
- osmo_strlcpy(new_cname,
- inet_ntoa(rss->sin_local.sin_addr),
- sizeof(new_cname));
- rc = rtcp_sdes_cname_mangle(msg, rtph, &old_len,
- new_cname);
- if (rc < 0)
- return rc;
- }
- rtph = (void *)rtph + old_len;
- }
-
- return 0;
-}
-
-/* read from incoming RTP/RTCP socket */
-static int rtp_socket_read(struct rtp_socket *rs, struct rtp_sub_socket *rss)
-{
- int rc;
- struct msgb *msg = msgb_alloc(RTP_ALLOC_SIZE, "RTP/RTCP");
- struct msgb *new_msg;
- struct rtp_sub_socket *other_rss;
-
- if (!msg)
- return -ENOMEM;
-
- rc = read(rss->bfd.fd, msg->data, RTP_ALLOC_SIZE);
- if (rc == 0) {
- rss->bfd.when &= ~BSC_FD_READ;
- goto out_free;
- } else if (rc < 0) {
- /* Ignore "connection refused". this happens, If we open the
- * socket faster than the remote side. */
- if (errno == ECONNREFUSED)
- goto out_free;
- DEBUGPC(DLMUX, "Read of RTP socket (%p) failed (errno %d, "
- "%s)\n", rs, errno, strerror(errno));
- rss->bfd.when &= ~BSC_FD_READ;
- goto out_free;
- }
-
- msgb_put(msg, rc);
-
- switch (rs->rx_action) {
- case RTP_PROXY:
- if (!rs->proxy.other_sock) {
- rc = -EIO;
- goto out_free;
- }
- if (rss->bfd.priv_nr == RTP_PRIV_RTP)
- other_rss = &rs->proxy.other_sock->rtp;
- else if (rss->bfd.priv_nr == RTP_PRIV_RTCP) {
- other_rss = &rs->proxy.other_sock->rtcp;
- /* modify RTCP SDES CNAME */
- rc = rtcp_mangle(msg, rs);
- if (rc < 0)
- goto out_free;
- } else {
- rc = -EINVAL;
- goto out_free;
- }
- msgb_enqueue(&other_rss->tx_queue, msg);
- other_rss->bfd.when |= BSC_FD_WRITE;
- break;
-
- case RTP_RECV_UPSTREAM:
- if (!rs->receive.callref || !rs->receive.net) {
- rc = -EIO;
- goto out_free;
- }
- if (rss->bfd.priv_nr == RTP_PRIV_RTCP) {
- if (!mangle_rtcp_cname) {
- msgb_free(msg);
- break;
- }
- /* modify RTCP SDES CNAME */
- rc = rtcp_mangle(msg, rs);
- if (rc < 0)
- goto out_free;
- msgb_enqueue(&rss->tx_queue, msg);
- rss->bfd.when |= BSC_FD_WRITE;
- break;
- }
- if (rss->bfd.priv_nr != RTP_PRIV_RTP) {
- rc = -EINVAL;
- goto out_free;
- }
- rc = rtp_decode(msg, rs->receive.callref, &new_msg);
- if (rc < 0)
- goto out_free;
- msgb_free(msg);
- trau_tx_to_mncc(rs->receive.net, new_msg);
- break;
-
- case RTP_NONE: /* if socket exists, but disabled by app */
- msgb_free(msg);
- break;
- }
-
- return 0;
-
-out_free:
- msgb_free(msg);
- return rc;
-}
-
-/* \brief write from tx_queue to RTP/RTCP socket */
-static int rtp_socket_write(struct rtp_socket *rs, struct rtp_sub_socket *rss)
-{
- struct msgb *msg;
- int written;
-
- msg = msgb_dequeue(&rss->tx_queue);
- if (!msg) {
- rss->bfd.when &= ~BSC_FD_WRITE;
- return 0;
- }
-
- written = write(rss->bfd.fd, msg->data, msg->len);
- if (written < msg->len) {
- LOGP(DLMIB, LOGL_ERROR, "short write");
- msgb_free(msg);
- return -EIO;
- }
-
- msgb_free(msg);
-
- return 0;
-}
-
-
-/*! \brief callback for the select.c:bfd_* layer */
-static int rtp_bfd_cb(struct osmo_fd *bfd, unsigned int flags)
-{
- struct rtp_socket *rs = bfd->data;
- struct rtp_sub_socket *rss;
-
- switch (bfd->priv_nr) {
- case RTP_PRIV_RTP:
- rss = &rs->rtp;
- break;
- case RTP_PRIV_RTCP:
- rss = &rs->rtcp;
- break;
- default:
- return -EINVAL;
- }
-
- if (flags & BSC_FD_READ)
- rtp_socket_read(rs, rss);
-
- if (flags & BSC_FD_WRITE)
- rtp_socket_write(rs, rss);
-
- return 0;
-}
-
-/*! \brief initialize one rtp sub-socket */
-static void init_rss(struct rtp_sub_socket *rss,
- struct rtp_socket *rs, int fd, int priv_nr)
-{
- /* initialize bfd */
- rss->bfd.fd = fd;
- rss->bfd.data = rs;
- rss->bfd.priv_nr = priv_nr;
- rss->bfd.cb = rtp_bfd_cb;
-}
-
-/*! \brief create a new RTP/RTCP socket and bind it */
-struct rtp_socket *rtp_socket_create(void)
-{
- int rc;
- struct rtp_socket *rs;
-
- DEBUGP(DLMUX, "rtp_socket_create(): ");
-
- rs = talloc_zero(tall_bsc_ctx, struct rtp_socket);
- if (!rs)
- return NULL;
-
- INIT_LLIST_HEAD(&rs->rtp.tx_queue);
- INIT_LLIST_HEAD(&rs->rtcp.tx_queue);
-
- rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
- if (rc < 0)
- goto out_free;
-
- init_rss(&rs->rtp, rs, rc, RTP_PRIV_RTP);
- rc = osmo_fd_register(&rs->rtp.bfd);
- if (rc < 0)
- goto out_rtp_socket;
-
- rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
- if (rc < 0)
- goto out_rtp_bfd;
-
- init_rss(&rs->rtcp, rs, rc, RTP_PRIV_RTCP);
- rc = osmo_fd_register(&rs->rtcp.bfd);
- if (rc < 0)
- goto out_rtcp_socket;
-
- DEBUGPC(DLMUX, "success\n");
-
- rc = rtp_socket_bind(rs, INADDR_ANY);
- if (rc < 0)
- goto out_rtcp_bfd;
-
- return rs;
-
-out_rtcp_bfd:
- osmo_fd_unregister(&rs->rtcp.bfd);
-out_rtcp_socket:
- close(rs->rtcp.bfd.fd);
-out_rtp_bfd:
- osmo_fd_unregister(&rs->rtp.bfd);
-out_rtp_socket:
- close(rs->rtp.bfd.fd);
-out_free:
- talloc_free(rs);
- DEBUGPC(DLMUX, "failed\n");
- return NULL;
-}
-
-static int rtp_sub_socket_bind(struct rtp_sub_socket *rss, uint32_t ip,
- uint16_t port)
-{
- int rc;
- socklen_t alen = sizeof(rss->sin_local);
-
- rss->sin_local.sin_family = AF_INET;
- rss->sin_local.sin_addr.s_addr = htonl(ip);
- rss->sin_local.sin_port = htons(port);
- rss->bfd.when |= BSC_FD_READ;
-
- rc = bind(rss->bfd.fd, (struct sockaddr *)&rss->sin_local,
- sizeof(rss->sin_local));
- if (rc < 0)
- return rc;
-
- /* retrieve the address we actually bound to, in case we
- * passed INADDR_ANY as IP address */
- return getsockname(rss->bfd.fd, (struct sockaddr *)&rss->sin_local,
- &alen);
-}
-
-#define RTP_PORT_BASE 30000
-static unsigned int next_udp_port = RTP_PORT_BASE;
-
-/*! \brief bind a RTP socket to a specific local address
- * \param[in] rs RTP socket to be bound
- * \param[in] ip local IP address to which socket is to be bound
- */
-int rtp_socket_bind(struct rtp_socket *rs, uint32_t ip)
-{
- int rc = -EIO;
- struct in_addr ia;
-
- ia.s_addr = htonl(ip);
- DEBUGP(DLMUX, "rtp_socket_bind(rs=%p, IP=%s): ", rs,
- inet_ntoa(ia));
-
- /* try to bind to a consecutive pair of ports */
- for (next_udp_port = next_udp_port % 0xffff;
- next_udp_port < 0xffff; next_udp_port += 2) {
- rc = rtp_sub_socket_bind(&rs->rtp, ip, next_udp_port);
- if (rc != 0)
- continue;
-
- rc = rtp_sub_socket_bind(&rs->rtcp, ip, next_udp_port+1);
- if (rc == 0)
- break;
- }
- if (rc < 0) {
- DEBUGPC(DLMUX, "failed\n");
- return rc;
- }
-
- ia.s_addr = rs->rtp.sin_local.sin_addr.s_addr;
- DEBUGPC(DLMUX, "BOUND_IP=%s, BOUND_PORT=%u\n",
- inet_ntoa(ia), ntohs(rs->rtp.sin_local.sin_port));
- return ntohs(rs->rtp.sin_local.sin_port);
-}
-
-static int rtp_sub_socket_connect(struct rtp_sub_socket *rss,
- uint32_t ip, uint16_t port)
-{
- int rc;
- socklen_t alen = sizeof(rss->sin_local);
-
- rss->sin_remote.sin_family = AF_INET;
- rss->sin_remote.sin_addr.s_addr = htonl(ip);
- rss->sin_remote.sin_port = htons(port);
-
- rc = connect(rss->bfd.fd, (struct sockaddr *) &rss->sin_remote,
- sizeof(rss->sin_remote));
- if (rc < 0)
- return rc;
-
- return getsockname(rss->bfd.fd, (struct sockaddr *)&rss->sin_local,
- &alen);
-}
-
-/*! \brief 'connect' a RTP socket to a remote peer
- * \param[in] rs RTP socket to be connected
- * \param[in] ip remote IP address to which to connect
- * \param[in] port remote UDP port number to which to connect
- */
-int rtp_socket_connect(struct rtp_socket *rs, uint32_t ip, uint16_t port)
-{
- int rc;
- struct in_addr ia;
-
- ia.s_addr = htonl(ip);
- DEBUGP(DLMUX, "rtp_socket_connect(rs=%p, ip=%s, port=%u)\n",
- rs, inet_ntoa(ia), port);
-
- rc = rtp_sub_socket_connect(&rs->rtp, ip, port);
- if (rc < 0)
- return rc;
-
- return rtp_sub_socket_connect(&rs->rtcp, ip, port+1);
-}
-
-/*! \brief bind two RTP/RTCP sockets together in the proxy
- * \param[in] this First RTP socket
- * \param[in] other Second RTP socket
- */
-int rtp_socket_proxy(struct rtp_socket *this, struct rtp_socket *other)
-{
- DEBUGP(DLMUX, "rtp_socket_proxy(this=%p, other=%p)\n",
- this, other);
-
- this->rx_action = RTP_PROXY;
- this->proxy.other_sock = other;
-
- other->rx_action = RTP_PROXY;
- other->proxy.other_sock = this;
-
- return 0;
-}
-
-/*! \brief bind RTP/RTCP socket to application, disabling proxy
- * \param[in] this RTP socket
- * \param[in] net gsm_network argument to trau_tx_to_mncc()
- * \param[in] callref callref argument to trau_tx_to_mncc()
- */
-int rtp_socket_upstream(struct rtp_socket *this, struct gsm_network *net,
- uint32_t callref)
-{
- DEBUGP(DLMUX, "rtp_socket_proxy(this=%p, callref=%u)\n",
- this, callref);
-
- if (callref) {
- this->rx_action = RTP_RECV_UPSTREAM;
- this->receive.net = net;
- this->receive.callref = callref;
- } else
- this->rx_action = RTP_NONE;
-
- return 0;
-}
-
-static void free_tx_queue(struct rtp_sub_socket *rss)
-{
- struct msgb *msg;
-
- while ((msg = msgb_dequeue(&rss->tx_queue)))
- msgb_free(msg);
-}
-
-/*! \brief Free/release a previously allocated RTP socket
- * \param[in[] rs RTP/RTCP socket to be released
- */
-int rtp_socket_free(struct rtp_socket *rs)
-{
- DEBUGP(DLMUX, "rtp_socket_free(rs=%p)\n", rs);
-
- /* make sure we don't leave references dangling to us */
- if (rs->rx_action == RTP_PROXY &&
- rs->proxy.other_sock)
- rs->proxy.other_sock->proxy.other_sock = NULL;
-
- osmo_fd_unregister(&rs->rtp.bfd);
- close(rs->rtp.bfd.fd);
- free_tx_queue(&rs->rtp);
-
- osmo_fd_unregister(&rs->rtcp.bfd);
- close(rs->rtcp.bfd.fd);
- free_tx_queue(&rs->rtcp);
-
- talloc_free(rs);
-
- return 0;
-}
diff --git a/openbsc/src/libtrau/trau_mux.c b/openbsc/src/libtrau/trau_mux.c
deleted file mode 100644
index b37c7650e..000000000
--- a/openbsc/src/libtrau/trau_mux.c
+++ /dev/null
@@ -1,547 +0,0 @@
-/* Simple TRAU frame reflector to route voice calls */
-
-/* (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 <errno.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <openbsc/gsm_data.h>
-#include <osmocom/abis/trau_frame.h>
-#include <openbsc/trau_mux.h>
-#include <osmocom/abis/subchan_demux.h>
-#include <osmocom/abis/e1_input.h>
-#include <openbsc/debug.h>
-#include <osmocom/core/talloc.h>
-#include <openbsc/trau_upqueue.h>
-#include <osmocom/core/crcgen.h>
-#include <openbsc/transaction.h>
-
-/* this corresponds to the bit-lengths of the individual codec
- * parameters as indicated in Table 1.1 of TS 06.10 */
-static const uint8_t gsm_fr_map[] = {
- 6, 6, 5, 5, 4, 4, 3, 3,
- 7, 2, 2, 6, 3, 3, 3, 3,
- 3, 3, 3, 3, 3, 3, 3, 3,
- 3, 7, 2, 2, 6, 3, 3, 3,
- 3, 3, 3, 3, 3, 3, 3, 3,
- 3, 3, 7, 2, 2, 6, 3, 3,
- 3, 3, 3, 3, 3, 3, 3, 3,
- 3, 3, 3, 7, 2, 2, 6, 3,
- 3, 3, 3, 3, 3, 3, 3, 3,
- 3, 3, 3, 3
-};
-
-
-/*
- * EFR TRAU parity
- *
- * g(x) = x^3 + x^1 + 1
- */
-static const struct osmo_crc8gen_code gsm0860_efr_crc3 = {
- .bits = 3,
- .poly = 0x3,
- .init = 0x0,
- .remainder = 0x7,
-};
-
-/* EFR parity bits */
-static inline void efr_parity_bits_1(ubit_t *check_bits, const ubit_t *d_bits)
-{
- memcpy(check_bits + 0 , d_bits + 0, 22);
- memcpy(check_bits + 22 , d_bits + 24, 3);
- check_bits[25] = d_bits[28];
-}
-
-static inline void efr_parity_bits_2(ubit_t *check_bits, const ubit_t *d_bits)
-{
- memcpy(check_bits + 0 , d_bits + 42, 10);
- memcpy(check_bits + 10 , d_bits + 90, 2);
-}
-
-static inline void efr_parity_bits_3(ubit_t *check_bits, const ubit_t *d_bits)
-{
- memcpy(check_bits + 0 , d_bits + 98, 5);
- check_bits[5] = d_bits[104];
- memcpy(check_bits + 6 , d_bits + 143, 2);
-}
-
-static inline void efr_parity_bits_4(ubit_t *check_bits, const ubit_t *d_bits)
-{
- memcpy(check_bits + 0 , d_bits + 151, 10);
- memcpy(check_bits + 10 , d_bits + 199, 2);
-}
-
-static inline void efr_parity_bits_5(ubit_t *check_bits, const ubit_t *d_bits)
-{
- memcpy(check_bits + 0 , d_bits + 207, 5);
- check_bits[5] = d_bits[213];
- memcpy(check_bits + 6 , d_bits + 252, 2);
-}
-
-struct map_entry {
- struct llist_head list;
- struct gsm_e1_subslot src, dst;
-};
-
-struct upqueue_entry {
- struct llist_head list;
- struct gsm_network *net;
- struct gsm_e1_subslot src;
- uint32_t callref;
-};
-
-static LLIST_HEAD(ss_map);
-static LLIST_HEAD(ss_upqueue);
-
-void *tall_map_ctx, *tall_upq_ctx;
-
-/* map one particular subslot to another subslot */
-int trau_mux_map(const struct gsm_e1_subslot *src,
- const struct gsm_e1_subslot *dst)
-{
- struct map_entry *me;
-
- me = talloc(tall_map_ctx, struct map_entry);
- if (!me) {
- LOGP(DLMIB, LOGL_FATAL, "Out of memory\n");
- return -ENOMEM;
- }
-
- DEBUGP(DCC, "Setting up TRAU mux map between (e1=%u,ts=%u,ss=%u) "
- "and (e1=%u,ts=%u,ss=%u)\n",
- src->e1_nr, src->e1_ts, src->e1_ts_ss,
- dst->e1_nr, dst->e1_ts, dst->e1_ts_ss);
-
- /* make sure to get rid of any stale old mappings */
- trau_mux_unmap(src, 0);
- trau_mux_unmap(dst, 0);
-
- memcpy(&me->src, src, sizeof(me->src));
- memcpy(&me->dst, dst, sizeof(me->dst));
- llist_add(&me->list, &ss_map);
-
- return 0;
-}
-
-int trau_mux_map_lchan(const struct gsm_lchan *src,
- const struct gsm_lchan *dst)
-{
- struct gsm_e1_subslot *src_ss, *dst_ss;
-
- src_ss = &src->ts->e1_link;
- dst_ss = &dst->ts->e1_link;
-
- return trau_mux_map(src_ss, dst_ss);
-}
-
-
-/* unmap one particular subslot from another subslot */
-int trau_mux_unmap(const struct gsm_e1_subslot *ss, uint32_t callref)
-{
- struct map_entry *me, *me2;
- struct upqueue_entry *ue, *ue2;
-
- if (ss)
- llist_for_each_entry_safe(me, me2, &ss_map, list) {
- if (!memcmp(&me->src, ss, sizeof(*ss)) ||
- !memcmp(&me->dst, ss, sizeof(*ss))) {
- llist_del(&me->list);
- return 0;
- }
- }
- llist_for_each_entry_safe(ue, ue2, &ss_upqueue, list) {
- if (ue->callref == callref) {
- llist_del(&ue->list);
- return 0;
- }
- if (ss && !memcmp(&ue->src, ss, sizeof(*ss))) {
- llist_del(&ue->list);
- return 0;
- }
- }
- return -ENOENT;
-}
-
-/* look-up an enty in the TRAU mux map */
-static struct gsm_e1_subslot *
-lookup_trau_mux_map(const struct gsm_e1_subslot *src)
-{
- struct map_entry *me;
-
- llist_for_each_entry(me, &ss_map, list) {
- if (!memcmp(&me->src, src, sizeof(*src)))
- return &me->dst;
- if (!memcmp(&me->dst, src, sizeof(*src)))
- return &me->src;
- }
- return NULL;
-}
-
-/* look-up an enty in the TRAU upqueue */
-struct upqueue_entry *
-lookup_trau_upqueue(const struct gsm_e1_subslot *src)
-{
- struct upqueue_entry *ue;
-
- llist_for_each_entry(ue, &ss_upqueue, list) {
- if (!memcmp(&ue->src, src, sizeof(*src)))
- return ue;
- }
- return NULL;
-}
-
-static const uint8_t c_bits_check_fr[] = { 0, 0, 0, 1, 0 };
-static const uint8_t c_bits_check_efr[] = { 1, 1, 0, 1, 0 };
-
-struct msgb *trau_decode_fr(uint32_t callref,
- const struct decoded_trau_frame *tf)
-{
- struct msgb *msg;
- struct gsm_data_frame *frame;
- unsigned char *data;
- int i, j, k, l, o;
-
- msg = msgb_alloc(sizeof(struct gsm_data_frame) + 33,
- "GSM-DATA");
- if (!msg)
- return NULL;
-
- frame = (struct gsm_data_frame *)msg->data;
- memset(frame, 0, sizeof(struct gsm_data_frame));
- data = frame->data;
- data[0] = 0xd << 4;
- /* reassemble d-bits */
- i = 0; /* counts bits */
- j = 4; /* counts output bits */
- k = gsm_fr_map[0]-1; /* current number bit in element */
- l = 0; /* counts element bits */
- o = 0; /* offset input bits */
- while (i < 260) {
- data[j/8] |= (tf->d_bits[k+o] << (7-(j%8)));
- /* to avoid out-of-bounds access in gsm_fr_map[++l] */
- if (i == 259)
- break;
- if (--k < 0) {
- o += gsm_fr_map[l];
- k = gsm_fr_map[++l]-1;
- }
- i++;
- j++;
- }
- if (tf->c_bits[11]) /* BFI */
- frame->msg_type = GSM_BAD_FRAME;
- else
- frame->msg_type = GSM_TCHF_FRAME;
- frame->callref = callref;
- msgb_put(msg, sizeof(struct gsm_data_frame) + 33);
-
- return msg;
-}
-
-struct msgb *trau_decode_efr(uint32_t callref,
- const struct decoded_trau_frame *tf)
-{
- struct msgb *msg;
- struct gsm_data_frame *frame;
- unsigned char *data;
- int i, j, rc;
- ubit_t check_bits[26];
-
- msg = msgb_alloc(sizeof(struct gsm_data_frame) + 31,
- "GSM-DATA");
- if (!msg)
- return NULL;
-
- frame = (struct gsm_data_frame *)msg->data;
- memset(frame, 0, sizeof(struct gsm_data_frame));
- frame->msg_type = GSM_TCHF_FRAME_EFR;
- frame->callref = callref;
- msgb_put(msg, sizeof(struct gsm_data_frame) + 31);
-
- if (tf->c_bits[11]) /* BFI */
- goto bad_frame;
-
- data = frame->data;
- data[0] = 0xc << 4;
- /* reassemble d-bits */
- for (i = 1, j = 4; i < 39; i++, j++)
- data[j/8] |= (tf->d_bits[i] << (7-(j%8)));
- efr_parity_bits_1(check_bits, tf->d_bits);
- rc = osmo_crc8gen_check_bits(&gsm0860_efr_crc3, check_bits, 26,
- tf->d_bits + 39);
- if (rc)
- goto bad_frame;
- for (i = 42, j = 42; i < 95; i++, j++)
- data[j/8] |= (tf->d_bits[i] << (7-(j%8)));
- efr_parity_bits_2(check_bits, tf->d_bits);
- rc = osmo_crc8gen_check_bits(&gsm0860_efr_crc3, check_bits, 12,
- tf->d_bits + 95);
- if (rc)
- goto bad_frame;
- for (i = 98, j = 95; i < 148; i++, j++)
- data[j/8] |= (tf->d_bits[i] << (7-(j%8)));
- efr_parity_bits_3(check_bits, tf->d_bits);
- rc = osmo_crc8gen_check_bits(&gsm0860_efr_crc3, check_bits, 8,
- tf->d_bits + 148);
- if (rc)
- goto bad_frame;
- for (i = 151, j = 145; i < 204; i++, j++)
- data[j/8] |= (tf->d_bits[i] << (7-(j%8)));
- efr_parity_bits_4(check_bits, tf->d_bits);
- rc = osmo_crc8gen_check_bits(&gsm0860_efr_crc3, check_bits, 12,
- tf->d_bits + 204);
- if (rc)
- goto bad_frame;
- for (i = 207, j = 198; i < 257; i++, j++)
- data[j/8] |= (tf->d_bits[i] << (7-(j%8)));
- efr_parity_bits_5(check_bits, tf->d_bits);
- rc = osmo_crc8gen_check_bits(&gsm0860_efr_crc3, check_bits, 8,
- tf->d_bits + 257);
- if (rc)
- goto bad_frame;
-
- return msg;
-
-bad_frame:
- frame->msg_type = GSM_BAD_FRAME;
-
- return msg;
-}
-
-/* we get called by subchan_demux */
-int trau_mux_input(struct gsm_e1_subslot *src_e1_ss,
- const uint8_t *trau_bits, int num_bits)
-{
- struct decoded_trau_frame tf;
- uint8_t trau_bits_out[TRAU_FRAME_BITS];
- struct gsm_e1_subslot *dst_e1_ss = lookup_trau_mux_map(src_e1_ss);
- struct subch_mux *mx;
- struct upqueue_entry *ue;
- int rc;
-
- /* decode TRAU, change it to downlink, re-encode */
- rc = decode_trau_frame(&tf, trau_bits);
- if (rc)
- return rc;
-
- if (!dst_e1_ss) {
- struct msgb *msg = NULL;
- /* frame shall be sent to upqueue */
- if (!(ue = lookup_trau_upqueue(src_e1_ss)))
- return -EINVAL;
- if (!ue->callref)
- return -EINVAL;
- if (!memcmp(tf.c_bits, c_bits_check_fr, 5))
- msg = trau_decode_fr(ue->callref, &tf);
- else if (!memcmp(tf.c_bits, c_bits_check_efr, 5))
- msg = trau_decode_efr(ue->callref, &tf);
- else {
- DEBUGPC(DLMUX, "illegal trau (C1-C5) %s\n",
- osmo_hexdump(tf.c_bits, 5));
- DEBUGPC(DLMUX, "test trau (C1-C5) %s\n",
- osmo_hexdump(c_bits_check_efr, 5));
- return -EINVAL;
- }
- if (!msg)
- return -ENOMEM;
- trau_tx_to_mncc(ue->net, msg);
-
- return 0;
- }
-
- mx = e1inp_get_mux(dst_e1_ss->e1_nr, dst_e1_ss->e1_ts);
- if (!mx)
- return -EINVAL;
-
- trau_frame_up2down(&tf);
- encode_trau_frame(trau_bits_out, &tf);
-
- /* and send it to the muxer */
- return subchan_mux_enqueue(mx, dst_e1_ss->e1_ts_ss, trau_bits_out,
- TRAU_FRAME_BITS);
-}
-
-/* callback when a TRAU frame was received */
-int subch_cb(struct subch_demux *dmx, int ch, uint8_t *data, int len,
- void *_priv)
-{
- struct e1inp_ts *e1i_ts = _priv;
- struct gsm_e1_subslot src_ss;
-
- src_ss.e1_nr = e1i_ts->line->num;
- src_ss.e1_ts = e1i_ts->num;
- src_ss.e1_ts_ss = ch;
-
- return trau_mux_input(&src_ss, data, len);
-}
-
-/* add receiver instance for lchan and callref */
-int trau_recv_lchan(struct gsm_lchan *lchan, uint32_t callref)
-{
- struct gsm_e1_subslot *src_ss;
- struct upqueue_entry *ue;
-
- ue = talloc(tall_upq_ctx, struct upqueue_entry);
- if (!ue)
- return -ENOMEM;
-
- src_ss = &lchan->ts->e1_link;
-
- DEBUGP(DCC, "Setting up TRAU receiver (e1=%u,ts=%u,ss=%u) "
- "and (callref 0x%x)\n",
- src_ss->e1_nr, src_ss->e1_ts, src_ss->e1_ts_ss,
- callref);
-
- /* make sure to get rid of any stale old mappings */
- trau_mux_unmap(src_ss, callref);
-
- memcpy(&ue->src, src_ss, sizeof(ue->src));
- ue->net = lchan->ts->trx->bts->network;
- ue->callref = callref;
- llist_add(&ue->list, &ss_upqueue);
-
- return 0;
-}
-
-void trau_encode_fr(struct decoded_trau_frame *tf,
- const unsigned char *data)
-{
- int i, j, k, l, o;
-
- /* set c-bits and t-bits */
- tf->c_bits[0] = 1;
- tf->c_bits[1] = 1;
- tf->c_bits[2] = 1;
- tf->c_bits[3] = 0;
- tf->c_bits[4] = 0;
- memset(&tf->c_bits[5], 0, 6);
- memset(&tf->c_bits[11], 1, 10);
- memset(&tf->t_bits[0], 1, 4);
- /* reassemble d-bits */
- i = 0; /* counts bits */
- j = 4; /* counts input bits */
- k = gsm_fr_map[0]-1; /* current number bit in element */
- l = 0; /* counts element bits */
- o = 0; /* offset output bits */
- while (i < 260) {
- tf->d_bits[k+o] = (data[j/8] >> (7-(j%8))) & 1;
- /* to avoid out-of-bounds access in gsm_fr_map[++l] */
- if (i == 259)
- break;
- if (--k < 0) {
- o += gsm_fr_map[l];
- k = gsm_fr_map[++l]-1;
- }
- i++;
- j++;
- }
-}
-
-void trau_encode_efr(struct decoded_trau_frame *tf,
- const unsigned char *data)
-{
- int i, j;
- ubit_t check_bits[26];
-
- /* set c-bits and t-bits */
- tf->c_bits[0] = 1;
- tf->c_bits[1] = 1;
- tf->c_bits[2] = 0;
- tf->c_bits[3] = 1;
- tf->c_bits[4] = 0;
- memset(&tf->c_bits[5], 0, 6);
- memset(&tf->c_bits[11], 1, 10);
- memset(&tf->t_bits[0], 1, 4);
- /* reassemble d-bits */
- tf->d_bits[0] = 1;
- for (i = 1, j = 4; i < 39; i++, j++)
- tf->d_bits[i] = (data[j/8] >> (7-(j%8))) & 1;
- efr_parity_bits_1(check_bits, tf->d_bits);
- osmo_crc8gen_set_bits(&gsm0860_efr_crc3, check_bits, 26,
- tf->d_bits + 39);
- for (i = 42, j = 42; i < 95; i++, j++)
- tf->d_bits[i] = (data[j/8] >> (7-(j%8))) & 1;
- efr_parity_bits_2(check_bits, tf->d_bits);
- osmo_crc8gen_set_bits(&gsm0860_efr_crc3, check_bits, 12,
- tf->d_bits + 95);
- for (i = 98, j = 95; i < 148; i++, j++)
- tf->d_bits[i] = (data[j/8] >> (7-(j%8))) & 1;
- efr_parity_bits_3(check_bits, tf->d_bits);
- osmo_crc8gen_set_bits(&gsm0860_efr_crc3, check_bits, 8,
- tf->d_bits + 148);
- for (i = 151, j = 145; i < 204; i++, j++)
- tf->d_bits[i] = (data[j/8] >> (7-(j%8))) & 1;
- efr_parity_bits_4(check_bits, tf->d_bits);
- osmo_crc8gen_set_bits(&gsm0860_efr_crc3, check_bits, 12,
- tf->d_bits + 204);
- for (i = 207, j = 198; i < 257; i++, j++)
- tf->d_bits[i] = (data[j/8] >> (7-(j%8))) & 1;
- efr_parity_bits_5(check_bits, tf->d_bits);
- osmo_crc8gen_set_bits(&gsm0860_efr_crc3, check_bits, 8,
- tf->d_bits + 257);
-}
-
-int trau_send_frame(struct gsm_lchan *lchan, struct gsm_data_frame *frame)
-{
- uint8_t trau_bits_out[TRAU_FRAME_BITS];
- struct gsm_e1_subslot *dst_e1_ss = &lchan->ts->e1_link;
- struct subch_mux *mx;
- struct decoded_trau_frame tf;
-
- mx = e1inp_get_mux(dst_e1_ss->e1_nr, dst_e1_ss->e1_ts);
- if (!mx)
- return -EINVAL;
-
- switch (frame->msg_type) {
- case GSM_TCHF_FRAME:
- trau_encode_fr(&tf, frame->data);
- break;
- case GSM_TCHF_FRAME_EFR:
- trau_encode_efr(&tf, frame->data);
- break;
- default:
- DEBUGPC(DLMUX, "unsupported message type %d\n",
- frame->msg_type);
- return -EINVAL;
- }
-
- encode_trau_frame(trau_bits_out, &tf);
-
- /* and send it to the muxer */
- return subchan_mux_enqueue(mx, dst_e1_ss->e1_ts_ss, trau_bits_out,
- TRAU_FRAME_BITS);
-}
-
-/* switch trau muxer to new lchan */
-int switch_trau_mux(struct gsm_lchan *old_lchan, struct gsm_lchan *new_lchan)
-{
- struct gsm_network *net = old_lchan->ts->trx->bts->network;
- struct gsm_trans *trans;
-
- /* look up transaction with TCH frame receive enabled */
- llist_for_each_entry(trans, &net->trans_list, entry) {
- if (trans->conn && trans->conn->lchan == old_lchan && trans->tch_recv) {
- /* switch */
- trau_recv_lchan(new_lchan, trans->callref);
- }
- }
-
- return 0;
-}
diff --git a/openbsc/src/libtrau/trau_upqueue.c b/openbsc/src/libtrau/trau_upqueue.c
deleted file mode 100644
index f8edaf0ff..000000000
--- a/openbsc/src/libtrau/trau_upqueue.c
+++ /dev/null
@@ -1,27 +0,0 @@
-/* trau_upqueue.c - Pass msgb's up the chain */
-
-/* (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 <openbsc/mncc.h>
-#include <openbsc/gsm_data.h>
-
-void trau_tx_to_mncc(struct gsm_network *net, struct msgb *msg)
-{
- net->mncc_recv(net, msg);
-}
diff --git a/openbsc/src/osmo-bsc/Makefile.am b/openbsc/src/osmo-bsc/Makefile.am
deleted file mode 100644
index ae9410c9d..000000000
--- a/openbsc/src/osmo-bsc/Makefile.am
+++ /dev/null
@@ -1,55 +0,0 @@
-AM_CPPFLAGS = \
- $(all_includes) \
- -I$(top_srcdir)/include \
- -I$(top_builddir) \
- $(NULL)
-
-AM_CFLAGS = \
- -Wall \
- $(LIBOSMOCORE_CFLAGS) \
- $(LIBOSMOGSM_CFLAGS) \
- $(LIBOSMOVTY_CFLAGS) \
- $(LIBOSMOCTRL_CFLAGS) \
- $(LIBOSMONETIF_CFLAGS) \
- $(LIBOSMOSCCP_CFLAGS) \
- $(COVERAGE_CFLAGS) \
- $(LIBOSMOABIS_CFLAGS) \
- $(NULL)
-
-AM_LDFLAGS = \
- $(COVERAGE_LDFLAGS) \
- $(NULL)
-
-bin_PROGRAMS = \
- osmo-bsc \
- $(NULL)
-
-osmo_bsc_SOURCES = \
- osmo_bsc_main.c \
- osmo_bsc_vty.c \
- osmo_bsc_api.c \
- osmo_bsc_grace.c \
- osmo_bsc_msc.c \
- osmo_bsc_sccp.c \
- osmo_bsc_filter.c \
- osmo_bsc_bssap.c \
- osmo_bsc_audio.c \
- osmo_bsc_ctrl.c \
- $(NULL)
-
-# once again since TRAU uses CC symbol :(
-osmo_bsc_LDADD = \
- $(top_builddir)/src/libfilter/libfilter.a \
- $(top_builddir)/src/libbsc/libbsc.a \
- $(top_builddir)/src/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) \
- $(LIBOSMOCORE_LIBS) \
- $(LIBOSMOGSM_LIBS) \
- $(LIBOSMOVTY_LIBS) \
- $(LIBOSMOCTRL_LIBS) \
- $(COVERAGE_LDFLAGS) \
- $(LIBOSMOABIS_LIBS) \
- $(NULL)
diff --git a/openbsc/src/osmo-bsc/osmo_bsc_api.c b/openbsc/src/osmo-bsc/osmo_bsc_api.c
deleted file mode 100644
index bac5e4717..000000000
--- a/openbsc/src/osmo-bsc/osmo_bsc_api.c
+++ /dev/null
@@ -1,550 +0,0 @@
-/* (C) 2009-2015 by Holger Hans Peter Freyther <zecke@selfish.org>
- * (C) 2009-2011 by On-Waves
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <openbsc/osmo_bsc.h>
-#include <openbsc/bsc_msc_data.h>
-#include <openbsc/debug.h>
-
-#include <openbsc/gsm_04_80.h>
-
-#include <osmocom/gsm/protocol/gsm_08_08.h>
-#include <osmocom/gsm/gsm0808.h>
-
-#include <osmocom/sccp/sccp.h>
-
-#define return_when_not_connected(conn) \
- if (!conn->sccp_con) {\
- LOGP(DMSC, LOGL_ERROR, "MSC Connection not present.\n"); \
- return; \
- }
-
-#define return_when_not_connected_val(conn, ret) \
- if (!conn->sccp_con) {\
- LOGP(DMSC, LOGL_ERROR, "MSC Connection not present.\n"); \
- return ret; \
- }
-
-#define queue_msg_or_return(resp) \
- if (!resp) { \
- LOGP(DMSC, LOGL_ERROR, "Failed to allocate response.\n"); \
- return; \
- } \
- bsc_queue_for_msc(conn->sccp_con, resp);
-
-static int bsc_clear_request(struct gsm_subscriber_connection *conn, uint32_t cause);
-static int complete_layer3(struct gsm_subscriber_connection *conn,
- struct msgb *msg, struct bsc_msc_data *msc);
-
-static uint16_t get_network_code_for_msc(struct bsc_msc_data *msc)
-{
- if (msc->core_mnc != -1)
- return msc->core_mnc;
- return msc->network->network_code;
-}
-
-static uint16_t get_country_code_for_msc(struct bsc_msc_data *msc)
-{
- if (msc->core_mcc != -1)
- return msc->core_mcc;
- return msc->network->country_code;
-}
-
-static uint16_t get_lac_for_msc(struct bsc_msc_data *msc, struct gsm_bts *bts)
-{
- if (msc->core_lac != -1)
- return msc->core_lac;
- return bts->location_area_code;
-}
-
-static uint16_t get_ci_for_msc(struct bsc_msc_data *msc, struct gsm_bts *bts)
-{
- if (msc->core_ci != -1)
- return msc->core_ci;
- return bts->cell_identity;
-}
-
-static void bsc_maybe_lu_reject(struct gsm_subscriber_connection *conn, int con_type, int cause)
-{
- struct msgb *msg;
-
- /* ignore cm service request or such */
- if (con_type != FLT_CON_TYPE_LU)
- return;
-
- msg = gsm48_create_loc_upd_rej(cause);
- if (!msg) {
- LOGP(DMM, LOGL_ERROR, "Failed to create msg for LOCATION UPDATING REJECT.\n");
- return;
- }
-
- msg->lchan = conn->lchan;
- gsm0808_submit_dtap(conn, msg, 0, 0);
-}
-
-static int bsc_filter_initial(struct osmo_bsc_data *bsc,
- struct bsc_msc_data *msc,
- struct gsm_subscriber_connection *conn,
- struct msgb *msg, char **imsi, int *con_type,
- int *lu_cause)
-{
- struct bsc_filter_request req;
- struct bsc_filter_reject_cause cause;
- struct gsm48_hdr *gh = msgb_l3(msg);
- int rc;
-
- req.ctx = conn;
- req.black_list = NULL;
- req.access_lists = bsc_access_lists();
- req.local_lst_name = msc->acc_lst_name;
- req.global_lst_name = conn->bts->network->bsc_data->acc_lst_name;
- req.bsc_nr = 0;
-
- rc = bsc_msg_filter_initial(gh, msgb_l3len(msg), &req,
- con_type, imsi, &cause);
- *lu_cause = cause.lu_reject_cause;
- return rc;
-}
-
-static int bsc_filter_data(struct gsm_subscriber_connection *conn,
- struct msgb *msg, int *lu_cause)
-{
- struct bsc_filter_request req;
- struct gsm48_hdr *gh = msgb_l3(msg);
- struct bsc_filter_reject_cause cause;
- int rc;
-
- req.ctx = conn;
- req.black_list = NULL;
- req.access_lists = bsc_access_lists();
- req.local_lst_name = conn->sccp_con->msc->acc_lst_name;
- req.global_lst_name = conn->bts->network->bsc_data->acc_lst_name;
- req.bsc_nr = 0;
-
- rc = bsc_msg_filter_data(gh, msgb_l3len(msg), &req,
- &conn->sccp_con->filter_state,
- &cause);
- *lu_cause = cause.lu_reject_cause;
- return rc;
-}
-
-static void bsc_sapi_n_reject(struct gsm_subscriber_connection *conn, int dlci)
-{
- struct msgb *resp;
- return_when_not_connected(conn);
-
- LOGP(DMSC, LOGL_NOTICE, "Tx MSC SAPI N REJECT DLCI=0x%02x\n", dlci);
-
- resp = gsm0808_create_sapi_reject(dlci);
- queue_msg_or_return(resp);
-}
-
-static void bsc_cipher_mode_compl(struct gsm_subscriber_connection *conn,
- struct msgb *msg, uint8_t chosen_encr)
-{
- struct msgb *resp;
- return_when_not_connected(conn);
-
- LOGP(DMSC, LOGL_DEBUG, "CIPHER MODE COMPLETE from MS, forwarding to MSC\n");
- resp = gsm0808_create_cipher_complete(msg, chosen_encr);
- queue_msg_or_return(resp);
-}
-
-static void bsc_send_ussd_no_srv(struct gsm_subscriber_connection *conn,
- struct msgb *msg, const char *text)
-{
- struct gsm48_hdr *gh;
- int8_t pdisc;
- uint8_t mtype;
- int drop_message = 1;
-
- if (!text)
- return;
-
- if (!msg || msgb_l3len(msg) < sizeof(*gh))
- return;
-
- gh = msgb_l3(msg);
- pdisc = gsm48_hdr_pdisc(gh);
- mtype = gsm48_hdr_msg_type(gh);
-
- /* Is CM service request? */
- if (pdisc == GSM48_PDISC_MM && mtype == GSM48_MT_MM_CM_SERV_REQ) {
- struct gsm48_service_request *cm;
-
- cm = (struct gsm48_service_request *) &gh->data[0];
-
- /* Is type SMS or call? */
- if (cm->cm_service_type == GSM48_CMSERV_SMS)
- drop_message = 0;
- else if (cm->cm_service_type == GSM48_CMSERV_MO_CALL_PACKET)
- drop_message = 0;
- }
-
- if (drop_message) {
- LOGP(DMSC, LOGL_DEBUG, "Skipping (not sending) USSD message: '%s'\n", text);
- return;
- }
-
- LOGP(DMSC, LOGL_INFO, "Sending CM Service Accept\n");
- gsm48_tx_mm_serv_ack(conn);
-
- LOGP(DMSC, LOGL_INFO, "Sending USSD message: '%s'\n", text);
- bsc_send_ussd_notify(conn, 1, text);
- bsc_send_ussd_release_complete(conn);
-}
-
-/*
- * Instruct to reserve data for a new connectiom, create the complete
- * layer three message, send it to open the connection.
- */
-static int bsc_compl_l3(struct gsm_subscriber_connection *conn, struct msgb *msg,
- uint16_t chosen_channel)
-{
- struct bsc_msc_data *msc;
-
- LOGP(DMSC, LOGL_INFO, "Tx MSC COMPL L3\n");
-
- /* find the MSC link we want to use */
- msc = bsc_find_msc(conn, msg);
- if (!msc) {
- LOGP(DMSC, LOGL_ERROR, "Failed to find a MSC for a connection.\n");
- bsc_send_ussd_no_srv(conn, msg,
- conn->bts->network->bsc_data->ussd_no_msc_txt);
- return -1;
- }
-
- return complete_layer3(conn, msg, msc);
-}
-
-static int complete_layer3(struct gsm_subscriber_connection *conn,
- struct msgb *msg, struct bsc_msc_data *msc)
-{
- int con_type, rc, lu_cause;
- char *imsi = NULL;
- struct timeval tv;
- struct msgb *resp;
- uint16_t network_code;
- uint16_t country_code;
- uint16_t lac;
- uint16_t ci;
- enum bsc_con ret;
- int send_ping = msc->advanced_ping;
-
- /* Advanced ping/pong handling */
- if (osmo_timer_pending(&msc->pong_timer))
- send_ping = 0;
- if (msc->ping_timeout <= 0)
- send_ping = 0;
- if (send_ping && osmo_timer_remaining(&msc->ping_timer, NULL, &tv) == -1)
- send_ping = 0;
-
- /* Check the filter */
- rc = bsc_filter_initial(msc->network->bsc_data, msc, conn, msg,
- &imsi, &con_type, &lu_cause);
- if (rc < 0) {
- bsc_maybe_lu_reject(conn, con_type, lu_cause);
- return BSC_API_CONN_POL_REJECT;
- }
-
- /* allocate resource for a new connection */
- ret = bsc_create_new_connection(conn, msc, send_ping);
-
- if (ret != BSC_CON_SUCCESS) {
- /* allocation has failed */
- if (ret == BSC_CON_REJECT_NO_LINK)
- bsc_send_ussd_no_srv(conn, msg, msc->ussd_msc_lost_txt);
- else if (ret == BSC_CON_REJECT_RF_GRACE)
- bsc_send_ussd_no_srv(conn, msg, msc->ussd_grace_txt);
-
- return BSC_API_CONN_POL_REJECT;
- }
-
- if (imsi)
- conn->sccp_con->filter_state.imsi = talloc_steal(conn, imsi);
- conn->sccp_con->filter_state.con_type = con_type;
-
- /* check return value, if failed check msg for and send USSD */
-
- network_code = get_network_code_for_msc(conn->sccp_con->msc);
- country_code = get_country_code_for_msc(conn->sccp_con->msc);
- lac = get_lac_for_msc(conn->sccp_con->msc, conn->bts);
- ci = get_ci_for_msc(conn->sccp_con->msc, conn->bts);
-
- bsc_scan_bts_msg(conn, msg);
-
- resp = gsm0808_create_layer3(msg, network_code, country_code, lac, ci);
- if (!resp) {
- LOGP(DMSC, LOGL_DEBUG, "Failed to create layer3 message.\n");
- sccp_connection_free(conn->sccp_con->sccp);
- bsc_delete_connection(conn->sccp_con);
- return BSC_API_CONN_POL_REJECT;
- }
-
- if (bsc_open_connection(conn->sccp_con, resp) != 0) {
- sccp_connection_free(conn->sccp_con->sccp);
- bsc_delete_connection(conn->sccp_con);
- msgb_free(resp);
- return BSC_API_CONN_POL_REJECT;
- }
-
- return BSC_API_CONN_POL_ACCEPT;
-}
-
-/*
- * Plastic surgery... we want to give up the current connection
- */
-static int move_to_msc(struct gsm_subscriber_connection *_conn,
- struct msgb *msg, struct bsc_msc_data *msc)
-{
- struct osmo_bsc_sccp_con *old_con = _conn->sccp_con;
-
- /*
- * 1. Give up the old connection.
- * This happens by sending a clear request to the MSC,
- * it should end with the MSC releasing the connection.
- */
- old_con->conn = NULL;
- bsc_clear_request(_conn, 0);
-
- /*
- * 2. Attempt to create a new connection to the local
- * MSC. If it fails the caller will need to handle this
- * properly.
- */
- _conn->sccp_con = NULL;
- if (complete_layer3(_conn, msg, msc) != BSC_API_CONN_POL_ACCEPT) {
- gsm0808_clear(_conn);
- bsc_subscr_con_free(_conn);
- return 1;
- }
-
- return 2;
-}
-
-static int handle_cc_setup(struct gsm_subscriber_connection *conn,
- struct msgb *msg)
-{
- struct gsm48_hdr *gh = msgb_l3(msg);
- uint8_t pdisc = gsm48_hdr_pdisc(gh);
- uint8_t mtype = gsm48_hdr_msg_type(gh);
-
- struct bsc_msc_data *msc;
- struct gsm_mncc_number called;
- struct tlv_parsed tp;
- unsigned payload_len;
-
- char _dest_nr[35];
-
- /*
- * Do we have a setup message here? if not return fast.
- */
- if (pdisc != GSM48_PDISC_CC || mtype != GSM48_MT_CC_SETUP)
- return 0;
-
- payload_len = msgb_l3len(msg) - sizeof(*gh);
-
- tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0);
- if (!TLVP_PRESENT(&tp, GSM48_IE_CALLED_BCD)) {
- LOGP(DMSC, LOGL_ERROR, "Called BCD not present in setup.\n");
- return -1;
- }
-
- memset(&called, 0, sizeof(called));
- gsm48_decode_called(&called,
- TLVP_VAL(&tp, GSM48_IE_CALLED_BCD) - 1);
-
- if (called.plan != 1 && called.plan != 0)
- return 0;
-
- if (called.plan == 1 && called.type == 1) {
- _dest_nr[0] = _dest_nr[1] = '0';
- memcpy(_dest_nr + 2, called.number, sizeof(called.number));
- } else
- memcpy(_dest_nr, called.number, sizeof(called.number));
-
- /*
- * Check if the connection should be moved...
- */
- llist_for_each_entry(msc, &conn->bts->network->bsc_data->mscs, entry) {
- if (msc->type != MSC_CON_TYPE_LOCAL)
- continue;
- if (!msc->local_pref)
- continue;
- if (regexec(&msc->local_pref_reg, _dest_nr, 0, NULL, 0) != 0)
- continue;
-
- return move_to_msc(conn, msg, msc);
- }
-
- return 0;
-}
-
-
-static void bsc_dtap(struct gsm_subscriber_connection *conn, uint8_t link_id, struct msgb *msg)
-{
- int lu_cause;
- struct msgb *resp;
- return_when_not_connected(conn);
-
- LOGP(DMSC, LOGL_INFO, "Tx MSC DTAP LINK_ID=0x%02x\n", link_id);
-
- /*
- * We might want to move this connection to a new MSC. Ask someone
- * to handle it. If it was handled we will return.
- */
- if (handle_cc_setup(conn, msg) >= 1)
- return;
-
- /* Check the filter */
- if (bsc_filter_data(conn, msg, &lu_cause) < 0) {
- bsc_maybe_lu_reject(conn,
- conn->sccp_con->filter_state.con_type,
- lu_cause);
- bsc_clear_request(conn, 0);
- return;
- }
-
- bsc_scan_bts_msg(conn, msg);
-
- resp = gsm0808_create_dtap(msg, link_id);
- queue_msg_or_return(resp);
-}
-
-static void bsc_assign_compl(struct gsm_subscriber_connection *conn, uint8_t rr_cause,
- uint8_t chosen_channel, uint8_t encr_alg_id,
- uint8_t speech_model)
-{
- struct msgb *resp;
- return_when_not_connected(conn);
-
- LOGP(DMSC, LOGL_INFO, "Tx MSC ASSIGN COMPL\n");
-
- resp = gsm0808_create_assignment_completed(rr_cause, chosen_channel,
- encr_alg_id, speech_model);
- queue_msg_or_return(resp);
-}
-
-static void bsc_assign_fail(struct gsm_subscriber_connection *conn,
- uint8_t cause, uint8_t *rr_cause)
-{
- struct msgb *resp;
- return_when_not_connected(conn);
-
- LOGP(DMSC, LOGL_INFO, "Tx MSC ASSIGN FAIL\n");
-
- resp = gsm0808_create_assignment_failure(cause, rr_cause);
- queue_msg_or_return(resp);
-}
-
-static int bsc_clear_request(struct gsm_subscriber_connection *conn, uint32_t cause)
-{
- struct osmo_bsc_sccp_con *sccp;
- struct msgb *resp;
- return_when_not_connected_val(conn, 1);
-
- LOGP(DMSC, LOGL_INFO, "Tx MSC CLEAR REQUEST\n");
-
- /*
- * Remove the connection from BSC<->SCCP part, the SCCP part
- * will either be cleared by channel release or MSC disconnect
- */
- sccp = conn->sccp_con;
- sccp->conn = NULL;
- conn->sccp_con = NULL;
-
- resp = gsm0808_create_clear_rqst(GSM0808_CAUSE_RADIO_INTERFACE_FAILURE);
- if (!resp) {
- LOGP(DMSC, LOGL_ERROR, "Failed to allocate response.\n");
- return 1;
- }
-
- bsc_queue_for_msc(sccp, resp);
- return 1;
-}
-
-static void bsc_cm_update(struct gsm_subscriber_connection *conn,
- const uint8_t *cm2, uint8_t cm2_len,
- const uint8_t *cm3, uint8_t cm3_len)
-{
- struct msgb *resp;
- return_when_not_connected(conn);
-
- resp = gsm0808_create_classmark_update(cm2, cm2_len, cm3, cm3_len);
-
- queue_msg_or_return(resp);
-}
-
-static void bsc_mr_config(struct gsm_subscriber_connection *conn,
- struct gsm_lchan *lchan, int full_rate)
-{
- struct bsc_msc_data *msc;
- struct gsm48_multi_rate_conf *ms_conf, *bts_conf;
-
- if (!conn->sccp_con) {
- LOGP(DMSC, LOGL_ERROR,
- "No msc data available on conn %p. Audio will be broken.\n",
- conn);
- return;
- }
-
- msc = conn->sccp_con->msc;
-
- /* initialize the data structure */
- lchan->mr_ms_lv[0] = sizeof(*ms_conf);
- lchan->mr_bts_lv[0] = sizeof(*bts_conf);
- ms_conf = (struct gsm48_multi_rate_conf *) &lchan->mr_ms_lv[1];
- bts_conf = (struct gsm48_multi_rate_conf *) &lchan->mr_bts_lv[1];
- memset(ms_conf, 0, sizeof(*ms_conf));
- memset(bts_conf, 0, sizeof(*bts_conf));
-
- bts_conf->ver = ms_conf->ver = 1;
- bts_conf->icmi = ms_conf->icmi = 1;
-
- /* maybe gcc see's it is copy of _one_ byte */
- bts_conf->m4_75 = ms_conf->m4_75 = msc->amr_conf.m4_75;
- bts_conf->m5_15 = ms_conf->m5_15 = msc->amr_conf.m5_15;
- bts_conf->m5_90 = ms_conf->m5_90 = msc->amr_conf.m5_90;
- bts_conf->m6_70 = ms_conf->m6_70 = msc->amr_conf.m6_70;
- bts_conf->m7_40 = ms_conf->m7_40 = msc->amr_conf.m7_40;
- bts_conf->m7_95 = ms_conf->m7_95 = msc->amr_conf.m7_95;
- if (full_rate) {
- bts_conf->m10_2 = ms_conf->m10_2 = msc->amr_conf.m10_2;
- bts_conf->m12_2 = ms_conf->m12_2 = msc->amr_conf.m12_2;
- }
-
- /* now copy this into the bts structure */
- memcpy(lchan->mr_bts_lv, lchan->mr_ms_lv, sizeof(lchan->mr_ms_lv));
-}
-
-static struct bsc_api bsc_handler = {
- .sapi_n_reject = bsc_sapi_n_reject,
- .cipher_mode_compl = bsc_cipher_mode_compl,
- .compl_l3 = bsc_compl_l3,
- .dtap = bsc_dtap,
- .assign_compl = bsc_assign_compl,
- .assign_fail = bsc_assign_fail,
- .clear_request = bsc_clear_request,
- .classmark_chg = bsc_cm_update,
- .mr_config = bsc_mr_config,
-};
-
-struct bsc_api *osmo_bsc_api()
-{
- return &bsc_handler;
-}
diff --git a/openbsc/src/osmo-bsc/osmo_bsc_audio.c b/openbsc/src/osmo-bsc/osmo_bsc_audio.c
deleted file mode 100644
index 116020900..000000000
--- a/openbsc/src/osmo-bsc/osmo_bsc_audio.c
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * ipaccess audio handling
- *
- * (C) 2009-2010 by Holger Hans Peter Freyther <zecke@selfish.org>
- * (C) 2009-2010 by On-Waves
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <openbsc/bsc_msc_data.h>
-#include <openbsc/osmo_bsc.h>
-#include <openbsc/abis_rsl.h>
-#include <openbsc/gsm_data.h>
-#include <openbsc/debug.h>
-#include <openbsc/signal.h>
-
-#include <arpa/inet.h>
-
-static int handle_abisip_signal(unsigned int subsys, unsigned int signal,
- void *handler_data, void *signal_data)
-{
- struct gsm_subscriber_connection *con;
- struct gsm_lchan *lchan = signal_data;
- int rc;
-
- if (subsys != SS_ABISIP)
- return 0;
-
- con = lchan->conn;
- if (!con || !con->sccp_con)
- return 0;
-
- switch (signal) {
- case S_ABISIP_CRCX_ACK:
- /*
- * TODO: handle handover here... then the audio should go to
- * the old mgcp port..
- */
- /* we can ask it to connect now */
- LOGP(DMSC, LOGL_DEBUG, "Connecting BTS to port: %d conn: %d\n",
- con->sccp_con->rtp_port, lchan->abis_ip.conn_id);
-
- rc = rsl_ipacc_mdcx(lchan, ntohl(INADDR_ANY),
- con->sccp_con->rtp_port,
- lchan->abis_ip.rtp_payload2);
- if (rc < 0) {
- LOGP(DMSC, LOGL_ERROR, "Failed to send MDCX: %d\n", rc);
- return rc;
- }
- break;
- }
-
- return 0;
-}
-
-int osmo_bsc_audio_init(struct gsm_network *net)
-{
- osmo_signal_register_handler(SS_ABISIP, handle_abisip_signal, net);
- return 0;
-}
diff --git a/openbsc/src/osmo-bsc/osmo_bsc_bssap.c b/openbsc/src/osmo-bsc/osmo_bsc_bssap.c
deleted file mode 100644
index 100f66441..000000000
--- a/openbsc/src/osmo-bsc/osmo_bsc_bssap.c
+++ /dev/null
@@ -1,548 +0,0 @@
-/* GSM 08.08 BSSMAP handling */
-/* (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
- * (C) 2009-2012 by On-Waves
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <openbsc/osmo_bsc.h>
-#include <openbsc/osmo_bsc_grace.h>
-#include <openbsc/osmo_bsc_rf.h>
-#include <openbsc/bsc_msc_data.h>
-#include <openbsc/debug.h>
-#include <openbsc/bsc_subscriber.h>
-#include <openbsc/mgcp.h>
-#include <openbsc/paging.h>
-
-#include <osmocom/gsm/protocol/gsm_08_08.h>
-#include <osmocom/gsm/gsm0808.h>
-
-/*
- * helpers for the assignment command
- */
-enum gsm0808_permitted_speech audio_support_to_gsm88(struct gsm_audio_support *audio)
-{
- if (audio->hr) {
- switch (audio->ver) {
- case 1:
- return GSM0808_PERM_HR1;
- break;
- case 2:
- return GSM0808_PERM_HR2;
- break;
- case 3:
- return GSM0808_PERM_HR3;
- break;
- default:
- LOGP(DMSC, LOGL_ERROR, "Wrong speech mode: %d\n", audio->ver);
- return GSM0808_PERM_FR1;
- }
- } else {
- switch (audio->ver) {
- case 1:
- return GSM0808_PERM_FR1;
- break;
- case 2:
- return GSM0808_PERM_FR2;
- break;
- case 3:
- return GSM0808_PERM_FR3;
- break;
- default:
- LOGP(DMSC, LOGL_ERROR, "Wrong speech mode: %d\n", audio->ver);
- return GSM0808_PERM_HR1;
- }
- }
-}
-
-enum gsm48_chan_mode gsm88_to_chan_mode(enum gsm0808_permitted_speech speech)
-{
- switch (speech) {
- case GSM0808_PERM_HR1:
- case GSM0808_PERM_FR1:
- return GSM48_CMODE_SPEECH_V1;
- break;
- case GSM0808_PERM_HR2:
- case GSM0808_PERM_FR2:
- return GSM48_CMODE_SPEECH_EFR;
- break;
- case GSM0808_PERM_HR3:
- case GSM0808_PERM_FR3:
- return GSM48_CMODE_SPEECH_AMR;
- break;
- }
-
- LOGP(DMSC, LOGL_FATAL, "Should not be reached.\n");
- return GSM48_CMODE_SPEECH_AMR;
-}
-
-static int bssmap_handle_reset_ack(struct bsc_msc_data *msc,
- struct msgb *msg, unsigned int length)
-{
- LOGP(DMSC, LOGL_NOTICE, "Reset ACK from MSC\n");
- return 0;
-}
-
-/* GSM 08.08 § 3.2.1.19 */
-static int bssmap_handle_paging(struct bsc_msc_data *msc,
- struct msgb *msg, unsigned int payload_length)
-{
- struct bsc_subscr *subscr;
- struct tlv_parsed tp;
- char mi_string[GSM48_MI_SIZE];
- uint32_t tmsi = GSM_RESERVED_TMSI;
- unsigned int lac = GSM_LAC_RESERVED_ALL_BTS;
- uint8_t data_length;
- const uint8_t *data;
- uint8_t chan_needed = RSL_CHANNEED_ANY;
-
- tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l4h + 1, payload_length - 1, 0, 0);
-
- if (!TLVP_PRESENT(&tp, GSM0808_IE_IMSI)) {
- LOGP(DMSC, LOGL_ERROR, "Mandatory IMSI not present.\n");
- return -1;
- } else if ((TLVP_VAL(&tp, GSM0808_IE_IMSI)[0] & GSM_MI_TYPE_MASK) != GSM_MI_TYPE_IMSI) {
- LOGP(DMSC, LOGL_ERROR, "Wrong content in the IMSI\n");
- return -1;
- }
-
- if (!TLVP_PRESENT(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST)) {
- LOGP(DMSC, LOGL_ERROR, "Mandatory CELL IDENTIFIER LIST not present.\n");
- return -1;
- }
-
- if (TLVP_PRESENT(&tp, GSM0808_IE_TMSI) &&
- TLVP_LEN(&tp, GSM0808_IE_TMSI) == 4) {
- tmsi = ntohl(tlvp_val32_unal(&tp, GSM0808_IE_TMSI));
- }
-
- /*
- * parse the IMSI
- */
- gsm48_mi_to_string(mi_string, sizeof(mi_string),
- TLVP_VAL(&tp, GSM0808_IE_IMSI), TLVP_LEN(&tp, GSM0808_IE_IMSI));
-
- /*
- * parse the cell identifier list
- */
- data_length = TLVP_LEN(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST);
- data = TLVP_VAL(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST);
-
- /*
- * Support paging to all network or one BTS at one LAC
- */
- if (data_length == 3 && data[0] == CELL_IDENT_LAC) {
- lac = osmo_load16be(&data[1]);
- } else if (data_length > 1 || (data[0] & 0x0f) != CELL_IDENT_BSS) {
- LOGP(DMSC, LOGL_ERROR, "Unsupported Cell Identifier List: %s\n", osmo_hexdump(data, data_length));
- return -1;
- }
-
- if (TLVP_PRESENT(&tp, GSM0808_IE_CHANNEL_NEEDED) && TLVP_LEN(&tp, GSM0808_IE_CHANNEL_NEEDED) == 1)
- chan_needed = TLVP_VAL(&tp, GSM0808_IE_CHANNEL_NEEDED)[0] & 0x03;
-
- if (TLVP_PRESENT(&tp, GSM0808_IE_EMLPP_PRIORITY)) {
- LOGP(DMSC, LOGL_ERROR, "eMLPP is not handled\n");
- }
-
- subscr = bsc_subscr_find_or_create_by_imsi(msc->network->bsc_subscribers,
- mi_string);
- if (!subscr) {
- LOGP(DMSC, LOGL_ERROR, "Failed to allocate a subscriber for %s\n", mi_string);
- return -1;
- }
-
- subscr->lac = lac;
- subscr->tmsi = tmsi;
-
- LOGP(DMSC, LOGL_INFO, "Paging request from MSC IMSI: '%s' TMSI: '0x%x/%u' LAC: 0x%x\n", mi_string, tmsi, tmsi, lac);
- bsc_grace_paging_request(msc->network->bsc_data->rf_ctrl->policy,
- subscr, chan_needed, msc);
- return 0;
-}
-
-/*
- * GSM 08.08 § 3.1.9.1 and 3.2.1.21...
- * release our gsm_subscriber_connection and send message
- */
-static int bssmap_handle_clear_command(struct osmo_bsc_sccp_con *conn,
- struct msgb *msg, unsigned int payload_length)
-{
- struct msgb *resp;
-
- /* TODO: handle the cause of this package */
-
- if (conn->conn) {
- LOGP(DMSC, LOGL_INFO, "Releasing all transactions on %p\n", conn);
- gsm0808_clear(conn->conn);
- bsc_subscr_con_free(conn->conn);
- conn->conn = NULL;
- }
-
- /* send the clear complete message */
- resp = gsm0808_create_clear_complete();
- if (!resp) {
- LOGP(DMSC, LOGL_ERROR, "Sending clear complete failed.\n");
- return -1;
- }
-
- bsc_queue_for_msc(conn, resp);
- return 0;
-}
-
-/*
- * GSM 08.08 § 3.4.7 cipher mode handling. We will have to pick
- * the cipher to be used for this. In case we are already using
- * a cipher we will have to send cipher mode reject to the MSC,
- * otherwise we will have to pick something that we and the MS
- * is supporting. Currently we are doing it in a rather static
- * way by picking one ecnryption or no encrytpion.
- */
-static int bssmap_handle_cipher_mode(struct osmo_bsc_sccp_con *conn,
- struct msgb *msg, unsigned int payload_length)
-{
- uint16_t len;
- struct gsm_network *network = NULL;
- const uint8_t *data;
- struct tlv_parsed tp;
- struct msgb *resp;
- int reject_cause = -1;
- int include_imeisv = 1;
-
- if (!conn->conn) {
- LOGP(DMSC, LOGL_ERROR, "No lchan/msc_data in cipher mode command.\n");
- goto reject;
- }
-
- if (conn->ciphering_handled) {
- LOGP(DMSC, LOGL_ERROR, "Already seen ciphering command. Protocol Error.\n");
- goto reject;
- }
-
- conn->ciphering_handled = 1;
-
- tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l4h + 1, payload_length - 1, 0, 0);
- if (!TLVP_PRESENT(&tp, GSM0808_IE_ENCRYPTION_INFORMATION)) {
- LOGP(DMSC, LOGL_ERROR, "IE Encryption Information missing.\n");
- goto reject;
- }
-
- /*
- * check if our global setting is allowed
- * - Currently we check for A5/0 and A5/1
- * - Copy the key if that is necessary
- * - Otherwise reject
- */
- len = TLVP_LEN(&tp, GSM0808_IE_ENCRYPTION_INFORMATION);
- if (len < 1) {
- LOGP(DMSC, LOGL_ERROR, "IE Encryption Information is too short.\n");
- goto reject;
- }
-
- network = conn->conn->bts->network;
- data = TLVP_VAL(&tp, GSM0808_IE_ENCRYPTION_INFORMATION);
-
- if (TLVP_PRESENT(&tp, GSM0808_IE_CIPHER_RESPONSE_MODE))
- include_imeisv = TLVP_VAL(&tp, GSM0808_IE_CIPHER_RESPONSE_MODE)[0] & 0x1;
-
- if (network->a5_encryption == 0 && (data[0] & 0x1) == 0x1) {
- gsm0808_cipher_mode(conn->conn, 0, NULL, 0, include_imeisv);
- } else if (network->a5_encryption != 0 && (data[0] & 0x2) == 0x2) {
- gsm0808_cipher_mode(conn->conn, 1, &data[1], len - 1, include_imeisv);
- } else {
- LOGP(DMSC, LOGL_ERROR, "Can not select encryption...\n");
- goto reject;
- }
-
- return 0;
-
-reject:
- resp = gsm0808_create_cipher_reject(reject_cause);
- if (!resp) {
- LOGP(DMSC, LOGL_ERROR, "Sending the cipher reject failed.\n");
- return -1;
- }
-
- bsc_queue_for_msc(conn, resp);
- return -1;
-}
-
-/*
- * Handle the assignment request message.
- *
- * See §3.2.1.1 for the message type
- */
-static int bssmap_handle_assignm_req(struct osmo_bsc_sccp_con *conn,
- struct msgb *msg, unsigned int length)
-{
- struct msgb *resp;
- struct bsc_msc_data *msc;
- struct tlv_parsed tp;
- uint8_t *data;
- uint8_t timeslot;
- uint8_t multiplex;
- enum gsm48_chan_mode chan_mode = GSM48_CMODE_SIGN;
- int i, supported, port, full_rate = -1;
-
- if (!conn->conn) {
- LOGP(DMSC, LOGL_ERROR, "No lchan/msc_data in cipher mode command.\n");
- return -1;
- }
-
- tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l4h + 1, length - 1, 0, 0);
-
- if (!TLVP_PRESENT(&tp, GSM0808_IE_CHANNEL_TYPE)) {
- LOGP(DMSC, LOGL_ERROR, "Mandatory channel type not present.\n");
- goto reject;
- }
-
- if (!TLVP_PRESENT(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE)) {
- LOGP(DMSC, LOGL_ERROR, "Identity code missing. Audio routing will not work.\n");
- goto reject;
- }
-
- conn->cic = osmo_load16be(TLVP_VAL(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE));
- timeslot = conn->cic & 0x1f;
- multiplex = (conn->cic & ~0x1f) >> 5;
-
- /*
- * Currently we only support a limited subset of all
- * possible channel types. The limitation ends by not using
- * multi-slot, limiting the channel coding, speech...
- */
- if (TLVP_LEN(&tp, GSM0808_IE_CHANNEL_TYPE) < 3) {
- LOGP(DMSC, LOGL_ERROR, "ChannelType len !=3 not supported: %d\n",
- TLVP_LEN(&tp, GSM0808_IE_CHANNEL_TYPE));
- goto reject;
- }
-
- /*
- * Try to figure out if we support the proposed speech codecs. For
- * now we will always pick the full rate codecs.
- */
-
- data = (uint8_t *) TLVP_VAL(&tp, GSM0808_IE_CHANNEL_TYPE);
- if ((data[0] & 0xf) != 0x1) {
- LOGP(DMSC, LOGL_ERROR, "ChannelType != speech: %d\n", data[0]);
- goto reject;
- }
-
- /*
- * go through the list of preferred codecs of our gsm network
- * and try to find it among the permitted codecs. If we found
- * it we will send chan_mode to the right mode and break the
- * inner loop. The outer loop will exit due chan_mode having
- * the correct value.
- */
- full_rate = 0;
- msc = conn->msc;
- for (supported = 0;
- chan_mode == GSM48_CMODE_SIGN && supported < msc->audio_length;
- ++supported) {
-
- int perm_val = audio_support_to_gsm88(msc->audio_support[supported]);
- for (i = 2; i < TLVP_LEN(&tp, GSM0808_IE_CHANNEL_TYPE); ++i) {
- if ((data[i] & 0x7f) == perm_val) {
- chan_mode = gsm88_to_chan_mode(perm_val);
- full_rate = (data[i] & 0x4) == 0;
- break;
- } else if ((data[i] & 0x80) == 0x00) {
- break;
- }
- }
- }
-
- if (chan_mode == GSM48_CMODE_SIGN) {
- LOGP(DMSC, LOGL_ERROR, "No supported audio type found.\n");
- goto reject;
- }
-
- /* map it to a MGCP Endpoint and a RTP port */
- port = mgcp_timeslot_to_endpoint(multiplex, timeslot);
- conn->rtp_port = rtp_calculate_port(port, msc->rtp_base);
-
- return gsm0808_assign_req(conn->conn, chan_mode, full_rate);
-
-reject:
- resp = gsm0808_create_assignment_failure(GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE, NULL);
- if (!resp) {
- LOGP(DMSC, LOGL_ERROR, "Channel allocation failure.\n");
- return -1;
- }
-
- bsc_queue_for_msc(conn, resp);
- return -1;
-}
-
-static int bssmap_rcvmsg_udt(struct bsc_msc_data *msc,
- struct msgb *msg, unsigned int length)
-{
- int ret = 0;
-
- if (length < 1) {
- LOGP(DMSC, LOGL_ERROR, "Not enough room: %d\n", length);
- return -1;
- }
-
- LOGP(DMSC, LOGL_INFO, "Rx MSC UDT BSSMAP %s\n",
- gsm0808_bssmap_name(msg->l4h[0]));
-
- switch (msg->l4h[0]) {
- case BSS_MAP_MSG_RESET_ACKNOWLEDGE:
- ret = bssmap_handle_reset_ack(msc, msg, length);
- break;
- case BSS_MAP_MSG_PAGING:
- ret = bssmap_handle_paging(msc, msg, length);
- break;
- }
-
- return ret;
-}
-
-static int bssmap_rcvmsg_dt1(struct osmo_bsc_sccp_con *conn,
- struct msgb *msg, unsigned int length)
-{
- int ret = 0;
-
- if (length < 1) {
- LOGP(DMSC, LOGL_ERROR, "Not enough room: %d\n", length);
- return -1;
- }
-
- LOGP(DMSC, LOGL_INFO, "Rx MSC DT1 BSSMAP %s\n",
- gsm0808_bssmap_name(msg->l4h[0]));
-
- switch (msg->l4h[0]) {
- case BSS_MAP_MSG_CLEAR_CMD:
- ret = bssmap_handle_clear_command(conn, msg, length);
- break;
- case BSS_MAP_MSG_CIPHER_MODE_CMD:
- ret = bssmap_handle_cipher_mode(conn, msg, length);
- break;
- case BSS_MAP_MSG_ASSIGMENT_RQST:
- ret = bssmap_handle_assignm_req(conn, msg, length);
- break;
- default:
- LOGP(DMSC, LOGL_NOTICE, "Unimplemented msg type: %s\n",
- gsm0808_bssmap_name(msg->l4h[0]));
- break;
- }
-
- return ret;
-}
-
-static int dtap_rcvmsg(struct osmo_bsc_sccp_con *conn,
- struct msgb *msg, unsigned int length)
-{
- struct dtap_header *header;
- struct msgb *gsm48;
- uint8_t *data;
- int rc, dtap_rc;
-
- LOGP(DMSC, LOGL_DEBUG, "Rx MSC DTAP: %s\n",
- osmo_hexdump(msg->l3h, length));
-
- if (!conn->conn) {
- LOGP(DMSC, LOGL_ERROR, "No subscriber connection available\n");
- return -1;
- }
-
- header = (struct dtap_header *) msg->l3h;
- if (sizeof(*header) >= length) {
- LOGP(DMSC, LOGL_ERROR, "The DTAP header does not fit. Wanted: %zu got: %u\n", sizeof(*header), length);
- LOGP(DMSC, LOGL_ERROR, "hex: %s\n", osmo_hexdump(msg->l3h, length));
- return -1;
- }
-
- if (header->length > length - sizeof(*header)) {
- LOGP(DMSC, LOGL_ERROR, "The DTAP l4 information does not fit: header: %u length: %u\n", header->length, length);
- LOGP(DMSC, LOGL_ERROR, "hex: %s\n", osmo_hexdump(msg->l3h, length));
- return -1;
- }
-
- LOGP(DMSC, LOGL_INFO, "Rx MSC DTAP, SAPI: %u CHAN: %u\n", header->link_id & 0x07, header->link_id & 0xC0);
-
- /* forward the data */
- gsm48 = gsm48_msgb_alloc_name("GSM 04.08 DTAP RCV");
- if (!gsm48) {
- LOGP(DMSC, LOGL_ERROR, "Allocation of the message failed.\n");
- return -1;
- }
-
- gsm48->l3h = gsm48->data;
- data = msgb_put(gsm48, length - sizeof(*header));
- memcpy(data, msg->l3h + sizeof(*header), length - sizeof(*header));
-
- /* pass it to the filter for extra actions */
- rc = bsc_scan_msc_msg(conn->conn, gsm48);
- dtap_rc = gsm0808_submit_dtap(conn->conn, gsm48, header->link_id, 1);
- if (rc == BSS_SEND_USSD)
- bsc_send_welcome_ussd(conn->conn);
- return dtap_rc;
-}
-
-int bsc_handle_udt(struct bsc_msc_data *msc,
- struct msgb *msgb, unsigned int length)
-{
- struct bssmap_header *bs;
-
- LOGP(DMSC, LOGL_DEBUG, "Rx MSC UDT: %s\n",
- osmo_hexdump(msgb->l3h, length));
-
- if (length < sizeof(*bs)) {
- LOGP(DMSC, LOGL_ERROR, "The header is too short.\n");
- return -1;
- }
-
- bs = (struct bssmap_header *) msgb->l3h;
- if (bs->length < length - sizeof(*bs))
- return -1;
-
- switch (bs->type) {
- case BSSAP_MSG_BSS_MANAGEMENT:
- msgb->l4h = &msgb->l3h[sizeof(*bs)];
- bssmap_rcvmsg_udt(msc, msgb, length - sizeof(*bs));
- break;
- default:
- LOGP(DMSC, LOGL_NOTICE, "Unimplemented msg type: %s\n",
- gsm0808_bssmap_name(bs->type));
- }
-
- return 0;
-}
-
-int bsc_handle_dt1(struct osmo_bsc_sccp_con *conn,
- struct msgb *msg, unsigned int len)
-{
- if (len < sizeof(struct bssmap_header)) {
- LOGP(DMSC, LOGL_ERROR, "The header is too short.\n");
- }
-
- switch (msg->l3h[0]) {
- case BSSAP_MSG_BSS_MANAGEMENT:
- msg->l4h = &msg->l3h[sizeof(struct bssmap_header)];
- bssmap_rcvmsg_dt1(conn, msg, len - sizeof(struct bssmap_header));
- break;
- case BSSAP_MSG_DTAP:
- dtap_rcvmsg(conn, msg, len);
- break;
- default:
- LOGP(DMSC, LOGL_NOTICE, "Unimplemented BSSAP msg type: %s\n",
- gsm0808_bssap_name(msg->l3h[0]));
- }
-
- return -1;
-}
diff --git a/openbsc/src/osmo-bsc/osmo_bsc_ctrl.c b/openbsc/src/osmo-bsc/osmo_bsc_ctrl.c
deleted file mode 100644
index c23ed2187..000000000
--- a/openbsc/src/osmo-bsc/osmo_bsc_ctrl.c
+++ /dev/null
@@ -1,680 +0,0 @@
-/* (C) 2011 by Daniel Willmann <daniel@totalueberwachung.de>
- * (C) 2011 by Holger Hans Peter Freyther
- * (C) 2011 by On-Waves
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <osmocom/ctrl/control_cmd.h>
-#include <openbsc/debug.h>
-#include <openbsc/gsm_data.h>
-#include <openbsc/osmo_bsc.h>
-#include <openbsc/osmo_bsc_rf.h>
-#include <openbsc/bsc_msc_data.h>
-#include <openbsc/signal.h>
-#include <openbsc/gsm_04_80.h>
-
-#include <osmocom/core/linuxlist.h>
-#include <osmocom/core/signal.h>
-#include <osmocom/core/talloc.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <time.h>
-#include <unistd.h>
-
-void osmo_bsc_send_trap(struct ctrl_cmd *cmd, struct bsc_msc_connection *msc_con)
-{
- struct ctrl_cmd *trap;
- struct ctrl_handle *ctrl;
- struct bsc_msc_data *msc_data;
-
- msc_data = (struct bsc_msc_data *) msc_con->write_queue.bfd.data;
- ctrl = msc_data->network->ctrl;
-
- trap = ctrl_cmd_trap(cmd);
- if (!trap) {
- LOGP(DCTRL, LOGL_ERROR, "Failed to create trap.\n");
- return;
- }
-
- ctrl_cmd_send_to_all(ctrl, trap);
- ctrl_cmd_send(&msc_con->write_queue, trap);
-
- talloc_free(trap);
-}
-
-CTRL_CMD_DEFINE_RO(msc_connection_status, "msc_connection_status");
-static int msc_connection_status = 0;
-
-static int get_msc_connection_status(struct ctrl_cmd *cmd, void *data)
-{
- if (msc_connection_status)
- cmd->reply = "connected";
- else
- cmd->reply = "disconnected";
- return CTRL_CMD_REPLY;
-}
-
-static int msc_connection_status_trap_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data)
-{
- struct ctrl_cmd *cmd;
- struct gsm_network *gsmnet = (struct gsm_network *)handler_data;
-
- if (signal == S_MSC_LOST && msc_connection_status == 1) {
- LOGP(DCTRL, LOGL_DEBUG, "MSC connection lost, sending TRAP.\n");
- msc_connection_status = 0;
- } else if (signal == S_MSC_CONNECTED && msc_connection_status == 0) {
- LOGP(DCTRL, LOGL_DEBUG, "MSC connection (re)established, sending TRAP.\n");
- msc_connection_status = 1;
- } else {
- return 0;
- }
-
- cmd = ctrl_cmd_create(tall_bsc_ctx, CTRL_TYPE_TRAP);
- if (!cmd) {
- LOGP(DCTRL, LOGL_ERROR, "Trap creation failed.\n");
- return 0;
- }
-
- cmd->id = "0";
- cmd->variable = "msc_connection_status";
-
- get_msc_connection_status(cmd, NULL);
-
- ctrl_cmd_send_to_all(gsmnet->ctrl, cmd);
-
- talloc_free(cmd);
-
- return 0;
-}
-
-CTRL_CMD_DEFINE_RO(bts_connection_status, "bts_connection_status");
-static int bts_connection_status = 0;
-
-static int get_bts_connection_status(struct ctrl_cmd *cmd, void *data)
-{
- if (bts_connection_status)
- cmd->reply = "connected";
- else
- cmd->reply = "disconnected";
- return CTRL_CMD_REPLY;
-}
-
-static int bts_connection_status_trap_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data)
-{
- struct ctrl_cmd *cmd;
- struct gsm_network *gsmnet = (struct gsm_network *)handler_data;
- struct gsm_bts *bts;
- int bts_current_status;
-
- if (signal != S_L_INP_TEI_DN && signal != S_L_INP_TEI_UP) {
- return 0;
- }
-
- bts_current_status = 0;
- /* Check if OML on at least one BTS is up */
- llist_for_each_entry(bts, &gsmnet->bts_list, list) {
- if (bts->oml_link) {
- bts_current_status = 1;
- break;
- }
- }
- if (bts_connection_status == 0 && bts_current_status == 1) {
- LOGP(DCTRL, LOGL_DEBUG, "BTS connection (re)established, sending TRAP.\n");
- } else if (bts_connection_status == 1 && bts_current_status == 0) {
- LOGP(DCTRL, LOGL_DEBUG, "No more BTS connected, sending TRAP.\n");
- } else {
- return 0;
- }
-
- cmd = ctrl_cmd_create(tall_bsc_ctx, CTRL_TYPE_TRAP);
- if (!cmd) {
- LOGP(DCTRL, LOGL_ERROR, "Trap creation failed.\n");
- return 0;
- }
-
- bts_connection_status = bts_current_status;
-
- cmd->id = "0";
- cmd->variable = "bts_connection_status";
-
- get_bts_connection_status(cmd, NULL);
-
- ctrl_cmd_send_to_all(gsmnet->ctrl, cmd);
-
- talloc_free(cmd);
-
- return 0;
-}
-
-static int get_bts_loc(struct ctrl_cmd *cmd, void *data);
-
-static void generate_location_state_trap(struct gsm_bts *bts, struct bsc_msc_connection *msc_con)
-{
- struct ctrl_cmd *cmd;
- const char *oper, *admin, *policy;
-
- cmd = ctrl_cmd_create(msc_con, CTRL_TYPE_TRAP);
- if (!cmd) {
- LOGP(DCTRL, LOGL_ERROR, "Failed to create TRAP command.\n");
- return;
- }
-
- cmd->id = "0";
- cmd->variable = talloc_asprintf(cmd, "bts.%i.location-state", bts->nr);
-
- /* Prepare the location reply */
- cmd->node = bts;
- get_bts_loc(cmd, NULL);
-
- oper = osmo_bsc_rf_get_opstate_name(osmo_bsc_rf_get_opstate_by_bts(bts));
- admin = osmo_bsc_rf_get_adminstate_name(osmo_bsc_rf_get_adminstate_by_bts(bts));
- policy = osmo_bsc_rf_get_policy_name(osmo_bsc_rf_get_policy_by_bts(bts));
-
- cmd->reply = talloc_asprintf_append(cmd->reply,
- ",%s,%s,%s,%d,%d",
- oper, admin, policy,
- bts->network->country_code,
- bts->network->network_code);
-
- osmo_bsc_send_trap(cmd, msc_con);
- talloc_free(cmd);
-}
-
-void bsc_gen_location_state_trap(struct gsm_bts *bts)
-{
- struct bsc_msc_data *msc;
-
- llist_for_each_entry(msc, &bts->network->bsc_data->mscs, entry)
- generate_location_state_trap(bts, msc->msc_con);
-}
-
-static int location_equal(struct bts_location *a, struct bts_location *b)
-{
- return ((a->tstamp == b->tstamp) && (a->valid == b->valid) && (a->lat == b->lat) &&
- (a->lon == b->lon) && (a->height == b->height));
-}
-
-static void cleanup_locations(struct llist_head *locations)
-{
- struct bts_location *myloc, *tmp;
- int invalpos = 0, i = 0;
-
- LOGP(DCTRL, LOGL_DEBUG, "Checking position list.\n");
- llist_for_each_entry_safe(myloc, tmp, locations, list) {
- i++;
- if (i > 3) {
- LOGP(DCTRL, LOGL_DEBUG, "Deleting old position.\n");
- llist_del(&myloc->list);
- talloc_free(myloc);
- } else if (myloc->valid == BTS_LOC_FIX_INVALID) {
- /* Only capture the newest of subsequent invalid positions */
- invalpos++;
- if (invalpos > 1) {
- LOGP(DCTRL, LOGL_DEBUG, "Deleting subsequent invalid position.\n");
- invalpos--;
- i--;
- llist_del(&myloc->list);
- talloc_free(myloc);
- }
- } else {
- invalpos = 0;
- }
- }
- LOGP(DCTRL, LOGL_DEBUG, "Found %i positions.\n", i);
-}
-
-CTRL_CMD_DEFINE(bts_loc, "location");
-static int get_bts_loc(struct ctrl_cmd *cmd, void *data)
-{
- struct bts_location *curloc;
- struct gsm_bts *bts = (struct gsm_bts *) cmd->node;
- if (!bts) {
- cmd->reply = "bts not found.";
- return CTRL_CMD_ERROR;
- }
-
- if (llist_empty(&bts->loc_list)) {
- cmd->reply = talloc_asprintf(cmd, "0,invalid,0,0,0");
- return CTRL_CMD_REPLY;
- } else {
- curloc = llist_entry(bts->loc_list.next, struct bts_location, list);
- }
-
- cmd->reply = talloc_asprintf(cmd, "%lu,%s,%f,%f,%f", curloc->tstamp,
- get_value_string(bts_loc_fix_names, curloc->valid), curloc->lat, curloc->lon, curloc->height);
- if (!cmd->reply) {
- cmd->reply = "OOM";
- return CTRL_CMD_ERROR;
- }
-
- return CTRL_CMD_REPLY;
-}
-
-static int set_bts_loc(struct ctrl_cmd *cmd, void *data)
-{
- char *saveptr, *lat, *lon, *height, *tstamp, *valid, *tmp;
- struct bts_location *curloc, *lastloc;
- int ret;
- struct gsm_bts *bts = (struct gsm_bts *) cmd->node;
- if (!bts) {
- cmd->reply = "bts not found.";
- return CTRL_CMD_ERROR;
- }
-
- tmp = talloc_strdup(cmd, cmd->value);
- if (!tmp)
- goto oom;
-
- curloc = talloc_zero(tall_bsc_ctx, struct bts_location);
- if (!curloc) {
- talloc_free(tmp);
- goto oom;
- }
- INIT_LLIST_HEAD(&curloc->list);
-
-
- tstamp = strtok_r(tmp, ",", &saveptr);
- valid = strtok_r(NULL, ",", &saveptr);
- lat = strtok_r(NULL, ",", &saveptr);
- lon = strtok_r(NULL, ",", &saveptr);
- height = strtok_r(NULL, "\0", &saveptr);
-
- curloc->tstamp = atol(tstamp);
- curloc->valid = get_string_value(bts_loc_fix_names, valid);
- curloc->lat = atof(lat);
- curloc->lon = atof(lon);
- curloc->height = atof(height);
- talloc_free(tmp);
-
- lastloc = llist_entry(bts->loc_list.next, struct bts_location, list);
-
- /* Add location to the end of the list */
- llist_add(&curloc->list, &bts->loc_list);
-
- ret = get_bts_loc(cmd, data);
-
- if (!location_equal(curloc, lastloc))
- bsc_gen_location_state_trap(bts);
-
- cleanup_locations(&bts->loc_list);
-
- return ret;
-
-oom:
- cmd->reply = "OOM";
- return CTRL_CMD_ERROR;
-}
-
-static int verify_bts_loc(struct ctrl_cmd *cmd, const char *value, void *data)
-{
- char *saveptr, *latstr, *lonstr, *heightstr, *tstampstr, *validstr, *tmp;
- time_t tstamp;
- int valid;
- double lat, lon, height __attribute__((unused));
-
- tmp = talloc_strdup(cmd, value);
- if (!tmp)
- return 1;
-
- tstampstr = strtok_r(tmp, ",", &saveptr);
- validstr = strtok_r(NULL, ",", &saveptr);
- latstr = strtok_r(NULL, ",", &saveptr);
- lonstr = strtok_r(NULL, ",", &saveptr);
- heightstr = strtok_r(NULL, "\0", &saveptr);
-
- if ((tstampstr == NULL) || (validstr == NULL) || (latstr == NULL) ||
- (lonstr == NULL) || (heightstr == NULL))
- goto err;
-
- tstamp = atol(tstampstr);
- valid = get_string_value(bts_loc_fix_names, validstr);
- lat = atof(latstr);
- lon = atof(lonstr);
- height = atof(heightstr);
- talloc_free(tmp);
- tmp = NULL;
-
- if (((tstamp == 0) && (valid != BTS_LOC_FIX_INVALID)) || (lat < -90) || (lat > 90) ||
- (lon < -180) || (lon > 180) || (valid < 0)) {
- goto err;
- }
-
- return 0;
-
-err:
- talloc_free(tmp);
- cmd->reply = talloc_strdup(cmd, "The format is <unixtime>,(invalid|fix2d|fix3d),<lat>,<lon>,<height>");
- return 1;
-}
-
-CTRL_CMD_DEFINE(net_timezone, "timezone");
-static int get_net_timezone(struct ctrl_cmd *cmd, void *data)
-{
- struct gsm_network *net = (struct gsm_network*)cmd->node;
-
- struct gsm_tz *tz = &net->tz;
- if (tz->override)
- cmd->reply = talloc_asprintf(cmd, "%d,%d,%d",
- tz->hr, tz->mn, tz->dst);
- else
- cmd->reply = talloc_asprintf(cmd, "off");
-
- if (!cmd->reply) {
- cmd->reply = "OOM";
- return CTRL_CMD_ERROR;
- }
-
- return CTRL_CMD_REPLY;
-}
-
-static int set_net_timezone(struct ctrl_cmd *cmd, void *data)
-{
- char *saveptr, *hourstr, *minstr, *dststr, *tmp = 0;
- int override;
- struct gsm_network *net = (struct gsm_network*)cmd->node;
-
- tmp = talloc_strdup(cmd, cmd->value);
- if (!tmp)
- goto oom;
-
- hourstr = strtok_r(tmp, ",", &saveptr);
- minstr = strtok_r(NULL, ",", &saveptr);
- dststr = strtok_r(NULL, ",", &saveptr);
-
- override = 0;
-
- if (hourstr != NULL)
- override = strcasecmp(hourstr, "off") != 0;
-
- struct gsm_tz *tz = &net->tz;
- tz->override = override;
-
- if (override) {
- tz->hr = hourstr ? atol(hourstr) : 0;
- tz->mn = minstr ? atol(minstr) : 0;
- tz->dst = dststr ? atol(dststr) : 0;
- }
-
- talloc_free(tmp);
- tmp = NULL;
-
- return get_net_timezone(cmd, data);
-
-oom:
- cmd->reply = "OOM";
- return CTRL_CMD_ERROR;
-}
-
-static int verify_net_timezone(struct ctrl_cmd *cmd, const char *value, void *data)
-{
- char *saveptr, *hourstr, *minstr, *dststr, *tmp;
- int override, tz_hours, tz_mins, tz_dst;
-
- tmp = talloc_strdup(cmd, value);
- if (!tmp)
- return 1;
-
- hourstr = strtok_r(tmp, ",", &saveptr);
- minstr = strtok_r(NULL, ",", &saveptr);
- dststr = strtok_r(NULL, ",", &saveptr);
-
- if (hourstr == NULL)
- goto err;
-
- override = strcasecmp(hourstr, "off") != 0;
-
- if (!override) {
- talloc_free(tmp);
- return 0;
- }
-
- if (minstr == NULL || dststr == NULL)
- goto err;
-
- tz_hours = atol(hourstr);
- tz_mins = atol(minstr);
- tz_dst = atol(dststr);
-
- talloc_free(tmp);
- tmp = NULL;
-
- if ((tz_hours < -19) || (tz_hours > 19) ||
- (tz_mins < 0) || (tz_mins >= 60) || (tz_mins % 15 != 0) ||
- (tz_dst < 0) || (tz_dst > 2))
- goto err;
-
- return 0;
-
-err:
- talloc_free(tmp);
- cmd->reply = talloc_strdup(cmd, "The format is <hours>,<mins>,<dst> or 'off' where -19 <= hours <= 19, mins in {0, 15, 30, 45}, and 0 <= dst <= 2");
- return 1;
-}
-
-CTRL_CMD_DEFINE(net_notification, "notification");
-static int get_net_notification(struct ctrl_cmd *cmd, void *data)
-{
- cmd->reply = "There is nothing to read";
- return CTRL_CMD_ERROR;
-}
-
-static int set_net_notification(struct ctrl_cmd *cmd, void *data)
-{
- struct ctrl_cmd *trap;
- struct gsm_network *net;
-
- net = cmd->node;
-
- trap = ctrl_cmd_create(tall_bsc_ctx, CTRL_TYPE_TRAP);
- if (!trap) {
- LOGP(DCTRL, LOGL_ERROR, "Trap creation failed\n");
- goto handled;
- }
-
- trap->id = "0";
- trap->variable = "notification";
- trap->reply = talloc_strdup(trap, cmd->value);
-
- /*
- * This should only be sent to local systems. In the future
- * we might even ask for systems to register to receive
- * the notifications.
- */
- ctrl_cmd_send_to_all(net->ctrl, trap);
- talloc_free(trap);
-
-handled:
- return CTRL_CMD_HANDLED;
-}
-
-static int verify_net_notification(struct ctrl_cmd *cmd, const char *value, void *data)
-{
- return 0;
-}
-
-CTRL_CMD_DEFINE(net_inform_msc, "inform-msc-v1");
-static int get_net_inform_msc(struct ctrl_cmd *cmd, void *data)
-{
- cmd->reply = "There is nothing to read";
- return CTRL_CMD_ERROR;
-}
-
-static int set_net_inform_msc(struct ctrl_cmd *cmd, void *data)
-{
- struct gsm_network *net;
- struct bsc_msc_data *msc;
-
- net = cmd->node;
- llist_for_each_entry(msc, &net->bsc_data->mscs, entry) {
- struct ctrl_cmd *trap;
-
- trap = ctrl_cmd_create(tall_bsc_ctx, CTRL_TYPE_TRAP);
- if (!trap) {
- LOGP(DCTRL, LOGL_ERROR, "Trap creation failed\n");
- continue;
- }
-
- trap->id = "0";
- trap->variable = "inform-msc-v1";
- trap->reply = talloc_strdup(trap, cmd->value);
- ctrl_cmd_send(&msc->msc_con->write_queue, trap);
- talloc_free(trap);
- }
-
-
- return CTRL_CMD_HANDLED;
-}
-
-static int verify_net_inform_msc(struct ctrl_cmd *cmd, const char *value, void *data)
-{
- return 0;
-}
-
-CTRL_CMD_DEFINE(net_ussd_notify, "ussd-notify-v1");
-static int get_net_ussd_notify(struct ctrl_cmd *cmd, void *data)
-{
- cmd->reply = "There is nothing to read";
- return CTRL_CMD_ERROR;
-}
-
-static int set_net_ussd_notify(struct ctrl_cmd *cmd, void *data)
-{
- struct gsm_subscriber_connection *conn;
- struct gsm_network *net;
- char *saveptr = NULL;
- char *cic_str, *alert_str, *text_str;
- int cic, alert;
-
- /* Verify has done the test for us */
- cic_str = strtok_r(cmd->value, ",", &saveptr);
- alert_str = strtok_r(NULL, ",", &saveptr);
- text_str = strtok_r(NULL, ",", &saveptr);
-
- if (!cic_str || !alert_str || !text_str) {
- cmd->reply = "Programming issue. How did this pass verify?";
- return CTRL_CMD_ERROR;
- }
-
- cmd->reply = "No connection found";
-
- cic = atoi(cic_str);
- alert = atoi(alert_str);
-
- net = cmd->node;
- llist_for_each_entry(conn, &net->subscr_conns, entry) {
- if (!conn->sccp_con)
- continue;
-
- if (conn->sccp_con->cic != cic)
- continue;
-
- /*
- * This is a hack. My E71 does not like to immediately
- * receive a release complete on a TCH. So schedule a
- * release complete to clear any previous attempt. The
- * right thing would be to track invokeId and only send
- * the release complete when we get a returnResultLast
- * for this invoke id.
- */
- bsc_send_ussd_release_complete(conn);
- bsc_send_ussd_notify(conn, alert, text_str);
- cmd->reply = "Found a connection";
- break;
- }
-
- return CTRL_CMD_REPLY;
-}
-
-static int verify_net_ussd_notify(struct ctrl_cmd *cmd, const char *value, void *data)
-{
- char *saveptr = NULL;
- char *inp, *cic, *alert, *text;
-
- OSMO_ASSERT(cmd);
- inp = talloc_strdup(cmd, value);
-
- cic = strtok_r(inp, ",", &saveptr);
- alert = strtok_r(NULL, ",", &saveptr);
- text = strtok_r(NULL, ",", &saveptr);
-
- talloc_free(inp);
- if (!cic || !alert || !text)
- return 1;
- return 0;
-}
-
-static int msc_signal_handler(unsigned int subsys, unsigned int signal,
- void *handler_data, void *signal_data)
-{
- struct msc_signal_data *msc;
- struct gsm_network *net;
- struct gsm_bts *bts;
-
- if (subsys != SS_MSC)
- return 0;
- if (signal != S_MSC_AUTHENTICATED)
- return 0;
-
- msc = signal_data;
-
- net = msc->data->network;
- llist_for_each_entry(bts, &net->bts_list, list)
- generate_location_state_trap(bts, msc->data->msc_con);
-
- return 0;
-}
-
-int bsc_ctrl_cmds_install(struct gsm_network *net)
-{
- int rc;
-
- rc = bsc_base_ctrl_cmds_install();
- if (rc)
- goto end;
- rc = ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_loc);
- if (rc)
- goto end;
- rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_timezone);
- if (rc)
- goto end;
- rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_msc_connection_status);
- if (rc)
- goto end;
- rc = osmo_signal_register_handler(SS_MSC, &msc_connection_status_trap_cb, net);
- if (rc)
- goto end;
- rc = osmo_signal_register_handler(SS_MSC, msc_signal_handler, NULL);
- if (rc)
- goto end;
- rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_bts_connection_status);
- if (rc)
- goto end;
- rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_notification);
- if (rc)
- goto end;
- rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_inform_msc);
- if (rc)
- goto end;
- rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_ussd_notify);
- if (rc)
- goto end;
- rc = osmo_signal_register_handler(SS_L_INPUT, &bts_connection_status_trap_cb, net);
-
-end:
- return rc;
-}
diff --git a/openbsc/src/osmo-bsc/osmo_bsc_filter.c b/openbsc/src/osmo-bsc/osmo_bsc_filter.c
deleted file mode 100644
index 2c84b169f..000000000
--- a/openbsc/src/osmo-bsc/osmo_bsc_filter.c
+++ /dev/null
@@ -1,381 +0,0 @@
-/* (C) 2009-2011 by Holger Hans Peter Freyther <zecke@selfish.org>
- * (C) 2009-2011 by On-Waves
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <openbsc/osmo_bsc.h>
-#include <openbsc/bsc_msc_data.h>
-#include <openbsc/gsm_04_80.h>
-#include <openbsc/gsm_subscriber.h>
-#include <openbsc/bsc_subscriber.h>
-#include <openbsc/debug.h>
-#include <openbsc/paging.h>
-
-#include <stdlib.h>
-
-static void handle_lu_request(struct gsm_subscriber_connection *conn,
- struct msgb *msg)
-{
- struct gsm48_hdr *gh;
- struct gsm48_loc_upd_req *lu;
- struct gsm48_loc_area_id lai;
- struct gsm_network *net;
-
- if (msgb_l3len(msg) < sizeof(*gh) + sizeof(*lu)) {
- LOGP(DMSC, LOGL_ERROR, "LU too small to look at: %u\n", msgb_l3len(msg));
- return;
- }
-
- net = conn->bts->network;
-
- gh = msgb_l3(msg);
- lu = (struct gsm48_loc_upd_req *) gh->data;
-
- gsm48_generate_lai(&lai, net->country_code, net->network_code,
- conn->bts->location_area_code);
-
- if (memcmp(&lai, &lu->lai, sizeof(lai)) != 0) {
- LOGP(DMSC, LOGL_DEBUG, "Marking con for welcome USSD.\n");
- conn->sccp_con->new_subscriber = 1;
- }
-}
-
-/* extract a subscriber from the paging response */
-static struct bsc_subscr *extract_sub(struct gsm_subscriber_connection *conn,
- struct msgb *msg)
-{
- uint8_t mi_type;
- char mi_string[GSM48_MI_SIZE];
- struct gsm48_hdr *gh;
- struct gsm48_pag_resp *resp;
- struct bsc_subscr *subscr;
-
- if (msgb_l3len(msg) < sizeof(*gh) + sizeof(*resp)) {
- LOGP(DMSC, LOGL_ERROR, "PagingResponse too small: %u\n", msgb_l3len(msg));
- return NULL;
- }
-
- gh = msgb_l3(msg);
- resp = (struct gsm48_pag_resp *) &gh->data[0];
-
- gsm48_paging_extract_mi(resp, msgb_l3len(msg) - sizeof(*gh),
- mi_string, &mi_type);
- DEBUGP(DRR, "PAGING RESPONSE: MI(%s)=%s\n",
- gsm48_mi_type_name(mi_type), mi_string);
-
- switch (mi_type) {
- case GSM_MI_TYPE_TMSI:
- subscr = bsc_subscr_find_by_tmsi(conn->network->bsc_subscribers,
- tmsi_from_string(mi_string));
- break;
- case GSM_MI_TYPE_IMSI:
- subscr = bsc_subscr_find_by_imsi(conn->network->bsc_subscribers,
- mi_string);
- break;
- default:
- subscr = NULL;
- break;
- }
-
- return subscr;
-}
-
-/* we will need to stop the paging request */
-static int handle_page_resp(struct gsm_subscriber_connection *conn, struct msgb *msg)
-{
- struct bsc_subscr *subscr = extract_sub(conn, msg);
-
- if (!subscr) {
- LOGP(DMSC, LOGL_ERROR, "Non active subscriber got paged.\n");
- return -1;
- }
-
- paging_request_stop(&conn->network->bts_list, conn->bts, subscr, conn,
- msg);
- bsc_subscr_put(subscr);
- return 0;
-}
-
-static int is_cm_service_for_emerg(struct msgb *msg)
-{
- struct gsm48_service_request *cm;
- struct gsm48_hdr *gh = msgb_l3(msg);
-
- if (msgb_l3len(msg) < sizeof(*gh) + sizeof(*cm)) {
- LOGP(DMSC, LOGL_ERROR, "CM ServiceRequest does not fit.\n");
- return 0;
- }
-
- cm = (struct gsm48_service_request *) &gh->data[0];
- return cm->cm_service_type == GSM48_CMSERV_EMERGENCY;
-}
-
-struct bsc_msc_data *bsc_find_msc(struct gsm_subscriber_connection *conn,
- struct msgb *msg)
-{
- struct gsm48_hdr *gh;
- int8_t pdisc;
- uint8_t mtype;
- struct osmo_bsc_data *bsc;
- struct bsc_msc_data *msc, *pag_msc;
- struct bsc_subscr *subscr;
- int is_emerg = 0;
-
- bsc = conn->bts->network->bsc_data;
-
- if (msgb_l3len(msg) < sizeof(*gh)) {
- LOGP(DMSC, LOGL_ERROR, "There is no GSM48 header here.\n");
- return NULL;
- }
-
- gh = msgb_l3(msg);
- pdisc = gsm48_hdr_pdisc(gh);
- mtype = gsm48_hdr_msg_type(gh);
-
- /*
- * We are asked to select a MSC here but they are not equal. We
- * want to respond to a paging request on the MSC where we got the
- * request from. This is where we need to decide where this connection
- * will go.
- */
- if (pdisc == GSM48_PDISC_RR && mtype == GSM48_MT_RR_PAG_RESP)
- goto paging;
- else if (pdisc == GSM48_PDISC_MM && mtype == GSM48_MT_MM_CM_SERV_REQ) {
- is_emerg = is_cm_service_for_emerg(msg);
- goto round_robin;
- } else
- goto round_robin;
-
-round_robin:
- llist_for_each_entry(msc, &bsc->mscs, entry) {
- if (!msc->msc_con->is_authenticated)
- continue;
- if (!is_emerg && msc->type != MSC_CON_TYPE_NORMAL)
- continue;
- if (is_emerg && !msc->allow_emerg)
- continue;
-
- /* force round robin by moving it to the end */
- llist_move_tail(&msc->entry, &bsc->mscs);
- return msc;
- }
-
- return NULL;
-
-paging:
- subscr = extract_sub(conn, msg);
-
- if (!subscr) {
- LOGP(DMSC, LOGL_ERROR, "Got paged but no subscriber found.\n");
- return NULL;
- }
-
- pag_msc = paging_get_data(conn->bts, subscr);
- bsc_subscr_put(subscr);
-
- llist_for_each_entry(msc, &bsc->mscs, entry) {
- if (msc != pag_msc)
- continue;
-
- /*
- * We don't check if the MSC is connected. In case it
- * is not the connection will be dropped.
- */
-
- /* force round robin by moving it to the end */
- llist_move_tail(&msc->entry, &bsc->mscs);
- return msc;
- }
-
- LOGP(DMSC, LOGL_ERROR, "Got paged but no request found.\n");
- return NULL;
-}
-
-
-/**
- * This is used to scan a message for extra functionality of the BSC. This
- * includes scanning for location updating requests/acceptd and then send
- * a welcome USSD message to the subscriber.
- */
-int bsc_scan_bts_msg(struct gsm_subscriber_connection *conn, struct msgb *msg)
-{
- struct gsm48_hdr *gh = msgb_l3(msg);
- uint8_t pdisc = gsm48_hdr_pdisc(gh);
- uint8_t mtype = gsm48_hdr_msg_type(gh);
-
- if (pdisc == GSM48_PDISC_MM) {
- if (mtype == GSM48_MT_MM_LOC_UPD_REQUEST)
- handle_lu_request(conn, msg);
- } else if (pdisc == GSM48_PDISC_RR) {
- if (mtype == GSM48_MT_RR_PAG_RESP)
- handle_page_resp(conn, msg);
- }
-
- return 0;
-}
-
-static int send_welcome_ussd(struct gsm_subscriber_connection *conn)
-{
- struct osmo_bsc_sccp_con *bsc_con;
-
- bsc_con = conn->sccp_con;
- if (!bsc_con) {
- LOGP(DMSC, LOGL_DEBUG, "No SCCP connection associated.\n");
- return 0;
- }
-
- if (!bsc_con->msc->ussd_welcome_txt) {
- LOGP(DMSC, LOGL_DEBUG, "No USSD Welcome text defined.\n");
- return 0;
- }
-
- return BSS_SEND_USSD;
-}
-
-int bsc_send_welcome_ussd(struct gsm_subscriber_connection *conn)
-{
- bsc_send_ussd_notify(conn, 1, conn->sccp_con->msc->ussd_welcome_txt);
- bsc_send_ussd_release_complete(conn);
-
- return 0;
-}
-
-static int bsc_patch_mm_info(struct gsm_subscriber_connection *conn,
- uint8_t *data, unsigned int length)
-{
- struct tlv_parsed tp;
- int parse_res;
- struct gsm_bts *bts = conn->bts;
- int tzunits;
- uint8_t tzbsd = 0;
- uint8_t dst = 0;
-
- parse_res = tlv_parse(&tp, &gsm48_mm_att_tlvdef, data, length, 0, 0);
- if (parse_res <= 0 && parse_res != -3)
- /* FIXME: -3 means unknown IE error, so this accepts messages
- * with unknown IEs. But parsing has aborted with the unknown
- * IE and the message is broken or parsed incompletely. */
- return 0;
-
- /* Is TZ patching enabled? */
- struct gsm_tz *tz = &bts->network->tz;
- if (!tz->override)
- return 0;
-
- /* Convert tz.hr and tz.mn to units */
- if (tz->hr < 0) {
- tzunits = -tz->hr*4;
- tzbsd |= 0x08;
- } else
- tzunits = tz->hr*4;
-
- tzunits = tzunits + (tz->mn/15);
-
- tzbsd |= (tzunits % 10)*0x10 + (tzunits / 10);
-
- /* Convert DST value */
- if (tz->dst >= 0 && tz->dst <= 2)
- dst = tz->dst;
-
- if (TLVP_PRESENT(&tp, GSM48_IE_UTC)) {
- LOGP(DMSC, LOGL_DEBUG,
- "Changing 'Local time zone' from 0x%02x to 0x%02x.\n",
- TLVP_VAL(&tp, GSM48_IE_UTC)[6], tzbsd);
- ((uint8_t *)(TLVP_VAL(&tp, GSM48_IE_UTC)))[0] = tzbsd;
- }
- if (TLVP_PRESENT(&tp, GSM48_IE_NET_TIME_TZ)) {
- LOGP(DMSC, LOGL_DEBUG,
- "Changing 'Universal time and local time zone' TZ from "
- "0x%02x to 0x%02x.\n",
- TLVP_VAL(&tp, GSM48_IE_NET_TIME_TZ)[6], tzbsd);
- ((uint8_t *)(TLVP_VAL(&tp, GSM48_IE_NET_TIME_TZ)))[6] = tzbsd;
- }
-#ifdef GSM48_IE_NET_DST
- if (TLVP_PRESENT(&tp, GSM48_IE_NET_DST)) {
- LOGP(DMSC, LOGL_DEBUG,
- "Changing 'Network daylight saving time' from "
- "0x%02x to 0x%02x.\n",
- TLVP_VAL(&tp, GSM48_IE_NET_DST)[0], dst);
- ((uint8_t *)(TLVP_VAL(&tp, GSM48_IE_NET_DST)))[0] = dst;
- }
-#endif
-
- return 0;
-}
-
-static int has_core_identity(struct bsc_msc_data *msc)
-{
- if (msc->core_mnc != -1)
- return 1;
- if (msc->core_mcc != -1)
- return 1;
- if (msc->core_lac != -1)
- return 1;
- if (msc->core_ci != -1)
- return 1;
- return 0;
-}
-
-/**
- * Messages coming back from the MSC.
- */
-int bsc_scan_msc_msg(struct gsm_subscriber_connection *conn, struct msgb *msg)
-{
- struct bsc_msc_data *msc;
- struct gsm_network *net;
- struct gsm48_loc_area_id *lai;
- struct gsm48_hdr *gh;
- uint8_t pdisc;
- uint8_t mtype;
- int length = msgb_l3len(msg);
-
- if (length < sizeof(*gh)) {
- LOGP(DMSC, LOGL_ERROR, "GSM48 header does not fit.\n");
- return -1;
- }
-
- gh = (struct gsm48_hdr *) msgb_l3(msg);
- length -= (const char *)&gh->data[0] - (const char *)gh;
-
- pdisc = gsm48_hdr_pdisc(gh);
- if (pdisc != GSM48_PDISC_MM)
- return 0;
-
- mtype = gsm48_hdr_msg_type(gh);
- net = conn->bts->network;
- msc = conn->sccp_con->msc;
-
- if (mtype == GSM48_MT_MM_LOC_UPD_ACCEPT) {
- if (has_core_identity(msc)) {
- if (msgb_l3len(msg) >= sizeof(*gh) + sizeof(*lai)) {
- /* overwrite LAI in the message */
- lai = (struct gsm48_loc_area_id *) &gh->data[0];
- gsm48_generate_lai(lai, net->country_code,
- net->network_code,
- conn->bts->location_area_code);
- }
- }
-
- if (conn->sccp_con->new_subscriber)
- return send_welcome_ussd(conn);
- return 0;
- } else if (mtype == GSM48_MT_MM_INFO) {
- bsc_patch_mm_info(conn, &gh->data[0], length);
- }
-
- return 0;
-}
diff --git a/openbsc/src/osmo-bsc/osmo_bsc_grace.c b/openbsc/src/osmo-bsc/osmo_bsc_grace.c
deleted file mode 100644
index 63afa20d0..000000000
--- a/openbsc/src/osmo-bsc/osmo_bsc_grace.c
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * (C) 2010-2013 by Holger Hans Peter Freyther <zecke@selfish.org>
- * (C) 2010-2013 by On-Waves
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <openbsc/osmo_bsc_grace.h>
-#include <openbsc/osmo_bsc_rf.h>
-#include <openbsc/bsc_msc_data.h>
-#include <openbsc/gsm_04_80.h>
-#include <openbsc/bsc_subscriber.h>
-#include <openbsc/paging.h>
-#include <openbsc/signal.h>
-
-int bsc_grace_allow_new_connection(struct gsm_network *network, struct gsm_bts *bts)
-{
- if (bts->excl_from_rf_lock)
- return 1;
- return network->bsc_data->rf_ctrl->policy == S_RF_ON;
-}
-
-
-static int normal_paging(struct bsc_subscr *subscr, int chan_needed,
- struct bsc_msc_data *msc)
-{
- /* we can't page by lac.. we need to page everything */
- if (msc->core_lac != -1) {
- struct gsm_bts *bts;
-
- llist_for_each_entry(bts, &msc->network->bts_list, list)
- paging_request_bts(bts, subscr, chan_needed, NULL, msc);
-
- return 0;
- }
-
- return paging_request(msc->network, subscr, chan_needed, NULL, msc);
-}
-
-static int locked_paging(struct bsc_subscr *subscr, int chan_needed,
- struct bsc_msc_data *msc)
-{
- struct gsm_bts *bts = NULL;
-
- /*
- * Check if there is any BTS that is on for the given lac. Start
- * with NULL and iterate through all bts.
- */
- llist_for_each_entry(bts, &msc->network->bts_list, list) {
- /*
- * continue if the BTS is not excluded from the lock
- */
- if (!bts->excl_from_rf_lock)
- continue;
-
- /* in case of no lac patching is in place, check the BTS */
- if (msc->core_lac == -1 && subscr->lac != bts->location_area_code)
- continue;
-
- /*
- * now page on this bts
- */
- paging_request_bts(bts, subscr, chan_needed, NULL, msc);
- };
-
- /* All bts are either off or in the grace period */
- return 0;
-}
-
-/**
- * Try to not page if everything the cell is not on.
- */
-int bsc_grace_paging_request(enum signal_rf rf_policy,
- struct bsc_subscr *subscr,
- int chan_needed,
- struct bsc_msc_data *msc)
-{
- if (rf_policy == S_RF_ON)
- return normal_paging(subscr, chan_needed, msc);
- return locked_paging(subscr, chan_needed, msc);
-}
-
-static int handle_sub(struct gsm_lchan *lchan, const char *text)
-{
- struct gsm_subscriber_connection *conn;
-
- /* only send it to TCH */
- if (lchan->type != GSM_LCHAN_TCH_H && lchan->type != GSM_LCHAN_TCH_F)
- return -1;
-
- /* only send on the primary channel */
- conn = lchan->conn;
- if (!conn)
- return -1;
-
- if (conn->lchan != lchan)
- return -1;
-
- /* only when active */
- if (lchan->state != LCHAN_S_ACTIVE)
- return -1;
-
- bsc_send_ussd_notify(conn, 0, text);
- bsc_send_ussd_release_complete(conn);
-
- return 0;
-}
-
-/*
- * The place to handle the grace mode. Right now we will send
- * USSD messages to the subscriber, in the future we might start
- * a timer to have different modes for the grace period.
- */
-static int handle_grace(struct gsm_network *network)
-{
- int ts_nr, lchan_nr;
- struct gsm_bts *bts;
- struct gsm_bts_trx *trx;
-
- if (!network->bsc_data->mid_call_txt)
- return 0;
-
- llist_for_each_entry(bts, &network->bts_list, list) {
- llist_for_each_entry(trx, &bts->trx_list, list) {
- for (ts_nr = 0; ts_nr < TRX_NR_TS; ++ts_nr) {
- struct gsm_bts_trx_ts *ts = &trx->ts[ts_nr];
- for (lchan_nr = 0; lchan_nr < TS_MAX_LCHAN; ++lchan_nr) {
- handle_sub(&ts->lchan[lchan_nr],
- network->bsc_data->mid_call_txt);
- }
- }
- }
- }
- return 0;
-}
-
-static int handle_rf_signal(unsigned int subsys, unsigned int signal,
- void *handler_data, void *signal_data)
-{
- struct rf_signal_data *sig;
-
- if (subsys != SS_RF)
- return -1;
-
- sig = signal_data;
-
- if (signal == S_RF_GRACE)
- handle_grace(sig->net);
-
- return 0;
-}
-
-static __attribute__((constructor)) void on_dso_load_grace(void)
-{
- osmo_signal_register_handler(SS_RF, handle_rf_signal, NULL);
-}
diff --git a/openbsc/src/osmo-bsc/osmo_bsc_main.c b/openbsc/src/osmo-bsc/osmo_bsc_main.c
deleted file mode 100644
index ee094d670..000000000
--- a/openbsc/src/osmo-bsc/osmo_bsc_main.c
+++ /dev/null
@@ -1,301 +0,0 @@
-/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
- * (C) 2009-2011 by Holger Hans Peter Freyther <zecke@selfish.org>
- * (C) 2009-2011 by On-Waves
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <openbsc/bss.h>
-#include <openbsc/debug.h>
-#include <openbsc/gsm_data.h>
-#include <openbsc/osmo_bsc.h>
-#include <openbsc/osmo_bsc_rf.h>
-#include <openbsc/bsc_msc_data.h>
-#include <openbsc/signal.h>
-#include <openbsc/vty.h>
-#include <openbsc/ipaccess.h>
-#include <openbsc/ctrl.h>
-
-#include <osmocom/ctrl/control_cmd.h>
-#include <osmocom/ctrl/control_if.h>
-#include <osmocom/ctrl/ports.h>
-#include <osmocom/ctrl/control_vty.h>
-
-#include <osmocom/core/application.h>
-#include <osmocom/core/linuxlist.h>
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/stats.h>
-#include <osmocom/gsm/protocol/gsm_12_21.h>
-
-#include <osmocom/abis/abis.h>
-
-#include <osmocom/sccp/sccp.h>
-
-#define _GNU_SOURCE
-#include <getopt.h>
-
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <time.h>
-#include <unistd.h>
-
-
-#include "../../bscconfig.h"
-
-struct gsm_network *bsc_gsmnet = 0;
-static const char *config_file = "openbsc.cfg";
-static const char *rf_ctrl = NULL;
-extern const char *openbsc_copyright;
-static int daemonize = 0;
-static struct llist_head access_lists;
-
-struct llist_head *bsc_access_lists(void)
-{
- return &access_lists;
-}
-
-static void print_usage()
-{
- printf("Usage: osmo-bsc\n");
-}
-
-static void print_help()
-{
- printf(" Some useful help...\n");
- printf(" -h --help this text\n");
- printf(" -D --daemonize Fork the process into a background daemon\n");
- printf(" -d option --debug=DRLL:DCC:DMM:DRR:DRSL:DNM enable debugging\n");
- printf(" -s --disable-color\n");
- printf(" -T --timestamp. Print a timestamp in the debug output.\n");
- printf(" -c --config-file filename The config file to use.\n");
- printf(" -l --local=IP. The local address of the MGCP.\n");
- printf(" -e --log-level number. Set a global loglevel.\n");
- printf(" -r --rf-ctl NAME. A unix domain socket to listen for cmds.\n");
- printf(" -t --testmode. A special mode to provoke failures at the MSC.\n");
-}
-
-static void handle_options(int argc, char **argv)
-{
- while (1) {
- int option_index = 0, c;
- static struct option long_options[] = {
- {"help", 0, 0, 'h'},
- {"debug", 1, 0, 'd'},
- {"daemonize", 0, 0, 'D'},
- {"config-file", 1, 0, 'c'},
- {"disable-color", 0, 0, 's'},
- {"timestamp", 0, 0, 'T'},
- {"local", 1, 0, 'l'},
- {"log-level", 1, 0, 'e'},
- {"rf-ctl", 1, 0, 'r'},
- {"testmode", 0, 0, 't'},
- {0, 0, 0, 0}
- };
-
- c = getopt_long(argc, argv, "hd:DsTc:e:r:t",
- long_options, &option_index);
- if (c == -1)
- break;
-
- switch (c) {
- case 'h':
- print_usage();
- print_help();
- exit(0);
- case 's':
- log_set_use_color(osmo_stderr_target, 0);
- break;
- case 'd':
- log_parse_category_mask(osmo_stderr_target, optarg);
- break;
- case 'D':
- daemonize = 1;
- break;
- case 'c':
- config_file = optarg;
- break;
- case 'T':
- log_set_print_timestamp(osmo_stderr_target, 1);
- break;
- case 'e':
- log_set_log_level(osmo_stderr_target, atoi(optarg));
- break;
- case 'r':
- rf_ctrl = optarg;
- break;
- default:
- /* ignore */
- break;
- }
- }
-}
-
-extern int bsc_vty_go_parent(struct vty *vty);
-
-static struct vty_app_info vty_info = {
- .name = "OsmoBSC",
- .version = PACKAGE_VERSION,
- .go_parent_cb = bsc_vty_go_parent,
- .is_config_node = bsc_vty_is_config_node,
-};
-
-extern int bsc_shutdown_net(struct gsm_network *net);
-static void signal_handler(int signal)
-{
- struct bsc_msc_data *msc;
-
- fprintf(stdout, "signal %u received\n", signal);
-
- switch (signal) {
- case SIGINT:
- bsc_shutdown_net(bsc_gsmnet);
- osmo_signal_dispatch(SS_L_GLOBAL, S_L_GLOBAL_SHUTDOWN, NULL);
- sleep(3);
- exit(0);
- break;
- case SIGABRT:
- /* in case of abort, we want to obtain a talloc report
- * and then return to the caller, who will abort the process */
- case SIGUSR1:
- talloc_report(tall_vty_ctx, stderr);
- talloc_report_full(tall_bsc_ctx, stderr);
- break;
- case SIGUSR2:
- if (!bsc_gsmnet->bsc_data)
- return;
- llist_for_each_entry(msc, &bsc_gsmnet->bsc_data->mscs, entry)
- bsc_msc_lost(msc->msc_con);
- break;
- default:
- break;
- }
-}
-
-int main(int argc, char **argv)
-{
- struct bsc_msc_data *msc;
- struct osmo_bsc_data *data;
- int rc;
-
- tall_bsc_ctx = talloc_named_const(NULL, 1, "openbsc");
- msgb_talloc_ctx_init(tall_bsc_ctx, 0);
-
- /* Allocate global gsm_network struct */
- rc = bsc_network_alloc(NULL);
- if (rc) {
- fprintf(stderr, "Allocation failed. exiting.\n");
- exit(1);
- }
-
- osmo_init_logging(&log_info);
- osmo_stats_init(tall_bsc_ctx);
-
- bts_init();
- libosmo_abis_init(tall_bsc_ctx);
-
- /* enable filters */
-
- /* This needs to precede handle_options() */
- vty_info.copyright = openbsc_copyright;
- vty_init(&vty_info);
- bsc_vty_init(bsc_gsmnet);
- bsc_msg_lst_vty_init(tall_bsc_ctx, &access_lists, BSC_NODE);
- ctrl_vty_init(tall_bsc_ctx);
-
- INIT_LLIST_HEAD(&access_lists);
-
- /* parse options */
- handle_options(argc, argv);
-
- /* seed the PRNG */
- srand(time(NULL));
-
- /* initialize SCCP */
- sccp_set_log_area(DSCCP);
-
- /* Read the config */
- rc = bsc_network_configure(config_file);
- if (rc < 0) {
- fprintf(stderr, "Bootstrapping the network failed. exiting.\n");
- exit(1);
- }
- bsc_api_init(bsc_gsmnet, osmo_bsc_api());
-
- /* start control interface after reading config for
- * ctrl_vty_get_bind_addr() */
- bsc_gsmnet->ctrl = bsc_controlif_setup(bsc_gsmnet,
- ctrl_vty_get_bind_addr(),
- OSMO_CTRL_PORT_NITB_BSC);
- if (!bsc_gsmnet->ctrl) {
- fprintf(stderr, "Failed to init the control interface. Exiting.\n");
- exit(1);
- }
-
- rc = bsc_ctrl_cmds_install(bsc_gsmnet);
- if (rc < 0) {
- fprintf(stderr, "Failed to install control commands. Exiting.\n");
- exit(1);
- }
-
- data = bsc_gsmnet->bsc_data;
- if (rf_ctrl)
- osmo_talloc_replace_string(data, &data->rf_ctrl_name, rf_ctrl);
-
- data->rf_ctrl = osmo_bsc_rf_create(data->rf_ctrl_name, bsc_gsmnet);
- if (!data->rf_ctrl) {
- fprintf(stderr, "Failed to create the RF service.\n");
- exit(1);
- }
-
- llist_for_each_entry(msc, &bsc_gsmnet->bsc_data->mscs, entry) {
- if (osmo_bsc_msc_init(msc) != 0) {
- LOGP(DNAT, LOGL_ERROR, "Failed to start up. Exiting.\n");
- exit(1);
- }
- }
-
-
- if (osmo_bsc_sccp_init(bsc_gsmnet) != 0) {
- LOGP(DNM, LOGL_ERROR, "Failed to register SCCP.\n");
- exit(1);
- }
-
- if (osmo_bsc_audio_init(bsc_gsmnet) != 0) {
- LOGP(DMSC, LOGL_ERROR, "Failed to register audio support.\n");
- exit(1);
- }
-
- signal(SIGINT, &signal_handler);
- signal(SIGABRT, &signal_handler);
- signal(SIGUSR1, &signal_handler);
- signal(SIGUSR2, &signal_handler);
- osmo_init_ignore_signals();
-
- if (daemonize) {
- rc = osmo_daemonize();
- if (rc < 0) {
- perror("Error during daemonize");
- exit(1);
- }
- }
-
- while (1) {
- osmo_select_main(0);
- }
-
- return 0;
-}
diff --git a/openbsc/src/osmo-bsc/osmo_bsc_msc.c b/openbsc/src/osmo-bsc/osmo_bsc_msc.c
deleted file mode 100644
index 8d02624b4..000000000
--- a/openbsc/src/osmo-bsc/osmo_bsc_msc.c
+++ /dev/null
@@ -1,586 +0,0 @@
-/*
- * Handle the connection to the MSC. This include ping/timeout/reconnect
- * (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
- * (C) 2009-2015 by Holger Hans Peter Freyther <zecke@selfish.org>
- * (C) 2009-2015 by On-Waves
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <openbsc/bsc_nat.h>
-#include <osmocom/ctrl/control_cmd.h>
-#include <osmocom/ctrl/control_if.h>
-#include <osmocom/crypt/auth.h>
-#include <openbsc/debug.h>
-#include <openbsc/gsm_data.h>
-#include <openbsc/ipaccess.h>
-#include <openbsc/bsc_msc_data.h>
-#include <openbsc/signal.h>
-
-#include <osmocom/core/talloc.h>
-
-#include <osmocom/gsm/gsm0808.h>
-
-#include <osmocom/sccp/sccp.h>
-
-#include <osmocom/abis/ipa.h>
-
-#include <sys/socket.h>
-#include <netinet/tcp.h>
-#include <unistd.h>
-
-
-static void initialize_if_needed(struct bsc_msc_connection *conn);
-static void send_lacs(struct gsm_network *net, struct bsc_msc_connection *conn);
-static void send_id_get_response(struct bsc_msc_data *data, int fd, struct msgb *inp);
-static void send_ping(struct bsc_msc_data *data);
-static void schedule_ping_pong(struct bsc_msc_data *data);
-
-/*
- * MGCP forwarding code
- */
-static int mgcp_do_read(struct osmo_fd *fd)
-{
- struct bsc_msc_data *data = (struct bsc_msc_data *) fd->data;
- struct msgb *mgcp;
- int ret;
-
- mgcp = msgb_alloc_headroom(4096, 128, "mgcp_from_gw");
- if (!mgcp) {
- LOGP(DMGCP, LOGL_ERROR, "Failed to allocate MGCP message.\n");
- return -1;
- }
-
- ret = read(fd->fd, mgcp->data, 4096 - 128);
- if (ret <= 0) {
- LOGP(DMGCP, LOGL_ERROR, "Failed to read: %d/%s\n", errno, strerror(errno));
- msgb_free(mgcp);
- return -1;
- } else if (ret > 4096 - 128) {
- LOGP(DMGCP, LOGL_ERROR, "Too much data: %d\n", ret);
- msgb_free(mgcp);
- return -1;
- }
-
- mgcp->l2h = msgb_put(mgcp, ret);
- msc_queue_write(data->msc_con, mgcp, IPAC_PROTO_MGCP_OLD);
- return 0;
-}
-
-static int mgcp_do_write(struct osmo_fd *fd, struct msgb *msg)
-{
- int ret;
-
- LOGP(DMGCP, LOGL_DEBUG, "Sending msg to MGCP GW size: %u\n", msg->len);
-
- ret = write(fd->fd, msg->data, msg->len);
- if (ret != msg->len)
- LOGP(DMGCP, LOGL_ERROR, "Failed to forward message to MGCP GW (%s).\n", strerror(errno));
-
- return ret;
-}
-
-static void mgcp_forward(struct bsc_msc_data *data, struct msgb *msg)
-{
- struct msgb *mgcp;
-
- if (msgb_l2len(msg) > 4096) {
- LOGP(DMGCP, LOGL_ERROR, "Can not forward too big message.\n");
- return;
- }
-
- mgcp = msgb_alloc(4096, "mgcp_to_gw");
- if (!mgcp) {
- LOGP(DMGCP, LOGL_ERROR, "Failed to send message.\n");
- return;
- }
-
- msgb_put(mgcp, msgb_l2len(msg));
- memcpy(mgcp->data, msg->l2h, mgcp->len);
- if (osmo_wqueue_enqueue(&data->mgcp_agent, mgcp) != 0) {
- LOGP(DMGCP, LOGL_FATAL, "Could not queue message to MGCP GW.\n");
- msgb_free(mgcp);
- }
-}
-
-static int mgcp_create_port(struct bsc_msc_data *data)
-{
- int on;
- struct sockaddr_in addr;
-
- data->mgcp_agent.bfd.fd = socket(AF_INET, SOCK_DGRAM, 0);
- if (data->mgcp_agent.bfd.fd < 0) {
- LOGP(DMGCP, LOGL_FATAL, "Failed to create UDP socket errno: %d\n", errno);
- return -1;
- }
-
- on = 1;
- setsockopt(data->mgcp_agent.bfd.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
-
- /* try to bind the socket */
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
- addr.sin_port = 0;
-
- if (bind(data->mgcp_agent.bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- LOGP(DMGCP, LOGL_FATAL, "Failed to bind to any port.\n");
- close(data->mgcp_agent.bfd.fd);
- data->mgcp_agent.bfd.fd = -1;
- return -1;
- }
-
- /* connect to the remote */
- addr.sin_port = htons(2427);
- if (connect(data->mgcp_agent.bfd.fd, (struct sockaddr *) & addr, sizeof(addr)) < 0) {
- LOGP(DMGCP, LOGL_FATAL, "Failed to connect to local MGCP GW. %s\n", strerror(errno));
- close(data->mgcp_agent.bfd.fd);
- data->mgcp_agent.bfd.fd = -1;
- return -1;
- }
-
- osmo_wqueue_init(&data->mgcp_agent, 10);
- data->mgcp_agent.bfd.when = BSC_FD_READ;
- data->mgcp_agent.bfd.data = data;
- data->mgcp_agent.read_cb = mgcp_do_read;
- data->mgcp_agent.write_cb = mgcp_do_write;
-
- if (osmo_fd_register(&data->mgcp_agent.bfd) != 0) {
- LOGP(DMGCP, LOGL_FATAL, "Failed to register BFD\n");
- close(data->mgcp_agent.bfd.fd);
- data->mgcp_agent.bfd.fd = -1;
- return -1;
- }
-
- return 0;
-}
-
-/*
- * Send data to the network
- */
-int msc_queue_write(struct bsc_msc_connection *conn, struct msgb *msg, int proto)
-{
- ipa_prepend_header(msg, proto);
- if (osmo_wqueue_enqueue(&conn->write_queue, msg) != 0) {
- LOGP(DMSC, LOGL_FATAL, "Failed to queue IPA/%d\n", proto);
- msgb_free(msg);
- return -1;
- }
-
- return 0;
-}
-
-int msc_queue_write_with_ping(struct bsc_msc_connection *conn,
- struct msgb *msg, int proto)
-{
- struct bsc_msc_data *data;
- uint8_t val;
-
- /* prepend the header */
- ipa_prepend_header(msg, proto);
- if (osmo_wqueue_enqueue(&conn->write_queue, msg) != 0) {
- LOGP(DMSC, LOGL_FATAL, "Failed to queue IPA/%d\n", proto);
- msgb_free(msg);
- return -1;
- }
-
- /* add the ping as the other message */
- val = IPAC_MSGT_PING;
- msgb_l16tv_put(msg, 1, IPAC_PROTO_IPACCESS, &val);
-
- data = (struct bsc_msc_data *) conn->write_queue.bfd.data;
- schedule_ping_pong(data);
- return 0;
-}
-
-static int msc_alink_do_write(struct osmo_fd *fd, struct msgb *msg)
-{
- int ret;
-
- LOGP(DMSC, LOGL_DEBUG, "Sending SCCP to MSC: %u\n", msgb_l2len(msg));
- LOGP(DLMI, LOGL_DEBUG, "MSC TX %s\n", osmo_hexdump(msg->data, msg->len));
-
- ret = write(fd->fd, msg->data, msg->len);
- if (ret < msg->len)
- perror("MSC: Failed to send SCCP");
-
- return ret;
-}
-
-static void handle_ctrl(struct bsc_msc_data *msc, struct msgb *msg)
-{
- int ret;
- struct ctrl_cmd *cmd;
-
- cmd = ctrl_cmd_parse(msc->msc_con, msg);
- if (!cmd) {
- LOGP(DMSC, LOGL_ERROR, "Failed to parse control message.\n");
- cmd = talloc_zero(msc->msc_con, struct ctrl_cmd);
- if (!cmd) {
- LOGP(DMSC, LOGL_ERROR, "OOM!\n");
- return;
- }
- cmd->type = CTRL_TYPE_ERROR;
- cmd->id = "err";
- cmd->reply = "Failed to parse control message.";
-
- ctrl_cmd_send(&msc->msc_con->write_queue, cmd);
- talloc_free(cmd);
-
- return;
- }
-
- ret = ctrl_cmd_handle(msc->network->ctrl, cmd, msc->network);
- if (ret != CTRL_CMD_HANDLED)
- ctrl_cmd_send(&msc->msc_con->write_queue, cmd);
- talloc_free(cmd);
-}
-
-static void osmo_ext_handle(struct bsc_msc_data *msc, struct msgb *msg)
-{
- struct ipaccess_head *hh;
- struct ipaccess_head_ext *hh_ext;
-
- hh = (struct ipaccess_head *) msg->data;
- hh_ext = (struct ipaccess_head_ext *) hh->data;
- if (msg->len < sizeof(*hh) + sizeof(*hh_ext)) {
- LOGP(DMSC, LOGL_ERROR, "Packet too short for extended header.\n");
- return;
- }
-
- msg->l2h = hh_ext->data;
- if (hh_ext->proto == IPAC_PROTO_EXT_MGCP)
- mgcp_forward(msc, msg);
- else if (hh_ext->proto == IPAC_PROTO_EXT_LAC)
- send_lacs(msc->network, msc->msc_con);
- else if (hh_ext->proto == IPAC_PROTO_EXT_CTRL)
- handle_ctrl(msc, msg);
-}
-
-static int ipaccess_a_fd_cb(struct osmo_fd *bfd)
-{
- struct msgb *msg = NULL;
- struct ipaccess_head *hh;
- struct bsc_msc_data *data = (struct bsc_msc_data *) bfd->data;
- int ret;
-
- ret = ipa_msg_recv_buffered(bfd->fd, &msg, &data->msc_con->pending_msg);
- if (ret <= 0) {
- if (ret == -EAGAIN)
- return 0;
- if (ret == 0) {
- LOGP(DMSC, LOGL_ERROR, "The connection to the MSC was lost.\n");
- bsc_msc_lost(data->msc_con);
- return -1;
- }
-
- LOGP(DMSC, LOGL_ERROR, "Failed to parse ip access message: %d\n", ret);
- return -1;
- }
-
- LOGP(DLMI, LOGL_DEBUG, "From MSC: %s proto: %d\n", osmo_hexdump(msg->data, msg->len), msg->l2h[0]);
-
- /* handle base message handling */
- hh = (struct ipaccess_head *) msg->data;
-
- /* initialize the networking. This includes sending a GSM08.08 message */
- msg->cb[0] = (unsigned long) data;
- if (hh->proto == IPAC_PROTO_IPACCESS) {
- ipa_ccm_rcvmsg_base(msg, bfd);
- if (msg->l2h[0] == IPAC_MSGT_ID_ACK)
- initialize_if_needed(data->msc_con);
- else if (msg->l2h[0] == IPAC_MSGT_ID_GET) {
- send_id_get_response(data, bfd->fd, msg);
- } else if (msg->l2h[0] == IPAC_MSGT_PONG) {
- osmo_timer_del(&data->pong_timer);
- }
- } else if (hh->proto == IPAC_PROTO_SCCP) {
- sccp_system_incoming_ctx(msg, data->msc_con);
- } else if (hh->proto == IPAC_PROTO_MGCP_OLD) {
- mgcp_forward(data, msg);
- } else if (hh->proto == IPAC_PROTO_OSMO) {
- osmo_ext_handle(data, msg);
- }
-
- msgb_free(msg);
- return 0;
-}
-
-static void send_ping(struct bsc_msc_data *data)
-{
- struct msgb *msg;
-
- msg = msgb_alloc_headroom(4096, 128, "ping");
- if (!msg) {
- LOGP(DMSC, LOGL_ERROR, "Failed to create PING.\n");
- return;
- }
-
- msg->l2h = msgb_put(msg, 1);
- msg->l2h[0] = IPAC_MSGT_PING;
-
- msc_queue_write(data->msc_con, msg, IPAC_PROTO_IPACCESS);
-}
-
-static void schedule_ping_pong(struct bsc_msc_data *data)
-{
- /* send another ping in 20 seconds */
- osmo_timer_schedule(&data->ping_timer, data->ping_timeout, 0);
-
- /* also start a pong timer */
- osmo_timer_schedule(&data->pong_timer, data->pong_timeout, 0);
-}
-
-static void msc_ping_timeout_cb(void *_data)
-{
- struct bsc_msc_data *data = (struct bsc_msc_data *) _data;
- if (data->ping_timeout <= 0)
- return;
-
- send_ping(data);
- schedule_ping_pong(data);
-}
-
-static void msc_pong_timeout_cb(void *_data)
-{
- struct bsc_msc_data *data = (struct bsc_msc_data *) _data;
-
- LOGP(DMSC, LOGL_ERROR, "MSC didn't answer PING. Closing connection.\n");
- bsc_msc_lost(data->msc_con);
-}
-
-static void msc_connection_connected(struct bsc_msc_connection *con)
-{
- struct msc_signal_data sig;
- struct bsc_msc_data *data;
- int ret, on;
- on = 1;
- ret = setsockopt(con->write_queue.bfd.fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on));
- if (ret != 0)
- LOGP(DMSC, LOGL_ERROR, "Failed to set TCP_NODELAY: %s\n", strerror(errno));
-
- data = (struct bsc_msc_data *) con->write_queue.bfd.data;
- msc_ping_timeout_cb(data);
-
- sig.data = data;
- osmo_signal_dispatch(SS_MSC, S_MSC_CONNECTED, &sig);
-}
-
-/*
- * The connection to the MSC was lost and we will need to free all
- * resources and then attempt to reconnect.
- */
-static void msc_connection_was_lost(struct bsc_msc_connection *msc)
-{
- struct msc_signal_data sig;
- struct bsc_msc_data *data;
-
- LOGP(DMSC, LOGL_ERROR, "Lost MSC connection. Freing stuff.\n");
-
- data = (struct bsc_msc_data *) msc->write_queue.bfd.data;
- osmo_timer_del(&data->ping_timer);
- osmo_timer_del(&data->pong_timer);
-
- sig.data = data;
- osmo_signal_dispatch(SS_MSC, S_MSC_LOST, &sig);
-
- msc->is_authenticated = 0;
- bsc_msc_schedule_connect(msc);
-}
-
-static void send_lacs(struct gsm_network *net, struct bsc_msc_connection *conn)
-{
- struct ipac_ext_lac_cmd *lac;
- struct gsm_bts *bts;
- struct msgb *msg;
- int lacs = 0;
-
- if (llist_empty(&net->bts_list)) {
- LOGP(DMSC, LOGL_ERROR, "No BTSs configured. Not sending LACs.\n");
- return;
- }
-
- msg = msgb_alloc_headroom(4096, 128, "LAC Command");
- if (!msg) {
- LOGP(DMSC, LOGL_ERROR, "Failed to create the LAC command.\n");
- return;
- }
-
- lac = (struct ipac_ext_lac_cmd *) msgb_put(msg, sizeof(*lac));
- lac->add_remove = 1;
-
- llist_for_each_entry(bts, &net->bts_list, list) {
- if (lacs++ == 0)
- lac->lac = htons(bts->location_area_code);
- else
- msgb_put_u16(msg, htons(bts->location_area_code));
- }
-
- lac->nr_extra_lacs = lacs - 1;
- ipa_prepend_header_ext(msg, IPAC_PROTO_EXT_LAC);
- msc_queue_write(conn, msg, IPAC_PROTO_OSMO);
-}
-
-static void initialize_if_needed(struct bsc_msc_connection *conn)
-{
- struct msgb *msg;
-
- if (!conn->is_authenticated) {
- /* send a gsm 08.08 reset message from here */
- msg = gsm0808_create_reset();
- if (!msg) {
- LOGP(DMSC, LOGL_ERROR, "Failed to create the reset message.\n");
- return;
- }
-
- sccp_write(msg, &sccp_ssn_bssap, &sccp_ssn_bssap, 0, conn);
- msgb_free(msg);
- conn->is_authenticated = 1;
- }
-}
-
-static int answer_challenge(struct bsc_msc_data *data, struct msgb *inp, struct osmo_auth_vector *vec)
-{
- int ret;
- struct tlv_parsed tvp;
- const uint8_t *mrand;
- uint8_t mrand_len;
- struct osmo_sub_auth_data auth = {
- .type = OSMO_AUTH_TYPE_GSM,
- .algo = OSMO_AUTH_ALG_MILENAGE,
- };
-
- ret = ipa_ccm_idtag_parse_off(&tvp,
- inp->l2h + 1,
- msgb_l2len(inp) - 1, 1);
- if (ret < 0) {
- LOGP(DMSC, LOGL_ERROR, "ignoring IPA response "
- "message with malformed TLVs: %s\n", osmo_hexdump(inp->l2h + 1,
- msgb_l2len(inp) - 1));
- return 0;
- }
-
- mrand = TLVP_VAL(&tvp, 0x23);
- mrand_len = TLVP_LEN(&tvp, 0x23);
- if (mrand_len != 16) {
- LOGP(DMSC, LOGL_ERROR,
- "RAND is not 16 bytes. Was %d\n",
- mrand_len);
- return 0;
- }
-
- /* copy the key */
- memcpy(auth.u.umts.opc, data->bsc_key, 16);
- memcpy(auth.u.umts.k, data->bsc_key, 16);
- memset(auth.u.umts.amf, 0, 2);
- auth.u.umts.sqn = 0;
-
- /* generate the result */
- memset(vec, 0, sizeof(*vec));
- osmo_auth_gen_vec(vec, &auth, mrand);
- return 1;
-}
-
-
-static void send_id_get_response(struct bsc_msc_data *data, int fd, struct msgb *inp)
-{
- struct msc_signal_data sig;
- struct msgb *msg;
- struct osmo_auth_vector vec;
- int valid = 0;
-
- if (data->bsc_key_present)
- valid = answer_challenge(data, inp, &vec);
-
- msg = bsc_msc_id_get_resp(valid, data->bsc_token,
- vec.res, valid ? vec.res_len : 0);
- if (!msg)
- return;
- msc_queue_write(data->msc_con, msg, IPAC_PROTO_IPACCESS);
-
- sig.data = data;
- osmo_signal_dispatch(SS_MSC, S_MSC_AUTHENTICATED, &sig);
-}
-
-int osmo_bsc_msc_init(struct bsc_msc_data *data)
-{
- if (mgcp_create_port(data) != 0)
- return -1;
-
- data->msc_con = bsc_msc_create(data, &data->dests);
- if (!data->msc_con) {
- LOGP(DMSC, LOGL_ERROR, "Creating the MSC network connection failed.\n");
- return -1;
- }
-
- osmo_timer_setup(&data->ping_timer, msc_ping_timeout_cb, data);
- osmo_timer_setup(&data->pong_timer, msc_pong_timeout_cb, data);
-
- data->msc_con->write_queue.bfd.data = data;
- data->msc_con->connection_loss = msc_connection_was_lost;
- data->msc_con->connected = msc_connection_connected;
- data->msc_con->write_queue.read_cb = ipaccess_a_fd_cb;
- data->msc_con->write_queue.write_cb = msc_alink_do_write;
- bsc_msc_connect(data->msc_con);
-
- return 0;
-}
-
-struct bsc_msc_data *osmo_msc_data_find(struct gsm_network *net, int nr)
-{
- struct bsc_msc_data *msc_data;
-
- llist_for_each_entry(msc_data, &net->bsc_data->mscs, entry)
- if (msc_data->nr == nr)
- return msc_data;
- return NULL;
-}
-
-struct bsc_msc_data *osmo_msc_data_alloc(struct gsm_network *net, int nr)
-{
- struct bsc_msc_data *msc_data;
-
- /* check if there is already one */
- msc_data = osmo_msc_data_find(net, nr);
- if (msc_data)
- return msc_data;
-
- msc_data = talloc_zero(net, struct bsc_msc_data);
- if (!msc_data)
- return NULL;
-
- llist_add_tail(&msc_data->entry, &net->bsc_data->mscs);
-
- /* Init back pointer */
- msc_data->network = net;
-
- INIT_LLIST_HEAD(&msc_data->dests);
- msc_data->ping_timeout = 20;
- msc_data->pong_timeout = 5;
- msc_data->core_mnc = -1;
- msc_data->core_mcc = -1;
- msc_data->core_ci = -1;
- msc_data->core_lac = -1;
- msc_data->rtp_base = 4000;
-
- msc_data->nr = nr;
- msc_data->allow_emerg = 1;
-
- /* Defaults for the audio setup */
- msc_data->amr_conf.m5_90 = 1;
-
- return msc_data;
-}
diff --git a/openbsc/src/osmo-bsc/osmo_bsc_sccp.c b/openbsc/src/osmo-bsc/osmo_bsc_sccp.c
deleted file mode 100644
index e242390ef..000000000
--- a/openbsc/src/osmo-bsc/osmo_bsc_sccp.c
+++ /dev/null
@@ -1,328 +0,0 @@
-/* Interaction with the SCCP subsystem */
-/*
- * (C) 2009-2014 by Holger Hans Peter Freyther <zecke@selfish.org>
- * (C) 2009-2014 by On-Waves
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <openbsc/gsm_data.h>
-#include <openbsc/osmo_bsc.h>
-#include <openbsc/osmo_bsc_grace.h>
-#include <openbsc/bsc_msc_data.h>
-#include <openbsc/gsm_04_80.h>
-#include <openbsc/debug.h>
-#include <openbsc/ipaccess.h>
-#include <openbsc/signal.h>
-
-#include <osmocom/core/talloc.h>
-#include <osmocom/gsm/gsm0808.h>
-#include <osmocom/gsm/protocol/gsm_08_08.h>
-
-#include <osmocom/sccp/sccp.h>
-
-/* SCCP helper */
-#define SCCP_IT_TIMER 60
-
-static LLIST_HEAD(active_connections);
-
-static void free_queued(struct osmo_bsc_sccp_con *conn)
-{
- struct msgb *msg;
-
- while (!llist_empty(&conn->sccp_queue)) {
- /* this is not allowed to fail */
- msg = msgb_dequeue(&conn->sccp_queue);
- msgb_free(msg);
- }
-
- conn->sccp_queue_size = 0;
-}
-
-static void send_queued(struct osmo_bsc_sccp_con *conn)
-{
- struct msgb *msg;
-
- while (!llist_empty(&conn->sccp_queue)) {
- /* this is not allowed to fail */
- msg = msgb_dequeue(&conn->sccp_queue);
- sccp_connection_write(conn->sccp, msg);
- msgb_free(msg);
- conn->sccp_queue_size -= 1;
- }
-}
-
-static void msc_outgoing_sccp_data(struct sccp_connection *conn,
- struct msgb *msg, unsigned int len)
-{
- struct osmo_bsc_sccp_con *bsc_con =
- (struct osmo_bsc_sccp_con *) conn->data_ctx;
-
- bsc_handle_dt1(bsc_con, msg, len);
-}
-
-static void msc_outgoing_sccp_state(struct sccp_connection *conn, int old_state)
-{
- struct osmo_bsc_sccp_con *con_data;
-
- if (conn->connection_state >= SCCP_CONNECTION_STATE_RELEASE_COMPLETE) {
- con_data = (struct osmo_bsc_sccp_con *) conn->data_ctx;
- if (con_data->conn) {
- LOGP(DMSC, LOGL_ERROR,
- "ERROR: The lchan is still associated.\n");
- gsm0808_clear(con_data->conn);
- bsc_subscr_con_free(con_data->conn);
- con_data->conn = NULL;
- }
-
- con_data->sccp = NULL;
- free_queued(con_data);
- sccp_connection_free(conn);
- bsc_delete_connection(con_data);
- } else if (conn->connection_state == SCCP_CONNECTION_STATE_ESTABLISHED) {
- LOGP(DMSC, LOGL_DEBUG, "Connection established: %p\n", conn);
- con_data = (struct osmo_bsc_sccp_con *) conn->data_ctx;
-
- osmo_timer_del(&con_data->sccp_cc_timeout);
- osmo_timer_schedule(&con_data->sccp_it_timeout, SCCP_IT_TIMER, 0);
-
- send_queued(con_data);
- }
-}
-
-static void bsc_sccp_force_free(struct osmo_bsc_sccp_con *data)
-{
- if (data->conn) {
- gsm0808_clear(data->conn);
- bsc_subscr_con_free(data->conn);
- data->conn = NULL;
- }
-
- free_queued(data);
- sccp_connection_force_free(data->sccp);
- data->sccp = NULL;
- bsc_delete_connection(data);
-}
-
-static void sccp_it_timeout(void *_data)
-{
- struct osmo_bsc_sccp_con *data =
- (struct osmo_bsc_sccp_con *) _data;
-
- sccp_connection_send_it(data->sccp);
- osmo_timer_schedule(&data->sccp_it_timeout, SCCP_IT_TIMER, 0);
-}
-
-static void sccp_cc_timeout(void *_data)
-{
- struct osmo_bsc_sccp_con *data =
- (struct osmo_bsc_sccp_con *) _data;
-
- if (data->sccp->connection_state >= SCCP_CONNECTION_STATE_ESTABLISHED)
- return;
-
- LOGP(DMSC, LOGL_ERROR, "The connection was never established.\n");
- bsc_sccp_force_free(data);
-}
-
-static void msc_sccp_write_ipa(struct sccp_connection *conn, struct msgb *msg,
- void *global_ctx, void *ctx)
-{
- struct bsc_msc_connection *msc_con;
-
- if (conn) {
- struct osmo_bsc_sccp_con *bsc_con = conn->data_ctx;
- msc_con = bsc_con->msc->msc_con;
- if (bsc_con->send_ping) {
- bsc_con->send_ping = 0;
- msc_queue_write_with_ping(msc_con, msg, IPAC_PROTO_SCCP);
- return;
- }
- } else {
- msc_con = ctx;
- }
-
- msc_queue_write(msc_con, msg, IPAC_PROTO_SCCP);
-}
-
-static int msc_sccp_accept(struct sccp_connection *connection, void *data)
-{
- LOGP(DMSC, LOGL_DEBUG, "Rejecting incoming SCCP connection.\n");
- return -1;
-}
-
-static int msc_sccp_read(struct msgb *msgb, unsigned int length, void *data)
-{
- struct bsc_msc_data *msc = (struct bsc_msc_data *) msgb->cb[0];
- return bsc_handle_udt(msc, msgb, length);
-}
-
-int bsc_queue_for_msc(struct osmo_bsc_sccp_con *conn, struct msgb *msg)
-{
- struct sccp_connection *sccp = conn->sccp;
-
- if (sccp->connection_state > SCCP_CONNECTION_STATE_ESTABLISHED) {
- LOGP(DMSC, LOGL_ERROR, "Connection closing, dropping packet on: %p\n", sccp);
- msgb_free(msg);
- } else if (sccp->connection_state == SCCP_CONNECTION_STATE_ESTABLISHED
- && conn->sccp_queue_size == 0) {
- sccp_connection_write(sccp, msg);
- msgb_free(msg);
- } else if (conn->sccp_queue_size > 10) {
- LOGP(DMSC, LOGL_ERROR, "Connection closing, dropping packet on: %p\n", sccp);
- msgb_free(msg);
- } else {
- LOGP(DMSC, LOGL_DEBUG, "Queueing packet on %p. Queue size: %d\n", sccp, conn->sccp_queue_size);
- conn->sccp_queue_size += 1;
- msgb_enqueue(&conn->sccp_queue, msg);
- }
-
- return 0;
-}
-
-enum bsc_con bsc_create_new_connection(struct gsm_subscriber_connection *conn,
- struct bsc_msc_data *msc, int send_ping)
-{
- struct osmo_bsc_sccp_con *bsc_con;
- struct sccp_connection *sccp;
-
- /* This should not trigger */
- if (!msc || !msc->msc_con->is_authenticated) {
- LOGP(DMSC, LOGL_ERROR,
- "How did this happen? MSC is not connected. Dropping.\n");
- return BSC_CON_REJECT_NO_LINK;
- }
-
- if (!bsc_grace_allow_new_connection(conn->bts->network, conn->bts)) {
- LOGP(DMSC, LOGL_NOTICE, "BSC in grace period. No new connections.\n");
- return BSC_CON_REJECT_RF_GRACE;
- }
-
- sccp = sccp_connection_socket();
- if (!sccp) {
- LOGP(DMSC, LOGL_ERROR, "Failed to allocate memory.\n");
- return BSC_CON_NO_MEM;
- }
-
- bsc_con = talloc_zero(conn->bts, struct osmo_bsc_sccp_con);
- if (!bsc_con) {
- LOGP(DMSC, LOGL_ERROR, "Failed to allocate.\n");
- sccp_connection_free(sccp);
- return BSC_CON_NO_MEM;
- }
-
- /* callbacks */
- sccp->state_cb = msc_outgoing_sccp_state;
- sccp->data_cb = msc_outgoing_sccp_data;
- sccp->data_ctx = bsc_con;
-
- bsc_con->send_ping = send_ping;
-
- /* prepare the timers */
- osmo_timer_setup(&bsc_con->sccp_it_timeout, sccp_it_timeout, bsc_con);
- osmo_timer_setup(&bsc_con->sccp_cc_timeout, sccp_cc_timeout, bsc_con);
-
- INIT_LLIST_HEAD(&bsc_con->sccp_queue);
-
- bsc_con->sccp = sccp;
- bsc_con->msc = msc;
- bsc_con->conn = conn;
- llist_add_tail(&bsc_con->entry, &active_connections);
- conn->sccp_con = bsc_con;
- return BSC_CON_SUCCESS;
-}
-
-int bsc_open_connection(struct osmo_bsc_sccp_con *conn, struct msgb *msg)
-{
- osmo_timer_schedule(&conn->sccp_cc_timeout, 10, 0);
- sccp_connection_connect(conn->sccp, &sccp_ssn_bssap, msg);
- msgb_free(msg);
- return 0;
-}
-
-int bsc_delete_connection(struct osmo_bsc_sccp_con *sccp)
-{
- if (!sccp)
- return 0;
-
- if (sccp->conn)
- LOGP(DMSC, LOGL_ERROR, "Should have been cleared.\n");
-
- llist_del(&sccp->entry);
- osmo_timer_del(&sccp->sccp_it_timeout);
- osmo_timer_del(&sccp->sccp_cc_timeout);
- talloc_free(sccp);
- return 0;
-}
-
-static void bsc_notify_msc_lost(struct osmo_bsc_sccp_con *con)
-{
- struct gsm_subscriber_connection *conn = con->conn;
-
- /* send USSD notification if string configured and con->data is set */
- if (!conn)
- return;
-
- /* check for config string */
- if (!con->msc->ussd_msc_lost_txt)
- return;
- if (con->msc->ussd_msc_lost_txt[0] == '\0')
- return;
-
- /* send USSD notification */
- bsc_send_ussd_notify(conn, 1, conn->sccp_con->msc->ussd_msc_lost_txt);
- bsc_send_ussd_release_complete(conn);
-}
-
-static void bsc_notify_and_close_conns(struct bsc_msc_connection *msc_con)
-{
- struct osmo_bsc_sccp_con *con, *tmp;
-
- llist_for_each_entry_safe(con, tmp, &active_connections, entry) {
- if (con->msc->msc_con != msc_con)
- continue;
-
- bsc_notify_msc_lost(con);
- bsc_sccp_force_free(con);
- }
-}
-
-static int handle_msc_signal(unsigned int subsys, unsigned int signal,
- void *handler_data, void *signal_data)
-{
- struct msc_signal_data *msc;
-
- if (subsys != SS_MSC)
- return 0;
-
- msc = signal_data;
- if (signal == S_MSC_LOST)
- bsc_notify_and_close_conns(msc->data->msc_con);
-
- return 0;
-}
-
-int osmo_bsc_sccp_init(struct gsm_network *gsmnet)
-{
- sccp_set_log_area(DSCCP);
- sccp_system_init(msc_sccp_write_ipa, gsmnet);
- sccp_connection_set_incoming(&sccp_ssn_bssap, msc_sccp_accept, NULL);
- sccp_set_read(&sccp_ssn_bssap, msc_sccp_read, gsmnet);
-
- osmo_signal_register_handler(SS_MSC, handle_msc_signal, gsmnet);
-
- return 0;
-}
diff --git a/openbsc/src/osmo-bsc/osmo_bsc_vty.c b/openbsc/src/osmo-bsc/osmo_bsc_vty.c
deleted file mode 100644
index 2e2e99bfd..000000000
--- a/openbsc/src/osmo-bsc/osmo_bsc_vty.c
+++ /dev/null
@@ -1,945 +0,0 @@
-/* Osmo BSC VTY Configuration */
-/* (C) 2009-2015 by Holger Hans Peter Freyther
- * (C) 2009-2014 by On-Waves
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <openbsc/gsm_data.h>
-#include <openbsc/osmo_bsc.h>
-#include <openbsc/bsc_msc_data.h>
-#include <openbsc/vty.h>
-#include <openbsc/bsc_subscriber.h>
-#include <openbsc/debug.h>
-#include <openbsc/bsc_msg_filter.h>
-
-#include <osmocom/core/talloc.h>
-#include <osmocom/vty/logging.h>
-
-#include <time.h>
-
-
-#define IPA_STR "IP.ACCESS specific\n"
-
-extern struct gsm_network *bsc_gsmnet;
-
-static struct osmo_bsc_data *osmo_bsc_data(struct vty *vty)
-{
- return bsc_gsmnet->bsc_data;
-}
-
-static struct bsc_msc_data *bsc_msc_data(struct vty *vty)
-{
- return vty->index;
-}
-
-static struct cmd_node bsc_node = {
- BSC_NODE,
- "%s(config-bsc)# ",
- 1,
-};
-
-static struct cmd_node msc_node = {
- MSC_NODE,
- "%s(config-msc)# ",
- 1,
-};
-
-DEFUN(cfg_net_msc, cfg_net_msc_cmd,
- "msc [<0-1000>]", "Configure MSC details\n" "MSC connection to configure\n")
-{
- int index = argc == 1 ? atoi(argv[0]) : 0;
- struct bsc_msc_data *msc;
-
- msc = osmo_msc_data_alloc(bsc_gsmnet, index);
- if (!msc) {
- vty_out(vty, "%%Failed to allocate MSC data.%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- vty->index = msc;
- vty->node = MSC_NODE;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_bsc, cfg_net_bsc_cmd,
- "bsc", "Configure BSC\n")
-{
- vty->node = BSC_NODE;
- return CMD_SUCCESS;
-}
-
-static void write_msc_amr_options(struct vty *vty, struct bsc_msc_data *msc)
-{
-#define WRITE_AMR(vty, msc, name, var) \
- vty_out(vty, " amr-config %s %s%s", \
- name, msc->amr_conf.var ? "allowed" : "forbidden", \
- VTY_NEWLINE);
-
- WRITE_AMR(vty, msc, "12_2k", m12_2);
- WRITE_AMR(vty, msc, "10_2k", m10_2);
- WRITE_AMR(vty, msc, "7_95k", m7_95);
- WRITE_AMR(vty, msc, "7_40k", m7_40);
- WRITE_AMR(vty, msc, "6_70k", m6_70);
- WRITE_AMR(vty, msc, "5_90k", m5_90);
- WRITE_AMR(vty, msc, "5_15k", m5_15);
- WRITE_AMR(vty, msc, "4_75k", m4_75);
-#undef WRITE_AMR
-}
-
-static void write_msc(struct vty *vty, struct bsc_msc_data *msc)
-{
- struct bsc_msc_dest *dest;
-
- vty_out(vty, "msc %d%s", msc->nr, VTY_NEWLINE);
- if (msc->bsc_token)
- vty_out(vty, " token %s%s", msc->bsc_token, VTY_NEWLINE);
- if (msc->bsc_key_present)
- vty_out(vty, " auth-key %s%s",
- osmo_hexdump(msc->bsc_key, sizeof(msc->bsc_key)), VTY_NEWLINE);
- if (msc->core_mnc != -1)
- vty_out(vty, " core-mobile-network-code %d%s",
- msc->core_mnc, VTY_NEWLINE);
- if (msc->core_mcc != -1)
- vty_out(vty, " core-mobile-country-code %d%s",
- msc->core_mcc, VTY_NEWLINE);
- if (msc->core_lac != -1)
- vty_out(vty, " core-location-area-code %d%s",
- msc->core_lac, VTY_NEWLINE);
- if (msc->core_ci != -1)
- vty_out(vty, " core-cell-identity %d%s",
- msc->core_ci, VTY_NEWLINE);
- vty_out(vty, " ip.access rtp-base %d%s", msc->rtp_base, VTY_NEWLINE);
-
- if (msc->ping_timeout == -1)
- vty_out(vty, " no timeout-ping%s", VTY_NEWLINE);
- else {
- vty_out(vty, " timeout-ping %d%s", msc->ping_timeout, VTY_NEWLINE);
- vty_out(vty, " timeout-pong %d%s", msc->pong_timeout, VTY_NEWLINE);
- if (msc->advanced_ping)
- vty_out(vty, " timeout-ping advanced%s", VTY_NEWLINE);
- else
- vty_out(vty, " no timeout-ping advanced%s", VTY_NEWLINE);
- }
-
- if (msc->ussd_welcome_txt)
- vty_out(vty, " bsc-welcome-text %s%s", msc->ussd_welcome_txt, VTY_NEWLINE);
- else
- vty_out(vty, " no bsc-welcome-text%s", VTY_NEWLINE);
-
- if (msc->ussd_msc_lost_txt && msc->ussd_msc_lost_txt[0])
- vty_out(vty, " bsc-msc-lost-text %s%s", msc->ussd_msc_lost_txt, VTY_NEWLINE);
- else
- vty_out(vty, " no bsc-msc-lost-text%s", VTY_NEWLINE);
-
- if (msc->ussd_grace_txt && msc->ussd_grace_txt[0])
- vty_out(vty, " bsc-grace-text %s%s", msc->ussd_grace_txt, VTY_NEWLINE);
- else
- vty_out(vty, " no bsc-grace-text%s", VTY_NEWLINE);
-
- if (msc->audio_length != 0) {
- int i;
-
- vty_out(vty, " codec-list ");
- for (i = 0; i < msc->audio_length; ++i) {
- if (i != 0)
- vty_out(vty, " ");
-
- if (msc->audio_support[i]->hr)
- vty_out(vty, "hr%.1u", msc->audio_support[i]->ver);
- else
- vty_out(vty, "fr%.1u", msc->audio_support[i]->ver);
- }
- vty_out(vty, "%s", VTY_NEWLINE);
-
- }
-
- llist_for_each_entry(dest, &msc->dests, list)
- vty_out(vty, " dest %s %d %d%s", dest->ip, dest->port,
- dest->dscp, VTY_NEWLINE);
-
- vty_out(vty, " type %s%s", msc->type == MSC_CON_TYPE_NORMAL ?
- "normal" : "local", VTY_NEWLINE);
- vty_out(vty, " allow-emergency %s%s", msc->allow_emerg ?
- "allow" : "deny", VTY_NEWLINE);
-
- if (msc->local_pref)
- vty_out(vty, " local-prefix %s%s", msc->local_pref, VTY_NEWLINE);
-
- if (msc->acc_lst_name)
- vty_out(vty, " access-list-name %s%s", msc->acc_lst_name, VTY_NEWLINE);
-
- /* write amr options */
- write_msc_amr_options(vty, msc);
-}
-
-static int config_write_msc(struct vty *vty)
-{
- struct bsc_msc_data *msc;
- struct osmo_bsc_data *bsc = osmo_bsc_data(vty);
-
- llist_for_each_entry(msc, &bsc->mscs, entry)
- write_msc(vty, msc);
-
- return CMD_SUCCESS;
-}
-
-static int config_write_bsc(struct vty *vty)
-{
- struct osmo_bsc_data *bsc = osmo_bsc_data(vty);
-
- vty_out(vty, "bsc%s", VTY_NEWLINE);
- if (bsc->mid_call_txt)
- vty_out(vty, " mid-call-text %s%s", bsc->mid_call_txt, VTY_NEWLINE);
- vty_out(vty, " mid-call-timeout %d%s", bsc->mid_call_timeout, VTY_NEWLINE);
- if (bsc->rf_ctrl_name)
- vty_out(vty, " bsc-rf-socket %s%s",
- bsc->rf_ctrl_name, VTY_NEWLINE);
-
- if (bsc->auto_off_timeout != -1)
- vty_out(vty, " bsc-auto-rf-off %d%s",
- bsc->auto_off_timeout, VTY_NEWLINE);
-
- if (bsc->ussd_no_msc_txt && bsc->ussd_no_msc_txt[0])
- vty_out(vty, " missing-msc-text %s%s", bsc->ussd_no_msc_txt, VTY_NEWLINE);
- else
- vty_out(vty, " no missing-msc-text%s", VTY_NEWLINE);
- if (bsc->acc_lst_name)
- vty_out(vty, " access-list-name %s%s", bsc->acc_lst_name, VTY_NEWLINE);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_bsc_token,
- cfg_net_bsc_token_cmd,
- "token TOKEN",
- "A token for the BSC to be sent to the MSC\n" "A token\n")
-{
- struct bsc_msc_data *data = bsc_msc_data(vty);
-
- osmo_talloc_replace_string(osmo_bsc_data(vty), &data->bsc_token, argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_bsc_key,
- cfg_net_bsc_key_cmd,
- "auth-key KEY",
- "Authentication (secret) key configuration\n"
- "Security key\n")
-{
- struct bsc_msc_data *data = bsc_msc_data(vty);
-
- osmo_hexparse(argv[0], data->bsc_key, sizeof(data->bsc_key));
- data->bsc_key_present = 1;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_no_bsc_key, cfg_net_bsc_no_key_cmd,
- "no auth-key",
- NO_STR "Authentication (secret) key configuration\n")
-{
- struct bsc_msc_data *data = bsc_msc_data(vty);
-
- memset(data->bsc_key, 0, sizeof(data->bsc_key));
- data->bsc_key_present = 0;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_bsc_ncc,
- cfg_net_bsc_ncc_cmd,
- "core-mobile-network-code <1-999>",
- "Use this network code for the core network\n" "MNC value\n")
-{
- struct bsc_msc_data *data = bsc_msc_data(vty);
- data->core_mnc = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_bsc_mcc,
- cfg_net_bsc_mcc_cmd,
- "core-mobile-country-code <1-999>",
- "Use this country code for the core network\n" "MCC value\n")
-{
- struct bsc_msc_data *data = bsc_msc_data(vty);
- data->core_mcc = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_bsc_lac,
- cfg_net_bsc_lac_cmd,
- "core-location-area-code <0-65535>",
- "Use this location area code for the core network\n" "LAC value\n")
-{
- struct bsc_msc_data *data = bsc_msc_data(vty);
- data->core_lac = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_bsc_ci,
- cfg_net_bsc_ci_cmd,
- "core-cell-identity <0-65535>",
- "Use this cell identity for the core network\n" "CI value\n")
-{
- struct bsc_msc_data *data = bsc_msc_data(vty);
- data->core_ci = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_bsc_rtp_base,
- cfg_net_bsc_rtp_base_cmd,
- "ip.access rtp-base <1-65000>",
- IPA_STR
- "Set the rtp-base port for the RTP stream\n"
- "Port number\n")
-{
- struct bsc_msc_data *data = bsc_msc_data(vty);
- data->rtp_base = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_bsc_codec_list,
- cfg_net_bsc_codec_list_cmd,
- "codec-list .LIST",
- "Set the allowed audio codecs\n"
- "List of audio codecs, e.g. fr3 fr1 hr3\n")
-{
- struct bsc_msc_data *data = bsc_msc_data(vty);
- int saw_fr, saw_hr;
- int i;
-
- saw_fr = saw_hr = 0;
-
- /* free the old list... if it exists */
- if (data->audio_support) {
- talloc_free(data->audio_support);
- data->audio_support = NULL;
- data->audio_length = 0;
- }
-
- /* create a new array */
- data->audio_support =
- talloc_zero_array(osmo_bsc_data(vty), struct gsm_audio_support *, argc);
- data->audio_length = argc;
-
- for (i = 0; i < argc; ++i) {
- /* check for hrX or frX */
- if (strlen(argv[i]) != 3
- || argv[i][1] != 'r'
- || (argv[i][0] != 'h' && argv[i][0] != 'f')
- || argv[i][2] < 0x30
- || argv[i][2] > 0x39)
- goto error;
-
- data->audio_support[i] = talloc_zero(data->audio_support,
- struct gsm_audio_support);
- data->audio_support[i]->ver = atoi(argv[i] + 2);
-
- if (strncmp("hr", argv[i], 2) == 0) {
- data->audio_support[i]->hr = 1;
- saw_hr = 1;
- } else if (strncmp("fr", argv[i], 2) == 0) {
- data->audio_support[i]->hr = 0;
- saw_fr = 1;
- }
-
- if (saw_hr && saw_fr) {
- vty_out(vty, "Can not have full-rate and half-rate codec.%s",
- VTY_NEWLINE);
- return CMD_ERR_INCOMPLETE;
- }
- }
-
- return CMD_SUCCESS;
-
-error:
- vty_out(vty, "Codec name must be hrX or frX. Was '%s'%s",
- argv[i], VTY_NEWLINE);
- return CMD_ERR_INCOMPLETE;
-}
-
-DEFUN(cfg_net_msc_dest,
- cfg_net_msc_dest_cmd,
- "dest A.B.C.D <1-65000> <0-255>",
- "Add a destination to a MUX/MSC\n"
- "IP Address\n" "Port\n" "DSCP\n")
-{
- struct bsc_msc_dest *dest;
- struct bsc_msc_data *data = bsc_msc_data(vty);
-
- dest = talloc_zero(osmo_bsc_data(vty), struct bsc_msc_dest);
- if (!dest) {
- vty_out(vty, "%%Failed to create structure.%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- dest->ip = talloc_strdup(dest, argv[0]);
- if (!dest->ip) {
- vty_out(vty, "%%Failed to copy dest ip.%s", VTY_NEWLINE);
- talloc_free(dest);
- return CMD_WARNING;
- }
-
- dest->port = atoi(argv[1]);
- dest->dscp = atoi(argv[2]);
- llist_add_tail(&dest->list, &data->dests);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_msc_no_dest,
- cfg_net_msc_no_dest_cmd,
- "no dest A.B.C.D <1-65000> <0-255>",
- NO_STR "Remove a destination to a MUX/MSC\n"
- "IP Address\n" "Port\n" "DSCP\n")
-{
- struct bsc_msc_dest *dest, *tmp;
- struct bsc_msc_data *data = bsc_msc_data(vty);
-
- int port = atoi(argv[1]);
- int dscp = atoi(argv[2]);
-
- llist_for_each_entry_safe(dest, tmp, &data->dests, list) {
- if (port != dest->port || dscp != dest->dscp
- || strcmp(dest->ip, argv[0]) != 0)
- continue;
-
- llist_del(&dest->list);
- talloc_free(dest);
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_msc_no_ping_time,
- cfg_net_msc_no_ping_time_cmd,
- "no timeout-ping",
- NO_STR "Disable the ping/pong handling on A-link\n")
-{
- struct bsc_msc_data *data = bsc_msc_data(vty);
- data->ping_timeout = -1;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_msc_ping_time,
- cfg_net_msc_ping_time_cmd,
- "timeout-ping <1-2147483647>",
- "Set the PING interval, negative for not sending PING\n"
- "Timeout in seconds\n")
-{
- struct bsc_msc_data *data = bsc_msc_data(vty);
- data->ping_timeout = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_msc_pong_time,
- cfg_net_msc_pong_time_cmd,
- "timeout-pong <1-2147483647>",
- "Set the time to wait for a PONG\n" "Timeout in seconds\n")
-{
- struct bsc_msc_data *data = bsc_msc_data(vty);
- data->pong_timeout = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_msc_advanced_ping,
- cfg_net_msc_advanced_ping_cmd,
- "timeout-ping advanced",
- "Ping timeout handling\nEnable advanced mode during SCCP\n")
-{
- struct bsc_msc_data *data = bsc_msc_data(vty);
-
- if (data->ping_timeout == -1) {
- vty_out(vty, "%%ping handling is disabled. Enable it first.%s",
- VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- data->advanced_ping = 1;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_no_net_msc_advanced_ping,
- cfg_no_net_msc_advanced_ping_cmd,
- "no timeout-ping advanced",
- NO_STR "Ping timeout handling\nEnable advanced mode during SCCP\n")
-{
- struct bsc_msc_data *data = bsc_msc_data(vty);
- data->advanced_ping = 0;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_msc_welcome_ussd,
- cfg_net_msc_welcome_ussd_cmd,
- "bsc-welcome-text .TEXT",
- "Set the USSD notification to be sent\n" "Text to be sent\n")
-{
- struct bsc_msc_data *data = bsc_msc_data(vty);
- char *str = argv_concat(argv, argc, 0);
- if (!str)
- return CMD_WARNING;
-
- osmo_talloc_replace_string(osmo_bsc_data(vty), &data->ussd_welcome_txt, str);
- talloc_free(str);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_msc_no_welcome_ussd,
- cfg_net_msc_no_welcome_ussd_cmd,
- "no bsc-welcome-text",
- NO_STR "Clear the USSD notification to be sent\n")
-{
- struct bsc_msc_data *data = bsc_msc_data(vty);
-
- talloc_free(data->ussd_welcome_txt);
- data->ussd_welcome_txt = NULL;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_msc_lost_ussd,
- cfg_net_msc_lost_ussd_cmd,
- "bsc-msc-lost-text .TEXT",
- "Set the USSD notification to be sent on MSC connection loss\n" "Text to be sent\n")
-{
- struct bsc_msc_data *data = bsc_msc_data(vty);
- char *str = argv_concat(argv, argc, 0);
- if (!str)
- return CMD_WARNING;
-
- osmo_talloc_replace_string(osmo_bsc_data(vty), &data->ussd_msc_lost_txt, str);
- talloc_free(str);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_msc_no_lost_ussd,
- cfg_net_msc_no_lost_ussd_cmd,
- "no bsc-msc-lost-text",
- NO_STR "Clear the USSD notification to be sent on MSC connection loss\n")
-{
- struct bsc_msc_data *data = bsc_msc_data(vty);
-
- talloc_free(data->ussd_msc_lost_txt);
- data->ussd_msc_lost_txt = 0;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_msc_grace_ussd,
- cfg_net_msc_grace_ussd_cmd,
- "bsc-grace-text .TEXT",
- "Set the USSD notification to be sent when the MSC has entered the grace period\n" "Text to be sent\n")
-{
- struct bsc_msc_data *data = bsc_msc_data(vty);
- char *str = argv_concat(argv, argc, 0);
- if (!str)
- return CMD_WARNING;
-
- osmo_talloc_replace_string(osmo_bsc_data(vty), &data->ussd_grace_txt, str);
- talloc_free(str);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_msc_no_grace_ussd,
- cfg_net_msc_no_grace_ussd_cmd,
- "no bsc-grace-text",
- NO_STR "Clear the USSD notification to be sent when the MSC has entered the grace period\n")
-{
- struct bsc_msc_data *data = bsc_msc_data(vty);
-
- talloc_free(data->ussd_grace_txt);
- data->ussd_grace_txt = NULL;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_bsc_missing_msc_ussd,
- cfg_net_bsc_missing_msc_ussd_cmd,
- "missing-msc-text .TEXT",
- "Set the USSD notification to be send when a MSC has not been found.\n" "Text to be sent\n")
-{
- struct osmo_bsc_data *data = osmo_bsc_data(vty);
- char *txt = argv_concat(argv, argc, 0);
- if (!txt)
- return CMD_WARNING;
-
- osmo_talloc_replace_string(data, &data->ussd_no_msc_txt, txt);
- talloc_free(txt);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_bsc_no_missing_msc_text,
- cfg_net_bsc_no_missing_msc_text_cmd,
- "no missing-msc-text",
- NO_STR "Clear the USSD notification to be send when a MSC has not been found.\n")
-{
- struct osmo_bsc_data *data = osmo_bsc_data(vty);
-
- talloc_free(data->ussd_no_msc_txt);
- data->ussd_no_msc_txt = 0;
-
- return CMD_SUCCESS;
-}
-
-
-DEFUN(cfg_net_msc_type,
- cfg_net_msc_type_cmd,
- "type (normal|local)",
- "Select the MSC type\n"
- "Plain GSM MSC\n" "Special MSC for local call routing\n")
-{
- struct bsc_msc_data *data = bsc_msc_data(vty);
-
- if (strcmp(argv[0], "normal") == 0)
- data->type = MSC_CON_TYPE_NORMAL;
- else if (strcmp(argv[0], "local") == 0)
- data->type = MSC_CON_TYPE_LOCAL;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_msc_emerg,
- cfg_net_msc_emerg_cmd,
- "allow-emergency (allow|deny)",
- "Allow CM ServiceRequests with type emergency\n"
- "Allow\n" "Deny\n")
-{
- struct bsc_msc_data *data = bsc_msc_data(vty);
- data->allow_emerg = strcmp("allow", argv[0]) == 0;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_msc_local_prefix,
- cfg_net_msc_local_prefix_cmd,
- "local-prefix REGEXP",
- "Prefix for local numbers\n" "REGEXP used\n")
-{
- struct bsc_msc_data *msc = bsc_msc_data(vty);
-
- if (gsm_parse_reg(msc, &msc->local_pref_reg, &msc->local_pref, argc, argv) != 0) {
- vty_out(vty, "%%Failed to parse the regexp: '%s'%s",
- argv[0], VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- return CMD_SUCCESS;
-}
-
-#define AMR_CONF_STR "AMR Multirate Configuration\n"
-#define AMR_COMMAND(name) \
- DEFUN(cfg_net_msc_amr_##name, \
- cfg_net_msc_amr_##name##_cmd, \
- "amr-config " #name "k (allowed|forbidden)", \
- AMR_CONF_STR "Bitrate\n" "Allowed\n" "Forbidden\n") \
-{ \
- struct bsc_msc_data *msc = bsc_msc_data(vty); \
- \
- msc->amr_conf.m##name = strcmp(argv[0], "allowed") == 0; \
- return CMD_SUCCESS; \
-}
-
-AMR_COMMAND(12_2)
-AMR_COMMAND(10_2)
-AMR_COMMAND(7_95)
-AMR_COMMAND(7_40)
-AMR_COMMAND(6_70)
-AMR_COMMAND(5_90)
-AMR_COMMAND(5_15)
-AMR_COMMAND(4_75)
-
-DEFUN(cfg_msc_acc_lst_name,
- cfg_msc_acc_lst_name_cmd,
- "access-list-name NAME",
- "Set the name of the access list to use.\n"
- "The name of the to be used access list.")
-{
- struct bsc_msc_data *msc = bsc_msc_data(vty);
-
- osmo_talloc_replace_string(msc, &msc->acc_lst_name, argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_msc_no_acc_lst_name,
- cfg_msc_no_acc_lst_name_cmd,
- "no access-list-name",
- NO_STR "Remove the access list from the NAT.\n")
-{
- struct bsc_msc_data *msc = bsc_msc_data(vty);
-
- if (msc->acc_lst_name) {
- talloc_free(msc->acc_lst_name);
- msc->acc_lst_name = NULL;
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_bsc_mid_call_text,
- cfg_net_bsc_mid_call_text_cmd,
- "mid-call-text .TEXT",
- "Set the USSD notification to be send.\n" "Text to be sent\n")
-{
- struct osmo_bsc_data *data = osmo_bsc_data(vty);
- char *txt = argv_concat(argv, argc, 0);
- if (!txt)
- return CMD_WARNING;
-
- osmo_talloc_replace_string(data, &data->mid_call_txt, txt);
- talloc_free(txt);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_bsc_mid_call_timeout,
- cfg_net_bsc_mid_call_timeout_cmd,
- "mid-call-timeout NR",
- "Switch from Grace to Off in NR seconds.\n" "Timeout in seconds\n")
-{
- struct osmo_bsc_data *data = osmo_bsc_data(vty);
- data->mid_call_timeout = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_rf_socket,
- cfg_net_rf_socket_cmd,
- "bsc-rf-socket PATH",
- "Set the filename for the RF control interface.\n" "RF Control path\n")
-{
- struct osmo_bsc_data *data = osmo_bsc_data(vty);
-
- osmo_talloc_replace_string(data, &data->rf_ctrl_name, argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_rf_off_time,
- cfg_net_rf_off_time_cmd,
- "bsc-auto-rf-off <1-65000>",
- "Disable RF on MSC Connection\n" "Timeout\n")
-{
- struct osmo_bsc_data *data = osmo_bsc_data(vty);
- data->auto_off_timeout = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_net_no_rf_off_time,
- cfg_net_no_rf_off_time_cmd,
- "no bsc-auto-rf-off",
- NO_STR "Disable RF on MSC Connection\n")
-{
- struct osmo_bsc_data *data = osmo_bsc_data(vty);
- data->auto_off_timeout = -1;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bsc_acc_lst_name,
- cfg_bsc_acc_lst_name_cmd,
- "access-list-name NAME",
- "Set the name of the access list to use.\n"
- "The name of the to be used access list.")
-{
- struct osmo_bsc_data *bsc = osmo_bsc_data(vty);
-
- osmo_talloc_replace_string(bsc, &bsc->acc_lst_name, argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bsc_no_acc_lst_name,
- cfg_bsc_no_acc_lst_name_cmd,
- "no access-list-name",
- NO_STR "Remove the access list from the BSC\n")
-{
- struct osmo_bsc_data *bsc = osmo_bsc_data(vty);
-
- if (bsc->acc_lst_name) {
- talloc_free(bsc->acc_lst_name);
- bsc->acc_lst_name = NULL;
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN(show_statistics,
- show_statistics_cmd,
- "show statistics",
- SHOW_STR "Statistics about the BSC\n")
-{
- openbsc_vty_print_statistics(vty, bsc_gsmnet);
- return CMD_SUCCESS;
-}
-
-DEFUN(show_mscs,
- show_mscs_cmd,
- "show mscs",
- SHOW_STR "MSC Connections and State\n")
-{
- struct bsc_msc_data *msc;
- llist_for_each_entry(msc, &bsc_gsmnet->bsc_data->mscs, entry) {
- vty_out(vty, "MSC Nr: %d is connected: %d auth: %d.%s",
- msc->nr,
- msc->msc_con ? msc->msc_con->is_connected : -1,
- msc->msc_con ? msc->msc_con->is_authenticated : -1,
- VTY_NEWLINE);
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN(show_pos,
- show_pos_cmd,
- "show position",
- SHOW_STR "Position information of the BTS\n")
-{
- struct gsm_bts *bts;
- struct bts_location *curloc;
- struct tm time;
- char timestr[50];
-
- llist_for_each_entry(bts, &bsc_gsmnet->bts_list, list) {
- if (llist_empty(&bts->loc_list)) {
- vty_out(vty, "BTS Nr: %d position invalid%s", bts->nr,
- VTY_NEWLINE);
- continue;
- }
- curloc = llist_entry(bts->loc_list.next, struct bts_location, list);
- if (gmtime_r(&curloc->tstamp, &time) == NULL) {
- vty_out(vty, "Time conversion failed for BTS %d%s", bts->nr,
- VTY_NEWLINE);
- continue;
- }
- if (asctime_r(&time, timestr) == NULL) {
- vty_out(vty, "Time conversion failed for BTS %d%s", bts->nr,
- VTY_NEWLINE);
- continue;
- }
- /* Last character in asctime is \n */
- timestr[strlen(timestr)-1] = 0;
-
- vty_out(vty, "BTS Nr: %d position: %s time: %s%s", bts->nr,
- get_value_string(bts_loc_fix_names, curloc->valid), timestr,
- VTY_NEWLINE);
- vty_out(vty, " lat: %f lon: %f height: %f%s", curloc->lat, curloc->lon,
- curloc->height, VTY_NEWLINE);
- }
- return CMD_SUCCESS;
-}
-
-DEFUN(gen_position_trap,
- gen_position_trap_cmd,
- "generate-location-state-trap <0-255>",
- "Generate location state report\n"
- "BTS to report\n")
-{
- int bts_nr;
- struct gsm_bts *bts;
- struct gsm_network *net = bsc_gsmnet;
-
- bts_nr = atoi(argv[0]);
- if (bts_nr >= net->num_bts) {
- vty_out(vty, "%% can't find BTS '%s'%s", argv[0],
- VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- bts = gsm_bts_num(net, bts_nr);
- bsc_gen_location_state_trap(bts);
- return CMD_SUCCESS;
-}
-
-DEFUN(logging_fltr_imsi,
- logging_fltr_imsi_cmd,
- "logging filter imsi IMSI",
- LOGGING_STR FILTER_STR
- "Filter log messages by IMSI\n" "IMSI to be used as filter\n")
-{
- struct bsc_subscr *bsc_subscr;
- struct log_target *tgt = osmo_log_vty2tgt(vty);
- const char *imsi = argv[0];
-
- bsc_subscr = bsc_subscr_find_by_imsi(bsc_gsmnet->bsc_subscribers, imsi);
-
- if (!bsc_subscr) {
- vty_out(vty, "%%no subscriber with IMSI(%s)%s",
- imsi, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- log_set_filter_bsc_subscr(tgt, bsc_subscr);
- return CMD_SUCCESS;
-}
-
-int bsc_vty_init_extra(void)
-{
- install_element(CONFIG_NODE, &cfg_net_msc_cmd);
- install_element(CONFIG_NODE, &cfg_net_bsc_cmd);
-
- install_node(&bsc_node, config_write_bsc);
- vty_install_default(BSC_NODE);
- install_element(BSC_NODE, &cfg_net_bsc_mid_call_text_cmd);
- install_element(BSC_NODE, &cfg_net_bsc_mid_call_timeout_cmd);
- install_element(BSC_NODE, &cfg_net_rf_socket_cmd);
- install_element(BSC_NODE, &cfg_net_rf_off_time_cmd);
- install_element(BSC_NODE, &cfg_net_no_rf_off_time_cmd);
- install_element(BSC_NODE, &cfg_net_bsc_missing_msc_ussd_cmd);
- install_element(BSC_NODE, &cfg_net_bsc_no_missing_msc_text_cmd);
- install_element(BSC_NODE, &cfg_bsc_acc_lst_name_cmd);
- install_element(BSC_NODE, &cfg_bsc_no_acc_lst_name_cmd);
-
- install_node(&msc_node, config_write_msc);
- vty_install_default(MSC_NODE);
- install_element(MSC_NODE, &cfg_net_bsc_token_cmd);
- install_element(MSC_NODE, &cfg_net_bsc_key_cmd);
- install_element(MSC_NODE, &cfg_net_bsc_no_key_cmd);
- install_element(MSC_NODE, &cfg_net_bsc_ncc_cmd);
- install_element(MSC_NODE, &cfg_net_bsc_mcc_cmd);
- install_element(MSC_NODE, &cfg_net_bsc_lac_cmd);
- install_element(MSC_NODE, &cfg_net_bsc_ci_cmd);
- install_element(MSC_NODE, &cfg_net_bsc_rtp_base_cmd);
- install_element(MSC_NODE, &cfg_net_bsc_codec_list_cmd);
- install_element(MSC_NODE, &cfg_net_msc_dest_cmd);
- install_element(MSC_NODE, &cfg_net_msc_no_dest_cmd);
- install_element(MSC_NODE, &cfg_net_msc_no_ping_time_cmd);
- install_element(MSC_NODE, &cfg_net_msc_ping_time_cmd);
- install_element(MSC_NODE, &cfg_net_msc_pong_time_cmd);
- install_element(MSC_NODE, &cfg_net_msc_advanced_ping_cmd);
- install_element(MSC_NODE, &cfg_no_net_msc_advanced_ping_cmd);
- install_element(MSC_NODE, &cfg_net_msc_welcome_ussd_cmd);
- install_element(MSC_NODE, &cfg_net_msc_no_welcome_ussd_cmd);
- install_element(MSC_NODE, &cfg_net_msc_lost_ussd_cmd);
- install_element(MSC_NODE, &cfg_net_msc_no_lost_ussd_cmd);
- install_element(MSC_NODE, &cfg_net_msc_grace_ussd_cmd);
- install_element(MSC_NODE, &cfg_net_msc_no_grace_ussd_cmd);
- install_element(MSC_NODE, &cfg_net_msc_type_cmd);
- install_element(MSC_NODE, &cfg_net_msc_emerg_cmd);
- install_element(MSC_NODE, &cfg_net_msc_local_prefix_cmd);
- install_element(MSC_NODE, &cfg_net_msc_amr_12_2_cmd);
- install_element(MSC_NODE, &cfg_net_msc_amr_10_2_cmd);
- install_element(MSC_NODE, &cfg_net_msc_amr_7_95_cmd);
- install_element(MSC_NODE, &cfg_net_msc_amr_7_40_cmd);
- install_element(MSC_NODE, &cfg_net_msc_amr_6_70_cmd);
- install_element(MSC_NODE, &cfg_net_msc_amr_5_90_cmd);
- install_element(MSC_NODE, &cfg_net_msc_amr_5_15_cmd);
- install_element(MSC_NODE, &cfg_net_msc_amr_4_75_cmd);
- install_element(MSC_NODE, &cfg_msc_acc_lst_name_cmd);
- install_element(MSC_NODE, &cfg_msc_no_acc_lst_name_cmd);
-
- install_element_ve(&show_statistics_cmd);
- install_element_ve(&show_mscs_cmd);
- install_element_ve(&show_pos_cmd);
- install_element_ve(&logging_fltr_imsi_cmd);
-
- install_element(ENABLE_NODE, &gen_position_trap_cmd);
-
- install_element(CFG_LOG_NODE, &logging_fltr_imsi_cmd);
-
- return 0;
-}
diff --git a/openbsc/src/osmo-bsc_mgcp/Makefile.am b/openbsc/src/osmo-bsc_mgcp/Makefile.am
deleted file mode 100644
index a19a4ebc0..000000000
--- a/openbsc/src/osmo-bsc_mgcp/Makefile.am
+++ /dev/null
@@ -1,35 +0,0 @@
-AM_CPPFLAGS = \
- $(all_includes) \
- -I$(top_srcdir)/include \
- -I$(top_builddir) \
- $(NULL)
-
-AM_CFLAGS = \
- -Wall \
- $(LIBOSMOCORE_CFLAGS) \
- $(LIBOSMOGSM_CFLAGS) \
- $(LIBOSMOVTY_CFLAGS) \
- $(LIBOSMOABIS_CFLAGS) \
- $(LIBOSMONETIF_CFLAGS) \
- $(COVERAGE_CFLAGS) \
- $(NULL)
-
-bin_PROGRAMS = \
- osmo-bsc_mgcp \
- $(NULL)
-
-osmo_bsc_mgcp_SOURCES = \
- mgcp_main.c \
- $(NULL)
-
-osmo_bsc_mgcp_LDADD = \
- $(top_builddir)/src/libcommon/libcommon.a \
- $(top_builddir)/src/libmgcp/libmgcp.a \
- $(LIBOSMOVTY_LIBS) \
- $(LIBOSMOCORE_LIBS) \
- $(LIBOSMOGSM_LIBS) \
- $(LIBOSMONETIF_LIBS) \
- $(LIBBCG729_LIBS) \
- $(LIBRARY_GSM) \
- -lrt \
- $(NULL)
diff --git a/openbsc/src/osmo-bsc_mgcp/mgcp_main.c b/openbsc/src/osmo-bsc_mgcp/mgcp_main.c
deleted file mode 100644
index 4ea070079..000000000
--- a/openbsc/src/osmo-bsc_mgcp/mgcp_main.c
+++ /dev/null
@@ -1,311 +0,0 @@
-/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
-/* The main method to drive it as a standalone process */
-
-/*
- * (C) 2009-2011 by Holger Hans Peter Freyther <zecke@selfish.org>
- * (C) 2009-2011 by On-Waves
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <ctype.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <limits.h>
-#include <unistd.h>
-
-#include <sys/socket.h>
-
-#include <openbsc/debug.h>
-#include <openbsc/gsm_data.h>
-#include <openbsc/mgcp.h>
-#include <openbsc/mgcp_internal.h>
-#include <openbsc/vty.h>
-
-#include <osmocom/core/application.h>
-#include <osmocom/core/msgb.h>
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/select.h>
-#include <osmocom/core/stats.h>
-#include <osmocom/core/rate_ctr.h>
-
-#include <osmocom/vty/telnet_interface.h>
-#include <osmocom/vty/logging.h>
-#include <osmocom/vty/ports.h>
-#include <osmocom/vty/command.h>
-#include <osmocom/vty/stats.h>
-
-#include "../../bscconfig.h"
-
-#ifdef BUILD_MGCP_TRANSCODING
-#include "openbsc/mgcp_transcode.h"
-#endif
-
-#define _GNU_SOURCE
-#include <getopt.h>
-
-#warning "Make use of the rtp proxy code"
-
-static struct mgcp_config *cfg;
-static struct mgcp_trunk_config *reset_trunk;
-static int reset_endpoints = 0;
-static int daemonize = 0;
-
-const char *openbsc_copyright =
- "Copyright (C) 2009-2010 Holger Freyther and On-Waves\r\n"
- "Contributions by Daniel Willmann, Jan Lübbe, Stefan Schmidt\r\n"
- "Dieter Spaar, Andreas Eversberg, Harald Welte\r\n\r\n"
- "License AGPLv3+: GNU AGPL version 3 or later <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 = "mgcp.cfg";
-
-/* used by msgb and mgcp */
-void *tall_bsc_ctx = NULL;
-
-static void print_help()
-{
- printf("Some useful help...\n");
- printf(" -h --help is printing this text.\n");
- printf(" -c --config-file filename The config file to use.\n");
- printf(" -s --disable-color\n");
- printf(" -D --daemonize Fork the process into a background daemon\n");
- printf(" -V --version Print the version number\n");
-}
-
-static void handle_options(int argc, char **argv)
-{
- while (1) {
- int option_index = 0, c;
- static struct option long_options[] = {
- {"help", 0, 0, 'h'},
- {"config-file", 1, 0, 'c'},
- {"daemonize", 0, 0, 'D'},
- {"version", 0, 0, 'V'},
- {"disable-color", 0, 0, 's'},
- {0, 0, 0, 0},
- };
-
- c = getopt_long(argc, argv, "hc:VD", long_options, &option_index);
-
- if (c == -1)
- break;
-
- switch(c) {
- case 'h':
- print_help();
- exit(0);
- break;
- case 'c':
- config_file = talloc_strdup(tall_bsc_ctx, optarg);
- break;
- case 's':
- log_set_use_color(osmo_stderr_target, 0);
- break;
- case 'V':
- print_version(1);
- exit(0);
- break;
- case 'D':
- daemonize = 1;
- break;
- default:
- /* ignore */
- break;
- };
- }
-}
-
-/* simply remember this */
-static int mgcp_rsip_cb(struct mgcp_trunk_config *tcfg)
-{
- reset_endpoints = 1;
- reset_trunk = tcfg;
-
- return 0;
-}
-
-static int read_call_agent(struct osmo_fd *fd, unsigned int what)
-{
- struct sockaddr_in addr;
- socklen_t slen = sizeof(addr);
- struct msgb *msg;
- struct msgb *resp;
- int i;
-
- msg = (struct msgb *) fd->data;
-
- /* read one less so we can use it as a \0 */
- int rc = recvfrom(cfg->gw_fd.bfd.fd, msg->data, msg->data_len - 1, 0,
- (struct sockaddr *) &addr, &slen);
- if (rc < 0) {
- perror("Gateway failed to read");
- return -1;
- } else if (slen > sizeof(addr)) {
- fprintf(stderr, "Gateway received message from outerspace: %zu %zu\n",
- (size_t) slen, sizeof(addr));
- return -1;
- }
-
- /* handle message now */
- msg->l2h = msgb_put(msg, rc);
- resp = mgcp_handle_message(cfg, msg);
- msgb_reset(msg);
-
- if (resp) {
- sendto(cfg->gw_fd.bfd.fd, resp->l2h, msgb_l2len(resp), 0, (struct sockaddr *) &addr, sizeof(addr));
- msgb_free(resp);
- }
-
- if (reset_endpoints) {
- LOGP(DMGCP, LOGL_NOTICE,
- "Asked to reset endpoints: %d/%d\n",
- reset_trunk->trunk_nr, reset_trunk->trunk_type);
- reset_endpoints = 0;
-
- /* is checking in_addr.s_addr == INADDR_LOOPBACK making it more secure? */
- for (i = 1; i < reset_trunk->number_endpoints; ++i)
- mgcp_release_endp(&reset_trunk->endpoints[i]);
- }
-
- return 0;
-}
-
-extern int bsc_vty_go_parent(struct vty *vty);
-
-static struct vty_app_info vty_info = {
- .name = "OpenBSC MGCP",
- .version = PACKAGE_VERSION,
- .go_parent_cb = bsc_vty_go_parent,
- .is_config_node = bsc_vty_is_config_node,
-};
-
-int main(int argc, char **argv)
-{
- struct gsm_network dummy_network;
- struct sockaddr_in addr;
- int on = 1, rc;
-
- tall_bsc_ctx = talloc_named_const(NULL, 1, "mgcp-callagent");
- msgb_talloc_ctx_init(tall_bsc_ctx, 0);
-
- osmo_init_ignore_signals();
- osmo_init_logging(&log_info);
-
- cfg = mgcp_config_alloc();
- if (!cfg)
- return -1;
-
-#ifdef BUILD_MGCP_TRANSCODING
- cfg->setup_rtp_processing_cb = &mgcp_transcoding_setup;
- cfg->rtp_processing_cb = &mgcp_transcoding_process_rtp;
- cfg->get_net_downlink_format_cb = &mgcp_transcoding_net_downlink_format;
-#endif
-
- vty_info.copyright = openbsc_copyright;
- vty_init(&vty_info);
- logging_vty_add_cmds(NULL);
- osmo_stats_vty_add_cmds(&log_info);
- mgcp_vty_init();
-
- handle_options(argc, argv);
-
- rate_ctr_init(tall_bsc_ctx);
- osmo_stats_init(tall_bsc_ctx);
-
- rc = mgcp_parse_config(config_file, cfg, MGCP_BSC);
- if (rc < 0)
- return rc;
-
- /* 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_BSC_MGCP);
- if (rc < 0)
- return rc;
-
- /* set some callbacks */
- cfg->reset_cb = mgcp_rsip_cb;
-
- /* we need to bind a socket */
- if (rc == 0) {
- cfg->gw_fd.bfd.when = BSC_FD_READ;
- cfg->gw_fd.bfd.cb = read_call_agent;
- cfg->gw_fd.bfd.fd = socket(AF_INET, SOCK_DGRAM, 0);
- if (cfg->gw_fd.bfd.fd < 0) {
- perror("Gateway failed to listen");
- return -1;
- }
-
- setsockopt(cfg->gw_fd.bfd.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
-
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_port = htons(cfg->source_port);
- inet_aton(cfg->source_addr, &addr.sin_addr);
-
- if (bind(cfg->gw_fd.bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- perror("Gateway failed to bind");
- return -1;
- }
-
- cfg->gw_fd.bfd.data = msgb_alloc(4096, "mgcp-msg");
- if (!cfg->gw_fd.bfd.data) {
- fprintf(stderr, "Gateway memory error.\n");
- return -1;
- }
-
- if (cfg->call_agent_addr) {
- addr.sin_port = htons(2727);
- inet_aton(cfg->call_agent_addr, &addr.sin_addr);
- if (connect(cfg->gw_fd.bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- LOGP(DMGCP, LOGL_ERROR, "Failed to connect to: '%s'. errno: %d\n",
- cfg->call_agent_addr, errno);
- close(cfg->gw_fd.bfd.fd);
- cfg->gw_fd.bfd.fd = -1;
- return -1;
- }
- }
-
- if (osmo_fd_register(&cfg->gw_fd.bfd) != 0) {
- LOGP(DMGCP, LOGL_FATAL, "Failed to register the fd\n");
- return -1;
- }
-
- LOGP(DMGCP, LOGL_NOTICE, "Configured for MGCP.\n");
- }
-
- /* initialisation */
- srand(time(NULL));
-
- if (daemonize) {
- rc = osmo_daemonize();
- if (rc < 0) {
- perror("Error during daemonize");
- exit(1);
- }
- }
-
- /* main loop */
- while (1) {
- osmo_select_main(0);
- }
-
-
- return 0;
-}
diff --git a/openbsc/src/osmo-bsc_nat/Makefile.am b/openbsc/src/osmo-bsc_nat/Makefile.am
deleted file mode 100644
index be33d289d..000000000
--- a/openbsc/src/osmo-bsc_nat/Makefile.am
+++ /dev/null
@@ -1,58 +0,0 @@
-AM_CPPFLAGS = \
- $(all_includes) \
- -I$(top_srcdir)/include \
- -I$(top_builddir) \
- $(NULL)
-
-AM_CFLAGS = \
- -Wall \
- $(LIBOSMOCORE_CFLAGS) \
- $(LIBOSMOGSM_CFLAGS) \
- $(LIBOSMOVTY_CFLAGS) \
- $(LIBOSMOCTRL_CFLAGS) \
- $(LIBOSMOSCCP_CFLAGS) \
- $(LIBOSMOABIS_CFLAGS) \
- $(LIBOSMONETIF_CFLAGS) \
- $(LIBCRYPTO_CFLAGS) \
- $(COVERAGE_CFLAGS) \
- $(NULL)
-
-AM_LDFLAGS = \
- $(COVERAGE_LDFLAGS) \
- $(NULL)
-
-bin_PROGRAMS = \
- osmo-bsc_nat \
- $(NULL)
-
-osmo_bsc_nat_SOURCES = \
- bsc_filter.c \
- bsc_mgcp_utils.c \
- bsc_nat.c \
- bsc_nat_utils.c \
- bsc_nat_vty.c \
- bsc_sccp.c \
- bsc_ussd.c \
- bsc_nat_ctrl.c \
- bsc_nat_rewrite.c \
- bsc_nat_rewrite_trie.c \
- bsc_nat_filter.c \
- $(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 \
- $(top_builddir)/src/libtrau/libtrau.a \
- $(top_builddir)/src/libcommon/libcommon.a \
- $(LIBOSMOSCCP_LIBS) \
- $(LIBOSMOCORE_LIBS) \
- $(LIBOSMOGSM_LIBS) \
- $(LIBOSMOVTY_LIBS) \
- $(LIBOSMOCTRL_LIBS) \
- $(LIBOSMOABIS_LIBS) \
- $(LIBOSMONETIF_LIBS) \
- $(LIBCRYPTO_LIBS) \
- -lrt \
- $(NULL)
diff --git a/openbsc/src/osmo-bsc_nat/bsc_filter.c b/openbsc/src/osmo-bsc_nat/bsc_filter.c
deleted file mode 100644
index 6a9e99fb8..000000000
--- a/openbsc/src/osmo-bsc_nat/bsc_filter.c
+++ /dev/null
@@ -1,218 +0,0 @@
-/* BSC Multiplexer/NAT */
-
-/*
- * (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
- * (C) 2010 by On-Waves
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <openbsc/bsc_nat.h>
-#include <openbsc/bsc_nat_sccp.h>
-#include <openbsc/ipaccess.h>
-#include <openbsc/debug.h>
-
-#include <osmocom/core/talloc.h>
-#include <osmocom/gsm/protocol/gsm_08_08.h>
-
-#include <osmocom/sccp/sccp.h>
-
-/*
- * The idea is to have a simple struct describing a IPA packet with
- * SCCP SSN and the GSM 08.08 payload and decide. We will both have
- * a white and a blacklist of packets we want to handle.
- *
- * TODO: Implement a "NOT" in the filter language.
- */
-
-#define ALLOW_ANY -1
-
-#define FILTER_TO_BSC 1
-#define FILTER_TO_MSC 2
-#define FILTER_TO_BOTH 3
-
-
-struct bsc_pkt_filter {
- int ipa_proto;
- int dest_ssn;
- int bssap;
- int gsm;
- int filter_dir;
-};
-
-static struct bsc_pkt_filter black_list[] = {
- /* filter reset messages to the MSC */
- { IPAC_PROTO_SCCP, SCCP_SSN_BSSAP, 0, BSS_MAP_MSG_RESET, FILTER_TO_MSC },
-
- /* filter reset ack messages to the BSC */
- { IPAC_PROTO_SCCP, SCCP_SSN_BSSAP, 0, BSS_MAP_MSG_RESET_ACKNOWLEDGE, FILTER_TO_BSC },
-
- /* filter ip access */
- { IPAC_PROTO_IPACCESS, ALLOW_ANY, ALLOW_ANY, ALLOW_ANY, FILTER_TO_MSC },
-};
-
-static struct bsc_pkt_filter white_list[] = {
- /* allow IPAC_PROTO_SCCP messages to both sides */
- { IPAC_PROTO_SCCP, ALLOW_ANY, ALLOW_ANY, ALLOW_ANY, FILTER_TO_BOTH },
-
- /* allow MGCP messages to both sides */
- { IPAC_PROTO_MGCP_OLD, ALLOW_ANY, ALLOW_ANY, ALLOW_ANY, FILTER_TO_BOTH },
-};
-
-struct bsc_nat_parsed *bsc_nat_parse(struct msgb *msg)
-{
- struct sccp_parse_result result;
- struct bsc_nat_parsed *parsed;
- struct ipaccess_head *hh;
-
- /* quick fail */
- if (msg->len < 4)
- return NULL;
-
- parsed = talloc_zero(msg, struct bsc_nat_parsed);
- if (!parsed)
- return NULL;
-
- /* more init */
- parsed->ipa_proto = parsed->called_ssn = parsed->calling_ssn = -1;
- parsed->sccp_type = parsed->bssap = parsed->gsm_type = -1;
-
- /* start parsing */
- hh = (struct ipaccess_head *) msg->data;
- parsed->ipa_proto = hh->proto;
-
- msg->l2h = &hh->data[0];
-
- /* do a size check on the input */
- if (ntohs(hh->len) != msgb_l2len(msg)) {
- LOGP(DLINP, LOGL_ERROR, "Wrong input length?\n");
- talloc_free(parsed);
- return NULL;
- }
-
- /* analyze sccp down here */
- if (parsed->ipa_proto == IPAC_PROTO_SCCP) {
- memset(&result, 0, sizeof(result));
- if (sccp_parse_header(msg, &result) != 0) {
- talloc_free(parsed);
- return 0;
- }
-
- if (msg->l3h && msgb_l3len(msg) < 3) {
- LOGP(DNAT, LOGL_ERROR, "Not enough space or GSM payload\n");
- talloc_free(parsed);
- return 0;
- }
-
- parsed->sccp_type = sccp_determine_msg_type(msg);
- parsed->src_local_ref = result.source_local_reference;
- parsed->dest_local_ref = result.destination_local_reference;
- if (parsed->dest_local_ref)
- parsed->original_dest_ref = *parsed->dest_local_ref;
- parsed->called_ssn = result.called.ssn;
- parsed->calling_ssn = result.calling.ssn;
-
- /* in case of connection confirm we have no payload */
- if (msg->l3h) {
- parsed->bssap = msg->l3h[0];
- parsed->gsm_type = msg->l3h[2];
- }
- }
-
- return parsed;
-}
-
-int bsc_nat_filter_ipa(int dir, struct msgb *msg, struct bsc_nat_parsed *parsed)
-{
- int i;
-
- /* go through the blacklist now */
- for (i = 0; i < ARRAY_SIZE(black_list); ++i) {
- /* ignore the rule? */
- if (black_list[i].filter_dir != FILTER_TO_BOTH
- && black_list[i].filter_dir != dir)
- continue;
-
- /* the proto is not blacklisted */
- if (black_list[i].ipa_proto != ALLOW_ANY
- && black_list[i].ipa_proto != parsed->ipa_proto)
- continue;
-
- if (parsed->ipa_proto == IPAC_PROTO_SCCP) {
- /* the SSN is not blacklisted */
- if (black_list[i].dest_ssn != ALLOW_ANY
- && black_list[i].dest_ssn != parsed->called_ssn)
- continue;
-
- /* bssap */
- if (black_list[i].bssap != ALLOW_ANY
- && black_list[i].bssap != parsed->bssap)
- continue;
-
- /* gsm */
- if (black_list[i].gsm != ALLOW_ANY
- && black_list[i].gsm != parsed->gsm_type)
- continue;
-
- /* blacklisted */
- LOGP(DNAT, LOGL_INFO, "Blacklisted with rule %d\n", i);
- return 1;
- } else {
- /* blacklisted, we have no content sniffing yet */
- LOGP(DNAT, LOGL_INFO, "Blacklisted with rule %d\n", i);
- return 1;
- }
- }
-
- /* go through the whitelust now */
- for (i = 0; i < ARRAY_SIZE(white_list); ++i) {
- /* ignore the rule? */
- if (white_list[i].filter_dir != FILTER_TO_BOTH
- && white_list[i].filter_dir != dir)
- continue;
-
- /* the proto is not whitelisted */
- if (white_list[i].ipa_proto != ALLOW_ANY
- && white_list[i].ipa_proto != parsed->ipa_proto)
- continue;
-
- if (parsed->ipa_proto == IPAC_PROTO_SCCP) {
- /* the SSN is not whitelisted */
- if (white_list[i].dest_ssn != ALLOW_ANY
- && white_list[i].dest_ssn != parsed->called_ssn)
- continue;
-
- /* bssap */
- if (white_list[i].bssap != ALLOW_ANY
- && white_list[i].bssap != parsed->bssap)
- continue;
-
- /* gsm */
- if (white_list[i].gsm != ALLOW_ANY
- && white_list[i].gsm != parsed->gsm_type)
- continue;
-
- /* whitelisted */
- LOGP(DNAT, LOGL_INFO, "Whitelisted with rule %d\n", i);
- return 0;
- } else {
- /* whitelisted */
- return 0;
- }
- }
-
- return 1;
-}
diff --git a/openbsc/src/osmo-bsc_nat/bsc_mgcp_utils.c b/openbsc/src/osmo-bsc_nat/bsc_mgcp_utils.c
deleted file mode 100644
index 48847865c..000000000
--- a/openbsc/src/osmo-bsc_nat/bsc_mgcp_utils.c
+++ /dev/null
@@ -1,1152 +0,0 @@
-/**
- * This file contains helper routines for MGCP Gateway handling.
- *
- * The first thing to remember is that each BSC has its own namespace/range
- * of endpoints. Whenever a BSSMAP ASSIGNMENT REQUEST is received this code
- * will be called to select an endpoint on the BSC. The mapping from original
- * multiplex/timeslot to BSC multiplex'/timeslot' will be stored.
- *
- * The second part is to take messages on the public MGCP GW interface
- * and forward them to the right BSC. This requires the MSC to first
- * assign the timeslot. This assumption has been true so far. We are using
- * the policy_cb of the MGCP protocol code to decide if the request should
- * be immediately answered or delayed. An extension "Z: noanswer" is used
- * to request the BSC to not respond. This is saving some bytes of bandwidth
- * and as we are using TCP to forward the message we know it will arrive.
- * The mgcp_do_read method reads these messages and hands them to the protocol
- * parsing code which will call the mentioned policy_cb. The bsc_mgcp_forward
- * method is used on the way back from the BSC to the network.
- *
- * The third part is to patch messages forwarded to the BSC. This includes
- * the endpoint number, the ports to be used inside the SDP file and maybe
- * some other bits.
- *
- */
-/*
- * (C) 2010-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
- * (C) 2010-2012 by On-Waves
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <openbsc/bsc_nat.h>
-#include <openbsc/bsc_nat_callstats.h>
-#include <openbsc/bsc_nat_sccp.h>
-#include <openbsc/gsm_data.h>
-#include <openbsc/debug.h>
-#include <openbsc/ipaccess.h>
-#include <openbsc/mgcp.h>
-#include <openbsc/mgcp_internal.h>
-#include <openbsc/osmux.h>
-
-#include <osmocom/ctrl/control_cmd.h>
-
-#include <osmocom/sccp/sccp.h>
-
-#include <osmocom/core/talloc.h>
-#include <osmocom/gsm/gsm0808.h>
-#include <osmocom/gsm/protocol/gsm_08_08.h>
-
-#include <netinet/in.h>
-#include <arpa/inet.h>
-
-#include <errno.h>
-#include <unistd.h>
-
-static void send_direct(struct bsc_nat *nat, struct msgb *output)
-{
- if (osmo_wqueue_enqueue(&nat->mgcp_cfg->gw_fd, output) != 0) {
- LOGP(DMGCP, LOGL_ERROR, "Failed to queue MGCP msg.\n");
- msgb_free(output);
- }
-}
-
-static void mgcp_queue_for_call_agent(struct bsc_nat *nat, struct msgb *output)
-{
- if (nat->mgcp_ipa)
- bsc_nat_send_mgcp_to_msc(nat, output);
- else
- send_direct(nat, output);
-}
-
-int bsc_mgcp_nr_multiplexes(int max_endpoints)
-{
- int div = max_endpoints / 32;
-
- if ((max_endpoints % 32) != 0)
- div += 1;
-
- return div;
-}
-
-static int bsc_init_endps_if_needed(struct bsc_connection *con)
-{
- int multiplexes;
-
- /* we have done that */
- if (con->_endpoint_status)
- return 0;
-
- /* we have no config... */
- if (!con->cfg)
- return -1;
-
- multiplexes = bsc_mgcp_nr_multiplexes(con->cfg->max_endpoints);
- con->number_multiplexes = multiplexes;
- con->max_endpoints = con->cfg->max_endpoints;
- con->_endpoint_status = talloc_zero_array(con, char, 32 * multiplexes + 1);
- return con->_endpoint_status == NULL;
-}
-
-static int bsc_assign_endpoint(struct bsc_connection *bsc, struct nat_sccp_connection *con)
-{
- int multiplex;
- int timeslot;
- const int number_endpoints = bsc->max_endpoints;
- int i;
-
- mgcp_endpoint_to_timeslot(bsc->last_endpoint, &multiplex, &timeslot);
- timeslot += 1;
-
- for (i = 0; i < number_endpoints; ++i) {
- int endpoint;
-
- /* Wrap around timeslots */
- if (timeslot == 0)
- timeslot = 1;
-
- if (timeslot == 0x1f) {
- timeslot = 1;
- multiplex += 1;
- }
-
- /* Wrap around the multiplex */
- if (multiplex >= bsc->number_multiplexes)
- multiplex = 0;
-
- endpoint = mgcp_timeslot_to_endpoint(multiplex, timeslot);
-
- /* Now check if we are allowed to assign this one */
- if (endpoint >= bsc->max_endpoints) {
- multiplex = 0;
- timeslot = 1;
- endpoint = mgcp_timeslot_to_endpoint(multiplex, timeslot);
- }
-
-
- if (bsc->_endpoint_status[endpoint] == 0) {
- bsc->_endpoint_status[endpoint] = 1;
- con->bsc_endp = endpoint;
- bsc->last_endpoint = endpoint;
- return 0;
- }
-
- timeslot += 1;
- }
-
- return -1;
-}
-
-static uint16_t create_cic(int endpoint)
-{
- int timeslot, multiplex;
-
- mgcp_endpoint_to_timeslot(endpoint, &multiplex, &timeslot);
- return (multiplex << 5) | (timeslot & 0x1f);
-}
-
-int bsc_mgcp_assign_patch(struct nat_sccp_connection *con, struct msgb *msg)
-{
- struct nat_sccp_connection *mcon;
- struct tlv_parsed tp;
- uint16_t cic;
- uint8_t timeslot;
- uint8_t multiplex;
- unsigned int endp;
-
- if (!msg->l3h) {
- LOGP(DNAT, LOGL_ERROR, "Assignment message should have l3h pointer.\n");
- return -1;
- }
-
- if (msgb_l3len(msg) < 3) {
- LOGP(DNAT, LOGL_ERROR, "Assignment message has not enough space for GSM0808.\n");
- return -1;
- }
-
- tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 3, msgb_l3len(msg) - 3, 0, 0);
- if (!TLVP_PRESENT(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE)) {
- LOGP(DNAT, LOGL_ERROR, "Circuit identity code not found in assignment message.\n");
- return -1;
- }
-
- cic = ntohs(tlvp_val16_unal(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE));
- timeslot = cic & 0x1f;
- multiplex = (cic & ~0x1f) >> 5;
-
-
- endp = mgcp_timeslot_to_endpoint(multiplex, timeslot);
-
- if (endp >= con->bsc->nat->mgcp_cfg->trunk.number_endpoints) {
- LOGP(DNAT, LOGL_ERROR,
- "MSC attempted to assign bad endpoint 0x%x\n",
- endp);
- return -1;
- }
-
- /* find stale connections using that endpoint */
- llist_for_each_entry(mcon, &con->bsc->nat->sccp_connections, list_entry) {
- if (mcon->msc_endp == endp) {
- LOGP(DNAT, LOGL_ERROR,
- "Endpoint %d was assigned to 0x%x and now 0x%x\n",
- endp,
- sccp_src_ref_to_int(&mcon->patched_ref),
- sccp_src_ref_to_int(&con->patched_ref));
- bsc_mgcp_dlcx(mcon);
- }
- }
-
- con->msc_endp = endp;
- if (bsc_init_endps_if_needed(con->bsc) != 0)
- return -1;
- if (bsc_assign_endpoint(con->bsc, con) != 0)
- return -1;
-
- /*
- * now patch the message for the new CIC...
- * still assumed to be one multiplex only
- */
- cic = htons(create_cic(con->bsc_endp));
- memcpy((uint8_t *) TLVP_VAL(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE),
- &cic, sizeof(cic));
-
- return 0;
-}
-
-static void bsc_mgcp_free_endpoint(struct bsc_nat *nat, int i)
-{
- if (nat->bsc_endpoints[i].transaction_id) {
- talloc_free(nat->bsc_endpoints[i].transaction_id);
- nat->bsc_endpoints[i].transaction_id = NULL;
- }
-
- nat->bsc_endpoints[i].transaction_state = 0;
- nat->bsc_endpoints[i].bsc = NULL;
-}
-
-void bsc_mgcp_free_endpoints(struct bsc_nat *nat)
-{
- int i;
-
- for (i = 1; i < nat->mgcp_cfg->trunk.number_endpoints; ++i){
- bsc_mgcp_free_endpoint(nat, i);
- mgcp_release_endp(&nat->mgcp_cfg->trunk.endpoints[i]);
- }
-}
-
-/* send a MDCX where we do not want a response */
-static void bsc_mgcp_send_mdcx(struct bsc_connection *bsc, int port, struct mgcp_endpoint *endp)
-{
- char buf[2096];
- int len;
-
- len = snprintf(buf, sizeof(buf),
- "MDCX 23 %x@mgw MGCP 1.0\r\n"
- "Z: noanswer\r\n"
- "\r\n"
- "c=IN IP4 %s\r\n"
- "m=audio %d RTP/AVP 255\r\n",
- port, mgcp_bts_src_addr(endp),
- endp->bts_end.local_port);
- if (len < 0) {
- LOGP(DMGCP, LOGL_ERROR, "snprintf for MDCX failed.\n");
- return;
- }
-
- bsc_write_mgcp(bsc, (uint8_t *) buf, len);
-}
-
-static void bsc_mgcp_send_dlcx(struct bsc_connection *bsc, int endpoint, int trans)
-{
- char buf[2096];
- int len;
-
- /*
- * The following is a bit of a spec violation. According to the
- * MGCP grammar the transaction id is are upto 9 digits but we
- * prefix it with an alpha numeric value so we can easily recognize
- * it as a response.
- */
- len = snprintf(buf, sizeof(buf),
- "DLCX nat-%u %x@mgw MGCP 1.0\r\n",
- trans, endpoint);
- if (len < 0) {
- LOGP(DMGCP, LOGL_ERROR, "snprintf for DLCX failed.\n");
- return;
- }
-
- bsc_write_mgcp(bsc, (uint8_t *) buf, len);
-}
-
-void bsc_mgcp_init(struct nat_sccp_connection *con)
-{
- con->msc_endp = -1;
- con->bsc_endp = -1;
-}
-
-/**
- * This code will remember the network side of the audio statistics and
- * once the internal DLCX response arrives this can be combined with the
- * the BSC side and forwarded as a trap.
- */
-static void remember_pending_dlcx(struct nat_sccp_connection *con, uint32_t transaction)
-{
- struct bsc_nat_call_stats *stats;
- struct bsc_connection *bsc = con->bsc;
- struct mgcp_endpoint *endp;
-
- stats = talloc_zero(bsc, struct bsc_nat_call_stats);
- if (!stats) {
- LOGP(DNAT, LOGL_NOTICE,
- "Failed to allocate statistics for endpoint 0x%x\n",
- con->msc_endp);
- return;
- }
-
- /* take the endpoint here */
- endp = &bsc->nat->mgcp_cfg->trunk.endpoints[con->msc_endp];
-
- stats->remote_ref = con->remote_ref;
- stats->src_ref = con->patched_ref;
-
- stats->ci = endp->ci;
- stats->bts_rtp_port = endp->bts_end.rtp_port;
- stats->bts_addr = endp->bts_end.addr;
- stats->net_rtp_port = endp->net_end.rtp_port;
- stats->net_addr = endp->net_end.addr;
-
- stats->net_ps = endp->net_end.packets;
- stats->net_os = endp->net_end.octets;
- stats->bts_pr = endp->bts_end.packets;
- stats->bts_or = endp->bts_end.octets;
- mgcp_state_calc_loss(&endp->bts_state, &endp->bts_end,
- &stats->bts_expected, &stats->bts_loss);
- stats->bts_jitter = mgcp_state_calc_jitter(&endp->bts_state);
-
- stats->trans_id = transaction;
- stats->msc_endpoint = con->msc_endp;
-
- /*
- * Too many pending requests.. let's remove the first two items.
- */
- if (!llist_empty(&bsc->pending_dlcx) &&
- bsc->pending_dlcx_count >= bsc->cfg->max_endpoints * 3) {
- struct bsc_nat_call_stats *tmp;
- LOGP(DNAT, LOGL_ERROR,
- "Too many(%d) pending DLCX responses on BSC: %d\n",
- bsc->pending_dlcx_count, bsc->cfg->nr);
- bsc->pending_dlcx_count -= 1;
- tmp = (struct bsc_nat_call_stats *) bsc->pending_dlcx.next;
- llist_del(&tmp->entry);
- talloc_free(tmp);
- }
-
- bsc->pending_dlcx_count += 1;
- llist_add_tail(&stats->entry, &bsc->pending_dlcx);
-}
-
-void bsc_mgcp_dlcx(struct nat_sccp_connection *con)
-{
- /* send a DLCX down the stream */
- if (con->bsc_endp != -1 && con->bsc->_endpoint_status) {
- LOGP(DNAT, LOGL_NOTICE,
- "Endpoint 0x%x was allocated for bsc: %d. Freeing it.\n",
- con->bsc_endp, con->bsc->cfg->nr);
- if (con->bsc->_endpoint_status[con->bsc_endp] != 1)
- LOGP(DNAT, LOGL_ERROR, "Endpoint 0x%x was not in use\n", con->bsc_endp);
- remember_pending_dlcx(con, con->bsc->next_transaction);
- con->bsc->_endpoint_status[con->bsc_endp] = 0;
- bsc_mgcp_send_dlcx(con->bsc, con->bsc_endp, con->bsc->next_transaction++);
- bsc_mgcp_free_endpoint(con->bsc->nat, con->msc_endp);
- }
-
- bsc_mgcp_init(con);
-
-}
-
-/*
- * Search for the pending request
- */
-static void handle_dlcx_response(struct bsc_connection *bsc, struct msgb *msg,
- int code, const char *transaction)
-{
- uint32_t trans_id = UINT32_MAX;
- uint32_t b_ps, b_os, n_pr, n_or, jitter;
- int loss;
- struct bsc_nat_call_stats *tmp, *stat = NULL;
- struct ctrl_cmd *cmd;
-
- /* parse the transaction identifier */
- int rc = sscanf(transaction, "nat-%u", &trans_id);
- if (rc != 1) {
- LOGP(DNAT, LOGL_ERROR, "Can not parse transaction id: '%s'\n",
- transaction);
- return;
- }
-
- /* find the answer for the request we made */
- llist_for_each_entry(tmp, &bsc->pending_dlcx, entry) {
- if (trans_id != tmp->trans_id)
- continue;
-
- stat = tmp;
- break;
- }
-
- if (!stat) {
- LOGP(DNAT, LOGL_ERROR,
- "Can not find transaction for: %u\n", trans_id);
- return;
- }
-
- /* attempt to parse the data now */
- rc = mgcp_parse_stats(msg, &b_ps, &b_os, &n_pr, &n_or, &loss, &jitter);
- if (rc != 0)
- LOGP(DNAT, LOGL_ERROR,
- "Can not parse connection statistics: %d\n", rc);
-
- /* send a trap now */
- cmd = ctrl_cmd_create(bsc, CTRL_TYPE_TRAP);
- if (!cmd) {
- LOGP(DNAT, LOGL_ERROR,
- "Creating a ctrl cmd failed.\n");
- goto free_stat;
- }
-
- cmd->id = "0";
- cmd->variable = talloc_asprintf(cmd, "net.0.bsc.%d.call_stats.v2",
- bsc->cfg->nr);
- cmd->reply = talloc_asprintf(cmd,
- "mg_ip_addr=%s,mg_port=%d,",
- inet_ntoa(stat->net_addr),
- stat->net_rtp_port);
- cmd->reply = talloc_asprintf_append(cmd->reply,
- "endpoint_ip_addr=%s,endpoint_port=%d,",
- inet_ntoa(stat->bts_addr),
- stat->bts_rtp_port);
- cmd->reply = talloc_asprintf_append(cmd->reply,
- "nat_pkt_in=%u,nat_pkt_out=%u,"
- "nat_bytes_in=%u,nat_bytes_out=%u,"
- "nat_jitter=%u,nat_pkt_lost=%d,",
- stat->bts_pr, stat->net_ps,
- stat->bts_or, stat->net_os,
- stat->bts_jitter, stat->bts_loss);
- cmd->reply = talloc_asprintf_append(cmd->reply,
- "bsc_pkt_in=%u,bsc_pkt_out=%u,"
- "bsc_bytes_in=%u,bsc_bytes_out=%u,"
- "bsc_jitter=%u,bsc_pkt_lost=%d,",
- n_pr, b_ps,
- n_or, b_os,
- jitter, loss);
- cmd->reply = talloc_asprintf_append(cmd->reply,
- "sccp_src_ref=%u,sccp_dst_ref=%u",
- sccp_src_ref_to_int(&stat->src_ref),
- sccp_src_ref_to_int(&stat->remote_ref));
-
- /* send it and be done */
- ctrl_cmd_send_to_all(bsc->nat->ctrl, cmd);
- talloc_free(cmd);
-
-free_stat:
- bsc->pending_dlcx_count -= 1;
- llist_del(&stat->entry);
- talloc_free(stat);
-}
-
-
-struct nat_sccp_connection *bsc_mgcp_find_con(struct bsc_nat *nat, int endpoint)
-{
- struct nat_sccp_connection *con = NULL;
- struct nat_sccp_connection *sccp;
-
- llist_for_each_entry(sccp, &nat->sccp_connections, list_entry) {
- if (sccp->msc_endp == -1)
- continue;
- if (sccp->msc_endp != endpoint)
- continue;
-
- con = sccp;
- }
-
- if (con)
- return con;
-
- LOGP(DMGCP, LOGL_ERROR,
- "Failed to find the connection for endpoint: 0x%x\n", endpoint);
- return NULL;
-}
-
-static int nat_osmux_only(struct mgcp_config *mgcp_cfg, struct bsc_config *bsc_cfg)
-{
- if (mgcp_cfg->osmux == OSMUX_USAGE_ONLY)
- return 1;
- if (bsc_cfg->osmux == OSMUX_USAGE_ONLY)
- return 1;
- return 0;
-}
-
-static int bsc_mgcp_policy_cb(struct mgcp_trunk_config *tcfg, int endpoint, int state, const char *transaction_id)
-{
- struct bsc_nat *nat;
- struct bsc_endpoint *bsc_endp;
- struct nat_sccp_connection *sccp;
- struct mgcp_endpoint *mgcp_endp;
- struct msgb *bsc_msg;
-
- nat = tcfg->cfg->data;
- bsc_endp = &nat->bsc_endpoints[endpoint];
- mgcp_endp = &nat->mgcp_cfg->trunk.endpoints[endpoint];
-
- if (bsc_endp->transaction_id) {
- LOGP(DMGCP, LOGL_ERROR, "Endpoint 0x%x had pending transaction: '%s'\n",
- endpoint, bsc_endp->transaction_id);
- talloc_free(bsc_endp->transaction_id);
- bsc_endp->transaction_id = NULL;
- bsc_endp->transaction_state = 0;
- }
- bsc_endp->bsc = NULL;
-
- sccp = bsc_mgcp_find_con(nat, endpoint);
-
- if (!sccp) {
- LOGP(DMGCP, LOGL_ERROR, "Did not find BSC for change on endpoint: 0x%x state: %d\n", endpoint, state);
-
- switch (state) {
- case MGCP_ENDP_CRCX:
- return MGCP_POLICY_REJECT;
- break;
- case MGCP_ENDP_DLCX:
- return MGCP_POLICY_CONT;
- break;
- case MGCP_ENDP_MDCX:
- return MGCP_POLICY_CONT;
- break;
- default:
- LOGP(DMGCP, LOGL_FATAL, "Unhandled state: %d\n", state);
- return MGCP_POLICY_CONT;
- break;
- }
- }
-
- /* Allocate a Osmux circuit ID */
- if (state == MGCP_ENDP_CRCX) {
- if (nat->mgcp_cfg->osmux && sccp->bsc->cfg->osmux) {
- osmux_allocate_cid(mgcp_endp);
- if (mgcp_endp->osmux.allocated_cid < 0 &&
- nat_osmux_only(nat->mgcp_cfg, sccp->bsc->cfg)) {
- LOGP(DMGCP, LOGL_ERROR,
- "Rejecting usage of endpoint\n");
- return MGCP_POLICY_REJECT;
- }
- }
- }
-
- /* we need to generate a new and patched message */
- bsc_msg = bsc_mgcp_rewrite((char *) nat->mgcp_msg, nat->mgcp_length,
- sccp->bsc_endp, mgcp_bts_src_addr(mgcp_endp),
- mgcp_endp->bts_end.local_port,
- mgcp_endp->osmux.allocated_cid,
- &mgcp_endp->net_end.codec.payload_type,
- nat->sdp_ensure_amr_mode_set);
- if (!bsc_msg) {
- LOGP(DMGCP, LOGL_ERROR, "Failed to patch the msg.\n");
- return MGCP_POLICY_CONT;
- }
-
-
- bsc_endp->transaction_id = talloc_strdup(nat, transaction_id);
- bsc_endp->transaction_state = state;
- bsc_endp->bsc = sccp->bsc;
-
- /* we need to update some bits */
- if (state == MGCP_ENDP_CRCX) {
- struct sockaddr_in sock;
-
- /* Annotate the allocated Osmux CID until the bsc confirms that
- * it agrees to use Osmux for this voice flow.
- */
- if (mgcp_endp->osmux.allocated_cid >= 0 &&
- mgcp_endp->osmux.state != OSMUX_STATE_ENABLED) {
- mgcp_endp->osmux.state = OSMUX_STATE_NEGOTIATING;
- mgcp_endp->osmux.cid = mgcp_endp->osmux.allocated_cid;
- }
-
- socklen_t len = sizeof(sock);
- if (getpeername(sccp->bsc->write_queue.bfd.fd, (struct sockaddr *) &sock, &len) != 0) {
- LOGP(DMGCP, LOGL_ERROR, "Can not get the peername...%d/%s\n",
- errno, strerror(errno));
- } else {
- mgcp_endp->bts_end.addr = sock.sin_addr;
- }
-
- /* send the message and a fake MDCX to force sending of a dummy packet */
- bsc_write(sccp->bsc, bsc_msg, IPAC_PROTO_MGCP_OLD);
- bsc_mgcp_send_mdcx(sccp->bsc, sccp->bsc_endp, mgcp_endp);
- return MGCP_POLICY_DEFER;
- } else if (state == MGCP_ENDP_DLCX) {
- /* we will free the endpoint now and send a DLCX to the BSC */
- msgb_free(bsc_msg);
- bsc_mgcp_dlcx(sccp);
-
- /* libmgcp clears the MGCP endpoint for us */
- if (mgcp_endp->osmux.state == OSMUX_STATE_ENABLED)
- osmux_release_cid(mgcp_endp);
-
- return MGCP_POLICY_CONT;
- } else {
- bsc_write(sccp->bsc, bsc_msg, IPAC_PROTO_MGCP_OLD);
- return MGCP_POLICY_DEFER;
- }
-}
-
-/*
- * We do have a failure, free data downstream..
- */
-static void free_chan_downstream(struct mgcp_endpoint *endp, struct bsc_endpoint *bsc_endp,
- struct bsc_connection *bsc)
-{
- LOGP(DMGCP, LOGL_ERROR, "No CI, freeing endpoint 0x%x in state %d\n",
- ENDPOINT_NUMBER(endp), bsc_endp->transaction_state);
-
- /* if a CRCX failed... send a DLCX down the stream */
- if (bsc_endp->transaction_state == MGCP_ENDP_CRCX) {
- struct nat_sccp_connection *con;
- con = bsc_mgcp_find_con(bsc->nat, ENDPOINT_NUMBER(endp));
- if (!con) {
- LOGP(DMGCP, LOGL_ERROR,
- "No SCCP connection for endp 0x%x\n",
- ENDPOINT_NUMBER(endp));
- } else {
- if (con->bsc == bsc) {
- bsc_mgcp_send_dlcx(bsc, con->bsc_endp, con->bsc->next_transaction++);
- } else {
- LOGP(DMGCP, LOGL_ERROR,
- "Endpoint belongs to a different BSC\n");
- }
- }
- }
-
- bsc_mgcp_free_endpoint(bsc->nat, ENDPOINT_NUMBER(endp));
- mgcp_release_endp(endp);
-}
-
-static void bsc_mgcp_osmux_confirm(struct mgcp_endpoint *endp, const char *str)
-{
- unsigned int osmux_cid;
- char *res;
-
- res = strstr(str, "X-Osmux: ");
- if (!res) {
- LOGP(DMGCP, LOGL_INFO,
- "BSC doesn't want to use Osmux, failing back to RTP\n");
- goto err;
- }
-
- if (sscanf(res, "X-Osmux: %u", &osmux_cid) != 1) {
- LOGP(DMGCP, LOGL_ERROR, "Failed to parse Osmux CID '%s'\n",
- str);
- goto err;
- }
-
- if (endp->osmux.cid != osmux_cid) {
- LOGP(DMGCP, LOGL_ERROR,
- "BSC sent us wrong CID %u, we expected %u",
- osmux_cid, endp->osmux.cid);
- goto err;
- }
-
- LOGP(DMGCP, LOGL_NOTICE, "bsc accepted to use Osmux (cid=%u)\n",
- osmux_cid);
- endp->osmux.state = OSMUX_STATE_ACTIVATING;
- return;
-err:
- osmux_release_cid(endp);
- endp->osmux.state = OSMUX_STATE_DISABLED;
-}
-
-/*
- * We have received a msg from the BSC. We will see if we know
- * this transaction and if it belongs to the BSC. Then we will
- * need to patch the content to point to the local network and we
- * need to update the I: that was assigned by the BSS.
- *
- * Only responses to CRCX and DLCX should arrive here. The DLCX
- * needs to be handled specially to combine the two statistics.
- */
-void bsc_mgcp_forward(struct bsc_connection *bsc, struct msgb *msg)
-{
- struct msgb *output;
- struct bsc_endpoint *bsc_endp = NULL;
- struct mgcp_endpoint *endp = NULL;
- int i, code;
- char transaction_id[60];
-
- /* Some assumption that our buffer is big enough.. and null terminate */
- if (msgb_l2len(msg) > 2000) {
- LOGP(DMGCP, LOGL_ERROR, "MGCP message too long.\n");
- return;
- }
-
- msg->l2h[msgb_l2len(msg)] = '\0';
-
- if (bsc_mgcp_parse_response((const char *) msg->l2h, &code, transaction_id) != 0) {
- LOGP(DMGCP, LOGL_ERROR, "Failed to parse response code.\n");
- return;
- }
-
- for (i = 1; i < bsc->nat->mgcp_cfg->trunk.number_endpoints; ++i) {
- if (bsc->nat->bsc_endpoints[i].bsc != bsc)
- continue;
- /* no one listening? a bug? */
- if (!bsc->nat->bsc_endpoints[i].transaction_id)
- continue;
- if (strcmp(transaction_id, bsc->nat->bsc_endpoints[i].transaction_id) != 0)
- continue;
-
- endp = &bsc->nat->mgcp_cfg->trunk.endpoints[i];
- bsc_endp = &bsc->nat->bsc_endpoints[i];
- break;
- }
-
- if (!bsc_endp && strncmp("nat-", transaction_id, 4) == 0) {
- handle_dlcx_response(bsc, msg, code, transaction_id);
- return;
- }
-
- if (!bsc_endp) {
- LOGP(DMGCP, LOGL_ERROR, "Could not find active endpoint: %s for msg: '%s'\n",
- transaction_id, (const char *) msg->l2h);
- return;
- }
-
- endp->ci = bsc_mgcp_extract_ci((const char *) msg->l2h);
- if (endp->ci == CI_UNUSED) {
- free_chan_downstream(endp, bsc_endp, bsc);
- return;
- }
-
- if (endp->osmux.state == OSMUX_STATE_NEGOTIATING)
- bsc_mgcp_osmux_confirm(endp, (const char *) msg->l2h);
-
- /* If we require osmux and it is disabled.. fail */
- if (nat_osmux_only(bsc->nat->mgcp_cfg, bsc->cfg) &&
- endp->osmux.state == OSMUX_STATE_DISABLED) {
- LOGP(DMGCP, LOGL_ERROR,
- "Failed to activate osmux endpoint 0x%x\n",
- ENDPOINT_NUMBER(endp));
- free_chan_downstream(endp, bsc_endp, bsc);
- return;
- }
-
- /* free some stuff */
- talloc_free(bsc_endp->transaction_id);
- bsc_endp->transaction_id = NULL;
- bsc_endp->transaction_state = 0;
-
- /*
- * rewrite the information. In case the endpoint was deleted
- * there should be nothing for us to rewrite so putting endp->rtp_port
- * with the value of 0 should be no problem.
- */
- output = bsc_mgcp_rewrite((char * ) msg->l2h, msgb_l2len(msg), -1,
- mgcp_net_src_addr(endp),
- endp->net_end.local_port, -1,
- &endp->bts_end.codec.payload_type,
- bsc->nat->sdp_ensure_amr_mode_set);
- if (!output) {
- LOGP(DMGCP, LOGL_ERROR, "Failed to rewrite MGCP msg.\n");
- return;
- }
-
- mgcp_queue_for_call_agent(bsc->nat, output);
-}
-
-int bsc_mgcp_parse_response(const char *str, int *code, char transaction[60])
-{
- int rc;
- /* we want to parse two strings */
- rc = sscanf(str, "%3d %59s\n", code, transaction) != 2;
- transaction[59] = '\0';
- return rc;
-}
-
-uint32_t bsc_mgcp_extract_ci(const char *str)
-{
- unsigned int ci;
- char *res = strstr(str, "I: ");
- if (!res) {
- LOGP(DMGCP, LOGL_ERROR, "No CI in msg '%s'\n", str);
- return CI_UNUSED;
- }
-
- if (sscanf(res, "I: %u", &ci) != 1) {
- LOGP(DMGCP, LOGL_ERROR, "Failed to parse CI in msg '%s'\n", str);
- return CI_UNUSED;
- }
-
- return ci;
-}
-
-/**
- * Create a new MGCPCommand based on the input and endpoint from a message
- */
-static void patch_mgcp(struct msgb *output, const char *op, const char *tok,
- int endp, int len, int cr, int osmux_cid)
-{
- int slen;
- int ret;
- char buf[40];
- char osmux_extension[strlen("\nX-Osmux: 255") + 1];
-
- buf[0] = buf[39] = '\0';
- ret = sscanf(tok, "%*s %s", buf);
- if (ret != 1) {
- LOGP(DMGCP, LOGL_ERROR,
- "Failed to find Endpoint in: %s\n", tok);
- return;
- }
-
- if (osmux_cid >= 0)
- sprintf(osmux_extension, "\nX-Osmux: %u", osmux_cid & 0xff);
- else
- osmux_extension[0] = '\0';
-
- slen = sprintf((char *) output->l3h, "%s %s %x@mgw MGCP 1.0%s%s",
- op, buf, endp, osmux_extension, cr ? "\r\n" : "\n");
- output->l3h = msgb_put(output, slen);
-}
-
-/* we need to replace some strings... */
-struct msgb *bsc_mgcp_rewrite(char *input, int length, int endpoint,
- const char *ip, int port, int osmux_cid,
- int *first_payload_type, int ensure_mode_set)
-{
- static const char crcx_str[] = "CRCX ";
- static const char dlcx_str[] = "DLCX ";
- static const char mdcx_str[] = "MDCX ";
-
- static const char ip_str[] = "c=IN IP4 ";
- static const char aud_str[] = "m=audio ";
- static const char fmt_str[] = "a=fmtp:";
-
- char buf[128];
- char *running, *token;
- struct msgb *output;
-
- /* keep state to add the a=fmtp line */
- int found_fmtp = 0;
- int payload = -1;
- int cr = 1;
-
- if (length > 4096 - 256) {
- LOGP(DMGCP, LOGL_ERROR, "Input is too long.\n");
- return NULL;
- }
-
- output = msgb_alloc_headroom(4096, 128, "MGCP rewritten");
- if (!output) {
- LOGP(DMGCP, LOGL_ERROR, "Failed to allocate new MGCP msg.\n");
- return NULL;
- }
-
- running = input;
- output->l2h = output->data;
- output->l3h = output->l2h;
- for (token = strsep(&running, "\n"); running; token = strsep(&running, "\n")) {
- int len = strlen(token);
- cr = len > 0 && token[len - 1] == '\r';
-
- if (strncmp(crcx_str, token, (sizeof crcx_str) - 1) == 0) {
- patch_mgcp(output, "CRCX", token, endpoint, len, cr, osmux_cid);
- } else if (strncmp(dlcx_str, token, (sizeof dlcx_str) - 1) == 0) {
- patch_mgcp(output, "DLCX", token, endpoint, len, cr, -1);
- } else if (strncmp(mdcx_str, token, (sizeof mdcx_str) - 1) == 0) {
- patch_mgcp(output, "MDCX", token, endpoint, len, cr, -1);
- } else if (strncmp(ip_str, token, (sizeof ip_str) - 1) == 0) {
- output->l3h = msgb_put(output, strlen(ip_str));
- memcpy(output->l3h, ip_str, strlen(ip_str));
- output->l3h = msgb_put(output, strlen(ip));
- memcpy(output->l3h, ip, strlen(ip));
-
- if (cr) {
- output->l3h = msgb_put(output, 2);
- output->l3h[0] = '\r';
- output->l3h[1] = '\n';
- } else {
- output->l3h = msgb_put(output, 1);
- output->l3h[0] = '\n';
- }
- } else if (strncmp(aud_str, token, (sizeof aud_str) - 1) == 0) {
- int offset;
- if (sscanf(token, "m=audio %*d RTP/AVP %n%d", &offset, &payload) != 1) {
- LOGP(DMGCP, LOGL_ERROR, "Could not parsed audio line.\n");
- msgb_free(output);
- return NULL;
- }
-
- snprintf(buf, sizeof(buf)-1, "m=audio %d RTP/AVP %s\n",
- port, &token[offset]);
- buf[sizeof(buf)-1] = '\0';
-
- output->l3h = msgb_put(output, strlen(buf));
- memcpy(output->l3h, buf, strlen(buf));
- } else if (strncmp(fmt_str, token, (sizeof fmt_str) - 1) == 0) {
- found_fmtp = 1;
- goto copy;
- } else {
-copy:
- output->l3h = msgb_put(output, len + 1);
- memcpy(output->l3h, token, len);
- output->l3h[len] = '\n';
- }
- }
-
- /*
- * the above code made sure that we have 128 bytes lefts. So we can
- * safely append another line.
- */
- if (ensure_mode_set && !found_fmtp && payload != -1) {
- snprintf(buf, sizeof(buf) - 1, "a=fmtp:%d mode-set=2%s",
- payload, cr ? "\r\n" : "\n");
- buf[sizeof(buf) - 1] = '\0';
- output->l3h = msgb_put(output, strlen(buf));
- memcpy(output->l3h, buf, strlen(buf));
- }
-
- if (payload != -1 && first_payload_type)
- *first_payload_type = payload;
-
- return output;
-}
-
-/*
- * This comes from the MSC and we will now parse it. The caller needs
- * to free the msgb.
- */
-void bsc_nat_handle_mgcp(struct bsc_nat *nat, struct msgb *msg)
-{
- struct msgb *resp;
-
- if (!nat->mgcp_ipa) {
- LOGP(DMGCP, LOGL_ERROR, "MGCP message not allowed on IPA.\n");
- return;
- }
-
- if (msgb_l2len(msg) > sizeof(nat->mgcp_msg) - 1) {
- LOGP(DMGCP, LOGL_ERROR, "MGCP msg too big for handling.\n");
- return;
- }
-
- memcpy(nat->mgcp_msg, msg->l2h, msgb_l2len(msg));
- nat->mgcp_length = msgb_l2len(msg);
- nat->mgcp_msg[nat->mgcp_length] = '\0';
-
- /* now handle the message */
- resp = mgcp_handle_message(nat->mgcp_cfg, msg);
-
- /* we do have a direct answer... e.g. AUEP */
- if (resp)
- mgcp_queue_for_call_agent(nat, resp);
-
- return;
-}
-
-static int mgcp_do_read(struct osmo_fd *fd)
-{
- struct bsc_nat *nat;
- struct msgb *msg, *resp;
- int rc;
-
- nat = fd->data;
-
- rc = read(fd->fd, nat->mgcp_msg, sizeof(nat->mgcp_msg) - 1);
- if (rc <= 0) {
- LOGP(DMGCP, LOGL_ERROR, "Failed to read errno: %d\n", errno);
- return -1;
- }
-
- nat->mgcp_msg[rc] = '\0';
- nat->mgcp_length = rc;
-
- msg = msgb_alloc(sizeof(nat->mgcp_msg), "MGCP GW Read");
- if (!msg) {
- LOGP(DMGCP, LOGL_ERROR, "Failed to create buffer.\n");
- return -1;
- }
-
- msg->l2h = msgb_put(msg, rc);
- memcpy(msg->l2h, nat->mgcp_msg, msgb_l2len(msg));
- resp = mgcp_handle_message(nat->mgcp_cfg, msg);
- msgb_free(msg);
-
- /* we do have a direct answer... e.g. AUEP */
- if (resp)
- mgcp_queue_for_call_agent(nat, resp);
-
- return 0;
-}
-
-static int mgcp_do_write(struct osmo_fd *bfd, struct msgb *msg)
-{
- int rc;
-
- rc = write(bfd->fd, msg->data, msg->len);
-
- if (rc != msg->len) {
- LOGP(DMGCP, LOGL_ERROR, "Failed to write msg to MGCP CallAgent.\n");
- return -1;
- }
-
- return rc;
-}
-
-static int init_mgcp_socket(struct bsc_nat *nat, struct mgcp_config *cfg)
-{
- struct sockaddr_in addr;
- int on;
-
- cfg->gw_fd.bfd.fd = socket(AF_INET, SOCK_DGRAM, 0);
- if (cfg->gw_fd.bfd.fd < 0) {
- LOGP(DMGCP, LOGL_ERROR, "Failed to create MGCP socket. errno: %d\n", errno);
- return -1;
- }
-
- on = 1;
- setsockopt(cfg->gw_fd.bfd.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
-
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_port = htons(cfg->source_port);
- inet_aton(cfg->source_addr, &addr.sin_addr);
-
- if (bind(cfg->gw_fd.bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- LOGP(DMGCP, LOGL_ERROR, "Failed to bind on %s:%d errno: %d\n",
- cfg->source_addr, cfg->source_port, errno);
- close(cfg->gw_fd.bfd.fd);
- cfg->gw_fd.bfd.fd = -1;
- return -1;
- }
-
- addr.sin_port = htons(2727);
- inet_aton(cfg->call_agent_addr, &addr.sin_addr);
- if (connect(cfg->gw_fd.bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- LOGP(DMGCP, LOGL_ERROR, "Failed to connect to: '%s'. errno: %d\n",
- cfg->call_agent_addr, errno);
- close(cfg->gw_fd.bfd.fd);
- cfg->gw_fd.bfd.fd = -1;
- return -1;
- }
-
- osmo_wqueue_init(&cfg->gw_fd, 10);
- cfg->gw_fd.bfd.when = BSC_FD_READ;
- cfg->gw_fd.bfd.data = nat;
- cfg->gw_fd.read_cb = mgcp_do_read;
- cfg->gw_fd.write_cb = mgcp_do_write;
-
- if (osmo_fd_register(&cfg->gw_fd.bfd) != 0) {
- LOGP(DMGCP, LOGL_ERROR, "Failed to register MGCP fd.\n");
- close(cfg->gw_fd.bfd.fd);
- cfg->gw_fd.bfd.fd = -1;
- return -1;
- }
-
- return 0;
-}
-
-int bsc_mgcp_nat_init(struct bsc_nat *nat)
-{
- struct mgcp_config *cfg = nat->mgcp_cfg;
-
- if (!cfg->call_agent_addr) {
- LOGP(DMGCP, LOGL_ERROR, "The BSC nat requires the call agent ip to be set.\n");
- return -1;
- }
-
- if (cfg->bts_ip) {
- LOGP(DMGCP, LOGL_ERROR, "Do not set the BTS ip for the nat.\n");
- return -1;
- }
-
- /* initialize the MGCP socket */
- if (!nat->mgcp_ipa) {
- int rc = init_mgcp_socket(nat, cfg);
- if (rc != 0)
- return rc;
- }
-
-
- /* some more MGCP config handling */
- cfg->data = nat;
- cfg->policy_cb = bsc_mgcp_policy_cb;
- cfg->trunk.force_realloc = 1;
-
- if (cfg->bts_ip)
- talloc_free(cfg->bts_ip);
- cfg->bts_ip = "";
-
- nat->bsc_endpoints = talloc_zero_array(nat,
- struct bsc_endpoint,
- cfg->trunk.number_endpoints + 1);
- if (!nat->bsc_endpoints) {
- LOGP(DMGCP, LOGL_ERROR, "Failed to allocate nat endpoints\n");
- close(cfg->gw_fd.bfd.fd);
- cfg->gw_fd.bfd.fd = -1;
- return -1;
- }
-
- if (mgcp_reset_transcoder(cfg) < 0) {
- LOGP(DMGCP, LOGL_ERROR, "Failed to send packet to the transcoder.\n");
- talloc_free(nat->bsc_endpoints);
- nat->bsc_endpoints = NULL;
- close(cfg->gw_fd.bfd.fd);
- cfg->gw_fd.bfd.fd = -1;
- return -1;
- }
-
- return 0;
-}
-
-void bsc_mgcp_clear_endpoints_for(struct bsc_connection *bsc)
-{
- struct rate_ctr *ctr = NULL;
- int i;
-
- if (bsc->cfg)
- ctr = &bsc->cfg->stats.ctrg->ctr[BCFG_CTR_DROPPED_CALLS];
-
- for (i = 1; i < bsc->nat->mgcp_cfg->trunk.number_endpoints; ++i) {
- struct bsc_endpoint *bsc_endp = &bsc->nat->bsc_endpoints[i];
-
- if (bsc_endp->bsc != bsc)
- continue;
-
- if (ctr)
- rate_ctr_inc(ctr);
-
- bsc_mgcp_free_endpoint(bsc->nat, i);
- mgcp_release_endp(&bsc->nat->mgcp_cfg->trunk.endpoints[i]);
- }
-}
diff --git a/openbsc/src/osmo-bsc_nat/bsc_nat.c b/openbsc/src/osmo-bsc_nat/bsc_nat.c
deleted file mode 100644
index daa066d05..000000000
--- a/openbsc/src/osmo-bsc_nat/bsc_nat.c
+++ /dev/null
@@ -1,1736 +0,0 @@
-/* BSC Multiplexer/NAT */
-
-/*
- * (C) 2010-2013 by Holger Hans Peter Freyther <zecke@selfish.org>
- * (C) 2010-2013 by On-Waves
- * (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 <sys/socket.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-#include <arpa/inet.h>
-
-#include <errno.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <time.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <libgen.h>
-
-#define _GNU_SOURCE
-#include <getopt.h>
-
-#include <openbsc/debug.h>
-#include <openbsc/bsc_msc.h>
-#include <openbsc/bsc_nat.h>
-#include <openbsc/bsc_nat_sccp.h>
-#include <openbsc/bsc_msg_filter.h>
-#include <openbsc/ipaccess.h>
-#include <openbsc/abis_nm.h>
-#include <openbsc/socket.h>
-#include <openbsc/vty.h>
-
-#include <osmocom/ctrl/control_cmd.h>
-#include <osmocom/ctrl/control_if.h>
-#include <osmocom/ctrl/ports.h>
-#include <osmocom/ctrl/control_vty.h>
-
-#include <osmocom/crypt/auth.h>
-
-#include <osmocom/core/application.h>
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/stats.h>
-
-#include <osmocom/gsm/tlv.h>
-#include <osmocom/gsm/gsm0808.h>
-#include <osmocom/gsm/protocol/gsm_08_08.h>
-
-#include <osmocom/vty/telnet_interface.h>
-#include <osmocom/vty/vty.h>
-#include <osmocom/vty/logging.h>
-#include <osmocom/vty/stats.h>
-#include <osmocom/vty/ports.h>
-
-#include <osmocom/sccp/sccp.h>
-
-#include <osmocom/abis/ipa.h>
-
-#include <openssl/rand.h>
-
-#include "../../bscconfig.h"
-
-#define SCCP_CLOSE_TIME 20
-#define SCCP_CLOSE_TIME_TIMEOUT 19
-
-static const char *config_file = "bsc-nat.cfg";
-static struct in_addr local_addr;
-static struct osmo_fd bsc_listen;
-static const char *msc_ip = NULL;
-static struct osmo_timer_list sccp_close;
-static int daemonize = 0;
-
-const char *openbsc_copyright =
- "Copyright (C) 2010 Holger Hans Peter Freyther and On-Waves\r\n"
- "License AGPLv3+: GNU AGPL version 3 or later <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 bsc_nat *nat;
-static void bsc_send_data(struct bsc_connection *bsc, const uint8_t *data, unsigned int length, int);
-static void msc_send_reset(struct bsc_msc_connection *con);
-static void bsc_stat_reject(int filter, struct bsc_connection *bsc, int normal);
-
-struct bsc_config *bsc_config_num(struct bsc_nat *nat, int num)
-{
- struct bsc_config *conf;
-
- llist_for_each_entry(conf, &nat->bsc_configs, entry)
- if (conf->nr == num)
- return conf;
-
- return NULL;
-}
-
-static void queue_for_msc(struct bsc_msc_connection *con, struct msgb *msg)
-{
- if (!con) {
- LOGP(DLINP, LOGL_ERROR, "No MSC Connection assigned. Check your code.\n");
- msgb_free(msg);
- return;
- }
-
-
- if (osmo_wqueue_enqueue(&con->write_queue, msg) != 0) {
- LOGP(DLINP, LOGL_ERROR, "Failed to enqueue the write.\n");
- msgb_free(msg);
- }
-}
-
-static void send_reset_ack(struct bsc_connection *bsc)
-{
- static const uint8_t gsm_reset_ack[] = {
- 0x09, 0x00, 0x03, 0x07, 0x0b, 0x04, 0x43, 0x01,
- 0x00, 0xfe, 0x04, 0x43, 0x5c, 0x00, 0xfe, 0x03,
- 0x00, 0x01, 0x31,
- };
-
- bsc_send_data(bsc, gsm_reset_ack, sizeof(gsm_reset_ack), IPAC_PROTO_SCCP);
-}
-
-static void send_ping(struct bsc_connection *bsc)
-{
- static const uint8_t id_ping[] = {
- IPAC_MSGT_PING,
- };
-
- bsc_send_data(bsc, id_ping, sizeof(id_ping), IPAC_PROTO_IPACCESS);
-}
-
-static void send_pong(struct bsc_connection *bsc)
-{
- static const uint8_t id_pong[] = {
- IPAC_MSGT_PONG,
- };
-
- bsc_send_data(bsc, id_pong, sizeof(id_pong), IPAC_PROTO_IPACCESS);
-}
-
-static void bsc_pong_timeout(void *_bsc)
-{
- struct bsc_connection *bsc = _bsc;
-
- LOGP(DNAT, LOGL_ERROR, "BSC Nr: %d PONG timeout.\n", bsc->cfg->nr);
- bsc_close_connection(bsc);
-}
-
-static void bsc_ping_timeout(void *_bsc)
-{
- struct bsc_connection *bsc = _bsc;
-
- if (bsc->nat->ping_timeout < 0)
- return;
-
- send_ping(bsc);
-
- /* send another ping in 20 seconds */
- osmo_timer_schedule(&bsc->ping_timeout, bsc->nat->ping_timeout, 0);
-
- /* also start a pong timer */
- osmo_timer_schedule(&bsc->pong_timeout, bsc->nat->pong_timeout, 0);
-}
-
-static void start_ping_pong(struct bsc_connection *bsc)
-{
- osmo_timer_setup(&bsc->pong_timeout, bsc_pong_timeout, bsc);
- osmo_timer_setup(&bsc->ping_timeout, bsc_ping_timeout, bsc);
-
- bsc_ping_timeout(bsc);
-}
-
-static void send_id_ack(struct bsc_connection *bsc)
-{
- static const uint8_t id_ack[] = {
- IPAC_MSGT_ID_ACK
- };
-
- bsc_send_data(bsc, id_ack, sizeof(id_ack), IPAC_PROTO_IPACCESS);
-}
-
-static void send_id_req(struct bsc_nat *nat, struct bsc_connection *bsc)
-{
- static const uint8_t s_id_req[] = {
- IPAC_MSGT_ID_GET,
- 0x01, IPAC_IDTAG_UNIT,
- 0x01, IPAC_IDTAG_MACADDR,
- 0x01, IPAC_IDTAG_LOCATION1,
- 0x01, IPAC_IDTAG_LOCATION2,
- 0x01, IPAC_IDTAG_EQUIPVERS,
- 0x01, IPAC_IDTAG_SWVERSION,
- 0x01, IPAC_IDTAG_UNITNAME,
- 0x01, IPAC_IDTAG_SERNR,
- };
-
- uint8_t *mrand;
- uint8_t id_req[sizeof(s_id_req) + (2+16)];
- uint8_t *buf = &id_req[sizeof(s_id_req)];
-
- /* copy the static data */
- memcpy(id_req, s_id_req, sizeof(s_id_req));
-
- /* put the RAND with length, tag, value */
- buf = v_put(buf, 0x11);
- buf = v_put(buf, 0x23);
- mrand = bsc->last_rand;
-
- if (RAND_bytes(mrand, 16) != 1)
- goto failed_random;
-
- memcpy(buf, mrand, 16);
- buf += 16;
-
- bsc_send_data(bsc, id_req, sizeof(id_req), IPAC_PROTO_IPACCESS);
- return;
-
-failed_random:
- /* the timeout will trigger and close this connection */
- LOGP(DNAT, LOGL_ERROR, "Failed to read from urandom.\n");
- return;
-}
-
-static struct msgb *nat_create_rlsd(struct nat_sccp_connection *conn)
-{
- struct sccp_connection_released *rel;
- struct msgb *msg;
-
- msg = msgb_alloc_headroom(4096, 128, "rlsd");
- if (!msg) {
- LOGP(DNAT, LOGL_ERROR, "Failed to allocate released.\n");
- return NULL;
- }
-
- msg->l2h = msgb_put(msg, sizeof(*rel));
- rel = (struct sccp_connection_released *) msg->l2h;
- rel->type = SCCP_MSG_TYPE_RLSD;
- rel->release_cause = SCCP_RELEASE_CAUSE_SCCP_FAILURE;
- rel->destination_local_reference = conn->remote_ref;
- rel->source_local_reference = conn->patched_ref;
-
- return msg;
-}
-
-static void nat_send_rlsd_ussd(struct bsc_nat *nat, struct nat_sccp_connection *conn)
-{
- struct msgb *msg;
-
- if (!nat->ussd_con)
- return;
-
- msg = nat_create_rlsd(conn);
- if (!msg)
- return;
-
- bsc_do_write(&nat->ussd_con->queue, msg, IPAC_PROTO_SCCP);
-}
-
-static void nat_send_rlsd_msc(struct nat_sccp_connection *conn)
-{
- struct msgb *msg;
-
- msg = nat_create_rlsd(conn);
- if (!msg)
- return;
-
- ipa_prepend_header(msg, IPAC_PROTO_SCCP);
- queue_for_msc(conn->msc_con, msg);
-}
-
-static void nat_send_rlsd_bsc(struct nat_sccp_connection *conn)
-{
- struct msgb *msg;
- struct sccp_connection_released *rel;
-
- msg = msgb_alloc_headroom(4096, 128, "rlsd");
- if (!msg) {
- LOGP(DNAT, LOGL_ERROR, "Failed to allocate clear command.\n");
- return;
- }
-
- msg->l2h = msgb_put(msg, sizeof(*rel));
- rel = (struct sccp_connection_released *) msg->l2h;
- rel->type = SCCP_MSG_TYPE_RLSD;
- rel->release_cause = SCCP_RELEASE_CAUSE_SCCP_FAILURE;
- rel->destination_local_reference = conn->real_ref;
- rel->source_local_reference = conn->remote_ref;
-
- bsc_write(conn->bsc, msg, IPAC_PROTO_SCCP);
-}
-
-static struct msgb *nat_creat_clrc(struct nat_sccp_connection *conn, uint8_t cause)
-{
- struct msgb *msg;
- struct msgb *sccp;
-
- msg = gsm0808_create_clear_command(cause);
- if (!msg) {
- LOGP(DNAT, LOGL_ERROR, "Failed to allocate clear command.\n");
- return NULL;
- }
-
- sccp = sccp_create_dt1(&conn->real_ref, msg->data, msg->len);
- if (!sccp) {
- LOGP(DNAT, LOGL_ERROR, "Failed to allocate SCCP msg.\n");
- msgb_free(msg);
- return NULL;
- }
-
- msgb_free(msg);
- return sccp;
-}
-
-static int nat_send_clrc_bsc(struct nat_sccp_connection *conn)
-{
- struct msgb *sccp;
-
- sccp = nat_creat_clrc(conn, 0x20);
- if (!sccp)
- return -1;
- return bsc_write(conn->bsc, sccp, IPAC_PROTO_SCCP);
-}
-
-static void nat_send_rlc(struct bsc_msc_connection *msc_con,
- struct sccp_source_reference *src,
- struct sccp_source_reference *dst)
-{
- struct sccp_connection_release_complete *rlc;
- struct msgb *msg;
-
- msg = msgb_alloc_headroom(4096, 128, "rlc");
- if (!msg) {
- LOGP(DNAT, LOGL_ERROR, "Failed to sccp rlc.\n");
- return;
- }
-
- msg->l2h = msgb_put(msg, sizeof(*rlc));
- rlc = (struct sccp_connection_release_complete *) msg->l2h;
- rlc->type = SCCP_MSG_TYPE_RLC;
- rlc->destination_local_reference = *dst;
- rlc->source_local_reference = *src;
-
- ipa_prepend_header(msg, IPAC_PROTO_SCCP);
-
- queue_for_msc(msc_con, msg);
-}
-
-static void send_mgcp_reset(struct bsc_connection *bsc)
-{
- static const uint8_t mgcp_reset[] = {
- "RSIP 1 13@mgw MGCP 1.0\r\n"
- };
-
- bsc_write_mgcp(bsc, mgcp_reset, sizeof mgcp_reset - 1);
-}
-
-void bsc_nat_send_mgcp_to_msc(struct bsc_nat *nat, struct msgb *msg)
-{
- ipa_prepend_header(msg, IPAC_PROTO_MGCP_OLD);
- queue_for_msc(nat->msc_con, msg);
-}
-
-/*
- * Below is the handling of messages coming
- * from the MSC and need to be forwarded to
- * a real BSC.
- */
-static void initialize_msc_if_needed(struct bsc_msc_connection *msc_con)
-{
- if (msc_con->first_contact)
- return;
-
- msc_con->first_contact = 1;
- msc_send_reset(msc_con);
-}
-
-static void send_id_get_response(struct bsc_msc_connection *msc_con)
-{
- struct msgb *msg = bsc_msc_id_get_resp(0, nat->token, NULL, 0);
- if (!msg)
- return;
-
- ipa_prepend_header(msg, IPAC_PROTO_IPACCESS);
- queue_for_msc(msc_con, msg);
-}
-
-/*
- * Currently we are lacking refcounting so we need to copy each message.
- */
-static void bsc_send_data(struct bsc_connection *bsc, const uint8_t *data, unsigned int length, int proto)
-{
- struct msgb *msg;
-
- if (length > 4096 - 128) {
- LOGP(DLINP, LOGL_ERROR, "Can not send message of that size.\n");
- return;
- }
-
- msg = msgb_alloc_headroom(4096, 128, "to-bsc");
- if (!msg) {
- LOGP(DLINP, LOGL_ERROR, "Failed to allocate memory for BSC msg.\n");
- return;
- }
-
- msg->l2h = msgb_put(msg, length);
- memcpy(msg->data, data, length);
-
- bsc_write(bsc, msg, proto);
-}
-
-/*
- * Update the release statistics
- */
-static void bsc_stat_reject(int filter, struct bsc_connection *bsc, int normal)
-{
- if (!bsc->cfg) {
- LOGP(DNAT, LOGL_ERROR, "BSC is not authenticated.");
- return;
- }
-
- if (filter >= 0) {
- LOGP(DNAT, LOGL_ERROR, "Connection was not rejected");
- return;
- }
-
- if (filter == -1)
- rate_ctr_inc(&bsc->cfg->stats.ctrg->ctr[BCFG_CTR_ILL_PACKET]);
- else if (normal)
- rate_ctr_inc(&bsc->cfg->stats.ctrg->ctr[BCFG_CTR_REJECTED_MSG]);
- else
- rate_ctr_inc(&bsc->cfg->stats.ctrg->ctr[BCFG_CTR_REJECTED_CR]);
-}
-
-/*
- * Release an established connection. We will have to release it to the BSC
- * and to the network and we do it the following way.
- * 1.) Give up on the MSC side
- * 1.1) Send a RLSD message, it is a bit non standard but should work, we
- * ignore the RLC... we might complain about it. Other options would
- * be to send a Release Request, handle the Release Complete..
- * 1.2) Mark the data structure to be con_local and wait for 2nd
- *
- * 2.) Give up on the BSC side
- * 2.1) Depending on the con type reject the service, or just close it
- */
-static void bsc_send_con_release(struct bsc_connection *bsc,
- struct nat_sccp_connection *con,
- struct bsc_filter_reject_cause *cause)
-{
- struct msgb *rlsd;
- /* 1. release the network */
- rlsd = sccp_create_rlsd(&con->patched_ref, &con->remote_ref,
- SCCP_RELEASE_CAUSE_END_USER_ORIGINATED);
- if (!rlsd)
- LOGP(DNAT, LOGL_ERROR, "Failed to create RLSD message.\n");
- else {
- ipa_prepend_header(rlsd, IPAC_PROTO_SCCP);
- queue_for_msc(con->msc_con, rlsd);
- }
- con->con_local = NAT_CON_END_LOCAL;
- con->msc_con = NULL;
-
- /* 2. release the BSC side */
- if (con->filter_state.con_type == FLT_CON_TYPE_LU) {
- struct msgb *payload, *udt;
- payload = gsm48_create_loc_upd_rej(cause->lu_reject_cause);
-
- if (payload) {
- gsm0808_prepend_dtap_header(payload, 0);
- udt = sccp_create_dt1(&con->real_ref, payload->data, payload->len);
- if (udt)
- bsc_write(bsc, udt, IPAC_PROTO_SCCP);
- else
- LOGP(DNAT, LOGL_ERROR, "Failed to create DT1\n");
-
- msgb_free(payload);
- } else {
- LOGP(DNAT, LOGL_ERROR, "Failed to allocate LU Reject.\n");
- }
- }
-
- nat_send_clrc_bsc(con);
-
- rlsd = sccp_create_rlsd(&con->remote_ref, &con->real_ref,
- SCCP_RELEASE_CAUSE_END_USER_ORIGINATED);
- if (!rlsd) {
- LOGP(DNAT, LOGL_ERROR, "Failed to allocate RLSD for the BSC.\n");
- sccp_connection_destroy(con);
- return;
- }
-
- con->filter_state.con_type = FLT_CON_TYPE_LOCAL_REJECT;
- bsc_write(bsc, rlsd, IPAC_PROTO_SCCP);
-}
-
-static void bsc_send_con_refuse(struct bsc_connection *bsc,
- struct bsc_nat_parsed *parsed, int con_type,
- struct bsc_filter_reject_cause *cause)
-{
- struct msgb *payload;
- struct msgb *refuse;
-
- if (con_type == FLT_CON_TYPE_LU)
- payload = gsm48_create_loc_upd_rej(cause->lu_reject_cause);
- else if (con_type == FLT_CON_TYPE_CM_SERV_REQ || con_type == FLT_CON_TYPE_SSA)
- payload = gsm48_create_mm_serv_rej(cause->cm_reject_cause);
- else {
- LOGP(DNAT, LOGL_ERROR, "Unknown connection type: %d\n", con_type);
- payload = NULL;
- }
-
- /*
- * Some BSCs do not handle the payload inside a SCCP CREF msg
- * so we will need to:
- * 1.) Allocate a local connection and mark it as local..
- * 2.) queue data for downstream.. and the RLC should delete everything
- */
- if (payload) {
- struct msgb *cc, *udt, *clear, *rlsd;
- struct nat_sccp_connection *con;
- con = create_sccp_src_ref(bsc, parsed);
- if (!con)
- goto send_refuse;
-
- /* declare it local and assign a unique remote_ref */
- con->filter_state.con_type = FLT_CON_TYPE_LOCAL_REJECT;
- con->con_local = NAT_CON_END_LOCAL;
- con->has_remote_ref = 1;
- con->remote_ref = con->patched_ref;
-
- /* 1. create a confirmation */
- cc = sccp_create_cc(&con->remote_ref, &con->real_ref);
- if (!cc)
- goto send_refuse;
-
- /* 2. create the DT1 */
- gsm0808_prepend_dtap_header(payload, 0);
- udt = sccp_create_dt1(&con->real_ref, payload->data, payload->len);
- if (!udt) {
- msgb_free(cc);
- goto send_refuse;
- }
-
- /* 3. send a Clear Command */
- clear = nat_creat_clrc(con, 0x20);
- if (!clear) {
- msgb_free(cc);
- msgb_free(udt);
- goto send_refuse;
- }
-
- /* 4. send a RLSD */
- rlsd = sccp_create_rlsd(&con->remote_ref, &con->real_ref,
- SCCP_RELEASE_CAUSE_END_USER_ORIGINATED);
- if (!rlsd) {
- msgb_free(cc);
- msgb_free(udt);
- msgb_free(clear);
- goto send_refuse;
- }
-
- bsc_write(bsc, cc, IPAC_PROTO_SCCP);
- bsc_write(bsc, udt, IPAC_PROTO_SCCP);
- bsc_write(bsc, clear, IPAC_PROTO_SCCP);
- bsc_write(bsc, rlsd, IPAC_PROTO_SCCP);
- msgb_free(payload);
- return;
- }
-
-
-send_refuse:
- if (payload)
- msgb_free(payload);
-
- refuse = sccp_create_refuse(parsed->src_local_ref,
- SCCP_REFUSAL_SCCP_FAILURE, NULL, 0);
- if (!refuse) {
- LOGP(DNAT, LOGL_ERROR,
- "Creating refuse msg failed for SCCP 0x%x on BSC Nr: %d.\n",
- sccp_src_ref_to_int(parsed->src_local_ref), bsc->cfg->nr);
- return;
- }
-
- bsc_write(bsc, refuse, IPAC_PROTO_SCCP);
-}
-
-static void bsc_nat_send_paging(struct bsc_connection *bsc, struct msgb *msg)
-{
- if (bsc->cfg->forbid_paging) {
- LOGP(DNAT, LOGL_DEBUG, "Paging forbidden for BTS: %d\n", bsc->cfg->nr);
- return;
- }
-
- bsc_send_data(bsc, msg->l2h, msgb_l2len(msg), IPAC_PROTO_SCCP);
-}
-
-static void bsc_nat_handle_paging(struct bsc_nat *nat, struct msgb *msg)
-{
- struct bsc_connection *bsc;
- const uint8_t *paging_start;
- int paging_length, i, ret;
-
- ret = bsc_nat_find_paging(msg, &paging_start, &paging_length);
- if (ret != 0) {
- LOGP(DNAT, LOGL_ERROR, "Could not parse paging message: %d\n", ret);
- return;
- }
-
- /* This is quite expensive now */
- for (i = 0; i < paging_length; i += 2) {
- unsigned int _lac = ntohs(*(unsigned int *) &paging_start[i]);
- unsigned int paged = 0;
- llist_for_each_entry(bsc, &nat->bsc_connections, list_entry) {
- if (!bsc->cfg)
- continue;
- if (!bsc->authenticated)
- continue;
- if (!bsc_config_handles_lac(bsc->cfg, _lac))
- continue;
- bsc_nat_send_paging(bsc, msg);
- paged += 1;
- }
-
- /* highlight a possible config issue */
- if (paged == 0)
- LOGP(DNAT, LOGL_ERROR, "No BSC for LAC %d/0x%d\n", _lac, _lac);
-
- }
-}
-
-
-/*
- * Update the auth status. This can be either a CIPHER MODE COMMAND or
- * a CM Serivce Accept. Maybe also LU Accept or such in the future.
- */
-static void update_con_authorize(struct nat_sccp_connection *con,
- struct bsc_nat_parsed *parsed,
- struct msgb *msg)
-{
- if (!con)
- return;
- if (con->authorized)
- return;
-
- if (parsed->bssap == BSSAP_MSG_BSS_MANAGEMENT &&
- parsed->gsm_type == BSS_MAP_MSG_CIPHER_MODE_CMD) {
- con->authorized = 1;
- } else if (parsed->bssap == BSSAP_MSG_DTAP) {
- uint8_t msg_type, proto;
- uint32_t len;
- struct gsm48_hdr *hdr48;
- hdr48 = bsc_unpack_dtap(parsed, msg, &len);
- if (!hdr48)
- return;
-
- proto = gsm48_hdr_pdisc(hdr48);
- msg_type = gsm48_hdr_msg_type(hdr48);
- if (proto == GSM48_PDISC_MM &&
- msg_type == GSM48_MT_MM_CM_SERV_ACC)
- con->authorized = 1;
- }
-}
-
-static int forward_sccp_to_bts(struct bsc_msc_connection *msc_con, struct msgb *msg)
-{
- struct nat_sccp_connection *con = NULL;
- struct bsc_connection *bsc;
- struct bsc_nat_parsed *parsed;
- int proto;
-
- /* filter, drop, patch the message? */
- parsed = bsc_nat_parse(msg);
- if (!parsed) {
- LOGP(DNAT, LOGL_ERROR, "Can not parse msg from BSC.\n");
- return -1;
- }
-
- if (bsc_nat_filter_ipa(DIR_BSC, msg, parsed))
- goto exit;
-
- proto = parsed->ipa_proto;
-
- /* Route and modify the SCCP packet */
- if (proto == IPAC_PROTO_SCCP) {
- switch (parsed->sccp_type) {
- case SCCP_MSG_TYPE_UDT:
- /* forward UDT messages to every BSC */
- goto send_to_all;
- break;
- case SCCP_MSG_TYPE_RLSD:
- case SCCP_MSG_TYPE_CREF:
- case SCCP_MSG_TYPE_DT1:
- case SCCP_MSG_TYPE_IT:
- con = patch_sccp_src_ref_to_bsc(msg, parsed, nat);
- if (parsed->gsm_type == BSS_MAP_MSG_ASSIGMENT_RQST) {
- osmo_counter_inc(nat->stats.sccp.calls);
-
- if (con) {
- struct rate_ctr_group *ctrg;
- ctrg = con->bsc->cfg->stats.ctrg;
- rate_ctr_inc(&ctrg->ctr[BCFG_CTR_SCCP_CALLS]);
- if (bsc_mgcp_assign_patch(con, msg) != 0)
- LOGP(DNAT, LOGL_ERROR, "Failed to assign...\n");
- } else
- LOGP(DNAT, LOGL_ERROR, "Assignment command but no BSC.\n");
- } else if (con && con->con_local == NAT_CON_END_USSD &&
- parsed->gsm_type == BSS_MAP_MSG_CLEAR_CMD) {
- LOGP(DNAT, LOGL_NOTICE, "Clear Command for USSD Connection. Ignoring.\n");
- con = NULL;
- }
- break;
- case SCCP_MSG_TYPE_CC:
- con = patch_sccp_src_ref_to_bsc(msg, parsed, nat);
- if (!con || update_sccp_src_ref(con, parsed) != 0)
- goto exit;
- break;
- case SCCP_MSG_TYPE_RLC:
- LOGP(DNAT, LOGL_ERROR, "Unexpected release complete from MSC.\n");
- goto exit;
- break;
- case SCCP_MSG_TYPE_CR:
- /* MSC never opens a SCCP connection, fall through */
- default:
- goto exit;
- }
-
- if (!con && parsed->sccp_type == SCCP_MSG_TYPE_RLSD) {
- LOGP(DNAT, LOGL_NOTICE, "Sending fake RLC on RLSD message to network.\n");
- /* Exchange src/dest for the reply */
- nat_send_rlc(msc_con, &parsed->original_dest_ref,
- parsed->src_local_ref);
- } else if (!con)
- LOGP(DNAT, LOGL_ERROR, "Unknown connection for msg type: 0x%x from the MSC.\n", parsed->sccp_type);
- }
-
- if (!con) {
- talloc_free(parsed);
- return -1;
- }
- if (!con->bsc->authenticated) {
- talloc_free(parsed);
- LOGP(DNAT, LOGL_ERROR, "Selected BSC not authenticated.\n");
- return -1;
- }
-
- update_con_authorize(con, parsed, msg);
- talloc_free(parsed);
-
- bsc_send_data(con->bsc, msg->l2h, msgb_l2len(msg), proto);
- return 0;
-
-send_to_all:
- /*
- * Filter Paging from the network. We do not want to send a PAGING
- * Command to every BSC in our network. We will analys the PAGING
- * message and then send it to the authenticated messages...
- */
- if (parsed->ipa_proto == IPAC_PROTO_SCCP && parsed->gsm_type == BSS_MAP_MSG_PAGING) {
- bsc_nat_handle_paging(nat, msg);
- goto exit;
- }
- /* currently send this to every BSC connected */
- llist_for_each_entry(bsc, &nat->bsc_connections, list_entry) {
- if (!bsc->authenticated)
- continue;
-
- bsc_send_data(bsc, msg->l2h, msgb_l2len(msg), parsed->ipa_proto);
- }
-
-exit:
- talloc_free(parsed);
- return 0;
-}
-
-static void msc_connection_was_lost(struct bsc_msc_connection *con)
-{
- struct bsc_connection *bsc, *tmp;
-
- LOGP(DMSC, LOGL_ERROR, "Closing all connections downstream.\n");
- llist_for_each_entry_safe(bsc, tmp, &nat->bsc_connections, list_entry)
- bsc_close_connection(bsc);
-
- bsc_mgcp_free_endpoints(nat);
- bsc_msc_schedule_connect(con);
-}
-
-static void msc_connection_connected(struct bsc_msc_connection *con)
-{
- osmo_counter_inc(nat->stats.msc.reconn);
-}
-
-static void msc_send_reset(struct bsc_msc_connection *msc_con)
-{
- static const uint8_t reset[] = {
- 0x00, 0x12, 0xfd,
- 0x09, 0x00, 0x03, 0x05, 0x07, 0x02, 0x42, 0xfe,
- 0x02, 0x42, 0xfe, 0x06, 0x00, 0x04, 0x30, 0x04,
- 0x01, 0x20
- };
-
- struct msgb *msg;
-
- msg = msgb_alloc_headroom(4096, 128, "08.08 reset");
- if (!msg) {
- LOGP(DMSC, LOGL_ERROR, "Failed to allocate reset msg.\n");
- return;
- }
-
- msg->l2h = msgb_put(msg, sizeof(reset));
- memcpy(msg->l2h, reset, msgb_l2len(msg));
-
- queue_for_msc(msc_con, msg);
-
- LOGP(DMSC, LOGL_NOTICE, "Scheduled GSM0808 reset msg for the MSC.\n");
-}
-
-static int ipaccess_msc_read_cb(struct osmo_fd *bfd)
-{
- struct bsc_msc_connection *msc_con;
- struct msgb *msg = NULL;
- struct ipaccess_head *hh;
- int ret;
-
- msc_con = (struct bsc_msc_connection *) bfd->data;
-
- ret = ipa_msg_recv_buffered(bfd->fd, &msg, &msc_con->pending_msg);
- if (ret <= 0) {
- if (ret == -EAGAIN)
- return 0;
- if (ret == 0)
- LOGP(DNAT, LOGL_FATAL,
- "The connection the MSC(%s) was lost, exiting\n",
- msc_con->name);
- else
- LOGP(DNAT, LOGL_ERROR,
- "Failed to parse ip access message on %s: %d\n",
- msc_con->name, ret);
-
- bsc_msc_lost(msc_con);
- return -1;
- }
-
- LOGP(DNAT, LOGL_DEBUG,
- "MSG from MSC(%s): %s proto: %d\n", msc_con->name,
- osmo_hexdump(msg->data, msg->len), msg->l2h[0]);
-
- /* handle base message handling */
- hh = (struct ipaccess_head *) msg->data;
-
- /* initialize the networking. This includes sending a GSM08.08 message */
- if (hh->proto == IPAC_PROTO_IPACCESS) {
- ipa_ccm_rcvmsg_base(msg, bfd);
- if (msg->l2h[0] == IPAC_MSGT_ID_ACK)
- initialize_msc_if_needed(msc_con);
- else if (msg->l2h[0] == IPAC_MSGT_ID_GET)
- send_id_get_response(msc_con);
- } else if (hh->proto == IPAC_PROTO_SCCP) {
- forward_sccp_to_bts(msc_con, msg);
- } else if (hh->proto == IPAC_PROTO_MGCP_OLD) {
- bsc_nat_handle_mgcp(nat, msg);
- }
-
- msgb_free(msg);
- return 0;
-}
-
-static int ipaccess_msc_write_cb(struct osmo_fd *bfd, struct msgb *msg)
-{
- int rc;
- rc = write(bfd->fd, msg->data, msg->len);
-
- if (rc != msg->len) {
- LOGP(DNAT, LOGL_ERROR, "Failed to write MSG to MSC.\n");
- return -1;
- }
-
- return rc;
-}
-
-/*
- * Below is the handling of messages coming
- * from the BSC and need to be forwarded to
- * a real BSC.
- */
-
-/*
- * Remove the connection from the connections list,
- * remove it from the patching of SCCP header lists
- * as well. Maybe in the future even close connection..
- */
-void bsc_close_connection(struct bsc_connection *connection)
-{
- struct nat_sccp_connection *sccp_patch, *tmp;
- struct bsc_cmd_list *cmd_entry, *cmd_tmp;
- struct rate_ctr *ctr = NULL;
-
- /* stop the timeout timer */
- osmo_timer_del(&connection->id_timeout);
- osmo_timer_del(&connection->ping_timeout);
- osmo_timer_del(&connection->pong_timeout);
-
- if (connection->cfg)
- ctr = &connection->cfg->stats.ctrg->ctr[BCFG_CTR_DROPPED_SCCP];
-
- /* remove all SCCP connections */
- llist_for_each_entry_safe(sccp_patch, tmp, &nat->sccp_connections, list_entry) {
- if (sccp_patch->bsc != connection)
- continue;
-
- if (ctr)
- rate_ctr_inc(ctr);
- if (sccp_patch->has_remote_ref) {
- if (sccp_patch->con_local == NAT_CON_END_MSC)
- nat_send_rlsd_msc(sccp_patch);
- else if (sccp_patch->con_local == NAT_CON_END_USSD)
- nat_send_rlsd_ussd(nat, sccp_patch);
- }
-
- sccp_connection_destroy(sccp_patch);
- }
-
- /* Reply to all outstanding commands */
- llist_for_each_entry_safe(cmd_entry, cmd_tmp, &connection->cmd_pending, list_entry) {
- cmd_entry->cmd->type = CTRL_TYPE_ERROR;
- cmd_entry->cmd->reply = "BSC closed the connection";
- ctrl_cmd_send(&cmd_entry->ccon->write_queue, cmd_entry->cmd);
- bsc_nat_ctrl_del_pending(cmd_entry);
- }
-
- /* close endpoints allocated by this BSC */
- bsc_mgcp_clear_endpoints_for(connection);
-
- osmo_fd_unregister(&connection->write_queue.bfd);
- close(connection->write_queue.bfd.fd);
- osmo_wqueue_clear(&connection->write_queue);
- llist_del(&connection->list_entry);
-
- if (connection->pending_msg) {
- LOGP(DNAT, LOGL_ERROR, "Dropping partial message on connection %d.\n",
- connection->cfg ? connection->cfg->nr : -1);
- msgb_free(connection->pending_msg);
- connection->pending_msg = NULL;
- }
-
- talloc_free(connection);
-}
-
-static void bsc_maybe_close(struct bsc_connection *bsc)
-{
- struct nat_sccp_connection *sccp;
- if (!bsc->nat->blocked)
- return;
-
- /* are there any connections left */
- llist_for_each_entry(sccp, &bsc->nat->sccp_connections, list_entry)
- if (sccp->bsc == bsc)
- return;
-
- /* nothing left, close the BSC */
- LOGP(DNAT, LOGL_NOTICE, "Cleaning up BSC %d in blocking mode.\n",
- bsc->cfg ? bsc->cfg->nr : -1);
- bsc_close_connection(bsc);
-}
-
-static void ipaccess_close_bsc(void *data)
-{
- struct sockaddr_in sock;
- socklen_t len = sizeof(sock);
- struct bsc_connection *conn = data;
-
-
- getpeername(conn->write_queue.bfd.fd, (struct sockaddr *) &sock, &len);
- LOGP(DNAT, LOGL_ERROR, "BSC on %s didn't respond to identity request. Closing.\n",
- inet_ntoa(sock.sin_addr));
- bsc_close_connection(conn);
-}
-
-static int verify_key(struct bsc_connection *conn, struct bsc_config *conf, const uint8_t *key, const int keylen)
-{
- struct osmo_auth_vector vec;
-
- struct osmo_sub_auth_data auth = {
- .type = OSMO_AUTH_TYPE_GSM,
- .algo = OSMO_AUTH_ALG_MILENAGE,
- };
-
- /* expect a specific keylen */
- if (keylen != 8) {
- LOGP(DNAT, LOGL_ERROR, "Key length is wrong: %d for bsc nr %d\n",
- keylen, conf->nr);
- return 0;
- }
-
- memcpy(auth.u.umts.opc, conf->key, 16);
- memcpy(auth.u.umts.k, conf->key, 16);
- memset(auth.u.umts.amf, 0, 2);
- auth.u.umts.sqn = 0;
-
- memset(&vec, 0, sizeof(vec));
- osmo_auth_gen_vec(&vec, &auth, conn->last_rand);
-
- if (vec.res_len != 8) {
- LOGP(DNAT, LOGL_ERROR, "Res length is wrong: %d for bsc nr %d\n",
- vec.res_len, conf->nr);
- return 0;
- }
-
- return osmo_constant_time_cmp(vec.res, key, 8) == 0;
-}
-
-static void ipaccess_auth_bsc(struct tlv_parsed *tvp, struct bsc_connection *bsc)
-{
- struct bsc_config *conf;
- const char *token = (const char *) TLVP_VAL(tvp, IPAC_IDTAG_UNITNAME);
- int len = TLVP_LEN(tvp, IPAC_IDTAG_UNITNAME);
- const uint8_t *xres = TLVP_VAL(tvp, 0x24);
- const int xlen = TLVP_LEN(tvp, 0x24);
-
- if (bsc->cfg) {
- LOGP(DNAT, LOGL_ERROR, "Reauth on fd %d bsc nr %d\n",
- bsc->write_queue.bfd.fd, bsc->cfg->nr);
- return;
- }
-
- if (len <= 0) {
- LOGP(DNAT, LOGL_ERROR, "Token with length zero on fd: %d\n",
- bsc->write_queue.bfd.fd);
- return;
- }
-
- if (token[len - 1] != '\0') {
- LOGP(DNAT, LOGL_ERROR, "Token not null terminated on fd: %d\n",
- bsc->write_queue.bfd.fd);
- return;
- }
-
- /*
- * New systems have fixed the structure of the message but
- * we need to support old ones too.
- */
- if (len >= 2 && token[len - 2] == '\0')
- len -= 1;
-
- conf = bsc_config_by_token(bsc->nat, token, len);
- if (!conf) {
- LOGP(DNAT, LOGL_ERROR,
- "No bsc found for token '%s' len %d on fd: %d.\n", token,
- bsc->write_queue.bfd.fd, len);
- bsc_close_connection(bsc);
- return;
- }
-
- /* We have set a key and expect it to be present */
- if (conf->key_present && !verify_key(bsc, conf, xres, xlen - 1)) {
- LOGP(DNAT, LOGL_ERROR,
- "Wrong key for bsc nr %d fd: %d.\n", conf->nr,
- bsc->write_queue.bfd.fd);
- bsc_close_connection(bsc);
- return;
- }
-
- rate_ctr_inc(&conf->stats.ctrg->ctr[BCFG_CTR_NET_RECONN]);
- bsc->authenticated = 1;
- bsc->cfg = conf;
- osmo_timer_del(&bsc->id_timeout);
- LOGP(DNAT, LOGL_NOTICE, "Authenticated bsc nr: %d on fd %d\n",
- conf->nr, bsc->write_queue.bfd.fd);
- start_ping_pong(bsc);
-}
-
-static void handle_con_stats(struct nat_sccp_connection *con)
-{
- struct rate_ctr_group *ctrg;
- int id = bsc_conn_type_to_ctr(con);
-
- if (id == -1)
- return;
-
- if (!con->bsc || !con->bsc->cfg)
- return;
-
- ctrg = con->bsc->cfg->stats.ctrg;
- rate_ctr_inc(&ctrg->ctr[id]);
-}
-
-static int forward_sccp_to_msc(struct bsc_connection *bsc, struct msgb *msg)
-{
- int con_filter = 0;
- char *imsi = NULL;
- struct bsc_msc_connection *con_msc = NULL;
- struct bsc_connection *con_bsc = NULL;
- int con_type;
- struct bsc_nat_parsed *parsed;
- struct bsc_filter_reject_cause cause;
-
- /* Parse and filter messages */
- parsed = bsc_nat_parse(msg);
- if (!parsed) {
- LOGP(DNAT, LOGL_ERROR, "Can not parse msg from BSC.\n");
- msgb_free(msg);
- return -1;
- }
-
- if (bsc_nat_filter_ipa(DIR_MSC, msg, parsed))
- goto exit;
-
- /*
- * check authentication after filtering to not reject auth
- * responses coming from the BSC. We have to make sure that
- * nothing from the exit path will forward things to the MSC
- */
- if (!bsc->authenticated) {
- LOGP(DNAT, LOGL_ERROR, "BSC is not authenticated.\n");
- msgb_free(msg);
- return -1;
- }
-
-
- /* modify the SCCP entries */
- if (parsed->ipa_proto == IPAC_PROTO_SCCP) {
- int filter;
- struct nat_sccp_connection *con;
- switch (parsed->sccp_type) {
- case SCCP_MSG_TYPE_CR:
- memset(&cause, 0, sizeof(cause));
- filter = bsc_nat_filter_sccp_cr(bsc, msg, parsed,
- &con_type, &imsi, &cause);
- if (filter < 0) {
- if (imsi)
- bsc_nat_inform_reject(bsc, imsi);
- bsc_stat_reject(filter, bsc, 0);
- goto exit3;
- }
-
- if (!create_sccp_src_ref(bsc, parsed))
- goto exit2;
- con = patch_sccp_src_ref_to_msc(msg, parsed, bsc);
- OSMO_ASSERT(con);
- con->msc_con = bsc->nat->msc_con;
- con_msc = con->msc_con;
- con->filter_state.con_type = con_type;
- con->filter_state.imsi_checked = filter;
- bsc_nat_extract_lac(bsc, con, parsed, msg);
- if (imsi)
- con->filter_state.imsi = talloc_steal(con, imsi);
- imsi = NULL;
- con_bsc = con->bsc;
- handle_con_stats(con);
- break;
- case SCCP_MSG_TYPE_RLSD:
- case SCCP_MSG_TYPE_CREF:
- case SCCP_MSG_TYPE_DT1:
- case SCCP_MSG_TYPE_CC:
- case SCCP_MSG_TYPE_IT:
- con = patch_sccp_src_ref_to_msc(msg, parsed, bsc);
- if (con) {
- /* only filter non local connections */
- if (!con->con_local) {
- memset(&cause, 0, sizeof(cause));
- filter = bsc_nat_filter_dt(bsc, msg,
- con, parsed, &cause);
- if (filter < 0) {
- if (con->filter_state.imsi)
- bsc_nat_inform_reject(bsc,
- con->filter_state.imsi);
- bsc_stat_reject(filter, bsc, 1);
- bsc_send_con_release(bsc, con, &cause);
- con = NULL;
- goto exit2;
- }
-
- /* hand data to a side channel */
- if (bsc_ussd_check(con, parsed, msg) == 1)
- con->con_local = NAT_CON_END_USSD;
-
- /*
- * Optionally rewrite setup message. This can
- * replace the msg and the parsed structure becomes
- * invalid.
- */
- msg = bsc_nat_rewrite_msg(bsc->nat, msg, parsed,
- con->filter_state.imsi);
- talloc_free(parsed);
- parsed = NULL;
- } else if (con->con_local == NAT_CON_END_USSD) {
- bsc_ussd_check(con, parsed, msg);
- }
-
- con_bsc = con->bsc;
- con_msc = con->msc_con;
- con_filter = con->con_local;
- }
-
- break;
- case SCCP_MSG_TYPE_RLC:
- con = patch_sccp_src_ref_to_msc(msg, parsed, bsc);
- if (con) {
- con_bsc = con->bsc;
- con_msc = con->msc_con;
- con_filter = con->con_local;
- }
- remove_sccp_src_ref(bsc, msg, parsed);
- bsc_maybe_close(bsc);
- break;
- case SCCP_MSG_TYPE_UDT:
- /* simply forward everything */
- con = NULL;
- break;
- default:
- LOGP(DNAT, LOGL_ERROR, "Not forwarding to msc sccp type: 0x%x\n", parsed->sccp_type);
- con = NULL;
- goto exit2;
- break;
- }
- } else if (parsed->ipa_proto == IPAC_PROTO_MGCP_OLD) {
- bsc_mgcp_forward(bsc, msg);
- goto exit2;
- } else {
- LOGP(DNAT, LOGL_ERROR, "Not forwarding unknown stream id: 0x%x\n", parsed->ipa_proto);
- goto exit2;
- }
-
- if (con_msc && con_bsc != bsc) {
- LOGP(DNAT, LOGL_ERROR, "The connection belongs to a different BTS: input: %d con: %d\n",
- bsc->cfg->nr, con_bsc->cfg->nr);
- goto exit2;
- }
-
- /* do not forward messages to the MSC */
- if (con_filter)
- goto exit2;
-
- if (!con_msc) {
- LOGP(DNAT, LOGL_ERROR, "Not forwarding data bsc_nr: %d ipa: %d type: 0x%x\n",
- bsc->cfg->nr,
- parsed ? parsed->ipa_proto : -1,
- parsed ? parsed->sccp_type : -1);
- goto exit2;
- }
-
- /* send the non-filtered but maybe modified msg */
- queue_for_msc(con_msc, msg);
- if (parsed)
- talloc_free(parsed);
- return 0;
-
-exit:
- /* if we filter out the reset send an ack to the BSC */
- if (parsed->bssap == 0 && parsed->gsm_type == BSS_MAP_MSG_RESET) {
- send_reset_ack(bsc);
- send_reset_ack(bsc);
- } else if (parsed->ipa_proto == IPAC_PROTO_IPACCESS) {
- /* do we know who is handling this? */
- if (msg->l2h[0] == IPAC_MSGT_ID_RESP && msgb_l2len(msg) > 2) {
- struct tlv_parsed tvp;
- int ret;
- ret = ipa_ccm_idtag_parse_off(&tvp,
- (unsigned char *) msg->l2h + 2,
- msgb_l2len(msg) - 2, 0);
- if (ret < 0) {
- LOGP(DNAT, LOGL_ERROR, "ignoring IPA response "
- "message with malformed TLVs\n");
- return ret;
- }
- if (TLVP_PRESENT(&tvp, IPAC_IDTAG_UNITNAME))
- ipaccess_auth_bsc(&tvp, bsc);
- }
-
- goto exit2;
- }
-
-exit2:
- if (imsi)
- talloc_free(imsi);
- talloc_free(parsed);
- msgb_free(msg);
- return -1;
-
-exit3:
- /* send a SCCP Connection Refused */
- if (imsi)
- talloc_free(imsi);
- bsc_send_con_refuse(bsc, parsed, con_type, &cause);
- talloc_free(parsed);
- msgb_free(msg);
- return -1;
-}
-
-static int ipaccess_bsc_read_cb(struct osmo_fd *bfd)
-{
- struct bsc_connection *bsc = bfd->data;
- struct msgb *msg = NULL;
- struct ipaccess_head *hh;
- struct ipaccess_head_ext *hh_ext;
- int ret;
-
- ret = ipa_msg_recv_buffered(bfd->fd, &msg, &bsc->pending_msg);
- if (ret <= 0) {
- if (ret == -EAGAIN)
- return 0;
- if (ret == 0)
- LOGP(DNAT, LOGL_ERROR,
- "The connection to the BSC Nr: %d was lost. Cleaning it\n",
- bsc->cfg ? bsc->cfg->nr : -1);
- else
- LOGP(DNAT, LOGL_ERROR,
- "Stream error on BSC Nr: %d. Failed to parse ip access message: %d (%s)\n",
- bsc->cfg ? bsc->cfg->nr : -1, ret, strerror(-ret));
-
- bsc_close_connection(bsc);
- return -1;
- }
-
-
- LOGP(DNAT, LOGL_DEBUG, "MSG from BSC: %s proto: %d\n", osmo_hexdump(msg->data, msg->len), msg->l2h[0]);
-
- /* Handle messages from the BSC */
- hh = (struct ipaccess_head *) msg->data;
-
- /* stop the pong timeout */
- if (hh->proto == IPAC_PROTO_IPACCESS) {
- if (msg->l2h[0] == IPAC_MSGT_PONG) {
- osmo_timer_del(&bsc->pong_timeout);
- msgb_free(msg);
- return 0;
- } else if (msg->l2h[0] == IPAC_MSGT_PING) {
- send_pong(bsc);
- msgb_free(msg);
- return 0;
- }
- /* Message contains the ipaccess_head_ext header, investigate further */
- } else if (hh->proto == IPAC_PROTO_OSMO &&
- msg->len > sizeof(*hh) + sizeof(*hh_ext)) {
-
- hh_ext = (struct ipaccess_head_ext *) hh->data;
- /* l2h is where the actual command data is expected */
- msg->l2h = hh_ext->data;
-
- if (hh_ext->proto == IPAC_PROTO_EXT_CTRL)
- return bsc_nat_handle_ctrlif_msg(bsc, msg);
- }
-
- /* FIXME: Currently no PONG is sent to the BSC */
- /* FIXME: Currently no ID ACK is sent to the BSC */
- forward_sccp_to_msc(bsc, msg);
-
- return 0;
-}
-
-static int ipaccess_listen_bsc_cb(struct osmo_fd *bfd, unsigned int what)
-{
- struct bsc_connection *bsc;
- int fd, rc, on;
- struct sockaddr_in sa;
- socklen_t sa_len = sizeof(sa);
-
- if (!(what & BSC_FD_READ))
- return 0;
-
- fd = accept(bfd->fd, (struct sockaddr *) &sa, &sa_len);
- if (fd < 0) {
- perror("accept");
- return fd;
- }
-
- /* count the reconnect */
- osmo_counter_inc(nat->stats.bsc.reconn);
-
- /*
- * if we are not connected to a msc... just close the socket
- */
- if (!bsc_nat_msc_is_connected(nat)) {
- LOGP(DNAT, LOGL_NOTICE, "Disconnecting BSC due lack of MSC connection.\n");
- close(fd);
- return 0;
- }
-
- if (nat->blocked) {
- LOGP(DNAT, LOGL_NOTICE, "Disconnecting BSC due NAT being blocked.\n");
- close(fd);
- return 0;
- }
-
- on = 1;
- rc = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on));
- if (rc != 0)
- LOGP(DNAT, LOGL_ERROR, "Failed to set TCP_NODELAY: %s\n", strerror(errno));
-
- rc = setsockopt(fd, IPPROTO_IP, IP_TOS,
- &nat->bsc_ip_dscp, sizeof(nat->bsc_ip_dscp));
- if (rc != 0)
- LOGP(DNAT, LOGL_ERROR, "Failed to set IP_TOS: %s\n", strerror(errno));
-
- /* todo... do something with the connection */
- /* todo... use GNUtls to see if we want to trust this as a BTS */
-
- /*
- *
- */
- bsc = bsc_connection_alloc(nat);
- if (!bsc) {
- LOGP(DNAT, LOGL_ERROR, "Failed to allocate BSC struct.\n");
- close(fd);
- return -1;
- }
-
- bsc->write_queue.bfd.data = bsc;
- bsc->write_queue.bfd.fd = fd;
- bsc->write_queue.read_cb = ipaccess_bsc_read_cb;
- bsc->write_queue.write_cb = bsc_write_cb;
- bsc->write_queue.bfd.when = BSC_FD_READ;
- if (osmo_fd_register(&bsc->write_queue.bfd) < 0) {
- LOGP(DNAT, LOGL_ERROR, "Failed to register BSC fd.\n");
- close(fd);
- talloc_free(bsc);
- return -2;
- }
-
- LOGP(DNAT, LOGL_NOTICE, "BSC connection on %d with IP: %s\n",
- fd, inet_ntoa(sa.sin_addr));
-
- llist_add(&bsc->list_entry, &nat->bsc_connections);
- bsc->last_id = 0;
-
- send_id_ack(bsc);
- send_id_req(nat, bsc);
- send_mgcp_reset(bsc);
-
- /*
- * start the hangup timer
- */
- osmo_timer_setup(&bsc->id_timeout, ipaccess_close_bsc, bsc);
- osmo_timer_schedule(&bsc->id_timeout, nat->auth_timeout, 0);
- return 0;
-}
-
-static void print_usage()
-{
- printf("Usage: bsc_nat\n");
-}
-
-static void print_help()
-{
- printf(" Some useful help...\n");
- printf(" -h --help this text\n");
- printf(" -d option --debug=DRLL:DCC:DMM:DRR:DRSL:DNM enable debugging\n");
- printf(" -D --daemonize Fork the process into a background daemon\n");
- printf(" -s --disable-color\n");
- printf(" -c --config-file filename The config file to use.\n");
- printf(" -m --msc=IP. The address of the MSC.\n");
- printf(" -l --local=IP. The local address of this BSC.\n");
-}
-
-static void handle_options(int argc, char **argv)
-{
- while (1) {
- int option_index = 0, c;
- static struct option long_options[] = {
- {"help", 0, 0, 'h'},
- {"debug", 1, 0, 'd'},
- {"daemonize", 0, 0, 'D'},
- {"config-file", 1, 0, 'c'},
- {"disable-color", 0, 0, 's'},
- {"timestamp", 0, 0, 'T'},
- {"msc", 1, 0, 'm'},
- {"local", 1, 0, 'l'},
- {0, 0, 0, 0}
- };
-
- c = getopt_long(argc, argv, "hd:sTPc:m:l:D",
- long_options, &option_index);
- if (c == -1)
- break;
-
- switch (c) {
- case 'h':
- print_usage();
- print_help();
- exit(0);
- case 's':
- log_set_use_color(osmo_stderr_target, 0);
- break;
- case 'd':
- log_parse_category_mask(osmo_stderr_target, optarg);
- break;
- case 'D':
- daemonize = 1;
- break;
- case 'c':
- config_file = optarg;
- break;
- case 'T':
- log_set_print_timestamp(osmo_stderr_target, 1);
- break;
- case 'm':
- msc_ip = optarg;
- break;
- case 'l':
- inet_aton(optarg, &local_addr);
- break;
- default:
- /* ignore */
- break;
- }
- }
-}
-
-static void signal_handler(int signal)
-{
- switch (signal) {
- case SIGABRT:
- /* in case of abort, we want to obtain a talloc report
- * and then return to the caller, who will abort the process */
- case SIGUSR1:
- talloc_report_full(tall_bsc_ctx, stderr);
- break;
- default:
- break;
- }
-}
-
-static void sccp_close_unconfirmed(void *_data)
-{
- int destroyed = 0;
- struct bsc_connection *bsc, *bsc_tmp;
- struct nat_sccp_connection *conn, *tmp1;
- struct timespec now;
- clock_gettime(CLOCK_MONOTONIC, &now);
-
- llist_for_each_entry_safe(conn, tmp1, &nat->sccp_connections, list_entry) {
- if (conn->has_remote_ref)
- continue;
-
- int diff = (now.tv_sec - conn->creation_time.tv_sec) / 60;
- if (diff < SCCP_CLOSE_TIME_TIMEOUT)
- continue;
-
- LOGP(DNAT, LOGL_ERROR,
- "SCCP connection 0x%x/0x%x was never confirmed on bsc nr. %d\n",
- sccp_src_ref_to_int(&conn->real_ref),
- sccp_src_ref_to_int(&conn->patched_ref),
- conn->bsc->cfg->nr);
- sccp_connection_destroy(conn);
- destroyed = 1;
- }
-
- if (!destroyed)
- goto out;
-
- /* now close out any BSC */
- llist_for_each_entry_safe(bsc, bsc_tmp, &nat->bsc_connections, list_entry)
- bsc_maybe_close(bsc);
-
-out:
- osmo_timer_schedule(&sccp_close, SCCP_CLOSE_TIME, 0);
-}
-
-extern void *tall_ctr_ctx;
-static void talloc_init_ctx()
-{
- tall_bsc_ctx = talloc_named_const(NULL, 0, "nat");
- msgb_talloc_ctx_init(tall_bsc_ctx, 0);
- tall_ctr_ctx = talloc_named_const(tall_bsc_ctx, 0, "counter");
-}
-
-extern int bsc_vty_go_parent(struct vty *vty);
-
-static struct vty_app_info vty_info = {
- .name = "OsmoBSCNAT",
- .version = PACKAGE_VERSION,
- .go_parent_cb = bsc_vty_go_parent,
- .is_config_node = bsc_vty_is_config_node,
-};
-
-
-int main(int argc, char **argv)
-{
- int rc;
-
- talloc_init_ctx();
-
- osmo_init_logging(&log_info);
-
- nat = bsc_nat_alloc();
- if (!nat) {
- fprintf(stderr, "Failed to allocate the BSC nat.\n");
- return -4;
- }
-
- nat->mgcp_cfg = mgcp_config_alloc();
- if (!nat->mgcp_cfg) {
- fprintf(stderr, "Failed to allocate MGCP cfg.\n");
- return -5;
- }
-
- /* We need to add mode-set for amr codecs */
- nat->sdp_ensure_amr_mode_set = 1;
-
- vty_info.copyright = openbsc_copyright;
- vty_init(&vty_info);
- logging_vty_add_cmds(NULL);
- osmo_stats_vty_add_cmds(&log_info);
- bsc_nat_vty_init(nat);
- ctrl_vty_init(tall_bsc_ctx);
-
-
- /* parse options */
- local_addr.s_addr = INADDR_ANY;
- handle_options(argc, argv);
-
- nat->include_base = dirname(talloc_strdup(tall_bsc_ctx, config_file));
-
- rate_ctr_init(tall_bsc_ctx);
- osmo_stats_init(tall_bsc_ctx);
-
- /* init vty and parse */
- if (mgcp_parse_config(config_file, nat->mgcp_cfg, MGCP_BSC_NAT) < 0) {
- fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
- return -3;
- }
-
- /* start telnet after reading config for vty_get_bind_addr() */
- if (telnet_init_dynif(tall_bsc_ctx, NULL, vty_get_bind_addr(),
- OSMO_VTY_PORT_BSC_NAT)) {
- fprintf(stderr, "Creating VTY telnet line failed\n");
- return -5;
- }
-
- /* over rule the VTY config for MSC IP */
- if (msc_ip)
- bsc_nat_set_msc_ip(nat, msc_ip);
-
- /* seed the PRNG */
- srand(time(NULL));
-
- LOGP(DNAT, LOGL_NOTICE, "BSCs configured from %s\n", nat->resolved_path);
-
- /*
- * Setup the MGCP code..
- */
- if (bsc_mgcp_nat_init(nat) != 0)
- return -4;
-
- /* connect to the MSC */
- nat->msc_con = bsc_msc_create(nat, &nat->dests);
- if (!nat->msc_con) {
- fprintf(stderr, "Creating a bsc_msc_connection failed.\n");
- exit(1);
- }
-
- /* start control interface after reading config for
- * ctrl_vty_get_bind_addr() */
- nat->ctrl = bsc_nat_controlif_setup(nat, ctrl_vty_get_bind_addr(),
- OSMO_CTRL_PORT_BSC_NAT);
- if (!nat->ctrl) {
- fprintf(stderr, "Creating the control interface failed.\n");
- exit(1);
- }
-
- nat->msc_con->name = "main MSC";
- nat->msc_con->connection_loss = msc_connection_was_lost;
- nat->msc_con->connected = msc_connection_connected;
- nat->msc_con->write_queue.read_cb = ipaccess_msc_read_cb;
- nat->msc_con->write_queue.write_cb = ipaccess_msc_write_cb;;
- nat->msc_con->write_queue.bfd.data = nat->msc_con;
- bsc_msc_connect(nat->msc_con);
-
- /* wait for the BSC */
- rc = make_sock(&bsc_listen, IPPROTO_TCP, ntohl(local_addr.s_addr),
- 5000, 0, ipaccess_listen_bsc_cb, nat);
- if (rc != 0) {
- fprintf(stderr, "Failed to listen for BSC.\n");
- exit(1);
- }
-
- rc = bsc_ussd_init(nat);
- if (rc != 0) {
- LOGP(DNAT, LOGL_ERROR, "Failed to bind the USSD socket.\n");
- exit(1);
- }
-
- signal(SIGABRT, &signal_handler);
- signal(SIGUSR1, &signal_handler);
- osmo_init_ignore_signals();
-
- if (daemonize) {
- rc = osmo_daemonize();
- if (rc < 0) {
- perror("Error during daemonize");
- exit(1);
- }
- }
-
- /* recycle timer */
- sccp_set_log_area(DSCCP);
- osmo_timer_setup(&sccp_close, sccp_close_unconfirmed, NULL);
- osmo_timer_schedule(&sccp_close, SCCP_CLOSE_TIME, 0);
-
- while (1) {
- osmo_select_main(0);
- }
-
- return 0;
-}
-
-/* Close all connections handed out to the USSD module */
-int bsc_ussd_close_connections(struct bsc_nat *nat)
-{
- struct nat_sccp_connection *con;
- llist_for_each_entry(con, &nat->sccp_connections, list_entry) {
- if (con->con_local != NAT_CON_END_USSD)
- continue;
- if (!con->bsc)
- continue;
-
- nat_send_clrc_bsc(con);
- nat_send_rlsd_bsc(con);
- }
-
- return 0;
-}
diff --git a/openbsc/src/osmo-bsc_nat/bsc_nat_ctrl.c b/openbsc/src/osmo-bsc_nat/bsc_nat_ctrl.c
deleted file mode 100644
index 128ea6518..000000000
--- a/openbsc/src/osmo-bsc_nat/bsc_nat_ctrl.c
+++ /dev/null
@@ -1,524 +0,0 @@
-/*
- * (C) 2011-2012 by Holger Hans Peter Freyther
- * (C) 2011-2012 by On-Waves
- * (C) 2011 by Daniel Willmann
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <osmocom/core/talloc.h>
-
-#include <osmocom/ctrl/control_cmd.h>
-#include <osmocom/ctrl/control_if.h>
-#include <osmocom/ctrl/ports.h>
-
-#include <osmocom/vty/misc.h>
-
-#include <openbsc/ctrl.h>
-#include <openbsc/bsc_nat.h>
-#include <openbsc/bsc_msg_filter.h>
-#include <openbsc/vty.h>
-#include <openbsc/gsm_data.h>
-
-#include <unistd.h>
-#include <string.h>
-#include <errno.h>
-
-
-#define NAT_MAX_CTRL_ID 65535
-
-static struct bsc_nat *g_nat;
-
-static int bsc_id_unused(int id, struct bsc_connection *bsc)
-{
- struct bsc_cmd_list *pending;
-
- llist_for_each_entry(pending, &bsc->cmd_pending, list_entry) {
- if (pending->nat_id == id)
- return 0;
- }
- return 1;
-}
-
-static int get_next_free_bsc_id(struct bsc_connection *bsc)
-{
- int new_id, overflow = 0;
-
- new_id = bsc->last_id;
-
- do {
- new_id++;
- if (new_id == NAT_MAX_CTRL_ID) {
- new_id = 1;
- overflow++;
- }
-
- if (bsc_id_unused(new_id, bsc)) {
- bsc->last_id = new_id;
- return new_id;
- }
- } while (overflow != 2);
-
- return -1;
-}
-
-void bsc_nat_ctrl_del_pending(struct bsc_cmd_list *pending)
-{
- llist_del(&pending->list_entry);
- osmo_timer_del(&pending->timeout);
- talloc_free(pending->cmd);
- talloc_free(pending);
-}
-
-static struct bsc_cmd_list *bsc_get_pending(struct bsc_connection *bsc, char *id_str)
-{
- struct bsc_cmd_list *cmd_entry;
- int id = atoi(id_str);
- if (id == 0)
- return NULL;
-
- llist_for_each_entry(cmd_entry, &bsc->cmd_pending, list_entry) {
- if (cmd_entry->nat_id == id) {
- return cmd_entry;
- }
- }
- return NULL;
-}
-
-int bsc_nat_handle_ctrlif_msg(struct bsc_connection *bsc, struct msgb *msg)
-{
- struct ctrl_cmd *cmd;
- struct bsc_cmd_list *pending;
- char *var, *id;
-
- cmd = ctrl_cmd_parse(bsc, msg);
- msgb_free(msg);
-
- if (!cmd) {
- cmd = talloc_zero(bsc, struct ctrl_cmd);
- if (!cmd) {
- LOGP(DNAT, LOGL_ERROR, "OOM!\n");
- return -ENOMEM;
- }
- cmd->type = CTRL_TYPE_ERROR;
- cmd->id = "err";
- cmd->reply = "Failed to parse command.";
- goto err;
- }
-
- if (bsc->cfg && !llist_empty(&bsc->cfg->lac_list)) {
- if (cmd->variable) {
- var = talloc_asprintf(cmd, "net.0.bsc.%i.%s", bsc->cfg->nr,
- cmd->variable);
- if (!var) {
- cmd->type = CTRL_TYPE_ERROR;
- cmd->reply = "OOM";
- goto err;
- }
- talloc_free(cmd->variable);
- cmd->variable = var;
- }
-
- /* We have to handle TRAPs before matching pending */
- if (cmd->type == CTRL_TYPE_TRAP) {
- ctrl_cmd_send_to_all(bsc->nat->ctrl, cmd);
- talloc_free(cmd);
- return 0;
- }
-
- /* Find the pending command */
- pending = bsc_get_pending(bsc, cmd->id);
- if (pending) {
- id = talloc_strdup(cmd, pending->cmd->id);
- if (!id) {
- cmd->type = CTRL_TYPE_ERROR;
- cmd->reply = "OOM";
- goto err;
- }
- cmd->id = id;
- ctrl_cmd_send(&pending->ccon->write_queue, cmd);
- bsc_nat_ctrl_del_pending(pending);
- } else {
- /* We need to handle TRAPS here */
- if ((cmd->type != CTRL_TYPE_ERROR) &&
- (cmd->type != CTRL_TYPE_TRAP)) {
- LOGP(DNAT, LOGL_NOTICE, "Got control message "
- "from BSC without pending entry\n");
- cmd->type = CTRL_TYPE_ERROR;
- cmd->reply = "No request outstanding";
- goto err;
- }
- }
- }
- talloc_free(cmd);
- return 0;
-err:
- ctrl_cmd_send(&bsc->write_queue, cmd);
- talloc_free(cmd);
- return 0;
-}
-
-static void pending_timeout_cb(void *data)
-{
- struct bsc_cmd_list *pending = data;
- LOGP(DNAT, LOGL_ERROR, "Command timed out\n");
- pending->cmd->type = CTRL_TYPE_ERROR;
- pending->cmd->reply = "Command timed out";
- ctrl_cmd_send(&pending->ccon->write_queue, pending->cmd);
-
- bsc_nat_ctrl_del_pending(pending);
-}
-
-static void ctrl_conn_closed_cb(struct ctrl_connection *connection)
-{
- struct bsc_connection *bsc;
- struct bsc_cmd_list *pending, *tmp;
-
- llist_for_each_entry(bsc, &g_nat->bsc_connections, list_entry) {
- llist_for_each_entry_safe(pending, tmp, &bsc->cmd_pending, list_entry) {
- if (pending->ccon == connection)
- bsc_nat_ctrl_del_pending(pending);
- }
- }
-}
-
-static int extract_bsc_nr_variable(char *variable, unsigned int *nr, char **bsc_variable)
-{
- char *nr_str, *tmp, *saveptr = NULL;
-
- tmp = strtok_r(variable, ".", &saveptr);
- tmp = strtok_r(NULL, ".", &saveptr);
- tmp = strtok_r(NULL, ".", &saveptr);
- nr_str = strtok_r(NULL, ".", &saveptr);
- if (!nr_str)
- return 0;
- *nr = atoi(nr_str);
-
- tmp = strtok_r(NULL, "\0", &saveptr);
- if (!tmp)
- return 0;
-
- *bsc_variable = tmp;
- return 1;
-}
-
-static int forward_to_bsc(struct ctrl_cmd *cmd)
-{
- int ret = CTRL_CMD_HANDLED;
- struct ctrl_cmd *bsc_cmd = NULL;
- struct bsc_connection *bsc;
- struct bsc_cmd_list *pending;
- unsigned int nr;
- char *bsc_variable;
-
- /* Skip over the beginning (bsc.) */
- if (!extract_bsc_nr_variable(cmd->variable, &nr, &bsc_variable)) {
- cmd->reply = "command incomplete";
- goto err;
- }
-
-
- llist_for_each_entry(bsc, &g_nat->bsc_connections, list_entry) {
- if (!bsc->cfg)
- continue;
- if (!bsc->authenticated)
- continue;
- if (bsc->cfg->nr == nr) {
- /* Add pending command to list */
- pending = talloc_zero(bsc, struct bsc_cmd_list);
- if (!pending) {
- cmd->reply = "OOM";
- goto err;
- }
-
- pending->nat_id = get_next_free_bsc_id(bsc);
- if (pending->nat_id < 0) {
- cmd->reply = "No free ID found";
- goto err;
- }
-
- bsc_cmd = ctrl_cmd_cpy(bsc, cmd);
- if (!bsc_cmd) {
- cmd->reply = "Could not forward command";
- goto err;
- }
-
- talloc_free(bsc_cmd->id);
- bsc_cmd->id = talloc_asprintf(bsc_cmd, "%i", pending->nat_id);
- if (!bsc_cmd->id) {
- cmd->reply = "OOM";
- goto err;
- }
-
- talloc_free(bsc_cmd->variable);
- bsc_cmd->variable = talloc_strdup(bsc_cmd, bsc_variable);
- if (!bsc_cmd->variable) {
- cmd->reply = "OOM";
- goto err;
- }
-
- if (ctrl_cmd_send(&bsc->write_queue, bsc_cmd)) {
- cmd->reply = "Sending failed";
- goto err;
- }
- pending->ccon = cmd->ccon;
- pending->ccon->closed_cb = ctrl_conn_closed_cb;
- pending->cmd = cmd;
-
- /* Setup the timeout */
- osmo_timer_setup(&pending->timeout, pending_timeout_cb,
- pending);
- /* TODO: Make timeout configurable */
- osmo_timer_schedule(&pending->timeout, 10, 0);
- llist_add_tail(&pending->list_entry, &bsc->cmd_pending);
-
- goto done;
- }
- }
- /* We end up here if there's no bsc to handle our LAC */
- cmd->reply = "no BSC with this nr";
-err:
- ret = CTRL_CMD_ERROR;
-done:
- talloc_free(bsc_cmd);
- return ret;
-
-}
-
-
-CTRL_CMD_DEFINE(fwd_cmd, "net 0 bsc *");
-static int get_fwd_cmd(struct ctrl_cmd *cmd, void *data)
-{
- return forward_to_bsc(cmd);
-}
-
-static int set_fwd_cmd(struct ctrl_cmd *cmd, void *data)
-{
- return forward_to_bsc(cmd);
-}
-
-static int verify_fwd_cmd(struct ctrl_cmd *cmd, const char *value, void *data)
-{
- return 0;
-}
-
-static int extract_bsc_cfg_variable(struct ctrl_cmd *cmd, struct bsc_config **cfg,
- char **bsc_variable)
-{
- unsigned int nr;
-
- if (!extract_bsc_nr_variable(cmd->variable, &nr, bsc_variable)) {
- cmd->reply = "command incomplete";
- return 0;
- }
-
- *cfg = bsc_config_num(g_nat, nr);
- if (!*cfg) {
- cmd->reply = "Unknown BSC";
- return 0;
- }
-
- return 1;
-}
-
-CTRL_CMD_DEFINE(net_cfg_cmd, "net 0 bsc_cfg *");
-static int get_net_cfg_cmd(struct ctrl_cmd *cmd, void *data)
-{
- char *bsc_variable;
- struct bsc_config *bsc_cfg;
-
- if (!extract_bsc_cfg_variable(cmd, &bsc_cfg, &bsc_variable))
- return CTRL_CMD_ERROR;
-
- if (strcmp(bsc_variable, "access-list-name") == 0) {
- cmd->reply = talloc_asprintf(cmd, "%s",
- bsc_cfg->acc_lst_name ? bsc_cfg->acc_lst_name : "");
- return CTRL_CMD_REPLY;
- }
-
- cmd->reply = "unknown command";
- return CTRL_CMD_ERROR;
-}
-
-static int set_net_cfg_cmd(struct ctrl_cmd *cmd, void *data)
-{
- char *bsc_variable;
- struct bsc_config *bsc_cfg;
-
- if (!extract_bsc_cfg_variable(cmd, &bsc_cfg, &bsc_variable))
- return CTRL_CMD_ERROR;
-
- if (strcmp(bsc_variable, "access-list-name") == 0) {
- osmo_talloc_replace_string(bsc_cfg, &bsc_cfg->acc_lst_name, cmd->value);
- cmd->reply = talloc_asprintf(cmd, "%s",
- bsc_cfg->acc_lst_name ? bsc_cfg->acc_lst_name : "");
- return CTRL_CMD_REPLY;
- } else if (strcmp(bsc_variable, "no-access-list-name") == 0) {
- talloc_free(bsc_cfg->acc_lst_name);
- bsc_cfg->acc_lst_name = NULL;
- cmd->reply = "";
- return CTRL_CMD_REPLY;
- }
-
- cmd->reply = "unknown command";
- return CTRL_CMD_ERROR;
-}
-
-static int verify_net_cfg_cmd(struct ctrl_cmd *cmd, const char *value, void *data)
-{
- return 0;
-}
-
-CTRL_CMD_DEFINE(net_cfg_acc_cmd, "net 0 add allow access-list *");
-static const char *extract_acc_name(const char *var)
-{
- char *str;
-
- str = strstr(var, "net.0.add.allow.access-list.");
- if (!str)
- return NULL;
- str += strlen("net.0.add.allow.access-list.");
- if (strlen(str) == 0)
- return NULL;
- return str;
-}
-
-static int get_net_cfg_acc_cmd(struct ctrl_cmd *cmd, void *data)
-{
- cmd->reply = "Append only";
- return CTRL_CMD_ERROR;
-}
-
-static int set_net_cfg_acc_cmd(struct ctrl_cmd *cmd, void *data)
-{
- const char *access_name = extract_acc_name(cmd->variable);
- struct bsc_msg_acc_lst *acc;
- struct bsc_msg_acc_lst_entry *entry;
- const char *value = cmd->value;
- int rc;
-
- /* Should have been caught by verify_net_cfg_acc_cmd */
- acc = bsc_msg_acc_lst_find(&g_nat->access_lists, access_name);
- if (!acc) {
- cmd->reply = "Access list not found";
- return CTRL_CMD_ERROR;
- }
-
- entry = bsc_msg_acc_lst_entry_create(acc);
- if (!entry) {
- cmd->reply = "OOM";
- return CTRL_CMD_ERROR;
- }
-
- rc = gsm_parse_reg(acc, &entry->imsi_allow_re, &entry->imsi_allow, 1, &value);
- if (rc != 0) {
- cmd->reply = "Failed to compile expression";
- return CTRL_CMD_ERROR;
- }
-
- cmd->reply = "IMSI allow added to access list";
- return CTRL_CMD_REPLY;
-}
-
-static int verify_net_cfg_acc_cmd(struct ctrl_cmd *cmd, const char *value, void *data)
-{
- const char *access_name = extract_acc_name(cmd->variable);
- struct bsc_msg_acc_lst *acc = bsc_msg_acc_lst_find(&g_nat->access_lists, access_name);
-
- if (!acc) {
- cmd->reply = "Access list not known";
- return -1;
- }
-
- return 0;
-}
-
-CTRL_CMD_DEFINE_WO_NOVRF(net_save_cmd, "net 0 save-configuration");
-
-static int set_net_save_cmd(struct ctrl_cmd *cmd, void *data)
-{
- int rc = osmo_vty_save_config_file();
- cmd->reply = talloc_asprintf(cmd, "%d", rc);
- if (!cmd->reply) {
- cmd->reply = "OOM";
- return CTRL_CMD_ERROR;
- }
-
- return CTRL_CMD_REPLY;
-}
-
-struct ctrl_handle *bsc_nat_controlif_setup(struct bsc_nat *nat,
- const char *bind_addr, int port)
-{
- struct ctrl_handle *ctrl;
- int rc;
-
-
- ctrl = bsc_controlif_setup(NULL, bind_addr, OSMO_CTRL_PORT_BSC_NAT);
- if (!ctrl) {
- fprintf(stderr, "Failed to initialize the control interface. Exiting.\n");
- return NULL;
- }
-
- rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_fwd_cmd);
- if (rc) {
- fprintf(stderr, "Failed to install the control command. Exiting.\n");
- goto error;
- }
- rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_cfg_cmd);
- if (rc) {
- fprintf(stderr, "Failed to install the net cfg command. Exiting.\n");
- goto error;
- }
- rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_cfg_acc_cmd);
- if (rc) {
- fprintf(stderr, "Failed to install the net acc command. Exiting.\n");
- goto error;
- }
- rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_save_cmd);
- if (rc) {
- fprintf(stderr, "Failed to install the net save command. Exiting.\n");
- goto error;
- }
-
- g_nat = nat;
- return ctrl;
-
-error:
- osmo_fd_unregister(&ctrl->listen_fd);
- close(ctrl->listen_fd.fd);
- talloc_free(ctrl);
- return NULL;
-}
-
-void bsc_nat_inform_reject(struct bsc_connection *conn, const char *imsi)
-{
- struct ctrl_cmd *cmd;
-
- cmd = ctrl_cmd_create(conn, CTRL_TYPE_TRAP);
- if (!cmd) {
- LOGP(DCTRL, LOGL_ERROR, "Failed to create TRAP command.\n");
- return;
- }
-
- cmd->id = "0";
- cmd->variable = talloc_asprintf(cmd, "net.0.bsc.%d.notification-rejection-v1",
- conn->cfg->nr);
- cmd->reply = talloc_asprintf(cmd, "imsi=%s", imsi);
-
- ctrl_cmd_send_to_all(conn->cfg->nat->ctrl, cmd);
- talloc_free(cmd);
-}
diff --git a/openbsc/src/osmo-bsc_nat/bsc_nat_filter.c b/openbsc/src/osmo-bsc_nat/bsc_nat_filter.c
deleted file mode 100644
index e73529097..000000000
--- a/openbsc/src/osmo-bsc_nat/bsc_nat_filter.c
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * (C) 2010-2015 by Holger Hans Peter Freyther <zecke@selfish.org>
- * (C) 2010-2012 by On-Waves
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <openbsc/bsc_nat.h>
-#include <openbsc/bsc_nat_sccp.h>
-#include <openbsc/bsc_msg_filter.h>
-#include <openbsc/debug.h>
-
-#include <osmocom/gsm/gsm0808.h>
-
-#include <osmocom/gsm/protocol/gsm_08_08.h>
-#include <osmocom/gsm/protocol/gsm_04_11.h>
-
-#include <osmocom/sccp/sccp.h>
-
-/* Filter out CR data... */
-int bsc_nat_filter_sccp_cr(struct bsc_connection *bsc, struct msgb *msg,
- struct bsc_nat_parsed *parsed, int *con_type,
- char **imsi, struct bsc_filter_reject_cause *cause)
-{
- struct bsc_filter_request req;
- struct tlv_parsed tp;
- struct gsm48_hdr *hdr48;
- int hdr48_len;
- int len;
-
- *con_type = FLT_CON_TYPE_NONE;
- cause->cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED;
- cause->lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED;
- *imsi = NULL;
-
- if (parsed->gsm_type != BSS_MAP_MSG_COMPLETE_LAYER_3) {
- LOGP(DNAT, LOGL_ERROR,
- "Rejecting CR message due wrong GSM Type %d\n", parsed->gsm_type);
- return -1;
- }
-
- /* the parsed has had some basic l3 length check */
- len = msg->l3h[1];
- if (msgb_l3len(msg) - 3 < len) {
- LOGP(DNAT, LOGL_ERROR,
- "The CR Data has not enough space...\n");
- return -1;
- }
-
- msg->l4h = &msg->l3h[3];
- len -= 1;
-
- tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l4h, len, 0, 0);
-
- if (!TLVP_PRESENT(&tp, GSM0808_IE_LAYER_3_INFORMATION)) {
- LOGP(DNAT, LOGL_ERROR, "CR Data does not contain layer3 information.\n");
- return -1;
- }
-
- hdr48_len = TLVP_LEN(&tp, GSM0808_IE_LAYER_3_INFORMATION);
-
- if (hdr48_len < sizeof(*hdr48)) {
- LOGP(DNAT, LOGL_ERROR, "GSM48 header does not fit.\n");
- return -1;
- }
-
- hdr48 = (struct gsm48_hdr *) TLVP_VAL(&tp, GSM0808_IE_LAYER_3_INFORMATION);
- req.ctx = bsc;
- req.black_list = &bsc->nat->imsi_black_list;
- req.access_lists = &bsc->nat->access_lists;
- req.local_lst_name = bsc->cfg->acc_lst_name;
- req.global_lst_name = bsc->nat->acc_lst_name;
- req.bsc_nr = bsc->cfg->nr;
- return bsc_msg_filter_initial(hdr48, hdr48_len, &req, con_type, imsi, cause);
-}
-
-int bsc_nat_filter_dt(struct bsc_connection *bsc, struct msgb *msg,
- struct nat_sccp_connection *con, struct bsc_nat_parsed *parsed,
- struct bsc_filter_reject_cause *cause)
-{
- uint32_t len;
- struct gsm48_hdr *hdr48;
- struct bsc_filter_request req;
-
- cause->cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED;
- cause->lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED;
-
- if (con->filter_state.imsi_checked)
- return 0;
-
- /* only care about DTAP messages */
- if (parsed->bssap != BSSAP_MSG_DTAP)
- return 0;
-
- hdr48 = bsc_unpack_dtap(parsed, msg, &len);
- if (!hdr48)
- return -1;
-
- req.ctx = con;
- req.black_list = &bsc->nat->imsi_black_list;
- req.access_lists = &bsc->nat->access_lists;
- req.local_lst_name = bsc->cfg->acc_lst_name;
- req.global_lst_name = bsc->nat->acc_lst_name;
- req.bsc_nr = bsc->cfg->nr;
- return bsc_msg_filter_data(hdr48, len, &req, &con->filter_state, cause);
-}
diff --git a/openbsc/src/osmo-bsc_nat/bsc_nat_rewrite.c b/openbsc/src/osmo-bsc_nat/bsc_nat_rewrite.c
deleted file mode 100644
index e7c387c22..000000000
--- a/openbsc/src/osmo-bsc_nat/bsc_nat_rewrite.c
+++ /dev/null
@@ -1,714 +0,0 @@
-/*
- * Message rewriting functionality
- */
-/*
- * (C) 2010-2013 by Holger Hans Peter Freyther <zecke@selfish.org>
- * (C) 2010-2013 by On-Waves
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <openbsc/bsc_nat.h>
-#include <openbsc/bsc_nat_sccp.h>
-#include <openbsc/bsc_msc.h>
-#include <openbsc/gsm_data.h>
-#include <openbsc/debug.h>
-#include <openbsc/ipaccess.h>
-#include <openbsc/nat_rewrite_trie.h>
-
-#include <osmocom/core/linuxlist.h>
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/utils.h>
-#include <osmocom/gsm/gsm0808.h>
-#include <osmocom/gsm/ipa.h>
-
-#include <osmocom/gsm/protocol/gsm_08_08.h>
-#include <osmocom/gsm/protocol/gsm_04_11.h>
-
-#include <osmocom/sccp/sccp.h>
-
-static char *trie_lookup(struct nat_rewrite *trie, const char *number,
- regoff_t off, void *ctx)
-{
- struct nat_rewrite_rule *rule;
-
- if (!trie) {
- LOGP(DCC, LOGL_ERROR,
- "Asked to do a table lookup but no table.\n");
- return NULL;
- }
-
- rule = nat_rewrite_lookup(trie, number);
- if (!rule) {
- LOGP(DCC, LOGL_DEBUG,
- "Couldn't find a prefix rule for %s\n", number);
- return NULL;
- }
-
- return talloc_asprintf(ctx, "%s%s", rule->rewrite, &number[off]);
-}
-
-static char *match_and_rewrite_number(void *ctx, const char *number,
- const char *imsi, struct llist_head *list,
- struct nat_rewrite *trie)
-{
- struct bsc_nat_num_rewr_entry *entry;
- char *new_number = NULL;
-
- /* need to find a replacement and then fix it */
- llist_for_each_entry(entry, list, list) {
- regmatch_t matches[2];
-
- /* check the IMSI match */
- if (regexec(&entry->msisdn_reg, imsi, 0, NULL, 0) != 0)
- continue;
-
- /* this regexp matches... */
- if (regexec(&entry->num_reg, number, 2, matches, 0) == 0
- && matches[1].rm_eo != -1) {
- if (entry->is_prefix_lookup)
- new_number = trie_lookup(trie, number,
- matches[1].rm_so, ctx);
- else
- new_number = talloc_asprintf(ctx, "%s%s",
- entry->replace,
- &number[matches[1].rm_so]);
- }
-
- if (new_number)
- break;
- }
-
- return new_number;
-}
-
-static char *rewrite_isdn_number(struct bsc_nat *nat, struct llist_head *rewr_list,
- void *ctx, const char *imsi,
- struct gsm_mncc_number *called)
-{
- char int_number[sizeof(called->number) + 2];
- char *number = called->number;
-
- if (llist_empty(&nat->num_rewr)) {
- LOGP(DCC, LOGL_DEBUG, "Rewrite rules empty.\n");
- return NULL;
- }
-
- /* only ISDN plan */
- if (called->plan != 1) {
- LOGP(DCC, LOGL_DEBUG, "Called plan is not 1 it was %d\n",
- called->plan);
- return NULL;
- }
-
- /* international, prepend */
- if (called->type == 1) {
- int_number[0] = '+';
- memcpy(&int_number[1], number, strlen(number) + 1);
- number = int_number;
- }
-
- return match_and_rewrite_number(ctx, number,
- imsi, rewr_list, nat->num_rewr_trie);
-}
-
-static void update_called_number(struct gsm_mncc_number *called,
- const char *chosen_number)
-{
- if (strncmp(chosen_number, "00", 2) == 0) {
- called->type = 1;
- osmo_strlcpy(called->number, chosen_number + 2,
- sizeof(called->number));
- } else {
- /* rewrite international to unknown */
- if (called->type == 1)
- called->type = 0;
- osmo_strlcpy(called->number, chosen_number,
- sizeof(called->number));
- }
-}
-
-/**
- * Rewrite non global numbers... according to rules based on the IMSI
- */
-static struct msgb *rewrite_setup(struct bsc_nat *nat, struct msgb *msg,
- struct bsc_nat_parsed *parsed, const char *imsi,
- struct gsm48_hdr *hdr48, const uint32_t len)
-{
- struct tlv_parsed tp;
- unsigned int payload_len;
- struct gsm_mncc_number called;
- struct msgb *out;
- char *new_number_pre = NULL, *new_number_post = NULL, *chosen_number;
- uint8_t *outptr;
- const uint8_t *msgptr;
- int sec_len;
-
- /* decode and rewrite the message */
- payload_len = len - sizeof(*hdr48);
- tlv_parse(&tp, &gsm48_att_tlvdef, hdr48->data, payload_len, 0, 0);
-
- /* no number, well let us ignore it */
- if (!TLVP_PRESENT(&tp, GSM48_IE_CALLED_BCD))
- return NULL;
-
- memset(&called, 0, sizeof(called));
- gsm48_decode_called(&called,
- TLVP_VAL(&tp, GSM48_IE_CALLED_BCD) - 1);
-
- /* check if it looks international and stop */
- LOGP(DCC, LOGL_DEBUG,
- "Pre-Rewrite for IMSI(%s) Plan(%d) Type(%d) Number(%s)\n",
- imsi, called.plan, called.type, called.number);
- new_number_pre = rewrite_isdn_number(nat, &nat->num_rewr, msg, imsi, &called);
-
- if (!new_number_pre) {
- LOGP(DCC, LOGL_DEBUG, "No IMSI(%s) match found, returning message.\n",
- imsi);
- return NULL;
- }
-
- if (strlen(new_number_pre) > sizeof(called.number)) {
- LOGP(DCC, LOGL_ERROR, "Number %s is too long for structure.\n",
- new_number_pre);
- talloc_free(new_number_pre);
- return NULL;
- }
- update_called_number(&called, new_number_pre);
-
- /* another run through the re-write engine with other rules */
- LOGP(DCC, LOGL_DEBUG,
- "Post-Rewrite for IMSI(%s) Plan(%d) Type(%d) Number(%s)\n",
- imsi, called.plan, called.type, called.number);
- new_number_post = rewrite_isdn_number(nat, &nat->num_rewr_post, msg,
- imsi, &called);
- chosen_number = new_number_post ? new_number_post : new_number_pre;
-
-
- if (strlen(chosen_number) > sizeof(called.number)) {
- LOGP(DCC, LOGL_ERROR, "Number %s is too long for structure.\n",
- chosen_number);
- talloc_free(new_number_pre);
- talloc_free(new_number_post);
- return NULL;
- }
-
- /*
- * Need to create a new message now based on the old onew
- * with a new number. We can sadly not patch this in place
- * so we will need to regenerate it.
- */
-
- out = msgb_alloc_headroom(4096, 128, "changed-setup");
- if (!out) {
- LOGP(DCC, LOGL_ERROR, "Failed to allocate.\n");
- talloc_free(new_number_pre);
- talloc_free(new_number_post);
- return NULL;
- }
-
- /* copy the header */
- outptr = msgb_put(out, sizeof(*hdr48));
- memcpy(outptr, hdr48, sizeof(*hdr48));
-
- /* copy everything up to the number */
- sec_len = TLVP_VAL(&tp, GSM48_IE_CALLED_BCD) - 2 - &hdr48->data[0];
- outptr = msgb_put(out, sec_len);
- memcpy(outptr, &hdr48->data[0], sec_len);
-
- /* create the new number */
- update_called_number(&called, chosen_number);
- LOGP(DCC, LOGL_DEBUG,
- "Chosen number for IMSI(%s) is Plan(%d) Type(%d) Number(%s)\n",
- imsi, called.plan, called.type, called.number);
- gsm48_encode_called(out, &called);
-
- /* copy thre rest */
- msgptr = TLVP_VAL(&tp, GSM48_IE_CALLED_BCD) +
- TLVP_LEN(&tp, GSM48_IE_CALLED_BCD);
- sec_len = payload_len - (msgptr - &hdr48->data[0]);
- outptr = msgb_put(out, sec_len);
- memcpy(outptr, msgptr, sec_len);
-
- talloc_free(new_number_pre);
- talloc_free(new_number_post);
- return out;
-}
-
-/**
- * Find a new SMSC address, returns an allocated string that needs to be
- * freed or is NULL.
- */
-static char *find_new_smsc(struct bsc_nat *nat, void *ctx, const char *imsi,
- const char *smsc_addr, const char *dest_nr)
-{
- struct bsc_nat_num_rewr_entry *entry;
- char *new_number = NULL;
- uint8_t dest_match = llist_empty(&nat->tpdest_match);
-
- /* We will find a new number now */
- llist_for_each_entry(entry, &nat->smsc_rewr, list) {
- regmatch_t matches[2];
-
- /* check the IMSI match */
- if (regexec(&entry->msisdn_reg, imsi, 0, NULL, 0) != 0)
- continue;
-
- /* this regexp matches... */
- if (regexec(&entry->num_reg, smsc_addr, 2, matches, 0) == 0 &&
- matches[1].rm_eo != -1)
- new_number = talloc_asprintf(ctx, "%s%s",
- entry->replace,
- &smsc_addr[matches[1].rm_so]);
- if (new_number)
- break;
- }
-
- if (!new_number)
- return NULL;
-
- /*
- * now match the number against another list
- */
- llist_for_each_entry(entry, &nat->tpdest_match, list) {
- /* check the IMSI match */
- if (regexec(&entry->msisdn_reg, imsi, 0, NULL, 0) != 0)
- continue;
-
- if (regexec(&entry->num_reg, dest_nr, 0, NULL, 0) == 0) {
- dest_match = 1;
- break;
- }
- }
-
- if (!dest_match) {
- talloc_free(new_number);
- return NULL;
- }
-
- return new_number;
-}
-
-/**
- * Clear the TP-SRR from the TPDU header
- */
-static uint8_t sms_new_tpdu_hdr(struct bsc_nat *nat, const char *imsi,
- const char *dest_nr, uint8_t hdr)
-{
- struct bsc_nat_num_rewr_entry *entry;
-
- /* We will find a new number now */
- llist_for_each_entry(entry, &nat->sms_clear_tp_srr, list) {
- /* check the IMSI match */
- if (regexec(&entry->msisdn_reg, imsi, 0, NULL, 0) != 0)
- continue;
- if (regexec(&entry->num_reg, dest_nr, 0, NULL, 0) != 0)
- continue;
-
- /* matched phone number and imsi */
- return hdr & ~0x20;
- }
-
- return hdr;
-}
-
-/**
- * Check if we need to rewrite the number. For this SMS.
- */
-static char *sms_new_dest_nr(struct bsc_nat *nat, void *ctx,
- const char *imsi, const char *dest_nr)
-{
- return match_and_rewrite_number(ctx, dest_nr, imsi,
- &nat->sms_num_rewr, NULL);
-}
-
-/**
- * This is a helper for GSM 04.11 8.2.5.2 Destination address element
- */
-void sms_encode_addr_element(struct msgb *out, const char *new_number,
- int format, int tp_data)
-{
- uint8_t new_addr_len;
- uint8_t new_addr[26];
-
- /*
- * Copy the new number. We let libosmocore encode it, then set
- * the extension followed after the length. Depending on if
- * we want to write RP we will let the TLV code add the
- * length for us or we need to use strlen... This is not very clear
- * as of 03.40 and 04.11.
- */
- new_addr_len = gsm48_encode_bcd_number(new_addr, ARRAY_SIZE(new_addr),
- 1, new_number);
- new_addr[1] = format;
- if (tp_data) {
- uint8_t *data = msgb_put(out, new_addr_len);
- memcpy(data, new_addr, new_addr_len);
- data[0] = strlen(new_number);
- } else {
- msgb_lv_put(out, new_addr_len - 1, new_addr + 1);
- }
-}
-
-static struct msgb *sms_create_new(uint8_t type, uint8_t ref,
- struct gsm48_hdr *old_hdr48,
- const uint8_t *orig_addr_ptr,
- int orig_addr_len, const char *new_number,
- const uint8_t *data_ptr, int data_len,
- uint8_t tpdu_first_byte,
- const int old_dest_len, const char *new_dest_nr)
-{
- struct gsm48_hdr *new_hdr48;
- struct msgb *out;
-
- /*
- * We need to re-create the patched structure. This is why we have
- * saved the above pointers.
- */
- out = msgb_alloc_headroom(4096, 128, "changed-smsc");
- if (!out) {
- LOGP(DNAT, LOGL_ERROR, "Failed to allocate.\n");
- return NULL;
- }
-
- out->l2h = out->data;
- msgb_v_put(out, GSM411_MT_RP_DATA_MO);
- msgb_v_put(out, ref);
- msgb_lv_put(out, orig_addr_len, orig_addr_ptr);
-
- sms_encode_addr_element(out, new_number, 0x91, 0);
-
-
- /* Patch the TPDU from here on */
-
- /**
- * Do we need to put a new TP-Destination-Address (TP-DA) here or
- * can we copy the old thing? For the TP-DA we need to find out the
- * new size.
- */
- if (new_dest_nr) {
- uint8_t *data, *new_size;
-
- /* reserve the size and write the header */
- new_size = msgb_put(out, 1);
- out->l3h = new_size + 1;
- msgb_v_put(out, tpdu_first_byte);
- msgb_v_put(out, data_ptr[1]);
-
- /* encode the new number and put it */
- if (strncmp(new_dest_nr, "00", 2) == 0)
- sms_encode_addr_element(out, new_dest_nr + 2, 0x91, 1);
- else
- sms_encode_addr_element(out, new_dest_nr, 0x81, 1);
-
- /* Copy the rest after the TP-DS */
- data = msgb_put(out, data_len - 2 - 1 - old_dest_len);
- memcpy(data, &data_ptr[2 + 1 + old_dest_len], data_len - 2 - 1 - old_dest_len);
-
- /* fill in the new size */
- new_size[0] = msgb_l3len(out);
- } else {
- msgb_v_put(out, data_len);
- msgb_tv_fixed_put(out, tpdu_first_byte, data_len - 1, &data_ptr[1]);
- }
-
- /* prepend GSM 04.08 header */
- new_hdr48 = (struct gsm48_hdr *) msgb_push(out, sizeof(*new_hdr48) + 1);
- memcpy(new_hdr48, old_hdr48, sizeof(*old_hdr48));
- new_hdr48->data[0] = msgb_l2len(out);
-
- return out;
-}
-
-/**
- * Parse the SMS and check if it needs to be rewritten
- */
-static struct msgb *rewrite_sms(struct bsc_nat *nat, struct msgb *msg,
- struct bsc_nat_parsed *parsed, const char *imsi,
- struct gsm48_hdr *hdr48, const uint32_t len)
-{
- unsigned int payload_len;
- unsigned int cp_len;
-
- uint8_t ref;
- uint8_t orig_addr_len, *orig_addr_ptr;
- uint8_t dest_addr_len, *dest_addr_ptr;
- uint8_t data_len, *data_ptr;
- char smsc_addr[30];
-
-
- uint8_t dest_len, orig_dest_len;
- char _dest_nr[30];
- char *dest_nr;
- char *new_dest_nr;
-
- char *new_number = NULL;
- uint8_t tpdu_hdr;
- struct msgb *out;
-
- payload_len = len - sizeof(*hdr48);
- if (payload_len < 1) {
- LOGP(DNAT, LOGL_ERROR, "SMS too short for things. %d\n", payload_len);
- return NULL;
- }
-
- cp_len = hdr48->data[0];
- if (payload_len + 1 < cp_len) {
- LOGP(DNAT, LOGL_ERROR, "SMS RPDU can not fit in: %d %d\n", cp_len, payload_len);
- return NULL;
- }
-
- if (hdr48->data[1] != GSM411_MT_RP_DATA_MO)
- return NULL;
-
- if (cp_len < 5) {
- LOGP(DNAT, LOGL_ERROR, "RD-DATA can not fit in the CP len: %d\n", cp_len);
- return NULL;
- }
-
- /* RP */
- ref = hdr48->data[2];
- orig_addr_len = hdr48->data[3];
- orig_addr_ptr = &hdr48->data[4];
-
- /* the +1 is for checking if the following element has some space */
- if (cp_len < 3 + orig_addr_len + 1) {
- LOGP(DNAT, LOGL_ERROR, "RP-Originator addr does not fit: %d\n", orig_addr_len);
- return NULL;
- }
-
- dest_addr_len = hdr48->data[3 + orig_addr_len + 1];
- dest_addr_ptr = &hdr48->data[3 + orig_addr_len + 2];
-
- if (cp_len < 3 + orig_addr_len + 1 + dest_addr_len + 1) {
- LOGP(DNAT, LOGL_ERROR, "RP-Destination addr does not fit: %d\n", dest_addr_len);
- return NULL;
- }
- gsm48_decode_bcd_number(smsc_addr, ARRAY_SIZE(smsc_addr), dest_addr_ptr - 1, 1);
-
- data_len = hdr48->data[3 + orig_addr_len + 1 + dest_addr_len + 1];
- data_ptr = &hdr48->data[3 + orig_addr_len + 1 + dest_addr_len + 2];
-
- if (cp_len < 3 + orig_addr_len + 1 + dest_addr_len + 1 + data_len) {
- LOGP(DNAT, LOGL_ERROR, "RP-Data does not fit: %d\n", data_len);
- return NULL;
- }
-
- if (data_len < 3) {
- LOGP(DNAT, LOGL_ERROR, "SMS-SUBMIT is too short.\n");
- return NULL;
- }
-
- /* TP-PDU starts here */
- if ((data_ptr[0] & 0x03) != GSM340_SMS_SUBMIT_MS2SC)
- return NULL;
-
- /*
- * look into the phone number. The length is in semi-octets, we will
- * need to add the byte for the number type as well.
- */
- orig_dest_len = data_ptr[2];
- dest_len = ((orig_dest_len + 1) / 2) + 1;
- if (data_len < dest_len + 3 || dest_len < 2) {
- LOGP(DNAT, LOGL_ERROR, "SMS-SUBMIT can not have TP-DestAddr.\n");
- return NULL;
- }
-
- if ((data_ptr[3] & 0x80) == 0) {
- LOGP(DNAT, LOGL_ERROR, "TP-DestAddr has extension. Not handled.\n");
- return NULL;
- }
-
- if ((data_ptr[3] & 0x0F) == 0) {
- LOGP(DNAT, LOGL_ERROR, "TP-DestAddr is of unknown type.\n");
- return NULL;
- }
-
- /**
- * Besides of what I think I read in GSM 03.40 and 04.11 the TP-DA
- * contains the semi-octets as length (strlen), change it to the
- * the number of bytes, but then change it back.
- */
- data_ptr[2] = dest_len;
- gsm48_decode_bcd_number(_dest_nr + 2, ARRAY_SIZE(_dest_nr) - 2,
- &data_ptr[2], 1);
- data_ptr[2] = orig_dest_len;
- if ((data_ptr[3] & 0x70) == 0x10) {
- _dest_nr[0] = _dest_nr[1] = '0';
- dest_nr = &_dest_nr[0];
- } else {
- dest_nr = &_dest_nr[2];
- }
-
- /**
- * Call functions to rewrite the data
- */
- tpdu_hdr = sms_new_tpdu_hdr(nat, imsi, dest_nr, data_ptr[0]);
- new_number = find_new_smsc(nat, msg, imsi, smsc_addr, dest_nr);
- new_dest_nr = sms_new_dest_nr(nat, msg, imsi, dest_nr);
-
- if (tpdu_hdr == data_ptr[0] && !new_number && !new_dest_nr)
- return NULL;
-
- out = sms_create_new(GSM411_MT_RP_DATA_MO, ref, hdr48,
- orig_addr_ptr, orig_addr_len,
- new_number ? new_number : smsc_addr,
- data_ptr, data_len, tpdu_hdr,
- dest_len, new_dest_nr);
- talloc_free(new_number);
- talloc_free(new_dest_nr);
- return out;
-}
-
-struct msgb *bsc_nat_rewrite_msg(struct bsc_nat *nat, struct msgb *msg, struct bsc_nat_parsed *parsed, const char *imsi)
-{
- struct gsm48_hdr *hdr48;
- uint32_t len;
- uint8_t msg_type, proto;
- struct msgb *new_msg = NULL, *sccp;
- uint8_t link_id;
-
- if (!imsi || strlen(imsi) < 5)
- return msg;
-
- /* only care about DTAP messages */
- if (parsed->bssap != BSSAP_MSG_DTAP)
- return msg;
- if (!parsed->dest_local_ref)
- return msg;
-
- hdr48 = bsc_unpack_dtap(parsed, msg, &len);
- if (!hdr48)
- return msg;
-
- link_id = msg->l3h[1];
- proto = gsm48_hdr_pdisc(hdr48);
- msg_type = gsm48_hdr_msg_type(hdr48);
-
- if (proto == GSM48_PDISC_CC && msg_type == GSM48_MT_CC_SETUP)
- new_msg = rewrite_setup(nat, msg, parsed, imsi, hdr48, len);
- else if (proto == GSM48_PDISC_SMS && msg_type == GSM411_MT_CP_DATA)
- new_msg = rewrite_sms(nat, msg, parsed, imsi, hdr48, len);
-
- if (!new_msg)
- return msg;
-
- /* wrap with DTAP, SCCP, then IPA. TODO: Stop copying */
- gsm0808_prepend_dtap_header(new_msg, link_id);
- sccp = sccp_create_dt1(parsed->dest_local_ref, new_msg->data, new_msg->len);
- talloc_free(new_msg);
-
- if (!sccp) {
- LOGP(DNAT, LOGL_ERROR, "Failed to allocate.\n");
- return msg;
- }
-
- ipa_prepend_header(sccp, IPAC_PROTO_SCCP);
-
- /* the parsed hangs off from msg but it needs to survive */
- talloc_steal(sccp, parsed);
- msgb_free(msg);
- return sccp;
-}
-
-static void num_rewr_free_data(struct bsc_nat_num_rewr_entry *entry)
-{
- regfree(&entry->msisdn_reg);
- regfree(&entry->num_reg);
- talloc_free(entry->replace);
-}
-
-void bsc_nat_num_rewr_entry_adapt(void *ctx, struct llist_head *head,
- const struct osmo_config_list *list)
-{
- struct bsc_nat_num_rewr_entry *entry, *tmp;
- struct osmo_config_entry *cfg_entry;
-
- /* free the old data */
- llist_for_each_entry_safe(entry, tmp, head, list) {
- num_rewr_free_data(entry);
- llist_del(&entry->list);
- talloc_free(entry);
- }
-
-
- if (!list)
- return;
-
- llist_for_each_entry(cfg_entry, &list->entry, list) {
- char *regexp;
- if (cfg_entry->text[0] == '+') {
- LOGP(DNAT, LOGL_ERROR,
- "Plus is not allowed in the number\n");
- continue;
- }
-
- entry = talloc_zero(ctx, struct bsc_nat_num_rewr_entry);
- if (!entry) {
- LOGP(DNAT, LOGL_ERROR,
- "Allocation of the num_rewr entry failed.\n");
- continue;
- }
-
- entry->replace = talloc_strdup(entry, cfg_entry->text);
- if (!entry->replace) {
- LOGP(DNAT, LOGL_ERROR,
- "Failed to copy the replacement text.\n");
- talloc_free(entry);
- continue;
- }
-
- if (strcmp("prefix_lookup", entry->replace) == 0)
- entry->is_prefix_lookup = 1;
-
- /* we will now build a regexp string */
- if (cfg_entry->mcc[0] == '^') {
- regexp = talloc_strdup(entry, cfg_entry->mcc);
- } else {
- regexp = talloc_asprintf(entry, "^%s%s",
- cfg_entry->mcc[0] == '*' ?
- "[0-9][0-9][0-9]" : cfg_entry->mcc,
- cfg_entry->mnc[0] == '*' ?
- "[0-9][0-9]" : cfg_entry->mnc);
- }
-
- if (!regexp) {
- LOGP(DNAT, LOGL_ERROR, "Failed to create a regexp string.\n");
- talloc_free(entry);
- continue;
- }
-
- if (regcomp(&entry->msisdn_reg, regexp, 0) != 0) {
- LOGP(DNAT, LOGL_ERROR,
- "Failed to compile regexp '%s'\n", regexp);
- talloc_free(regexp);
- talloc_free(entry);
- continue;
- }
-
- talloc_free(regexp);
- if (regcomp(&entry->num_reg, cfg_entry->option, REG_EXTENDED) != 0) {
- LOGP(DNAT, LOGL_ERROR,
- "Failed to compile regexp '%s'\n", cfg_entry->option);
- regfree(&entry->msisdn_reg);
- talloc_free(entry);
- continue;
- }
-
- /* we have copied the number */
- llist_add_tail(&entry->list, head);
- }
-}
diff --git a/openbsc/src/osmo-bsc_nat/bsc_nat_rewrite_trie.c b/openbsc/src/osmo-bsc_nat/bsc_nat_rewrite_trie.c
deleted file mode 100644
index 633fa87d0..000000000
--- a/openbsc/src/osmo-bsc_nat/bsc_nat_rewrite_trie.c
+++ /dev/null
@@ -1,259 +0,0 @@
-/* Handling for loading a re-write file/database */
-/*
- * (C) 2013 by On-Waves
- * (C) 2013 by Holger Hans Peter Freyther <zecke@selfish.org>
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <openbsc/nat_rewrite_trie.h>
-#include <openbsc/debug.h>
-#include <openbsc/vty.h>
-
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/utils.h>
-
-#include <assert.h>
-#include <stdio.h>
-#include <string.h>
-#include <ctype.h>
-
-#define CHECK_IS_DIGIT_OR_FAIL(prefix, pos) \
- if (!isdigit(prefix[pos]) && prefix[pos] != '+') { \
- LOGP(DNAT, LOGL_ERROR, \
- "Prefix(%s) contains non ascii text at(%d=%c)\n", \
- prefix, pos, prefix[pos]); \
- goto fail; \
- }
-#define TO_INT(c) \
- ((c) == '+' ? 10 : ((c - '0') % 10))
-
-static void insert_rewrite_node(struct nat_rewrite_rule *rule, struct nat_rewrite *root)
-{
- struct nat_rewrite_rule *new = &root->rule;
-
- const int len = strlen(rule->prefix);
- int i;
-
- if (len <= 0) {
- LOGP(DNAT, LOGL_ERROR, "An empty prefix does not make sense.\n");
- goto fail;
- }
-
- for (i = 0; i < len - 1; ++i) {
- int pos;
-
- /* check if the input is valid */
- CHECK_IS_DIGIT_OR_FAIL(rule->prefix, i);
-
- /* check if the next node is already valid */
- pos = TO_INT(rule->prefix[i]);
- if (!new->rules[pos]) {
- new->rules[pos] = talloc_zero(root, struct nat_rewrite_rule);
- if (!new->rules[pos]) {
- LOGP(DNAT, LOGL_ERROR,
- "Failed to allocate memory.\n");
- goto fail;
- }
-
- new->rules[pos]->empty = 1;
- }
-
- /* we continue here */
- new = new->rules[pos];
- }
-
- /* new now points to the place where we want to add it */
- int pos;
-
- /* check if the input is valid */
- CHECK_IS_DIGIT_OR_FAIL(rule->prefix, (len - 1));
-
- /* check if the next node is already valid */
- pos = TO_INT(rule->prefix[len - 1]);
- if (!new->rules[pos])
- new->rules[pos] = rule;
- else if (new->rules[pos]->empty) {
- /* copy over entries */
- new->rules[pos]->empty = 0;
- memcpy(new->rules[pos]->prefix, rule->prefix, sizeof(rule->prefix));
- memcpy(new->rules[pos]->rewrite, rule->rewrite, sizeof(rule->rewrite));
- talloc_free(rule);
- } else {
- LOGP(DNAT, LOGL_ERROR,
- "Prefix(%s) is already installed\n", rule->prefix);
- goto fail;
- }
-
- root->prefixes += 1;
- return;
-
-fail:
- talloc_free(rule);
- return;
-}
-
-static void handle_line(struct nat_rewrite *rewrite, char *line)
-{
- char *split;
- struct nat_rewrite_rule *rule;
- size_t size_prefix, size_end, len;
-
-
- /* Find the ',' in the line */
- len = strlen(line);
- split = strstr(line, ",");
- if (!split) {
- LOGP(DNAT, LOGL_ERROR, "Line doesn't contain ','\n");
- return;
- }
-
- /* Check if there is space for the rewrite rule */
- size_prefix = split - line;
- if (len - size_prefix <= 2) {
- LOGP(DNAT, LOGL_ERROR, "No rewrite available.\n");
- return;
- }
-
- /* Continue after the ',' to the end */
- split = &line[size_prefix + 1];
- size_end = strlen(split) - 1;
-
- /* Check if both strings can fit into the static array */
- if (size_prefix > sizeof(rule->prefix) - 1) {
- LOGP(DNAT, LOGL_ERROR,
- "Prefix is too long with %zu\n", size_prefix);
- return;
- }
-
- if (size_end > sizeof(rule->rewrite) - 1) {
- LOGP(DNAT, LOGL_ERROR,
- "Rewrite is too long with %zu on %s\n",
- size_end, &line[size_prefix + 1]);
- return;
- }
-
- /* Now create the entry and insert it into the trie */
- rule = talloc_zero(rewrite, struct nat_rewrite_rule);
- if (!rule) {
- LOGP(DNAT, LOGL_ERROR, "Can not allocate memory\n");
- return;
- }
-
- memcpy(rule->prefix, line, size_prefix);
- assert(size_prefix < sizeof(rule->prefix));
- rule->prefix[size_prefix] = '\0';
-
- memcpy(rule->rewrite, split, size_end);
- assert(size_end < sizeof(rule->rewrite));
- rule->rewrite[size_end] = '\0';
-
- /* now insert and balance the tree */
- insert_rewrite_node(rule, rewrite);
-}
-
-struct nat_rewrite *nat_rewrite_parse(void *ctx, const char *filename)
-{
- FILE *file;
- char *line = NULL;
- size_t n = 0;
- struct nat_rewrite *res;
-
- file = fopen(filename, "r");
- if (!file)
- return NULL;
-
- res = talloc_zero(ctx, struct nat_rewrite);
- if (!res) {
- fclose(file);
- return NULL;
- }
-
- /* mark the root as empty */
- res->rule.empty = 1;
-
- while (getline(&line, &n, file) != -1) {
- handle_line(res, line);
- }
-
- free(line);
- fclose(file);
- return res;
-}
-
-/**
- * Simple find that tries to do a longest match...
- */
-struct nat_rewrite_rule *nat_rewrite_lookup(struct nat_rewrite *rewrite,
- const char *prefix)
-{
- struct nat_rewrite_rule *rule = &rewrite->rule;
- struct nat_rewrite_rule *last = NULL;
- const int len = OSMO_MIN(strlen(prefix), (sizeof(rule->prefix) - 1));
- int i;
-
- for (i = 0; rule && i < len; ++i) {
- int pos;
-
- CHECK_IS_DIGIT_OR_FAIL(prefix, i);
- pos = TO_INT(prefix[i]);
-
- rule = rule->rules[pos];
- if (rule && !rule->empty)
- last = rule;
- }
-
- return last;
-
-fail:
- return NULL;
-}
-
-static void nat_rewrite_dump_rec(struct nat_rewrite_rule *rule)
-{
- int i;
- if (!rule->empty)
- printf("%s,%s\n", rule->prefix, rule->rewrite);
-
- for (i = 0; i < ARRAY_SIZE(rule->rules); ++i) {
- if (!rule->rules[i])
- continue;
- nat_rewrite_dump_rec(rule->rules[i]);
- }
-}
-
-void nat_rewrite_dump(struct nat_rewrite *rewrite)
-{
- nat_rewrite_dump_rec(&rewrite->rule);
-}
-
-static void nat_rewrite_dump_rec_vty(struct vty *vty, struct nat_rewrite_rule *rule)
-{
- int i;
- if (!rule->empty)
- vty_out(vty, "%s,%s%s", rule->prefix, rule->rewrite, VTY_NEWLINE);
-
- for (i = 0; i < ARRAY_SIZE(rule->rules); ++i) {
- if (!rule->rules[i])
- continue;
- nat_rewrite_dump_rec_vty(vty, rule->rules[i]);
- }
-}
-
-void nat_rewrite_dump_vty(struct vty *vty, struct nat_rewrite *rewrite)
-{
- nat_rewrite_dump_rec_vty(vty, &rewrite->rule);
-}
diff --git a/openbsc/src/osmo-bsc_nat/bsc_nat_utils.c b/openbsc/src/osmo-bsc_nat/bsc_nat_utils.c
deleted file mode 100644
index c12b29f1f..000000000
--- a/openbsc/src/osmo-bsc_nat/bsc_nat_utils.c
+++ /dev/null
@@ -1,535 +0,0 @@
-
-/* BSC Multiplexer/NAT Utilities */
-
-/*
- * (C) 2010-2011 by Holger Hans Peter Freyther <zecke@selfish.org>
- * (C) 2010-2011 by On-Waves
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <openbsc/bsc_nat.h>
-#include <openbsc/bsc_nat_sccp.h>
-#include <openbsc/bsc_msg_filter.h>
-#include <openbsc/bsc_msc.h>
-#include <openbsc/gsm_data.h>
-#include <openbsc/debug.h>
-#include <openbsc/ipaccess.h>
-#include <openbsc/vty.h>
-
-#include <osmocom/core/linuxlist.h>
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/stats.h>
-#include <osmocom/gsm/gsm0808.h>
-#include <osmocom/gsm/ipa.h>
-
-#include <osmocom/gsm/protocol/gsm_08_08.h>
-#include <osmocom/gsm/protocol/gsm_04_11.h>
-
-#include <osmocom/sccp/sccp.h>
-
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <unistd.h>
-
-static const struct rate_ctr_desc bsc_cfg_ctr_description[] = {
- [BCFG_CTR_SCCP_CONN] = { "sccp.conn", "SCCP Connections "},
- [BCFG_CTR_SCCP_CALLS] = { "sccp.calls", "SCCP Assignment Commands "},
- [BCFG_CTR_NET_RECONN] = { "net.reconnects", "Network reconnects "},
- [BCFG_CTR_DROPPED_SCCP] = { "dropped.sccp", "Dropped SCCP connections."},
- [BCFG_CTR_DROPPED_CALLS] = { "dropped.calls", "Dropped active calls. "},
- [BCFG_CTR_REJECTED_CR] = { "rejected.cr", "Rejected CR due filter "},
- [BCFG_CTR_REJECTED_MSG] = { "rejected.msg", "Rejected MSG due filter "},
- [BCFG_CTR_ILL_PACKET] = { "rejected.ill", "Rejected due parse error "},
- [BCFG_CTR_CON_TYPE_LU] = { "conn.lu", "Conn Location Update "},
- [BCFG_CTR_CON_CMSERV_RQ] = { "conn.rq", "Conn CM Service Req "},
- [BCFG_CTR_CON_PAG_RESP] = { "conn.pag", "Conn Paging Response "},
- [BCFG_CTR_CON_SSA] = { "conn.ssa", "Conn USSD "},
- [BCFG_CTR_CON_OTHER] = { "conn.other", "Conn Other "},
-};
-
-static const struct rate_ctr_group_desc bsc_cfg_ctrg_desc = {
- .group_name_prefix = "nat.bsc",
- .group_description = "NAT BSC Statistics",
- .num_ctr = ARRAY_SIZE(bsc_cfg_ctr_description),
- .ctr_desc = bsc_cfg_ctr_description,
- .class_id = OSMO_STATS_CLASS_PEER,
-};
-
-struct bsc_nat *bsc_nat_alloc(void)
-{
- struct bsc_nat *nat = talloc_zero(tall_bsc_ctx, struct bsc_nat);
- if (!nat)
- return NULL;
-
- nat->main_dest = talloc_zero(nat, struct bsc_msc_dest);
- if (!nat->main_dest) {
- talloc_free(nat);
- return NULL;
- }
-
- INIT_LLIST_HEAD(&nat->sccp_connections);
- INIT_LLIST_HEAD(&nat->bsc_connections);
- INIT_LLIST_HEAD(&nat->paging_groups);
- INIT_LLIST_HEAD(&nat->bsc_configs);
- INIT_LLIST_HEAD(&nat->access_lists);
- INIT_LLIST_HEAD(&nat->dests);
- INIT_LLIST_HEAD(&nat->num_rewr);
- INIT_LLIST_HEAD(&nat->num_rewr_post);
- INIT_LLIST_HEAD(&nat->smsc_rewr);
- INIT_LLIST_HEAD(&nat->tpdest_match);
- INIT_LLIST_HEAD(&nat->sms_clear_tp_srr);
- INIT_LLIST_HEAD(&nat->sms_num_rewr);
-
- nat->stats.sccp.conn = osmo_counter_alloc("nat.sccp.conn");
- nat->stats.sccp.calls = osmo_counter_alloc("nat.sccp.calls");
- nat->stats.bsc.reconn = osmo_counter_alloc("nat.bsc.conn");
- nat->stats.bsc.auth_fail = osmo_counter_alloc("nat.bsc.auth_fail");
- nat->stats.msc.reconn = osmo_counter_alloc("nat.msc.conn");
- nat->stats.ussd.reconn = osmo_counter_alloc("nat.ussd.conn");
- nat->auth_timeout = 2;
- nat->ping_timeout = 20;
- nat->pong_timeout = 5;
-
- llist_add(&nat->main_dest->list, &nat->dests);
- nat->main_dest->ip = talloc_strdup(nat, "127.0.0.1");
- nat->main_dest->port = 5000;
-
- return nat;
-}
-
-void bsc_nat_free(struct bsc_nat *nat)
-{
- struct bsc_config *cfg, *tmp;
- struct bsc_msg_acc_lst *lst, *tmp_lst;
-
- llist_for_each_entry_safe(cfg, tmp, &nat->bsc_configs, entry)
- bsc_config_free(cfg);
- llist_for_each_entry_safe(lst, tmp_lst, &nat->access_lists, list)
- bsc_msg_acc_lst_delete(lst);
-
- bsc_nat_num_rewr_entry_adapt(nat, &nat->num_rewr, NULL);
- bsc_nat_num_rewr_entry_adapt(nat, &nat->num_rewr_post, NULL);
- bsc_nat_num_rewr_entry_adapt(nat, &nat->sms_clear_tp_srr, NULL);
- bsc_nat_num_rewr_entry_adapt(nat, &nat->sms_num_rewr, NULL);
- bsc_nat_num_rewr_entry_adapt(nat, &nat->tpdest_match, NULL);
-
- osmo_counter_free(nat->stats.sccp.conn);
- osmo_counter_free(nat->stats.sccp.calls);
- osmo_counter_free(nat->stats.bsc.reconn);
- osmo_counter_free(nat->stats.bsc.auth_fail);
- osmo_counter_free(nat->stats.msc.reconn);
- osmo_counter_free(nat->stats.ussd.reconn);
- talloc_free(nat->mgcp_cfg);
- talloc_free(nat);
-}
-
-void bsc_nat_set_msc_ip(struct bsc_nat *nat, const char *ip)
-{
- osmo_talloc_replace_string(nat, &nat->main_dest->ip, ip);
-}
-
-struct bsc_connection *bsc_connection_alloc(struct bsc_nat *nat)
-{
- struct bsc_connection *con = talloc_zero(nat, struct bsc_connection);
- if (!con)
- return NULL;
-
- con->nat = nat;
- osmo_wqueue_init(&con->write_queue, 100);
- INIT_LLIST_HEAD(&con->cmd_pending);
- INIT_LLIST_HEAD(&con->pending_dlcx);
- return con;
-}
-
-struct bsc_config *bsc_config_alloc(struct bsc_nat *nat, const char *token,
- unsigned int number)
-{
- struct bsc_config *conf = talloc_zero(nat, struct bsc_config);
- if (!conf)
- return NULL;
-
- conf->token = talloc_strdup(conf, token);
- conf->nr = number;
- conf->nat = nat;
- conf->max_endpoints = 32;
- conf->paging_group = PAGIN_GROUP_UNASSIGNED;
-
- INIT_LLIST_HEAD(&conf->lac_list);
-
- llist_add_tail(&conf->entry, &nat->bsc_configs);
- ++nat->num_bsc;
-
- conf->stats.ctrg = rate_ctr_group_alloc(conf, &bsc_cfg_ctrg_desc, conf->nr);
- if (!conf->stats.ctrg) {
- llist_del(&conf->entry);
- talloc_free(conf);
- return NULL;
- }
-
- return conf;
-}
-
-struct bsc_config *bsc_config_by_token(struct bsc_nat *nat, const char *token, int len)
-{
- struct bsc_config *conf;
-
- llist_for_each_entry(conf, &nat->bsc_configs, entry) {
- /*
- * Add the '\0' of the token for the memcmp, the IPA messages
- * for some reason added null termination.
- */
- const int token_len = strlen(conf->token) + 1;
-
- if (token_len == len && memcmp(conf->token, token, token_len) == 0)
- return conf;
- }
-
- return NULL;
-}
-
-void bsc_config_free(struct bsc_config *cfg)
-{
- llist_del(&cfg->entry);
- rate_ctr_group_free(cfg->stats.ctrg);
- cfg->nat->num_bsc--;
- OSMO_ASSERT(cfg->nat->num_bsc >= 0)
- talloc_free(cfg);
-}
-
-static void _add_lac(void *ctx, struct llist_head *list, int _lac)
-{
- struct bsc_lac_entry *lac;
-
- llist_for_each_entry(lac, list, entry)
- if (lac->lac == _lac)
- return;
-
- lac = talloc_zero(ctx, struct bsc_lac_entry);
- if (!lac) {
- LOGP(DNAT, LOGL_ERROR, "Failed to allocate.\n");
- return;
- }
-
- lac->lac = _lac;
- llist_add_tail(&lac->entry, list);
-}
-
-static void _del_lac(struct llist_head *list, int _lac)
-{
- struct bsc_lac_entry *lac;
-
- llist_for_each_entry(lac, list, entry)
- if (lac->lac == _lac) {
- llist_del(&lac->entry);
- talloc_free(lac);
- return;
- }
-}
-
-void bsc_config_add_lac(struct bsc_config *cfg, int _lac)
-{
- _add_lac(cfg, &cfg->lac_list, _lac);
-}
-
-void bsc_config_del_lac(struct bsc_config *cfg, int _lac)
-{
- _del_lac(&cfg->lac_list, _lac);
-}
-
-struct bsc_nat_paging_group *bsc_nat_paging_group_create(struct bsc_nat *nat, int group)
-{
- struct bsc_nat_paging_group *pgroup;
-
- pgroup = talloc_zero(nat, struct bsc_nat_paging_group);
- if (!pgroup) {
- LOGP(DNAT, LOGL_ERROR, "Failed to allocate a paging group.\n");
- return NULL;
- }
-
- pgroup->nr = group;
- INIT_LLIST_HEAD(&pgroup->lists);
- llist_add_tail(&pgroup->entry, &nat->paging_groups);
- return pgroup;
-}
-
-void bsc_nat_paging_group_delete(struct bsc_nat_paging_group *pgroup)
-{
- llist_del(&pgroup->entry);
- talloc_free(pgroup);
-}
-
-struct bsc_nat_paging_group *bsc_nat_paging_group_num(struct bsc_nat *nat, int group)
-{
- struct bsc_nat_paging_group *pgroup;
-
- llist_for_each_entry(pgroup, &nat->paging_groups, entry)
- if (pgroup->nr == group)
- return pgroup;
-
- return NULL;
-}
-
-void bsc_nat_paging_group_add_lac(struct bsc_nat_paging_group *pgroup, int lac)
-{
- _add_lac(pgroup, &pgroup->lists, lac);
-}
-
-void bsc_nat_paging_group_del_lac(struct bsc_nat_paging_group *pgroup, int lac)
-{
- _del_lac(&pgroup->lists, lac);
-}
-
-int bsc_config_handles_lac(struct bsc_config *cfg, int lac_nr)
-{
- struct bsc_nat_paging_group *pgroup;
- struct bsc_lac_entry *entry;
-
- llist_for_each_entry(entry, &cfg->lac_list, entry)
- if (entry->lac == lac_nr)
- return 1;
-
- /* now lookup the paging group */
- pgroup = bsc_nat_paging_group_num(cfg->nat, cfg->paging_group);
- if (!pgroup)
- return 0;
-
- llist_for_each_entry(entry, &pgroup->lists, entry)
- if (entry->lac == lac_nr)
- return 1;
-
- return 0;
-}
-
-void sccp_connection_destroy(struct nat_sccp_connection *conn)
-{
- LOGP(DNAT, LOGL_DEBUG, "Destroy 0x%x <-> 0x%x mapping for con %p\n",
- sccp_src_ref_to_int(&conn->real_ref),
- sccp_src_ref_to_int(&conn->patched_ref), conn->bsc);
- bsc_mgcp_dlcx(conn);
- llist_del(&conn->list_entry);
- talloc_free(conn);
-}
-
-
-int bsc_nat_find_paging(struct msgb *msg,
- const uint8_t **out_data, int *out_leng)
-{
- int data_length;
- const uint8_t *data;
- struct tlv_parsed tp;
-
- if (!msg->l3h || msgb_l3len(msg) < 3) {
- LOGP(DNAT, LOGL_ERROR, "Paging message is too short.\n");
- return -1;
- }
-
- tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 3, msgb_l3len(msg) - 3, 0, 0);
- if (!TLVP_PRESENT(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST)) {
- LOGP(DNAT, LOGL_ERROR, "No CellIdentifier List inside paging msg.\n");
- return -2;
- }
-
- data_length = TLVP_LEN(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST);
- data = TLVP_VAL(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST);
-
- /* No need to try a different BSS */
- if (data[0] == CELL_IDENT_BSS) {
- return -3;
- } else if (data[0] != CELL_IDENT_LAC) {
- LOGP(DNAT, LOGL_ERROR, "Unhandled cell ident discrminator: %d\n", data[0]);
- return -4;
- }
-
- *out_data = &data[1];
- *out_leng = data_length - 1;
- return 0;
-}
-
-int bsc_write_mgcp(struct bsc_connection *bsc, const uint8_t *data, unsigned int length)
-{
- struct msgb *msg;
-
- if (length > 4096 - 128) {
- LOGP(DLINP, LOGL_ERROR, "Can not send message of that size.\n");
- return -1;
- }
-
- msg = msgb_alloc_headroom(4096, 128, "to-bsc");
- if (!msg) {
- LOGP(DLINP, LOGL_ERROR, "Failed to allocate memory for BSC msg.\n");
- return -1;
- }
-
- /* copy the data */
- msg->l3h = msgb_put(msg, length);
- memcpy(msg->l3h, data, length);
-
- return bsc_write(bsc, msg, IPAC_PROTO_MGCP_OLD);
-}
-
-int bsc_write(struct bsc_connection *bsc, struct msgb *msg, int proto)
-{
- return bsc_do_write(&bsc->write_queue, msg, proto);
-}
-
-int bsc_do_write(struct osmo_wqueue *queue, struct msgb *msg, int proto)
-{
- /* prepend the header */
- ipa_prepend_header(msg, proto);
- return bsc_write_msg(queue, msg);
-}
-
-int bsc_write_msg(struct osmo_wqueue *queue, struct msgb *msg)
-{
- if (osmo_wqueue_enqueue(queue, msg) != 0) {
- LOGP(DLINP, LOGL_ERROR, "Failed to enqueue the write.\n");
- msgb_free(msg);
- return -1;
- }
-
- return 0;
-}
-
-struct gsm48_hdr *bsc_unpack_dtap(struct bsc_nat_parsed *parsed,
- struct msgb *msg, uint32_t *len)
-{
- /* gsm_type is actually the size of the dtap */
- *len = parsed->gsm_type;
- if (*len < msgb_l3len(msg) - 3) {
- LOGP(DNAT, LOGL_ERROR, "Not enough space for DTAP.\n");
- return NULL;
- }
-
- if (msgb_l3len(msg) - 3 < msg->l3h[2]) {
- LOGP(DNAT, LOGL_ERROR,
- "GSM48 payload does not fit: %d %d\n",
- msg->l3h[2], msgb_l3len(msg) - 3);
- return NULL;
- }
-
- msg->l4h = &msg->l3h[3];
- return (struct gsm48_hdr *) msg->l4h;
-}
-
-static const char *con_types [] = {
- [FLT_CON_TYPE_NONE] = "n/a",
- [FLT_CON_TYPE_LU] = "Location Update",
- [FLT_CON_TYPE_CM_SERV_REQ] = "CM Serv Req",
- [FLT_CON_TYPE_PAG_RESP] = "Paging Response",
- [FLT_CON_TYPE_SSA] = "Supplementar Service Activation",
- [FLT_CON_TYPE_LOCAL_REJECT] = "Local Reject",
- [FLT_CON_TYPE_OTHER] = "Other",
-};
-
-const char *bsc_con_type_to_string(int type)
-{
- return con_types[type];
-}
-
-int bsc_nat_msc_is_connected(struct bsc_nat *nat)
-{
- return nat->msc_con->is_connected;
-}
-
-static const int con_to_ctr[] = {
- [FLT_CON_TYPE_NONE] = -1,
- [FLT_CON_TYPE_LU] = BCFG_CTR_CON_TYPE_LU,
- [FLT_CON_TYPE_CM_SERV_REQ] = BCFG_CTR_CON_CMSERV_RQ,
- [FLT_CON_TYPE_PAG_RESP] = BCFG_CTR_CON_PAG_RESP,
- [FLT_CON_TYPE_SSA] = BCFG_CTR_CON_SSA,
- [FLT_CON_TYPE_LOCAL_REJECT] = -1,
- [FLT_CON_TYPE_OTHER] = BCFG_CTR_CON_OTHER,
-};
-
-int bsc_conn_type_to_ctr(struct nat_sccp_connection *conn)
-{
- return con_to_ctr[conn->filter_state.con_type];
-}
-
-int bsc_write_cb(struct osmo_fd *bfd, struct msgb *msg)
-{
- int rc;
-
- rc = write(bfd->fd, msg->data, msg->len);
- if (rc != msg->len)
- LOGP(DNAT, LOGL_ERROR, "Failed to write message to the BSC.\n");
-
- return rc;
-}
-
-static void extract_lac(const uint8_t *data, uint16_t *lac, uint16_t *ci)
-{
- memcpy(lac, &data[0], sizeof(*lac));
- memcpy(ci, &data[2], sizeof(*ci));
-
- *lac = ntohs(*lac);
- *ci = ntohs(*ci);
-}
-
-int bsc_nat_extract_lac(struct bsc_connection *bsc,
- struct nat_sccp_connection *con,
- struct bsc_nat_parsed *parsed, struct msgb *msg)
-{
- int data_length;
- const uint8_t *data;
- struct tlv_parsed tp;
- uint16_t lac, ci;
-
- if (parsed->gsm_type != BSS_MAP_MSG_COMPLETE_LAYER_3) {
- LOGP(DNAT, LOGL_ERROR, "Can only extract LAC from Complete Layer3\n");
- return -1;
- }
-
- if (!msg->l3h || msgb_l3len(msg) < 3) {
- LOGP(DNAT, LOGL_ERROR, "Complete Layer3 mssage is too short.\n");
- return -1;
- }
-
- tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 3, msgb_l3len(msg) - 3, 0, 0);
- if (!TLVP_PRESENT(&tp, GSM0808_IE_CELL_IDENTIFIER)) {
- LOGP(DNAT, LOGL_ERROR, "No CellIdentifier List inside paging msg.\n");
- return -2;
- }
-
- data_length = TLVP_LEN(&tp, GSM0808_IE_CELL_IDENTIFIER);
- data = TLVP_VAL(&tp, GSM0808_IE_CELL_IDENTIFIER);
-
- /* Attemt to get the LAC/CI from it */
- if (data[0] == CELL_IDENT_WHOLE_GLOBAL) {
- if (data_length != 8) {
- LOGP(DNAT, LOGL_ERROR,
- "Ident too short: %d\n", data_length);
- return -3;
- }
- extract_lac(&data[1 + 3], &lac, &ci);
- } else if (data[0] == CELL_IDENT_LAC_AND_CI) {
- if (data_length != 5) {
- LOGP(DNAT, LOGL_ERROR,
- "Ident too short: %d\n", data_length);
- return -3;
- }
- extract_lac(&data[1], &lac, &ci);
- } else {
- LOGP(DNAT, LOGL_ERROR,
- "Unhandled cell identifier: %d\n", data[0]);
- return -1;
- }
-
- con->lac = lac;
- con->ci = ci;
- return 0;
-}
diff --git a/openbsc/src/osmo-bsc_nat/bsc_nat_vty.c b/openbsc/src/osmo-bsc_nat/bsc_nat_vty.c
deleted file mode 100644
index a11ae151e..000000000
--- a/openbsc/src/osmo-bsc_nat/bsc_nat_vty.c
+++ /dev/null
@@ -1,1336 +0,0 @@
-/* OpenBSC NAT interface to quagga VTY */
-/* (C) 2010-2015 by Holger Hans Peter Freyther
- * (C) 2010-2015 by On-Waves
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <openbsc/vty.h>
-#include <openbsc/gsm_data.h>
-#include <openbsc/bsc_nat.h>
-#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 <openbsc/vty.h>
-#include <openbsc/nat_rewrite_trie.h>
-
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/rate_ctr.h>
-#include <osmocom/core/utils.h>
-#include <osmocom/vty/logging.h>
-#include <osmocom/vty/misc.h>
-#include <openbsc/osmux.h>
-
-#include <osmocom/sccp/sccp.h>
-
-#include <stdlib.h>
-#include <stdbool.h>
-
-static struct bsc_nat *_nat;
-
-
-#define BSC_STR "Information about BSCs\n"
-#define MGCP_STR "MGCP related status\n"
-#define PAGING_STR "Paging\n"
-#define SMSC_REWRITE "SMSC Rewriting\n"
-
-static struct cmd_node nat_node = {
- NAT_NODE,
- "%s(config-nat)# ",
- 1,
-};
-
-static struct cmd_node bsc_node = {
- NAT_BSC_NODE,
- "%s(config-nat-bsc)# ",
- 1,
-};
-
-static struct cmd_node pgroup_node = {
- PGROUP_NODE,
- "%s(config-nat-paging-group)# ",
- 1,
-};
-
-static int config_write_pgroup(struct vty *vty)
-{
- return CMD_SUCCESS;
-}
-
-static void dump_lac(struct vty *vty, struct llist_head *head)
-{
- struct bsc_lac_entry *lac;
- llist_for_each_entry(lac, head, entry)
- vty_out(vty, " location_area_code %u%s", lac->lac, VTY_NEWLINE);
-}
-
-
-static void write_pgroup_lst(struct vty *vty, struct bsc_nat_paging_group *pgroup)
-{
- vty_out(vty, " paging-group %d%s", pgroup->nr, VTY_NEWLINE);
- dump_lac(vty, &pgroup->lists);
-}
-
-static int config_write_nat(struct vty *vty)
-{
- struct bsc_msg_acc_lst *lst;
- struct bsc_nat_paging_group *pgroup;
-
- vty_out(vty, "nat%s", VTY_NEWLINE);
- vty_out(vty, " msc ip %s%s", _nat->main_dest->ip, VTY_NEWLINE);
- vty_out(vty, " msc port %d%s", _nat->main_dest->port, VTY_NEWLINE);
- vty_out(vty, " timeout auth %d%s", _nat->auth_timeout, VTY_NEWLINE);
- vty_out(vty, " timeout ping %d%s", _nat->ping_timeout, VTY_NEWLINE);
- vty_out(vty, " timeout pong %d%s", _nat->pong_timeout, VTY_NEWLINE);
- if (_nat->include_file)
- vty_out(vty, " bscs-config-file %s%s", _nat->include_file, VTY_NEWLINE);
- if (_nat->token)
- vty_out(vty, " token %s%s", _nat->token, VTY_NEWLINE);
- vty_out(vty, " ip-dscp %d%s", _nat->bsc_ip_dscp, VTY_NEWLINE);
- if (_nat->acc_lst_name)
- vty_out(vty, " access-list-name %s%s", _nat->acc_lst_name, VTY_NEWLINE);
- if (_nat->imsi_black_list_fn)
- vty_out(vty, " imsi-black-list-file-name %s%s",
- _nat->imsi_black_list_fn, VTY_NEWLINE);
- if (_nat->ussd_lst_name)
- vty_out(vty, " ussd-list-name %s%s", _nat->ussd_lst_name, VTY_NEWLINE);
- if (_nat->ussd_query)
- vty_out(vty, " ussd-query %s%s", _nat->ussd_query, VTY_NEWLINE);
- if (_nat->ussd_token)
- vty_out(vty, " ussd-token %s%s", _nat->ussd_token, VTY_NEWLINE);
- if (_nat->ussd_local)
- vty_out(vty, " ussd-local-ip %s%s", _nat->ussd_local, VTY_NEWLINE);
-
- if (_nat->num_rewr_name)
- vty_out(vty, " number-rewrite %s%s", _nat->num_rewr_name, VTY_NEWLINE);
- if (_nat->num_rewr_post_name)
- vty_out(vty, " number-rewrite-post %s%s",
- _nat->num_rewr_post_name, VTY_NEWLINE);
-
- if (_nat->smsc_rewr_name)
- vty_out(vty, " rewrite-smsc addr %s%s",
- _nat->smsc_rewr_name, VTY_NEWLINE);
- if (_nat->tpdest_match_name)
- vty_out(vty, " rewrite-smsc tp-dest-match %s%s",
- _nat->tpdest_match_name, VTY_NEWLINE);
- if (_nat->sms_clear_tp_srr_name)
- vty_out(vty, " sms-clear-tp-srr %s%s",
- _nat->sms_clear_tp_srr_name, VTY_NEWLINE);
- if (_nat->sms_num_rewr_name)
- vty_out(vty, " sms-number-rewrite %s%s",
- _nat->sms_num_rewr_name, VTY_NEWLINE);
- if (_nat->num_rewr_trie_name)
- vty_out(vty, " prefix-tree %s%s",
- _nat->num_rewr_trie_name, VTY_NEWLINE);
-
- llist_for_each_entry(lst, &_nat->access_lists, list)
- bsc_msg_acc_lst_write(vty, lst);
- llist_for_each_entry(pgroup, &_nat->paging_groups, entry)
- write_pgroup_lst(vty, pgroup);
- if (_nat->mgcp_ipa)
- vty_out(vty, " use-msc-ipa-for-mgcp%s", VTY_NEWLINE);
- vty_out(vty, " %ssdp-ensure-amr-mode-set%s",
- _nat->sdp_ensure_amr_mode_set ? "" : "no ", VTY_NEWLINE);
-
- return CMD_SUCCESS;
-}
-
-static void config_write_bsc_single(struct vty *vty, struct bsc_config *bsc)
-{
- vty_out(vty, " bsc %u%s", bsc->nr, VTY_NEWLINE);
- vty_out(vty, " token %s%s", bsc->token, VTY_NEWLINE);
- if (bsc->key_present)
- vty_out(vty, " auth-key %s%s", osmo_hexdump(bsc->key, 16), VTY_NEWLINE);
- dump_lac(vty, &bsc->lac_list);
- if (bsc->description)
- vty_out(vty, " description %s%s", bsc->description, VTY_NEWLINE);
- if (bsc->acc_lst_name)
- vty_out(vty, " access-list-name %s%s", bsc->acc_lst_name, VTY_NEWLINE);
- vty_out(vty, " max-endpoints %d%s", bsc->max_endpoints, VTY_NEWLINE);
- if (bsc->paging_group != -1)
- vty_out(vty, " paging group %d%s", bsc->paging_group, VTY_NEWLINE);
- vty_out(vty, " paging forbidden %d%s", bsc->forbid_paging, VTY_NEWLINE);
- switch (bsc->osmux) {
- case OSMUX_USAGE_ON:
- vty_out(vty, " osmux on%s", VTY_NEWLINE);
- break;
- case OSMUX_USAGE_ONLY:
- vty_out(vty, " osmux only%s", VTY_NEWLINE);
- break;
- }
-}
-
-static int config_write_bsc(struct vty *vty)
-{
- struct bsc_config *bsc;
-
- llist_for_each_entry(bsc, &_nat->bsc_configs, entry)
- config_write_bsc_single(vty, bsc);
- return CMD_SUCCESS;
-}
-
-DEFUN(show_bscs, show_bscs_cmd, "show bscs-config",
- SHOW_STR "Show configured BSCs\n"
- "Both from included file and vty\n")
-{
- vty_out(vty, "BSCs configuration loaded from %s:%s", _nat->resolved_path,
- VTY_NEWLINE);
- return config_write_bsc(vty);
-}
-
-DEFUN(show_sccp, show_sccp_cmd, "show sccp connections",
- SHOW_STR "Display information about SCCP\n"
- "All active connections\n")
-{
- struct nat_sccp_connection *con;
- vty_out(vty, "Listing all open SCCP connections%s", VTY_NEWLINE);
-
- llist_for_each_entry(con, &_nat->sccp_connections, list_entry) {
- vty_out(vty, "For BSC Nr: %d BSC ref: 0x%x; MUX ref: 0x%x; Network has ref: %d ref: 0x%x MSC/BSC mux: 0x%x/0x%x type: %s%s",
- con->bsc->cfg ? con->bsc->cfg->nr : -1,
- sccp_src_ref_to_int(&con->real_ref),
- sccp_src_ref_to_int(&con->patched_ref),
- con->has_remote_ref,
- sccp_src_ref_to_int(&con->remote_ref),
- con->msc_endp, con->bsc_endp,
- bsc_con_type_to_string(con->filter_state.con_type),
- VTY_NEWLINE);
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN(show_nat_bsc, show_nat_bsc_cmd, "show nat num-bscs-configured",
- SHOW_STR "Display NAT configuration details\n"
- "BSCs-related\n")
-{
- vty_out(vty, "%d BSCs configured%s", _nat->num_bsc, VTY_NEWLINE);
- return CMD_SUCCESS;
-}
-
-DEFUN(show_bsc, show_bsc_cmd, "show bsc connections",
- SHOW_STR BSC_STR
- "All active connections\n")
-{
- struct bsc_connection *con;
- struct sockaddr_in sock;
- socklen_t len = sizeof(sock);
-
- llist_for_each_entry(con, &_nat->bsc_connections, list_entry) {
- getpeername(con->write_queue.bfd.fd, (struct sockaddr *) &sock, &len);
- vty_out(vty, "BSC nr: %d auth: %d fd: %d peername: %s pending-stats: %u%s",
- con->cfg ? con->cfg->nr : -1,
- con->authenticated, con->write_queue.bfd.fd,
- inet_ntoa(sock.sin_addr), con->pending_dlcx_count,
- VTY_NEWLINE);
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN(show_bsc_mgcp, show_bsc_mgcp_cmd, "show bsc mgcp NR",
- SHOW_STR BSC_STR MGCP_STR "Identifier of the BSC\n")
-{
- struct bsc_connection *con;
- int nr = atoi(argv[0]);
- int i, j, endp;
-
- llist_for_each_entry(con, &_nat->bsc_connections, list_entry) {
- int max;
- if (!con->cfg)
- continue;
- if (con->cfg->nr != nr)
- continue;
-
- /* this bsc has no audio endpoints yet */
- if (!con->_endpoint_status)
- continue;
-
- vty_out(vty, "MGCP Status for %d%s", con->cfg->nr, VTY_NEWLINE);
- max = bsc_mgcp_nr_multiplexes(con->max_endpoints);
- for (i = 0; i < max; ++i) {
- for (j = 1; j < 32; ++j) {
- endp = mgcp_timeslot_to_endpoint(i, j);
- vty_out(vty, " Endpoint 0x%x %s%s", endp,
- con->_endpoint_status[endp] == 0
- ? "free" : "allocated",
- VTY_NEWLINE);
- }
- }
- break;
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN(show_bsc_cfg, show_bsc_cfg_cmd, "show bsc config",
- SHOW_STR BSC_STR "Configuration of BSCs\n")
-{
- struct bsc_config *conf;
- llist_for_each_entry(conf, &_nat->bsc_configs, entry) {
- vty_out(vty, "BSC token: '%s' nr: %u%s",
- conf->token, conf->nr, VTY_NEWLINE);
- if (conf->acc_lst_name)
- vty_out(vty, " access-list: %s%s",
- conf->acc_lst_name, VTY_NEWLINE);
- vty_out(vty, " paging forbidden: %d%s",
- conf->forbid_paging, VTY_NEWLINE);
- if (conf->description)
- vty_out(vty, " description: %s%s", conf->description, VTY_NEWLINE);
- else
- vty_out(vty, " No description.%s", VTY_NEWLINE);
-
- }
-
- return CMD_SUCCESS;
-}
-
-static void dump_stat_total(struct vty *vty, struct bsc_nat *nat)
-{
- vty_out(vty, "NAT statistics%s", VTY_NEWLINE);
- vty_out(vty, " SCCP Connections %lu total, %lu calls%s",
- osmo_counter_get(nat->stats.sccp.conn),
- osmo_counter_get(nat->stats.sccp.calls), VTY_NEWLINE);
- vty_out(vty, " MSC Connections %lu%s",
- osmo_counter_get(nat->stats.msc.reconn), VTY_NEWLINE);
- vty_out(vty, " MSC Connected: %d%s",
- bsc_nat_msc_is_connected(nat), VTY_NEWLINE);
- vty_out(vty, " BSC Connections %lu total, %lu auth failed.%s",
- osmo_counter_get(nat->stats.bsc.reconn),
- osmo_counter_get(nat->stats.bsc.auth_fail), VTY_NEWLINE);
-}
-
-static void dump_stat_bsc(struct vty *vty, struct bsc_config *conf)
-{
- int connected = 0;
- struct bsc_connection *con;
-
- vty_out(vty, " BSC nr: %d%s",
- conf->nr, VTY_NEWLINE);
- vty_out_rate_ctr_group(vty, " ", conf->stats.ctrg);
-
- llist_for_each_entry(con, &conf->nat->bsc_connections, list_entry) {
- if (con->cfg != conf)
- continue;
- connected = 1;
- break;
- }
-
- vty_out(vty, " Connected: %d%s", connected, VTY_NEWLINE);
-}
-
-DEFUN(show_stats,
- show_stats_cmd,
- "show statistics [NR]",
- SHOW_STR "Display network statistics\n"
- "Number of the BSC\n")
-{
- struct bsc_config *conf;
-
- int nr = -1;
-
- if (argc == 1)
- nr = atoi(argv[0]);
-
- dump_stat_total(vty, _nat);
- llist_for_each_entry(conf, &_nat->bsc_configs, entry) {
- if (argc == 1 && nr != conf->nr)
- continue;
- dump_stat_bsc(vty, conf);
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN(show_stats_lac,
- show_stats_lac_cmd,
- "show statistics-by-lac <0-65535>",
- SHOW_STR "Display network statistics by lac\n"
- "The lac of the BSC\n")
-{
- int lac;
- struct bsc_config *conf;
-
- lac = atoi(argv[0]);
-
- dump_stat_total(vty, _nat);
- llist_for_each_entry(conf, &_nat->bsc_configs, entry) {
- if (!bsc_config_handles_lac(conf, lac))
- continue;
- dump_stat_bsc(vty, conf);
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN(show_msc,
- show_msc_cmd,
- "show msc connection",
- SHOW_STR "MSC related information\n"
- "Status of the A-link connection\n")
-{
- if (!_nat->msc_con) {
- vty_out(vty, "The MSC is not yet configured.%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- vty_out(vty, "MSC is connected: %d%s",
- bsc_nat_msc_is_connected(_nat), VTY_NEWLINE);
- return CMD_SUCCESS;
-}
-
-DEFUN(close_bsc,
- close_bsc_cmd,
- "close bsc connection BSC_NR",
- "Close\n" "A-link\n" "Connection\n" "Identifier of the BSC\n")
-{
- struct bsc_connection *bsc;
- int bsc_nr = atoi(argv[0]);
-
- llist_for_each_entry(bsc, &_nat->bsc_connections, list_entry) {
- if (!bsc->cfg || bsc->cfg->nr != bsc_nr)
- continue;
- bsc_close_connection(bsc);
- break;
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_nat, cfg_nat_cmd, "nat", "Configure the NAT")
-{
- vty->index = _nat;
- vty->node = NAT_NODE;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_nat_msc_ip,
- cfg_nat_msc_ip_cmd,
- "msc ip A.B.C.D",
- "MSC related configuration\n"
- "Configure the IP address\n" IP_STR)
-{
- bsc_nat_set_msc_ip(_nat, argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_nat_msc_port,
- cfg_nat_msc_port_cmd,
- "msc port <1-65500>",
- "MSC related configuration\n"
- "Configure the port\n"
- "Port number\n")
-{
- _nat->main_dest->port = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_nat_auth_time,
- cfg_nat_auth_time_cmd,
- "timeout auth <1-256>",
- "Timeout configuration\n"
- "Authentication timeout\n"
- "Timeout in seconds\n")
-{
- _nat->auth_timeout = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_nat_ping_time,
- cfg_nat_ping_time_cmd,
- "timeout ping NR",
- "Timeout configuration\n"
- "Time between two pings\n"
- "Timeout in seconds\n")
-{
- _nat->ping_timeout = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_nat_pong_time,
- cfg_nat_pong_time_cmd,
- "timeout pong NR",
- "Timeout configuration\n"
- "Waiting for pong timeout\n"
- "Timeout in seconds\n")
-{
- _nat->pong_timeout = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_nat_token, cfg_nat_token_cmd,
- "token TOKEN",
- "Authentication token configuration\n"
- "Token of the BSC, currently transferred in cleartext\n")
-{
- osmo_talloc_replace_string(_nat, &_nat->token, argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_nat_bsc_ip_dscp, cfg_nat_bsc_ip_dscp_cmd,
- "ip-dscp <0-255>",
- "Set the IP DSCP for the BSCs to use\n" "Set the IP_TOS attribute")
-{
- _nat->bsc_ip_dscp = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
-ALIAS_DEPRECATED(cfg_nat_bsc_ip_dscp, cfg_nat_bsc_ip_tos_cmd,
- "ip-tos <0-255>",
- "Use ip-dscp in the future.\n" "Set the DSCP\n")
-
-
-DEFUN(cfg_nat_acc_lst_name,
- cfg_nat_acc_lst_name_cmd,
- "access-list-name NAME",
- "Set the name of the access list to use.\n"
- "The name of the to be used access list.")
-{
- osmo_talloc_replace_string(_nat, &_nat->acc_lst_name, argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_nat_include,
- cfg_nat_include_cmd,
- "bscs-config-file NAME",
- "Set the filename of the BSC configuration to include.\n"
- "The filename to be included.")
-{
- char *path;
- int rc;
- struct bsc_config *cf1, *cf2;
- struct bsc_connection *con1, *con2;
-
- if ('/' == argv[0][0])
- osmo_talloc_replace_string(_nat, &_nat->resolved_path, argv[0]);
- else {
- path = talloc_asprintf(_nat, "%s/%s", _nat->include_base,
- argv[0]);
- osmo_talloc_replace_string(_nat, &_nat->resolved_path, path);
- talloc_free(path);
- }
-
- llist_for_each_entry_safe(cf1, cf2, &_nat->bsc_configs, entry) {
- cf1->remove = true;
- cf1->token_updated = false;
- }
-
- rc = vty_read_config_file(_nat->resolved_path, NULL);
- if (rc < 0) {
- vty_out(vty, "Failed to parse the config file %s: %s%s",
- _nat->resolved_path, strerror(-rc), VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- osmo_talloc_replace_string(_nat, &_nat->include_file, argv[0]);
-
- llist_for_each_entry_safe(con1, con2, &_nat->bsc_connections,
- list_entry) {
- if (con1->cfg)
- if (con1->cfg->token_updated || con1->cfg->remove)
- bsc_close_connection(con1);
- }
-
- llist_for_each_entry_safe(cf1, cf2, &_nat->bsc_configs, entry) {
- if (cf1->remove)
- bsc_config_free(cf1);
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_nat_no_acc_lst_name,
- cfg_nat_no_acc_lst_name_cmd,
- "no access-list-name",
- NO_STR "Remove the access list from the NAT.\n")
-{
- if (_nat->acc_lst_name) {
- talloc_free(_nat->acc_lst_name);
- _nat->acc_lst_name = NULL;
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_nat_imsi_black_list_fn,
- cfg_nat_imsi_black_list_fn_cmd,
- "imsi-black-list-file-name NAME",
- "IMSI black listing\n" "Filename IMSI and reject-cause\n")
-{
-
- osmo_talloc_replace_string(_nat, &_nat->imsi_black_list_fn, argv[0]);
- if (_nat->imsi_black_list_fn) {
- int rc;
- struct osmo_config_list *rewr = NULL;
- rewr = osmo_config_list_parse(_nat, _nat->imsi_black_list_fn);
- rc = bsc_filter_barr_adapt(_nat, &_nat->imsi_black_list, rewr);
- if (rc != 0) {
- vty_out(vty, "%%There was an error parsing the list."
- " Please see the error log.%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- return CMD_SUCCESS;
- }
-
- bsc_filter_barr_adapt(_nat, &_nat->imsi_black_list, NULL);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_nat_no_imsi_black_list_fn,
- cfg_nat_no_imsi_black_list_fn_cmd,
- "no imsi-black-list-file-name",
- NO_STR "Remove the imsi-black-list\n")
-{
- talloc_free(_nat->imsi_black_list_fn);
- _nat->imsi_black_list_fn = NULL;
- bsc_filter_barr_adapt(_nat, &_nat->imsi_black_list, NULL);
- return CMD_SUCCESS;
-}
-
-static int replace_rules(struct bsc_nat *nat, char **name,
- struct llist_head *head, const char *file)
-{
- struct osmo_config_list *rewr = NULL;
-
- osmo_talloc_replace_string(nat, name, file);
- if (*name) {
- rewr = osmo_config_list_parse(nat, *name);
- bsc_nat_num_rewr_entry_adapt(nat, head, rewr);
- talloc_free(rewr);
- return CMD_SUCCESS;
- } else {
- bsc_nat_num_rewr_entry_adapt(nat, head, NULL);
- return CMD_SUCCESS;
- }
-}
-
-DEFUN(cfg_nat_number_rewrite,
- cfg_nat_number_rewrite_cmd,
- "number-rewrite FILENAME",
- "Set the file with rewriting rules.\n" "Filename")
-{
- return replace_rules(_nat, &_nat->num_rewr_name,
- &_nat->num_rewr, argv[0]);
-}
-
-DEFUN(cfg_nat_no_number_rewrite,
- cfg_nat_no_number_rewrite_cmd,
- "no number-rewrite",
- NO_STR "Set the file with rewriting rules.\n")
-{
- talloc_free(_nat->num_rewr_name);
- _nat->num_rewr_name = NULL;
-
- bsc_nat_num_rewr_entry_adapt(NULL, &_nat->num_rewr, NULL);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_nat_number_rewrite_post,
- cfg_nat_number_rewrite_post_cmd,
- "number-rewrite-post FILENAME",
- "Set the file with post-routing rewriting rules.\n" "Filename")
-{
- return replace_rules(_nat, &_nat->num_rewr_post_name,
- &_nat->num_rewr_post, argv[0]);
-}
-
-DEFUN(cfg_nat_no_number_rewrite_post,
- cfg_nat_no_number_rewrite_post_cmd,
- "no number-rewrite-post",
- NO_STR "Set the file with post-routing rewriting rules.\n")
-{
- talloc_free(_nat->num_rewr_post_name);
- _nat->num_rewr_post_name = NULL;
-
- bsc_nat_num_rewr_entry_adapt(NULL, &_nat->num_rewr_post, NULL);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_nat_smsc_addr,
- cfg_nat_smsc_addr_cmd,
- "rewrite-smsc addr FILENAME",
- SMSC_REWRITE
- "The SMSC Address to match and replace in RP-DATA\n"
- "File with rules for the SMSC Address replacing\n")
-{
- return replace_rules(_nat, &_nat->smsc_rewr_name,
- &_nat->smsc_rewr, argv[0]);
-}
-
-DEFUN(cfg_nat_smsc_tpdest,
- cfg_nat_smsc_tpdest_cmd,
- "rewrite-smsc tp-dest-match FILENAME",
- SMSC_REWRITE
- "Match TP-Destination of a SMS.\n"
- "File with rules for matching MSISDN and TP-DEST\n")
-{
- return replace_rules(_nat, &_nat->tpdest_match_name,
- &_nat->tpdest_match, argv[0]);
-}
-
-DEFUN(cfg_nat_sms_clear_tpsrr,
- cfg_nat_sms_clear_tpsrr_cmd,
- "sms-clear-tp-srr FILENAME",
- "SMS TPDU Sender Report Request clearing\n"
- "Files with rules for matching MSISDN\n")
-{
- return replace_rules(_nat, &_nat->sms_clear_tp_srr_name,
- &_nat->sms_clear_tp_srr, argv[0]);
-}
-
-DEFUN(cfg_nat_no_sms_clear_tpsrr,
- cfg_nat_no_sms_clear_tpsrr_cmd,
- "no sms-clear-tp-srr",
- NO_STR
- "SMS TPDU Sender Report Request clearing\n")
-{
- talloc_free(_nat->sms_clear_tp_srr_name);
- _nat->sms_clear_tp_srr_name = NULL;
-
- bsc_nat_num_rewr_entry_adapt(NULL, &_nat->sms_clear_tp_srr, NULL);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_nat_sms_number_rewrite,
- cfg_nat_sms_number_rewrite_cmd,
- "sms-number-rewrite FILENAME",
- "SMS TP-DA Number rewriting\n"
- "Files with rules for matching MSISDN\n")
-{
- return replace_rules(_nat, &_nat->sms_num_rewr_name,
- &_nat->sms_num_rewr, argv[0]);
-}
-
-DEFUN(cfg_nat_no_sms_number_rewrite,
- cfg_nat_no_sms_number_rewrite_cmd,
- "no sms-number-rewrite",
- NO_STR "Disable SMS TP-DA rewriting\n")
-{
- talloc_free(_nat->sms_num_rewr_name);
- _nat->sms_num_rewr_name = NULL;
-
- bsc_nat_num_rewr_entry_adapt(NULL, &_nat->sms_num_rewr, NULL);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_nat_prefix_trie,
- cfg_nat_prefix_trie_cmd,
- "prefix-tree FILENAME",
- "Prefix tree for number rewriting\n" "File to load\n")
-{
- /* give up the old data */
- talloc_free(_nat->num_rewr_trie);
- _nat->num_rewr_trie = NULL;
-
- /* replace the file name */
- osmo_talloc_replace_string(_nat, &_nat->num_rewr_trie_name, argv[0]);
- if (!_nat->num_rewr_trie_name) {
- vty_out(vty, "%% prefix-tree no filename is present.%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- _nat->num_rewr_trie = nat_rewrite_parse(_nat, _nat->num_rewr_trie_name);
- if (!_nat->num_rewr_trie) {
- vty_out(vty, "%% prefix-tree parsing has failed.%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- vty_out(vty, "%% prefix-tree loaded %zu rules.%s",
- _nat->num_rewr_trie->prefixes, VTY_NEWLINE);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_nat_no_prefix_trie, cfg_nat_no_prefix_trie_cmd,
- "no prefix-tree",
- NO_STR "Prefix tree for number rewriting\n")
-{
- talloc_free(_nat->num_rewr_trie);
- _nat->num_rewr_trie = NULL;
- talloc_free(_nat->num_rewr_trie_name);
- _nat->num_rewr_trie_name = NULL;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(show_prefix_tree, show_prefix_tree_cmd,
- "show prefix-tree",
- SHOW_STR "Prefix tree for number rewriting\n")
-{
- if (!_nat->num_rewr_trie) {
- vty_out(vty, "%% there is now prefix tree loaded.%s",
- VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- nat_rewrite_dump_vty(vty, _nat->num_rewr_trie);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_nat_ussd_lst_name,
- cfg_nat_ussd_lst_name_cmd,
- "ussd-list-name NAME",
- "Set the name of the access list to check for IMSIs for USSD message\n"
- "The name of the access list for HLR USSD handling")
-{
- osmo_talloc_replace_string(_nat, &_nat->ussd_lst_name, argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_nat_ussd_query,
- cfg_nat_ussd_query_cmd,
- "ussd-query REGEXP",
- "Set the USSD query to match with the ussd-list-name\n"
- "The query to match")
-{
- if (gsm_parse_reg(_nat, &_nat->ussd_query_re, &_nat->ussd_query, argc, argv) != 0)
- return CMD_WARNING;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_nat_ussd_token,
- cfg_nat_ussd_token_cmd,
- "ussd-token TOKEN",
- "Set the token used to identify the USSD module\n" "Secret key\n")
-{
- osmo_talloc_replace_string(_nat, &_nat->ussd_token, argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_nat_ussd_local,
- cfg_nat_ussd_local_cmd,
- "ussd-local-ip A.B.C.D",
- "Set the IP to listen for the USSD Provider\n" "IP Address\n")
-{
- osmo_talloc_replace_string(_nat, &_nat->ussd_local, argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_nat_use_ipa_for_mgcp,
- cfg_nat_use_ipa_for_mgcp_cmd,
- "use-msc-ipa-for-mgcp",
- "This needs to be set at start. Handle MGCP messages through "
- "the IPA protocol and not through the UDP socket.\n")
-{
- if (_nat->mgcp_cfg->data)
- vty_out(vty,
- "%%the setting will not be applied right now.%s",
- VTY_NEWLINE);
- _nat->mgcp_ipa = 1;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_nat_sdp_amr_mode_set,
- cfg_nat_sdp_amr_mode_set_cmd,
- "sdp-ensure-amr-mode-set",
- "Ensure that SDP records include a mode-set\n")
-{
- _nat->sdp_ensure_amr_mode_set = 1;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_nat_no_sdp_amr_mode_set,
- cfg_nat_no_sdp_amr_mode_set_cmd,
- "no sdp-ensure-amr-mode-set",
- NO_STR "Ensure that SDP records include a mode-set\n")
-{
- _nat->sdp_ensure_amr_mode_set = 0;
- return CMD_SUCCESS;
-}
-
-/* per BSC configuration */
-DEFUN(cfg_bsc, cfg_bsc_cmd, "bsc BSC_NR",
- "BSC configuration\n" "Identifier of the BSC\n")
-{
- int bsc_nr = atoi(argv[0]);
- struct bsc_config *bsc = bsc_config_num(_nat, bsc_nr);
-
- /* allocate a new one */
- if (!bsc)
- bsc = bsc_config_alloc(_nat, "unknown", bsc_nr);
-
- if (!bsc)
- return CMD_WARNING;
-
- bsc->remove = false;
- vty->index = bsc;
- vty->node = NAT_BSC_NODE;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bsc_token, cfg_bsc_token_cmd, "token TOKEN",
- "Authentication token configuration\n"
- "Token of the BSC, currently transferred in cleartext\n")
-{
- struct bsc_config *conf = vty->index;
-
- if (strncmp(conf->token, argv[0], 128) != 0)
- conf->token_updated = true;
-
- osmo_talloc_replace_string(conf, &conf->token, argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bsc_auth_key, cfg_bsc_auth_key_cmd,
- "auth-key KEY",
- "Authentication (secret) key configuration\n"
- "Non null security key\n")
-{
- struct bsc_config *conf = vty->index;
-
- memset(conf->key, 0, sizeof(conf->key));
- osmo_hexparse(argv[0], conf->key, sizeof(conf->key));
- conf->key_present = 1;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bsc_no_auth_key, cfg_bsc_no_auth_key_cmd,
- "no auth-key",
- NO_STR "Authentication (secret) key configuration\n")
-{
- struct bsc_config *conf = vty->index;
-
- memset(conf->key, 0, sizeof(conf->key));
- conf->key_present = 0;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bsc_lac, cfg_bsc_lac_cmd, "location_area_code <0-65535>",
- "Add the Location Area Code (LAC) of this BSC\n" "LAC\n")
-{
- struct bsc_config *tmp;
- struct bsc_config *conf = vty->index;
-
- int lac = atoi(argv[0]);
-
- if (lac == GSM_LAC_RESERVED_DETACHED || lac == GSM_LAC_RESERVED_ALL_BTS) {
- vty_out(vty, "%% LAC %d is reserved by GSM 04.08%s",
- lac, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- /* verify that the LACs are unique */
- llist_for_each_entry(tmp, &_nat->bsc_configs, entry) {
- if (bsc_config_handles_lac(tmp, lac)) {
- if (tmp->nr != conf->nr) {
- vty_out(vty, "%% LAC %d is already used.%s", lac,
- VTY_NEWLINE);
- return CMD_ERR_INCOMPLETE;
- }
- }
- }
-
- bsc_config_add_lac(conf, lac);
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bsc_no_lac, cfg_bsc_no_lac_cmd,
- "no location_area_code <0-65535>",
- NO_STR "Remove the Location Area Code (LAC) of this BSC\n" "LAC\n")
-{
- int lac = atoi(argv[0]);
- struct bsc_config *conf = vty->index;
-
- bsc_config_del_lac(conf, lac);
- return CMD_SUCCESS;
-}
-
-DEFUN(show_bar_lst,
- show_bar_lst_cmd,
- "show imsi-black-list",
- SHOW_STR "IMSIs barred from the network\n")
-{
- struct rb_node *node;
-
- vty_out(vty, "IMSIs barred from the network:%s", VTY_NEWLINE);
-
- for (node = rb_first(&_nat->imsi_black_list); node; node = rb_next(node)) {
- struct bsc_filter_barr_entry *entry;
- entry = rb_entry(node, struct bsc_filter_barr_entry, node);
-
- vty_out(vty, " IMSI(%s) CM-Reject-Cause(%d) LU-Reject-Cause(%d)%s",
- entry->imsi, entry->cm_reject_cause, entry->lu_reject_cause,
- VTY_NEWLINE);
- }
-
- return CMD_SUCCESS;
-}
-
-
-DEFUN(cfg_bsc_acc_lst_name,
- cfg_bsc_acc_lst_name_cmd,
- "access-list-name NAME",
- "Set the name of the access list to use.\n"
- "The name of the to be used access list.")
-{
- struct bsc_config *conf = vty->index;
-
- osmo_talloc_replace_string(conf, &conf->acc_lst_name, argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bsc_no_acc_lst_name,
- cfg_bsc_no_acc_lst_name_cmd,
- "no access-list-name",
- NO_STR "Do not use an access-list for the BSC.\n")
-{
- struct bsc_config *conf = vty->index;
-
- if (conf->acc_lst_name) {
- talloc_free(conf->acc_lst_name);
- conf->acc_lst_name = NULL;
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bsc_max_endps, cfg_bsc_max_endps_cmd,
- "max-endpoints <1-1024>",
- "Highest endpoint to use (exclusively)\n" "Number of ports\n")
-{
- struct bsc_config *conf = vty->index;
-
- conf->max_endpoints = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bsc_paging,
- cfg_bsc_paging_cmd,
- "paging forbidden (0|1)",
- PAGING_STR "Forbid sending PAGING REQUESTS to the BSC.\n"
- "Do not forbid\n" "Forbid\n")
-{
- struct bsc_config *conf = vty->index;
-
- if (strcmp("1", argv[0]) == 0)
- conf->forbid_paging = 1;
- else
- conf->forbid_paging = 0;
-
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bsc_desc,
- cfg_bsc_desc_cmd,
- "description DESC",
- "Provide a description for the given BSC.\n" "Description\n")
-{
- struct bsc_config *conf = vty->index;
-
- osmo_talloc_replace_string(conf, &conf->description, argv[0]);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_bsc_paging_grp,
- cfg_bsc_paging_grp_cmd,
- "paging group <0-1000>",
- PAGING_STR "Use a paging group\n" "Paging Group to use\n")
-{
- struct bsc_config *conf = vty->index;
- conf->paging_group = atoi(argv[0]);
- return CMD_SUCCESS;
-}
-
-ALIAS_DEPRECATED(cfg_bsc_paging_grp, cfg_bsc_old_grp_cmd,
- "paging-group <0-1000>",
- "Use a paging group\n" "Paging Group to use\n")
-
-DEFUN(cfg_bsc_no_paging_grp,
- cfg_bsc_no_paging_grp_cmd,
- "no paging group",
- NO_STR PAGING_STR "Disable the usage of a paging group.\n")
-{
- struct bsc_config *conf = vty->index;
- conf->paging_group = PAGIN_GROUP_UNASSIGNED;
- return CMD_SUCCESS;
-}
-
-DEFUN(test_regex, test_regex_cmd,
- "test regex PATTERN STRING",
- "Test utilities\n"
- "Regexp testing\n" "The regexp pattern\n"
- "The string to match\n")
-{
- regex_t reg;
- char *str = NULL;
-
- memset(&reg, 0, sizeof(reg));
- if (gsm_parse_reg(_nat, &reg, &str, 1, argv) != 0)
- return CMD_WARNING;
-
- vty_out(vty, "String matches allow pattern: %d%s",
- regexec(&reg, argv[1], 0, NULL, 0) == 0, VTY_NEWLINE);
-
- talloc_free(str);
- regfree(&reg);
- return CMD_SUCCESS;
-}
-
-DEFUN(set_last_endp, set_last_endp_cmd,
- "set bsc last-used-endpoint <0-9999999999> <0-1024>",
- "Set a value\n" "Operate on a BSC\n"
- "Last used endpoint for an assignment\n" "BSC configuration number\n"
- "Endpoint number used\n")
-{
- struct bsc_connection *con;
- int nr = atoi(argv[0]);
- int endp = atoi(argv[1]);
-
-
- llist_for_each_entry(con, &_nat->bsc_connections, list_entry) {
- if (!con->cfg)
- continue;
- if (con->cfg->nr != nr)
- continue;
-
- con->last_endpoint = endp;
- vty_out(vty, "Updated the last endpoint for %d to %d.%s",
- con->cfg->nr, con->last_endpoint, VTY_NEWLINE);
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN(block_new_conn, block_new_conn_cmd,
- "nat-block (block|unblock)",
- "Block the NAT for new connections\n"
- "Block\n" "Unblock\n")
-{
- _nat->blocked = argv[0][0] == 'b';
- vty_out(vty, "%%Going to %s the NAT.%s",
- _nat->blocked ? "block" : "unblock", VTY_NEWLINE);
- return CMD_SUCCESS;
-}
-
-/* paging group */
-DEFUN(cfg_nat_pgroup, cfg_nat_pgroup_cmd,
- "paging-group <0-1000>",
- "Create a Paging Group\n" "Number of the Group\n")
-{
- int group = atoi(argv[0]);
- struct bsc_nat_paging_group *pgroup;
- pgroup = bsc_nat_paging_group_num(_nat, group);
- if (!pgroup)
- pgroup = bsc_nat_paging_group_create(_nat, group);
- if (!pgroup) {
- vty_out(vty, "Failed to create the group.%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- vty->index = pgroup;
- vty->node = PGROUP_NODE;
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_nat_no_pgroup, cfg_nat_no_pgroup_cmd,
- "no paging-group <0-1000>",
- NO_STR "Delete paging-group\n" "Paging-group number\n")
-{
- int group = atoi(argv[0]);
- struct bsc_nat_paging_group *pgroup;
- pgroup = bsc_nat_paging_group_num(_nat, group);
- if (!pgroup) {
- vty_out(vty, "No such paging group %d.%s", group, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- bsc_nat_paging_group_delete(pgroup);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_pgroup_lac, cfg_pgroup_lac_cmd,
- "location_area_code <0-65535>",
- "Add the Location Area Code (LAC)\n" "LAC\n")
-{
- struct bsc_nat_paging_group *pgroup = vty->index;
-
- int lac = atoi(argv[0]);
- if (lac == GSM_LAC_RESERVED_DETACHED || lac == GSM_LAC_RESERVED_ALL_BTS) {
- vty_out(vty, "%% LAC %d is reserved by GSM 04.08%s",
- lac, VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- bsc_nat_paging_group_add_lac(pgroup, lac);
- return CMD_SUCCESS;
-}
-
-DEFUN(cfg_pgroup_no_lac, cfg_pgroup_no_lac_cmd,
- "no location_area_code <0-65535>",
- NO_STR "Remove the Location Area Code (LAC)\n" "LAC\n")
-{
- int lac = atoi(argv[0]);
- struct bsc_nat_paging_group *pgroup = vty->index;
-
- bsc_nat_paging_group_del_lac(pgroup, lac);
- return CMD_SUCCESS;
-}
-
-DEFUN(show_ussd_connection,
- show_ussd_connection_cmd,
- "show ussd-connection",
- SHOW_STR "USSD connection related information\n")
-{
- vty_out(vty, "The USSD side channel provider is %sconnected and %sauthorized.%s",
- _nat->ussd_con ? "" : "not ",
- _nat->ussd_con && _nat->ussd_con->authorized? "" : "not ",
- VTY_NEWLINE);
- return CMD_SUCCESS;
-}
-
-#define OSMUX_STR "RTP multiplexing\n"
-DEFUN(cfg_bsc_osmux,
- cfg_bsc_osmux_cmd,
- "osmux (on|off|only)",
- OSMUX_STR "Enable OSMUX\n" "Disable OSMUX\n" "Only OSMUX\n")
-{
- struct bsc_config *conf = vty->index;
- int old = conf->osmux;
-
- if (strcmp(argv[0], "on") == 0)
- conf->osmux = OSMUX_USAGE_ON;
- else if (strcmp(argv[0], "off") == 0)
- conf->osmux = OSMUX_USAGE_OFF;
- else if (strcmp(argv[0], "only") == 0)
- conf->osmux = OSMUX_USAGE_ONLY;
-
- if (old == 0 && conf->osmux > 0 && !conf->nat->mgcp_cfg->osmux_init) {
- LOGP(DMGCP, LOGL_NOTICE, "Setting up OSMUX socket\n");
- if (osmux_init(OSMUX_ROLE_BSC_NAT, conf->nat->mgcp_cfg) < 0) {
- LOGP(DMGCP, LOGL_ERROR, "Cannot init OSMUX\n");
- vty_out(vty, "%% failed to create Osmux socket%s",
- VTY_NEWLINE);
- return CMD_WARNING;
- }
- } else if (old > 0 && conf->osmux == 0) {
- LOGP(DMGCP, LOGL_NOTICE, "Disabling OSMUX socket\n");
- /* Don't stop the socket, we may already have ongoing voice
- * flows already using Osmux. This just switch indicates that
- * new upcoming flows should use RTP.
- */
- }
-
- return CMD_SUCCESS;
-}
-
-int bsc_nat_vty_init(struct bsc_nat *nat)
-{
- _nat = nat;
-
- /* show commands */
- install_element_ve(&show_sccp_cmd);
- install_element_ve(&show_bsc_cmd);
- install_element_ve(&show_nat_bsc_cmd);
- install_element_ve(&show_bsc_cfg_cmd);
- install_element_ve(&show_stats_cmd);
- install_element_ve(&show_stats_lac_cmd);
- install_element_ve(&close_bsc_cmd);
- install_element_ve(&show_msc_cmd);
- install_element_ve(&test_regex_cmd);
- install_element_ve(&show_bsc_mgcp_cmd);
- install_element_ve(&show_bscs_cmd);
- install_element_ve(&show_bar_lst_cmd);
- install_element_ve(&show_prefix_tree_cmd);
- install_element_ve(&show_ussd_connection_cmd);
-
- install_element(ENABLE_NODE, &set_last_endp_cmd);
- install_element(ENABLE_NODE, &block_new_conn_cmd);
-
- /* nat group */
- install_element(CONFIG_NODE, &cfg_nat_cmd);
- install_node(&nat_node, config_write_nat);
- vty_install_default(NAT_NODE);
- install_element(NAT_NODE, &cfg_nat_msc_ip_cmd);
- install_element(NAT_NODE, &cfg_nat_msc_port_cmd);
- install_element(NAT_NODE, &cfg_nat_auth_time_cmd);
- install_element(NAT_NODE, &cfg_nat_ping_time_cmd);
- install_element(NAT_NODE, &cfg_nat_pong_time_cmd);
- install_element(NAT_NODE, &cfg_nat_token_cmd);
- install_element(NAT_NODE, &cfg_nat_bsc_ip_dscp_cmd);
- install_element(NAT_NODE, &cfg_nat_bsc_ip_tos_cmd);
- install_element(NAT_NODE, &cfg_nat_acc_lst_name_cmd);
- install_element(NAT_NODE, &cfg_nat_no_acc_lst_name_cmd);
- install_element(NAT_NODE, &cfg_nat_include_cmd);
- install_element(NAT_NODE, &cfg_nat_imsi_black_list_fn_cmd);
- install_element(NAT_NODE, &cfg_nat_no_imsi_black_list_fn_cmd);
- install_element(NAT_NODE, &cfg_nat_ussd_lst_name_cmd);
- install_element(NAT_NODE, &cfg_nat_ussd_query_cmd);
- install_element(NAT_NODE, &cfg_nat_ussd_token_cmd);
- install_element(NAT_NODE, &cfg_nat_ussd_local_cmd);
- install_element(NAT_NODE, &cfg_nat_use_ipa_for_mgcp_cmd);
-
- bsc_msg_lst_vty_init(nat, &nat->access_lists, NAT_NODE);
-
- /* number rewriting */
- install_element(NAT_NODE, &cfg_nat_number_rewrite_cmd);
- install_element(NAT_NODE, &cfg_nat_no_number_rewrite_cmd);
- install_element(NAT_NODE, &cfg_nat_number_rewrite_post_cmd);
- install_element(NAT_NODE, &cfg_nat_no_number_rewrite_post_cmd);
- install_element(NAT_NODE, &cfg_nat_smsc_addr_cmd);
- install_element(NAT_NODE, &cfg_nat_smsc_tpdest_cmd);
- install_element(NAT_NODE, &cfg_nat_sms_clear_tpsrr_cmd);
- install_element(NAT_NODE, &cfg_nat_no_sms_clear_tpsrr_cmd);
- install_element(NAT_NODE, &cfg_nat_sms_number_rewrite_cmd);
- install_element(NAT_NODE, &cfg_nat_no_sms_number_rewrite_cmd);
- install_element(NAT_NODE, &cfg_nat_prefix_trie_cmd);
- install_element(NAT_NODE, &cfg_nat_no_prefix_trie_cmd);
-
- install_element(NAT_NODE, &cfg_nat_sdp_amr_mode_set_cmd);
- install_element(NAT_NODE, &cfg_nat_no_sdp_amr_mode_set_cmd);
-
- install_element(NAT_NODE, &cfg_nat_pgroup_cmd);
- install_element(NAT_NODE, &cfg_nat_no_pgroup_cmd);
- install_node(&pgroup_node, config_write_pgroup);
- vty_install_default(PGROUP_NODE);
- install_element(PGROUP_NODE, &cfg_pgroup_lac_cmd);
- install_element(PGROUP_NODE, &cfg_pgroup_no_lac_cmd);
-
- /* BSC subgroups */
- install_element(NAT_NODE, &cfg_bsc_cmd);
- install_node(&bsc_node, NULL);
- vty_install_default(NAT_BSC_NODE);
- install_element(NAT_BSC_NODE, &cfg_bsc_token_cmd);
- install_element(NAT_BSC_NODE, &cfg_bsc_auth_key_cmd);
- install_element(NAT_BSC_NODE, &cfg_bsc_no_auth_key_cmd);
- install_element(NAT_BSC_NODE, &cfg_bsc_lac_cmd);
- install_element(NAT_BSC_NODE, &cfg_bsc_no_lac_cmd);
- install_element(NAT_BSC_NODE, &cfg_bsc_paging_cmd);
- install_element(NAT_BSC_NODE, &cfg_bsc_desc_cmd);
- install_element(NAT_BSC_NODE, &cfg_bsc_acc_lst_name_cmd);
- install_element(NAT_BSC_NODE, &cfg_bsc_no_acc_lst_name_cmd);
- install_element(NAT_BSC_NODE, &cfg_bsc_max_endps_cmd);
- install_element(NAT_BSC_NODE, &cfg_bsc_old_grp_cmd);
- install_element(NAT_BSC_NODE, &cfg_bsc_paging_grp_cmd);
- install_element(NAT_BSC_NODE, &cfg_bsc_no_paging_grp_cmd);
- install_element(NAT_BSC_NODE, &cfg_bsc_osmux_cmd);
-
- mgcp_vty_init();
-
- return 0;
-}
-
-
-/* called by the telnet interface... we have our own init above */
-int bsc_vty_init(struct gsm_network *network)
-{
- logging_vty_add_cmds(NULL);
- return 0;
-}
diff --git a/openbsc/src/osmo-bsc_nat/bsc_sccp.c b/openbsc/src/osmo-bsc_nat/bsc_sccp.c
deleted file mode 100644
index c6c265f7a..000000000
--- a/openbsc/src/osmo-bsc_nat/bsc_sccp.c
+++ /dev/null
@@ -1,247 +0,0 @@
-/* SCCP patching and handling routines */
-/*
- * (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
- * (C) 2010 by On-Waves
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <openbsc/debug.h>
-#include <openbsc/bsc_nat.h>
-#include <openbsc/bsc_nat_sccp.h>
-
-#include <osmocom/sccp/sccp.h>
-
-#include <osmocom/core/talloc.h>
-
-#include <string.h>
-#include <time.h>
-
-static int equal(struct sccp_source_reference *ref1, struct sccp_source_reference *ref2)
-{
- return memcmp(ref1, ref2, sizeof(*ref1)) == 0;
-}
-
-/*
- * SCCP patching below
- */
-
-/* check if we are using this ref for patched already */
-static int sccp_ref_is_free(struct sccp_source_reference *ref, struct bsc_nat *nat)
-{
- struct nat_sccp_connection *conn;
-
- llist_for_each_entry(conn, &nat->sccp_connections, list_entry) {
- if (equal(ref, &conn->patched_ref))
- return -1;
- }
-
- return 0;
-}
-
-/* copied from sccp.c */
-static int assign_src_local_reference(struct sccp_source_reference *ref, struct bsc_nat *nat)
-{
- static uint32_t last_ref = 0x50000;
- int wrapped = 0;
-
- do {
- struct sccp_source_reference reference;
- reference.octet1 = (last_ref >> 0) & 0xff;
- reference.octet2 = (last_ref >> 8) & 0xff;
- reference.octet3 = (last_ref >> 16) & 0xff;
-
- ++last_ref;
- /* do not use the reversed word and wrap around */
- if ((last_ref & 0x00FFFFFF) == 0x00FFFFFF) {
- LOGP(DNAT, LOGL_NOTICE, "Wrapped searching for a free code\n");
- last_ref = 0;
- ++wrapped;
- }
-
- if (sccp_ref_is_free(&reference, nat) == 0) {
- *ref = reference;
- return 0;
- }
- } while (wrapped != 2);
-
- LOGP(DNAT, LOGL_ERROR, "Finding a free reference failed\n");
- return -1;
-}
-
-struct nat_sccp_connection *create_sccp_src_ref(struct bsc_connection *bsc,
- struct bsc_nat_parsed *parsed)
-{
- struct nat_sccp_connection *conn;
-
- /* Some commercial BSCs like to reassign there SRC ref */
- llist_for_each_entry(conn, &bsc->nat->sccp_connections, list_entry) {
- if (conn->bsc != bsc)
- continue;
- if (!equal(parsed->src_local_ref, &conn->real_ref))
- continue;
-
- /* the BSC has reassigned the SRC ref and we failed to keep track */
- memset(&conn->remote_ref, 0, sizeof(conn->remote_ref));
- if (assign_src_local_reference(&conn->patched_ref, bsc->nat) != 0) {
- LOGP(DNAT, LOGL_ERROR, "BSC %d reused src ref: %d and we failed to generate a new id.\n",
- bsc->cfg->nr, sccp_src_ref_to_int(parsed->src_local_ref));
- bsc_mgcp_dlcx(conn);
- llist_del(&conn->list_entry);
- talloc_free(conn);
- return NULL;
- } else {
- clock_gettime(CLOCK_MONOTONIC, &conn->creation_time);
- bsc_mgcp_dlcx(conn);
- return conn;
- }
- }
-
-
- conn = talloc_zero(bsc->nat, struct nat_sccp_connection);
- if (!conn) {
- LOGP(DNAT, LOGL_ERROR, "Memory allocation failure.\n");
- return NULL;
- }
-
- conn->bsc = bsc;
- clock_gettime(CLOCK_MONOTONIC, &conn->creation_time);
- conn->real_ref = *parsed->src_local_ref;
- if (assign_src_local_reference(&conn->patched_ref, bsc->nat) != 0) {
- LOGP(DNAT, LOGL_ERROR, "Failed to assign a ref.\n");
- talloc_free(conn);
- return NULL;
- }
-
- bsc_mgcp_init(conn);
- llist_add_tail(&conn->list_entry, &bsc->nat->sccp_connections);
- rate_ctr_inc(&bsc->cfg->stats.ctrg->ctr[BCFG_CTR_SCCP_CONN]);
- osmo_counter_inc(bsc->cfg->nat->stats.sccp.conn);
-
- LOGP(DNAT, LOGL_DEBUG, "Created 0x%x <-> 0x%x mapping for con %p\n",
- sccp_src_ref_to_int(&conn->real_ref),
- sccp_src_ref_to_int(&conn->patched_ref), bsc);
-
- return conn;
-}
-
-int update_sccp_src_ref(struct nat_sccp_connection *sccp, struct bsc_nat_parsed *parsed)
-{
- if (!parsed->dest_local_ref || !parsed->src_local_ref) {
- LOGP(DNAT, LOGL_ERROR, "CC MSG should contain both local and dest address.\n");
- return -1;
- }
-
- sccp->remote_ref = *parsed->src_local_ref;
- sccp->has_remote_ref = 1;
- LOGP(DNAT, LOGL_DEBUG, "Updating 0x%x to remote 0x%x on %p\n",
- sccp_src_ref_to_int(&sccp->patched_ref),
- sccp_src_ref_to_int(&sccp->remote_ref), sccp->bsc);
-
- return 0;
-}
-
-void remove_sccp_src_ref(struct bsc_connection *bsc, struct msgb *msg, struct bsc_nat_parsed *parsed)
-{
- struct nat_sccp_connection *conn;
-
- llist_for_each_entry(conn, &bsc->nat->sccp_connections, list_entry) {
- if (equal(parsed->src_local_ref, &conn->patched_ref)) {
- sccp_connection_destroy(conn);
- return;
- }
- }
-
- LOGP(DNAT, LOGL_ERROR, "Can not remove connection: 0x%x\n",
- sccp_src_ref_to_int(parsed->src_local_ref));
-}
-
-/*
- * We have a message from the MSC to the BSC. The MSC is using
- * an address that was assigned by the MUX, we need to update the
- * dest reference to the real network.
- */
-struct nat_sccp_connection *patch_sccp_src_ref_to_bsc(struct msgb *msg,
- struct bsc_nat_parsed *parsed,
- struct bsc_nat *nat)
-{
- struct nat_sccp_connection *conn;
-
- if (!parsed->dest_local_ref) {
- LOGP(DNAT, LOGL_ERROR, "MSG should contain dest_local_ref.\n");
- return NULL;
- }
-
-
- llist_for_each_entry(conn, &nat->sccp_connections, list_entry) {
- if (!equal(parsed->dest_local_ref, &conn->patched_ref))
- continue;
-
- /* Change the dest address to the real one */
- *parsed->dest_local_ref = conn->real_ref;
- return conn;
- }
-
- return NULL;
-}
-
-/*
- * These are message to the MSC. We will need to find the BSC
- * Connection by either the SRC or the DST local reference.
- *
- * In case of a CR we need to work by the SRC local reference
- * in all other cases we need to work by the destination local
- * reference..
- */
-struct nat_sccp_connection *patch_sccp_src_ref_to_msc(struct msgb *msg,
- struct bsc_nat_parsed *parsed,
- struct bsc_connection *bsc)
-{
- struct nat_sccp_connection *conn;
-
- llist_for_each_entry(conn, &bsc->nat->sccp_connections, list_entry) {
- if (conn->bsc != bsc)
- continue;
-
- if (parsed->src_local_ref) {
- if (equal(parsed->src_local_ref, &conn->real_ref)) {
- *parsed->src_local_ref = conn->patched_ref;
- return conn;
- }
- } else if (parsed->dest_local_ref) {
- if (equal(parsed->dest_local_ref, &conn->remote_ref))
- return conn;
- } else {
- LOGP(DNAT, LOGL_ERROR, "Header has neither loc/dst ref.\n");
- return NULL;
- }
- }
-
- return NULL;
-}
-
-struct nat_sccp_connection *bsc_nat_find_con_by_bsc(struct bsc_nat *nat,
- struct sccp_source_reference *ref)
-{
- struct nat_sccp_connection *conn;
-
- llist_for_each_entry(conn, &nat->sccp_connections, list_entry) {
- if (equal(ref, &conn->real_ref))
- return conn;
- }
-
- return NULL;
-}
diff --git a/openbsc/src/osmo-bsc_nat/bsc_ussd.c b/openbsc/src/osmo-bsc_nat/bsc_ussd.c
deleted file mode 100644
index 0ba63270d..000000000
--- a/openbsc/src/osmo-bsc_nat/bsc_ussd.c
+++ /dev/null
@@ -1,453 +0,0 @@
-/* USSD Filter Code */
-
-/*
- * (C) 2010-2011 by Holger Hans Peter Freyther <zecke@selfish.org>
- * (C) 2010-2011 by On-Waves
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <openbsc/bsc_nat.h>
-#include <openbsc/bsc_nat_sccp.h>
-#include <openbsc/bsc_msg_filter.h>
-#include <openbsc/ipaccess.h>
-#include <openbsc/socket.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/sccp/sccp.h>
-
-#include <osmocom/abis/ipa.h>
-
-#include <sys/socket.h>
-#include <string.h>
-#include <unistd.h>
-
-#define USSD_LAC_IE 0
-#define USSD_CI_IE 1
-
-static void ussd_auth_con(struct tlv_parsed *, struct bsc_nat_ussd_con *);
-
-static struct bsc_nat_ussd_con *bsc_nat_ussd_alloc(struct bsc_nat *nat)
-{
- struct bsc_nat_ussd_con *con;
-
- con = talloc_zero(nat, struct bsc_nat_ussd_con);
- if (!con)
- return NULL;
-
- con->nat = nat;
- return con;
-}
-
-static void bsc_nat_ussd_destroy(struct bsc_nat_ussd_con *con)
-{
- if (con->nat->ussd_con == con) {
- bsc_ussd_close_connections(con->nat);
- con->nat->ussd_con = NULL;
- }
-
- close(con->queue.bfd.fd);
- osmo_fd_unregister(&con->queue.bfd);
- osmo_timer_del(&con->auth_timeout);
- osmo_wqueue_clear(&con->queue);
-
- msgb_free(con->pending_msg);
- talloc_free(con);
-}
-
-static void ussd_pong(struct bsc_nat_ussd_con *conn)
-{
- struct msgb *msg;
-
- msg = msgb_alloc_headroom(4096, 128, "pong message");
- if (!msg) {
- LOGP(DNAT, LOGL_ERROR, "Failed to allocate pong msg\n");
- return;
- }
-
- msgb_v_put(msg, IPAC_MSGT_PONG);
- bsc_do_write(&conn->queue, msg, IPAC_PROTO_IPACCESS);
-}
-
-static int forward_sccp(struct bsc_nat *nat, struct msgb *msg)
-{
- struct nat_sccp_connection *con;
- struct bsc_nat_parsed *parsed;
-
-
- parsed = bsc_nat_parse(msg);
- if (!parsed) {
- LOGP(DNAT, LOGL_ERROR, "Can not parse msg from USSD.\n");
- msgb_free(msg);
- return -1;
- }
-
- if (!parsed->dest_local_ref) {
- LOGP(DNAT, LOGL_ERROR, "No destination local reference.\n");
- msgb_free(msg);
- return -1;
- }
-
- con = bsc_nat_find_con_by_bsc(nat, parsed->dest_local_ref);
- if (!con || !con->bsc) {
- LOGP(DNAT, LOGL_ERROR, "No active connection found.\n");
- msgb_free(msg);
- return -1;
- }
-
- talloc_free(parsed);
- bsc_write_msg(&con->bsc->write_queue, msg);
- return 0;
-}
-
-static int ussd_read_cb(struct osmo_fd *bfd)
-{
- struct bsc_nat_ussd_con *conn = bfd->data;
- struct msgb *msg = NULL;
- struct ipaccess_head *hh;
- int ret;
-
- ret = ipa_msg_recv_buffered(bfd->fd, &msg, &conn->pending_msg);
- if (ret <= 0) {
- if (ret == -EAGAIN)
- return 0;
- LOGP(DNAT, LOGL_ERROR, "USSD Connection was lost.\n");
- bsc_nat_ussd_destroy(conn);
- return -1;
- }
-
- LOGP(DNAT, LOGL_NOTICE, "MSG from USSD: %s proto: %d\n",
- osmo_hexdump(msg->data, msg->len), msg->l2h[0]);
- hh = (struct ipaccess_head *) msg->data;
-
- if (hh->proto == IPAC_PROTO_IPACCESS) {
- if (msg->l2h[0] == IPAC_MSGT_ID_RESP) {
- struct tlv_parsed tvp;
- int ret;
- ret = ipa_ccm_idtag_parse(&tvp,
- (unsigned char *) msg->l2h + 2,
- msgb_l2len(msg) - 2);
- if (ret < 0) {
- LOGP(DNAT, LOGL_ERROR, "ignoring IPA response "
- "message with malformed TLVs\n");
- msgb_free(msg);
- return ret;
- }
- if (TLVP_PRESENT(&tvp, IPAC_IDTAG_UNITNAME))
- ussd_auth_con(&tvp, conn);
- } else if (msg->l2h[0] == IPAC_MSGT_PING) {
- LOGP(DNAT, LOGL_DEBUG, "Got USSD ping request.\n");
- ussd_pong(conn);
- } else {
- LOGP(DNAT, LOGL_NOTICE, "Got unknown IPACCESS message 0x%02x.\n", msg->l2h[0]);
- }
-
- msgb_free(msg);
- } else if (hh->proto == IPAC_PROTO_SCCP) {
- forward_sccp(conn->nat, msg);
- } else {
- msgb_free(msg);
- }
-
- return 0;
-}
-
-static void ussd_auth_cb(void *_data)
-{
- LOGP(DNAT, LOGL_ERROR, "USSD module didn't authenticate\n");
- bsc_nat_ussd_destroy((struct bsc_nat_ussd_con *) _data);
-}
-
-static void ussd_auth_con(struct tlv_parsed *tvp, struct bsc_nat_ussd_con *conn)
-{
- const char *token;
- int len;
- if (!conn->nat->ussd_token) {
- LOGP(DNAT, LOGL_ERROR, "No USSD token set. Closing\n");
- bsc_nat_ussd_destroy(conn);
- return;
- }
-
- token = (const char *) TLVP_VAL(tvp, IPAC_IDTAG_UNITNAME);
- len = TLVP_LEN(tvp, IPAC_IDTAG_UNITNAME);
-
- /* last byte should be a NULL */
- if (strlen(conn->nat->ussd_token) != len - 1)
- goto disconnect;
- /* compare everything including the null byte */
- if (memcmp(conn->nat->ussd_token, token, len) != 0)
- goto disconnect;
-
- /* it is authenticated now */
- if (conn->nat->ussd_con && conn->nat->ussd_con != conn)
- bsc_nat_ussd_destroy(conn->nat->ussd_con);
-
- LOGP(DNAT, LOGL_ERROR, "USSD token specified. USSD provider is connected.\n");
- osmo_timer_del(&conn->auth_timeout);
- conn->authorized = 1;
- conn->nat->ussd_con = conn;
- return;
-
-disconnect:
- LOGP(DNAT, LOGL_ERROR, "Wrong USSD token by client: %d\n",
- conn->queue.bfd.fd);
- bsc_nat_ussd_destroy(conn);
-}
-
-static void ussd_start_auth(struct bsc_nat_ussd_con *conn)
-{
- struct msgb *msg;
-
- osmo_timer_setup(&conn->auth_timeout, ussd_auth_cb, conn);
- osmo_timer_schedule(&conn->auth_timeout, conn->nat->auth_timeout, 0);
-
- msg = msgb_alloc_headroom(4096, 128, "auth message");
- if (!msg) {
- LOGP(DNAT, LOGL_ERROR, "Failed to allocate auth msg\n");
- return;
- }
-
- msgb_v_put(msg, IPAC_MSGT_ID_GET);
- bsc_do_write(&conn->queue, msg, IPAC_PROTO_IPACCESS);
-}
-
-static int ussd_listen_cb(struct osmo_fd *bfd, unsigned int what)
-{
- struct bsc_nat_ussd_con *conn;
- struct bsc_nat *nat;
- struct sockaddr_in sa;
- socklen_t sa_len = sizeof(sa);
- int fd;
-
- if (!(what & BSC_FD_READ))
- return 0;
-
- fd = accept(bfd->fd, (struct sockaddr *) &sa, &sa_len);
- if (fd < 0) {
- perror("accept");
- return fd;
- }
-
- nat = (struct bsc_nat *) bfd->data;
- osmo_counter_inc(nat->stats.ussd.reconn);
-
- conn = bsc_nat_ussd_alloc(nat);
- if (!conn) {
- LOGP(DNAT, LOGL_ERROR, "Failed to allocate USSD con struct.\n");
- close(fd);
- return -1;
- }
-
- osmo_wqueue_init(&conn->queue, 10);
- conn->queue.bfd.data = conn;
- conn->queue.bfd.fd = fd;
- conn->queue.bfd.when = BSC_FD_READ;
- conn->queue.read_cb = ussd_read_cb;
- conn->queue.write_cb = bsc_write_cb;
-
- if (osmo_fd_register(&conn->queue.bfd) < 0) {
- LOGP(DNAT, LOGL_ERROR, "Failed to register USSD fd.\n");
- bsc_nat_ussd_destroy(conn);
- return -1;
- }
-
- LOGP(DNAT, LOGL_NOTICE, "USSD Connection on %d with IP: %s\n",
- fd, inet_ntoa(sa.sin_addr));
-
- /* do authentication */
- ussd_start_auth(conn);
- return 0;
-}
-
-int bsc_ussd_init(struct bsc_nat *nat)
-{
- struct in_addr addr;
-
- addr.s_addr = INADDR_ANY;
- if (nat->ussd_local)
- inet_aton(nat->ussd_local, &addr);
-
- nat->ussd_listen.data = nat;
- return make_sock(&nat->ussd_listen, IPPROTO_TCP,
- ntohl(addr.s_addr), 5001, 0, ussd_listen_cb, nat);
-}
-
-static int forward_ussd_simple(struct nat_sccp_connection *con, struct msgb *input)
-{
- struct msgb *copy;
- struct bsc_nat_ussd_con *ussd;
-
- if (!con->bsc->nat->ussd_con)
- return -1;
-
- copy = msgb_alloc_headroom(4096, 128, "forward bts");
- if (!copy) {
- LOGP(DNAT, LOGL_ERROR, "Allocation failed, not forwarding.\n");
- return -1;
- }
-
- /* copy the data into the copy */
- copy->l2h = msgb_put(copy, msgb_l2len(input));
- memcpy(copy->l2h, input->l2h, msgb_l2len(input));
-
- /* send it out */
- ussd = con->bsc->nat->ussd_con;
- bsc_do_write(&ussd->queue, copy, IPAC_PROTO_SCCP);
- return 0;
-}
-
-static int forward_ussd(struct nat_sccp_connection *con, const struct ussd_request *req,
- struct msgb *input)
-{
- struct msgb *msg, *copy;
- struct ipac_msgt_sccp_state *state;
- struct bsc_nat_ussd_con *ussd;
- uint16_t lac, ci;
-
- if (!con->bsc->nat->ussd_con)
- return -1;
-
- msg = msgb_alloc_headroom(4096, 128, "forward ussd");
- if (!msg) {
- LOGP(DNAT, LOGL_ERROR, "Allocation failed, not forwarding.\n");
- return -1;
- }
-
- copy = msgb_alloc_headroom(4096, 128, "forward bts");
- if (!copy) {
- LOGP(DNAT, LOGL_ERROR, "Allocation failed, not forwarding.\n");
- msgb_free(msg);
- return -1;
- }
-
- copy->l2h = msgb_put(copy, msgb_l2len(input));
- memcpy(copy->l2h, input->l2h, msgb_l2len(input));
-
- msg->l2h = msgb_put(msg, 1);
- msg->l2h[0] = IPAC_MSGT_SCCP_OLD;
-
- /* fill out the data */
- state = (struct ipac_msgt_sccp_state *) msgb_put(msg, sizeof(*state));
- state->trans_id = req->transaction_id;
- state->invoke_id = req->invoke_id;
- memcpy(&state->src_ref, &con->remote_ref, sizeof(con->remote_ref));
- memcpy(&state->dst_ref, &con->real_ref, sizeof(con->real_ref));
- memcpy(state->imsi, con->filter_state.imsi, strlen(con->filter_state.imsi));
-
- /* add additional tag/values */
- lac = htons(con->lac);
- ci = htons(con->ci);
- msgb_tv_fixed_put(msg, USSD_LAC_IE, sizeof(lac), (const uint8_t *) &lac);
- msgb_tv_fixed_put(msg, USSD_CI_IE, sizeof(ci), (const uint8_t *) &ci);
-
- ussd = con->bsc->nat->ussd_con;
- bsc_do_write(&ussd->queue, msg, IPAC_PROTO_IPACCESS);
- bsc_do_write(&ussd->queue, copy, IPAC_PROTO_SCCP);
-
- return 0;
-}
-
-int bsc_ussd_check(struct nat_sccp_connection *con, struct bsc_nat_parsed *parsed,
- struct msgb *msg)
-{
- uint32_t len;
- uint8_t msg_type;
- uint8_t proto;
- uint8_t ti;
- struct gsm48_hdr *hdr48;
- struct bsc_msg_acc_lst *lst;
- struct ussd_request req;
-
- /*
- * various checks to avoid the decoding work. Right now we only want to
- * decode if the connection was created for USSD, we do have a USSD access
- * list, a query, a IMSI and such...
- */
- if (con->filter_state.con_type != FLT_CON_TYPE_SSA)
- return 0;
-
- if (!con->filter_state.imsi)
- return 0;
-
- /* We have not verified the IMSI yet */
- if (!con->authorized)
- return 0;
-
- if (!con->bsc->nat->ussd_lst_name)
- return 0;
- if (!con->bsc->nat->ussd_query)
- return 0;
-
- if (parsed->bssap != BSSAP_MSG_DTAP)
- return 0;
-
- if (strlen(con->filter_state.imsi) > GSM23003_IMSI_MAX_DIGITS)
- return 0;
-
- hdr48 = bsc_unpack_dtap(parsed, msg, &len);
- if (!hdr48)
- return 0;
-
- proto = gsm48_hdr_pdisc(hdr48);
- msg_type = gsm48_hdr_msg_type(hdr48);
- ti = gsm48_hdr_trans_id_no_ti(hdr48);
- if (proto != GSM48_PDISC_NC_SS)
- return 0;
-
- if (msg_type == GSM0480_MTYPE_REGISTER) {
-
- /* now check if it is a IMSI we care about */
- lst = bsc_msg_acc_lst_find(&con->bsc->nat->access_lists,
- con->bsc->nat->ussd_lst_name);
- if (!lst)
- return 0;
-
- if (bsc_msg_acc_lst_check_allow(lst, con->filter_state.imsi) != 0)
- return 0;
-
- /* now decode the message and see if we really want to handle it */
- memset(&req, 0, sizeof(req));
- if (gsm0480_decode_ussd_request(hdr48, len, &req) != 1)
- return 0;
- if (req.text[0] == 0xff)
- return 0;
-
- if (regexec(&con->bsc->nat->ussd_query_re,
- req.text, 0, NULL, 0) == REG_NOMATCH)
- return 0;
-
- /* found a USSD query for our subscriber */
- LOGP(DNAT, LOGL_NOTICE, "Found USSD query for %s\n",
- con->filter_state.imsi);
- con->ussd_ti[ti] = 1;
- if (forward_ussd(con, &req, msg) != 0)
- return 0;
- return 1;
- } else if (msg_type == GSM0480_MTYPE_FACILITY && con->ussd_ti[ti]) {
- LOGP(DNAT, LOGL_NOTICE, "Forwarding message part of TI: %d %s\n",
- ti, con->filter_state.imsi);
- if (forward_ussd_simple(con, msg) != 0)
- return 0;
- return 1;
- }
-
- return 0;
-}
diff --git a/openbsc/src/osmo-nitb/Makefile.am b/openbsc/src/osmo-nitb/Makefile.am
deleted file mode 100644
index f4ef487c6..000000000
--- a/openbsc/src/osmo-nitb/Makefile.am
+++ /dev/null
@@ -1,45 +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) \
- $(NULL)
-
-AM_LDFLAGS = \
- $(COVERAGE_LDFLAGS) \
- $(NULL)
-
-bin_PROGRAMS = \
- osmo-nitb \
- $(NULL)
-
-osmo_nitb_SOURCES = \
- bsc_hack.c \
- $(NULL)
-
-osmo_nitb_LDADD = \
- $(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 \
- $(LIBOSMOGSM_LIBS) \
- $(LIBOSMOVTY_LIBS) \
- $(LIBOSMOCORE_LIBS) \
- $(LIBOSMOCTRL_LIBS) \
- $(LIBOSMOABIS_LIBS) \
- $(LIBSMPP34_LIBS) \
- $(LIBCRYPTO_LIBS) \
- -ldbi \
- $(NULL)
diff --git a/openbsc/src/osmo-nitb/bsc_hack.c b/openbsc/src/osmo-nitb/bsc_hack.c
deleted file mode 100644
index 17b23b2b9..000000000
--- a/openbsc/src/osmo-nitb/bsc_hack.c
+++ /dev/null
@@ -1,402 +0,0 @@
-/* A hackish minimal BSC (+MSC +HLR) implementation */
-
-/* (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>
-
-#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 <openbsc/vty.h>
-#include <openbsc/bss.h>
-#include <openbsc/mncc.h>
-#include <openbsc/token_auth.h>
-#include <openbsc/handover_decision.h>
-#include <openbsc/rrlp.h>
-#include <osmocom/ctrl/control_if.h>
-#include <osmocom/ctrl/ports.h>
-#include <osmocom/ctrl/control_vty.h>
-#include <openbsc/ctrl.h>
-#include <openbsc/osmo_bsc_rf.h>
-#include <openbsc/smpp.h>
-
-#include "../../bscconfig.h"
-
-/* MCC and MNC for the Location Area Identifier */
-struct gsm_network *bsc_gsmnet = 0;
-static const char *database_name = "hlr.sqlite3";
-static const char *config_file = "openbsc.cfg";
-static const char *rf_ctrl_path = NULL;
-extern const char *openbsc_copyright;
-static int daemonize = 0;
-static const char *mncc_sock_path = NULL;
-static int use_db_counter = 1;
-
-/* timer to store statistics */
-#define DB_SYNC_INTERVAL 60, 0
-#define EXPIRE_INTERVAL 10, 0
-
-static struct osmo_timer_list db_sync_timer;
-
-static void create_pcap_file(char *file)
-{
- mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
- int fd = open(file, O_WRONLY|O_TRUNC|O_CREAT, mode);
-
- if (fd < 0) {
- perror("Failed to open file for pcap");
- return;
- }
-
- e1_set_pcap_fd(fd);
-}
-
-static void print_usage()
-{
- printf("Usage: osmo-nitb\n");
-}
-
-static void print_help()
-{
- printf(" Some useful help...\n");
- printf(" -h --help This text.\n");
- printf(" -d option --debug=DRLL:DCC:DMM:DRR:DRSL:DNM Enable debugging.\n");
- printf(" -D --daemonize Fork the process into a background daemon.\n");
- printf(" -c --config-file filename The config file to use.\n");
- printf(" -s --disable-color\n");
- printf(" -l --database db-name The database to use.\n");
- printf(" -a --authorize-everyone Authorize every new subscriber. Dangerous!\n");
- printf(" -T --timestamp Prefix every log line with a timestamp.\n");
- printf(" -V --version Print the version of OpenBSC.\n");
- printf(" -P --rtp-proxy Enable the RTP Proxy code inside OpenBSC.\n");
- printf(" -e --log-level number Set a global loglevel.\n");
- printf(" -M --mncc-sock-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'},
- {"rf-ctl", 1, 0, 'r'},
- {0, 0, 0, 0}
- };
-
- c = getopt_long(argc, argv, "hd:Dsl:ar:p:TPVc:e:mCr:M:",
- long_options, &option_index);
- if (c == -1)
- break;
-
- switch (c) {
- case 'h':
- print_usage();
- print_help();
- exit(0);
- case 's':
- log_set_use_color(osmo_stderr_target, 0);
- break;
- case 'd':
- log_parse_category_mask(osmo_stderr_target, optarg);
- break;
- case 'D':
- daemonize = 1;
- break;
- case 'l':
- database_name = optarg;
- break;
- case 'c':
- config_file = optarg;
- break;
- case 'p':
- create_pcap_file(optarg);
- break;
- case 'T':
- log_set_print_timestamp(osmo_stderr_target, 1);
- break;
- case 'P':
- ipacc_rtp_direct = 0;
- break;
- case 'e':
- log_set_log_level(osmo_stderr_target, atoi(optarg));
- break;
- case 'M':
- mncc_sock_path = optarg;
- break;
- case 'm':
- mncc_sock_path = "/tmp/bsc_mncc";
- break;
- case 'C':
- use_db_counter = 0;
- break;
- case 'V':
- print_version(1);
- exit(0);
- break;
- case 'r':
- rf_ctrl_path = optarg;
- break;
- default:
- /* catch unknown options *as well as* missing arguments. */
- fprintf(stderr, "Error in command line options. Exiting.\n");
- exit(-1);
- break;
- }
- }
-}
-
-extern void *tall_vty_ctx;
-static void signal_handler(int signal)
-{
- fprintf(stdout, "signal %u received\n", signal);
-
- switch (signal) {
- case SIGINT:
- bsc_shutdown_net(bsc_gsmnet);
- osmo_signal_dispatch(SS_L_GLOBAL, S_L_GLOBAL_SHUTDOWN, NULL);
- sleep(3);
- exit(0);
- break;
- case SIGABRT:
- 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_bsc_ctx, stderr);
- break;
- case SIGUSR2:
- talloc_report_full(tall_vty_ctx, stderr);
- break;
- default:
- break;
- }
-}
-
-/* timer handling */
-static int _db_store_counter(struct osmo_counter *counter, void *data)
-{
- return db_store_counter(counter);
-}
-
-static void db_sync_timer_cb(void *data)
-{
- /* store counters to database and re-schedule */
- osmo_counters_for_each(_db_store_counter, NULL);
- osmo_timer_schedule(&db_sync_timer, DB_SYNC_INTERVAL);
-}
-
-static void subscr_expire_cb(void *data)
-{
- subscr_expire(bsc_gsmnet->subscr_group);
- osmo_timer_schedule(&bsc_gsmnet->subscr_expire_timer, EXPIRE_INTERVAL);
-}
-
-extern int bsc_vty_go_parent(struct vty *vty);
-
-static struct vty_app_info vty_info = {
- .name = "OpenBSC",
- .version = PACKAGE_VERSION,
- .go_parent_cb = bsc_vty_go_parent,
- .is_config_node = bsc_vty_is_config_node,
-};
-
-int main(int argc, char **argv)
-{
- int rc;
-
- vty_info.copyright = openbsc_copyright;
-
- tall_bsc_ctx = talloc_named_const(NULL, 1, "openbsc");
- talloc_ctx_init(tall_bsc_ctx);
- on_dso_load_token();
- on_dso_load_rrlp();
- on_dso_load_ho_dec();
-
- libosmo_abis_init(tall_bsc_ctx);
- osmo_init_logging(&log_info);
- osmo_stats_init(tall_bsc_ctx);
- bts_init();
- vty_init(&vty_info);
-
- /* Parse options */
- handle_options(argc, argv);
-
- /* Allocate global gsm_network struct; choose socket/internal MNCC */
- rc = bsc_network_alloc(mncc_sock_path?
- mncc_sock_from_cc : int_mncc_recv);
- if (rc) {
- fprintf(stderr, "Allocation failed. Exiting.\n");
- exit(1);
- }
-
- /* Initialize VTY */
- bsc_vty_init(bsc_gsmnet);
- ctrl_vty_init(tall_bsc_ctx);
-
-#ifdef BUILD_SMPP
- if (smpp_openbsc_alloc_init(tall_bsc_ctx) < 0)
- return -1;
-#endif
-
- /* Initialize MNCC socket if appropriate */
- if (mncc_sock_path) {
- rc = mncc_sock_init(bsc_gsmnet, mncc_sock_path);
- if (rc) {
- fprintf(stderr, "MNCC socket initialization failed. exiting.\n");
- exit(1);
- }
- } else
- DEBUGP(DMNCC, "Using internal MNCC handler.\n");
-
- /*
- * 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.
- */
- bsc_gsmnet->dyn_ts_allow_tch_f = false;
-
- /* Read the config */
- rc = bsc_network_configure(config_file);
- if (rc < 0) {
- fprintf(stderr, "Reading config failed. Exiting.\n");
- exit(1);
- }
-
-#ifdef BUILD_SMPP
- smpp_openbsc_start(bsc_gsmnet);
-#endif
- bsc_api_init(bsc_gsmnet, msc_bsc_api());
-
- /* start control interface after reading config for
- * ctrl_vty_get_bind_addr() */
- bsc_gsmnet->ctrl = bsc_controlif_setup(bsc_gsmnet,
- ctrl_vty_get_bind_addr(),
- OSMO_CTRL_PORT_NITB_BSC);
- if (!bsc_gsmnet->ctrl) {
- printf("Failed to initialize control interface. Exiting.\n");
- return -1;
- }
-
- if (bsc_base_ctrl_cmds_install() != 0) {
- printf("Failed to initialize the BSC control commands.\n");
- return -1;
- }
-
- if (msc_ctrl_cmds_install() != 0) {
- printf("Failed to initialize the MSC control commands.\n");
- return -1;
- }
-
- /* seed the PRNG */
- srand(time(NULL));
-
- bsc_gsmnet->bsc_data->rf_ctrl = osmo_bsc_rf_create(rf_ctrl_path, bsc_gsmnet);
- if (!bsc_gsmnet->bsc_data->rf_ctrl) {
- fprintf(stderr, "Failed to create the RF service.\n");
- exit(1);
- }
-
- if (db_init(database_name)) {
- printf("DB: Failed to init database. Please check the option settings.\n");
- return -1;
- }
- printf("DB: Database initialized.\n");
-
- if (db_prepare()) {
- printf("DB: Failed to prepare database.\n");
- return -1;
- }
- printf("DB: Database prepared.\n");
-
- /* setup the timer */
- osmo_timer_setup(&db_sync_timer, db_sync_timer_cb, NULL);
- if (use_db_counter)
- osmo_timer_schedule(&db_sync_timer, DB_SYNC_INTERVAL);
-
- osmo_timer_setup(&bsc_gsmnet->subscr_expire_timer, subscr_expire_cb,
- NULL);
- osmo_timer_schedule(&bsc_gsmnet->subscr_expire_timer, EXPIRE_INTERVAL);
-
- signal(SIGINT, &signal_handler);
- signal(SIGABRT, &signal_handler);
- signal(SIGUSR1, &signal_handler);
- signal(SIGUSR2, &signal_handler);
- osmo_init_ignore_signals();
-
- /* start the SMS queue */
- if (sms_queue_start(bsc_gsmnet, 20) != 0)
- return -1;
-
- if (daemonize) {
- rc = osmo_daemonize();
- if (rc < 0) {
- perror("Error during daemonize");
- exit(1);
- }
- }
-
- while (1) {
- log_reset_context();
- osmo_select_main(0);
- }
-}
diff --git a/openbsc/src/utils/Makefile.am b/openbsc/src/utils/Makefile.am
deleted file mode 100644
index 26494e13d..000000000
--- a/openbsc/src/utils/Makefile.am
+++ /dev/null
@@ -1,147 +0,0 @@
-AM_CPPFLAGS = \
- $(all_includes) \
- -I$(top_srcdir)/include \
- -I$(top_builddir) \
- $(NULL)
-
-AM_CFLAGS = \
- -Wall \
- $(LIBOSMOCORE_CFLAGS) \
- $(LIBOSMOGSM_CFLAGS) \
- $(LIBOSMOABIS_CFLAGS) \
- $(COVERAGE_CFLAGS) \
- $(SQLITE3_CFLAGS) \
- $(LIBSMPP34_CFLAGS) \
- $(NULL)
-
-AM_LDFLAGS = \
- $(COVERAGE_LDFLAGS) \
- $(NULL)
-
-noinst_HEADERS = \
- meas_db.h \
- $(NULL)
-
-bin_PROGRAMS = \
- bs11_config \
- isdnsync \
- meas_json \
- $(NULL)
-if HAVE_SQLITE3
-bin_PROGRAMS += \
- osmo-meas-udp2db \
- $(NULL)
-if HAVE_PCAP
-bin_PROGRAMS += \
- osmo-meas-pcap2db \
- $(NULL)
-endif
-endif
-if HAVE_LIBCDK
-bin_PROGRAMS += \
- meas_vis \
- $(NULL)
-endif
-
-if BUILD_SMPP
-noinst_PROGRAMS = \
- smpp_mirror \
- $(NULL)
-endif
-
-bs11_config_SOURCES = \
- bs11_config.c \
- $(NULL)
-
-bs11_config_LDADD = \
- $(top_builddir)/src/libbsc/libbsc.a \
- $(top_builddir)/src/libcommon-cs/libcommon-cs.a \
- $(top_builddir)/src/libtrau/libtrau.a \
- $(top_builddir)/src/libcommon/libcommon.a \
- $(LIBOSMOCORE_LIBS) \
- $(LIBOSMOGSM_LIBS) \
- $(LIBOSMOABIS_LIBS) \
- $(NULL)
-
-isdnsync_SOURCES = \
- isdnsync.c \
- $(NULL)
-
-smpp_mirror_SOURCES = \
- smpp_mirror.c \
- $(NULL)
-
-smpp_mirror_LDADD = \
- $(top_builddir)/src/libcommon/libcommon.a \
- $(LIBOSMOCORE_LIBS) \
- $(LIBOSMOGSM_LIBS) \
- $(LIBSMPP34_LIBS) \
- $(NULL)
-
-meas_vis_SOURCES = \
- meas_vis.c \
- $(NULL)
-
-meas_vis_LDADD = \
- $(LIBOSMOCORE_LIBS) \
- $(LIBOSMOGSM_LIBS) \
- -lcdk \
- -lncurses \
- $(NULL)
-
-meas_vis_CFLAGS = \
- $(LIBOSMOCORE_CFLAGS) \
- $(LIBOSMOGSM_CFLAGS) \
- $(NULL)
-
-osmo_meas_pcap2db_SOURCES = \
- meas_pcap2db.c \
- meas_db.c \
- $(NULL)
-
-osmo_meas_pcap2db_LDADD = \
- $(LIBOSMOCORE_LIBS) \
- $(LIBOSMOGSM_LIBS) \
- $(SQLITE3_LIBS) \
- -lpcap \
- $(NULL)
-
-osmo_meas_pcap2db_CFLAGS = \
- $(LIBOSMOCORE_CFLAGS) \
- $(LIBOSMOGSM_CFLAGS) \
- $(LIBOSMOABIS_CFLAGS) \
- $(NULL)
-
-osmo_meas_udp2db_SOURCES = \
- meas_udp2db.c \
- meas_db.c \
- $(NULL)
-
-osmo_meas_udp2db_LDADD = \
- $(LIBOSMOCORE_LIBS) \
- $(LIBOSMOGSM_LIBS) \
- $(SQLITE3_LIBS) \
- $(NULL)
-
-osmo_meas_udp2db_CFLAGS = \
- $(LIBOSMOCORE_CFLAGS) \
- $(LIBOSMOGSM_CFLAGS) \
- $(LIBOSMOABIS_CFLAGS) \
- $(NULL)
-
-meas_json_SOURCES = \
- meas_json.c \
- $(NULL)
-
-meas_json_LDADD = \
- $(top_builddir)/src/libcommon/libcommon.a \
- $(LIBOSMOCORE_LIBS) \
- $(LIBOSMOGSM_LIBS) \
- $(NULL)
-
-meas_json_CFLAGS = \
- $(LIBOSMOCORE_CFLAGS) \
- $(LIBOSMOGSM_CFLAGS) \
- $(LIBOSMOABIS_CFLAGS) \
- $(NULL)
-
diff --git a/openbsc/src/utils/bs11_config.c b/openbsc/src/utils/bs11_config.c
deleted file mode 100644
index a0f3cb757..000000000
--- a/openbsc/src/utils/bs11_config.c
+++ /dev/null
@@ -1,953 +0,0 @@
-/* Siemens BS-11 microBTS configuration tool */
-
-/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
- * All Rights Reserved
- *
- * This software is based on ideas (but not code) of BS11Config
- * (C) 2009 by Dieter Spaar <spaar@mirider.augusta.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public 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 <stdlib.h>
-#include <stdio.h>
-#include <errno.h>
-#include <string.h>
-#include <getopt.h>
-#include <fcntl.h>
-#include <signal.h>
-
-#include <sys/stat.h>
-
-#include <openbsc/common_bsc.h>
-#include <openbsc/abis_nm.h>
-#include <osmocom/core/msgb.h>
-#include <osmocom/core/utils.h>
-#include <osmocom/gsm/tlv.h>
-#include <openbsc/debug.h>
-#include <osmocom/core/select.h>
-#include <openbsc/rs232.h>
-#include <osmocom/core/application.h>
-#include <osmocom/core/talloc.h>
-#include <osmocom/abis/abis.h>
-#include <osmocom/abis/e1_input.h>
-
-static void *tall_bs11cfg_ctx;
-static struct e1inp_sign_link *oml_link;
-
-/* state of our bs11_config application */
-enum bs11cfg_state {
- STATE_NONE,
- STATE_LOGON_WAIT,
- STATE_LOGON_ACK,
- STATE_SWLOAD,
- STATE_QUERY,
-};
-static enum bs11cfg_state bs11cfg_state = STATE_NONE;
-static char *command, *value;
-struct osmo_timer_list status_timer;
-
-static const uint8_t obj_li_attr[] = {
- NM_ATT_BS11_BIT_ERR_THESH, 0x09, 0x00,
- NM_ATT_BS11_L1_PROT_TYPE, 0x00,
- NM_ATT_BS11_LINE_CFG, 0x00,
-};
-static const uint8_t obj_bbsig0_attr[] = {
- NM_ATT_BS11_RSSI_OFFS, 0x02, 0x00, 0x00,
- NM_ATT_BS11_DIVERSITY, 0x01, 0x00,
-};
-static const uint8_t obj_pa0_attr[] = {
- NM_ATT_BS11_TXPWR, 0x01, BS11_TRX_POWER_GSM_30mW,
-};
-static const char *trx1_password = "1111111111";
-#define TEI_OML 25
-
-/* dummy function to keep gsm_data.c happy */
-struct osmo_counter *osmo_counter_alloc(const char *name)
-{
- return NULL;
-}
-
-int handle_serial_msg(struct msgb *rx_msg);
-
-/* create all objects for an initial configuration */
-static int create_objects(struct gsm_bts *bts)
-{
- fprintf(stdout, "Crating Objects for minimal config\n");
- abis_nm_bs11_create_object(bts, BS11_OBJ_LI, 0, sizeof(obj_li_attr),
- obj_li_attr);
- abis_nm_bs11_create_object(bts, BS11_OBJ_GPSU, 0, 0, NULL);
- abis_nm_bs11_create_object(bts, BS11_OBJ_ALCO, 0, 0, NULL);
- abis_nm_bs11_create_object(bts, BS11_OBJ_CCLK, 0, 0, NULL);
- abis_nm_bs11_create_object(bts, BS11_OBJ_BBSIG, 0,
- sizeof(obj_bbsig0_attr), obj_bbsig0_attr);
- abis_nm_bs11_create_object(bts, BS11_OBJ_PA, 0,
- sizeof(obj_pa0_attr), obj_pa0_attr);
- abis_nm_bs11_create_envaBTSE(bts, 0);
- abis_nm_bs11_create_envaBTSE(bts, 1);
- abis_nm_bs11_create_envaBTSE(bts, 2);
- abis_nm_bs11_create_envaBTSE(bts, 3);
-
- abis_nm_bs11_conn_oml_tei(bts, 0, 1, 0xff, TEI_OML);
-
- abis_nm_bs11_set_trx_power(bts->c0, BS11_TRX_POWER_GSM_30mW);
-
- sleep(1);
-
- abis_nm_bs11_set_trx1_pw(bts, trx1_password);
-
- sleep(1);
-
- return 0;
-}
-
-static int create_trx1(struct gsm_bts *bts)
-{
- uint8_t bbsig1_attr[sizeof(obj_bbsig0_attr)+12];
- uint8_t *cur = bbsig1_attr;
- struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, 1);
-
- if (!trx)
- trx = gsm_bts_trx_alloc(bts);
-
- fprintf(stdout, "Crating Objects for TRX1\n");
-
- abis_nm_bs11_set_trx1_pw(bts, trx1_password);
-
- sleep(1);
-
- cur = tlv_put(cur, NM_ATT_BS11_PASSWORD, 10,
- (uint8_t *)trx1_password);
- memcpy(cur, obj_bbsig0_attr, sizeof(obj_bbsig0_attr));
- abis_nm_bs11_create_object(bts, BS11_OBJ_BBSIG, 1,
- sizeof(bbsig1_attr), bbsig1_attr);
- abis_nm_bs11_create_object(bts, BS11_OBJ_PA, 1,
- sizeof(obj_pa0_attr), obj_pa0_attr);
- abis_nm_bs11_set_trx_power(trx, BS11_TRX_POWER_GSM_30mW);
-
- return 0;
-}
-
-static char *serial_port = "/dev/ttyUSB0";
-static char *fname_safety = "BTSBMC76.SWI";
-static char *fname_software = "HS011106.SWL";
-static int delay_ms = 0;
-static int win_size = 8;
-static int param_disconnect = 0;
-static int param_restart = 0;
-static int param_forced = 0;
-static struct gsm_bts *g_bts;
-
-static int file_is_readable(const char *fname)
-{
- int rc;
- struct stat st;
-
- rc = stat(fname, &st);
- if (rc < 0)
- return 0;
-
- if (S_ISREG(st.st_mode) && (st.st_mode & S_IRUSR))
- return 1;
-
- return 0;
-}
-
-static int percent;
-static int percent_old;
-
-/* callback function passed to the ABIS OML code */
-static int swload_cbfn(unsigned int hook, unsigned int event, struct msgb *msg,
- void *data, void *param)
-{
- if (hook != GSM_HOOK_NM_SWLOAD)
- return 0;
-
- switch (event) {
- case NM_MT_LOAD_INIT_ACK:
- fprintf(stdout, "Software Load Initiate ACK\n");
- break;
- case NM_MT_LOAD_INIT_NACK:
- fprintf(stderr, "ERROR: Software Load Initiate NACK\n");
- exit(5);
- break;
- case NM_MT_LOAD_END_ACK:
- if (data) {
- /* we did a safety load and must activate it */
- abis_nm_software_activate(g_bts, fname_safety,
- swload_cbfn, g_bts);
- sleep(5);
- }
- break;
- case NM_MT_LOAD_END_NACK:
- fprintf(stderr, "ERROR: Software Load End NACK\n");
- exit(3);
- break;
- case NM_MT_ACTIVATE_SW_NACK:
- fprintf(stderr, "ERROR: Activate Software NACK\n");
- exit(4);
- break;
- case NM_MT_ACTIVATE_SW_ACK:
- bs11cfg_state = STATE_NONE;
-
- break;
- case NM_MT_LOAD_SEG_ACK:
- percent = abis_nm_software_load_status(g_bts);
- if (percent > percent_old)
- printf("Software Download Progress: %d%%\n", percent);
- percent_old = percent;
- break;
- }
- return 0;
-}
-
-static const struct value_string bs11_linkst_names[] = {
- { 0, "Down" },
- { 1, "Up" },
- { 2, "Restoring" },
- { 0, NULL }
-};
-
-static const char *linkstate_name(uint8_t linkstate)
-{
- return get_value_string(bs11_linkst_names, linkstate);
-}
-
-static const struct value_string mbccu_load_names[] = {
- { 0, "No Load" },
- { 1, "Load BTSCAC" },
- { 2, "Load BTSDRX" },
- { 3, "Load BTSBBX" },
- { 4, "Load BTSARC" },
- { 5, "Load" },
- { 0, NULL }
-};
-
-static const char *mbccu_load_name(uint8_t linkstate)
-{
- return get_value_string(mbccu_load_names, linkstate);
-}
-
-static const char *bts_phase_name(uint8_t phase)
-{
- switch (phase) {
- case BS11_STATE_WARM_UP:
- case BS11_STATE_WARM_UP_2:
- return "Warm Up";
- break;
- case BS11_STATE_LOAD_SMU_SAFETY:
- return "Load SMU Safety";
- break;
- case BS11_STATE_LOAD_SMU_INTENDED:
- return "Load SMU Intended";
- break;
- case BS11_STATE_LOAD_MBCCU:
- return "Load MBCCU";
- break;
- case BS11_STATE_SOFTWARE_RQD:
- return "Software required";
- break;
- case BS11_STATE_WAIT_MIN_CFG:
- case BS11_STATE_WAIT_MIN_CFG_2:
- return "Wait minimal config";
- break;
- case BS11_STATE_MAINTENANCE:
- return "Maintenance";
- break;
- case BS11_STATE_NORMAL:
- return "Normal";
- break;
- case BS11_STATE_ABIS_LOAD:
- return "Abis load";
- break;
- default:
- return "Unknown";
- break;
- }
-}
-
-static const char *trx_power_name(uint8_t pwr)
-{
- switch (pwr) {
- case BS11_TRX_POWER_GSM_2W:
- return "2W (GSM)";
- case BS11_TRX_POWER_GSM_250mW:
- return "250mW (GSM)";
- case BS11_TRX_POWER_GSM_80mW:
- return "80mW (GSM)";
- case BS11_TRX_POWER_GSM_30mW:
- return "30mW (GSM)";
- case BS11_TRX_POWER_DCS_3W:
- return "3W (DCS)";
- case BS11_TRX_POWER_DCS_1W6:
- return "1.6W (DCS)";
- case BS11_TRX_POWER_DCS_500mW:
- return "500mW (DCS)";
- case BS11_TRX_POWER_DCS_160mW:
- return "160mW (DCS)";
- default:
- return "unknown value";
- }
-}
-
-static const char *pll_mode_name(uint8_t mode)
-{
- switch (mode) {
- case BS11_LI_PLL_LOCKED:
- return "E1 Locked";
- case BS11_LI_PLL_STANDALONE:
- return "Standalone";
- default:
- return "unknown";
- }
-}
-
-static const char *cclk_acc_name(uint8_t acc)
-{
- switch (acc) {
- case 0:
- /* Out of the demanded +/- 0.05ppm */
- return "Medium";
- case 1:
- /* Synchronized with Abis, within demanded tolerance +/- 0.05ppm */
- return "High";
- default:
- return "unknown";
- }
-}
-
-static const char *bport_lcfg_name(uint8_t lcfg)
-{
- switch (lcfg) {
- case BS11_LINE_CFG_STAR:
- return "Star";
- case BS11_LINE_CFG_MULTIDROP:
- return "Multi-Drop";
- default:
- return "unknown";
- }
-}
-
-static const char *obj_name(struct abis_om_fom_hdr *foh)
-{
- static char retbuf[256];
-
- retbuf[0] = 0;
-
- switch (foh->obj_class) {
- case NM_OC_BS11:
- strcat(retbuf, "BS11 ");
- switch (foh->obj_inst.bts_nr) {
- case BS11_OBJ_PA:
- sprintf(retbuf+strlen(retbuf), "Power Amplifier %d ",
- foh->obj_inst.ts_nr);
- break;
- case BS11_OBJ_LI:
- sprintf(retbuf+strlen(retbuf), "Line Interface ");
- break;
- case BS11_OBJ_CCLK:
- sprintf(retbuf+strlen(retbuf), "CCLK ");
- break;
- }
- break;
- case NM_OC_SITE_MANAGER:
- strcat(retbuf, "SITE MANAGER ");
- break;
- case NM_OC_BS11_BPORT:
- sprintf(retbuf+strlen(retbuf), "BPORT%u ",
- foh->obj_inst.bts_nr);
- break;
- }
- return retbuf;
-}
-
-static void print_state(struct tlv_parsed *tp)
-{
- if (TLVP_PRESENT(tp, NM_ATT_BS11_BTS_STATE)) {
- uint8_t phase, mbccu;
- if (TLVP_LEN(tp, NM_ATT_BS11_BTS_STATE) >= 1) {
- phase = *TLVP_VAL(tp, NM_ATT_BS11_BTS_STATE);
- printf("PHASE: %u %-20s ", phase & 0xf,
- bts_phase_name(phase));
- }
- if (TLVP_LEN(tp, NM_ATT_BS11_BTS_STATE) >= 2) {
- mbccu = *(TLVP_VAL(tp, NM_ATT_BS11_BTS_STATE)+1);
- printf("MBCCU0: %-11s MBCCU1: %-11s ",
- mbccu_load_name(mbccu & 0xf), mbccu_load_name(mbccu >> 4));
- }
- }
- if (TLVP_PRESENT(tp, NM_ATT_BS11_E1_STATE) &&
- TLVP_LEN(tp, NM_ATT_BS11_E1_STATE) >= 1) {
- uint8_t e1_state = *TLVP_VAL(tp, NM_ATT_BS11_E1_STATE);
- printf("Abis-link: %-9s ", linkstate_name(e1_state & 0xf));
- }
- printf("\n");
-}
-
-static int print_attr(struct tlv_parsed *tp)
-{
- if (TLVP_PRESENT(tp, NM_ATT_BS11_ESN_PCB_SERIAL)) {
- printf("\tBS-11 ESN PCB Serial Number: %s\n",
- TLVP_VAL(tp, NM_ATT_BS11_ESN_PCB_SERIAL));
- }
- if (TLVP_PRESENT(tp, NM_ATT_BS11_ESN_HW_CODE_NO)) {
- printf("\tBS-11 ESN Hardware Code Number: %s\n",
- TLVP_VAL(tp, NM_ATT_BS11_ESN_HW_CODE_NO)+6);
- }
- if (TLVP_PRESENT(tp, NM_ATT_BS11_ESN_FW_CODE_NO)) {
- printf("\tBS-11 ESN Firmware Code Number: %s\n",
- TLVP_VAL(tp, NM_ATT_BS11_ESN_FW_CODE_NO)+6);
- }
-#if 0
- if (TLVP_PRESENT(tp, NM_ATT_BS11_BOOT_SW_VERS)) {
- printf("BS-11 Boot Software Version: %s\n",
- TLVP_VAL(tp, NM_ATT_BS11_BOOT_SW_VERS)+6);
- }
-#endif
- if (TLVP_PRESENT(tp, NM_ATT_ABIS_CHANNEL) &&
- TLVP_LEN(tp, NM_ATT_ABIS_CHANNEL) >= 3) {
- const uint8_t *chan = TLVP_VAL(tp, NM_ATT_ABIS_CHANNEL);
- printf("\tE1 Channel: Port=%u Timeslot=%u ",
- chan[0], chan[1]);
- if (chan[2] == 0xff)
- printf("(Full Slot)\n");
- else
- printf("Subslot=%u\n", chan[2]);
- }
- if (TLVP_PRESENT(tp, NM_ATT_TEI))
- printf("\tTEI: %d\n", *TLVP_VAL(tp, NM_ATT_TEI));
- if (TLVP_PRESENT(tp, NM_ATT_BS11_TXPWR) &&
- TLVP_LEN(tp, NM_ATT_BS11_TXPWR) >= 1) {
- printf("\tTRX Power: %s\n",
- trx_power_name(*TLVP_VAL(tp, NM_ATT_BS11_TXPWR)));
- }
- if (TLVP_PRESENT(tp, NM_ATT_BS11_PLL_MODE) &&
- TLVP_LEN(tp, NM_ATT_BS11_PLL_MODE) >= 1) {
- printf("\tPLL Mode: %s\n",
- pll_mode_name(*TLVP_VAL(tp, NM_ATT_BS11_PLL_MODE)));
- }
- if (TLVP_PRESENT(tp, NM_ATT_BS11_PLL) &&
- TLVP_LEN(tp, NM_ATT_BS11_PLL) >= 4) {
- const uint8_t *vp = TLVP_VAL(tp, NM_ATT_BS11_PLL);
- printf("\tPLL Set Value=%d, Work Value=%d\n",
- vp[0] << 8 | vp[1], vp[2] << 8 | vp[3]);
- }
- if (TLVP_PRESENT(tp, NM_ATT_BS11_CCLK_ACCURACY) &&
- TLVP_LEN(tp, NM_ATT_BS11_CCLK_ACCURACY) >= 1) {
- const uint8_t *acc = TLVP_VAL(tp, NM_ATT_BS11_CCLK_ACCURACY);
- printf("\tCCLK Accuracy: %s (%d)\n", cclk_acc_name(*acc), *acc);
- }
- if (TLVP_PRESENT(tp, NM_ATT_BS11_CCLK_TYPE) &&
- TLVP_LEN(tp, NM_ATT_BS11_CCLK_TYPE) >= 1) {
- const uint8_t *acc = TLVP_VAL(tp, NM_ATT_BS11_CCLK_TYPE);
- printf("\tCCLK Type=%d\n", *acc);
- }
- if (TLVP_PRESENT(tp, NM_ATT_BS11_LINE_CFG) &&
- TLVP_LEN(tp, NM_ATT_BS11_LINE_CFG) >= 1) {
- const uint8_t *lcfg = TLVP_VAL(tp, NM_ATT_BS11_LINE_CFG);
- printf("\tLine Configuration: %s (%d)\n",
- bport_lcfg_name(*lcfg), *lcfg);
- }
-
-
-
- return 0;
-}
-
-static void cmd_query(void)
-{
- struct gsm_bts_trx *trx = g_bts->c0;
-
- bs11cfg_state = STATE_QUERY;
- abis_nm_bs11_get_serno(g_bts);
- abis_nm_bs11_get_oml_tei_ts(g_bts);
- abis_nm_bs11_get_pll_mode(g_bts);
- abis_nm_bs11_get_cclk(g_bts);
- abis_nm_bs11_get_trx_power(trx);
- trx = gsm_bts_trx_num(g_bts, 1);
- if (trx)
- abis_nm_bs11_get_trx_power(trx);
- abis_nm_bs11_get_bport_line_cfg(g_bts, 0);
- abis_nm_bs11_get_bport_line_cfg(g_bts, 1);
- sleep(1);
- abis_nm_bs11_factory_logon(g_bts, 0);
- command = NULL;
-}
-
-/* handle a response from the BTS to a GET STATE command */
-static int handle_state_resp(enum abis_bs11_phase state)
-{
- int rc = 0;
-
- switch (state) {
- case BS11_STATE_WARM_UP:
- case BS11_STATE_LOAD_SMU_SAFETY:
- case BS11_STATE_LOAD_SMU_INTENDED:
- case BS11_STATE_LOAD_MBCCU:
- break;
- case BS11_STATE_SOFTWARE_RQD:
- bs11cfg_state = STATE_SWLOAD;
- /* send safety load. Use g_bts as private 'param'
- * argument, so our swload_cbfn can distinguish
- * a safety load from a regular software */
- if (file_is_readable(fname_safety))
- rc = abis_nm_software_load(g_bts, 0xff, fname_safety,
- win_size, param_forced,
- swload_cbfn, g_bts);
- else
- fprintf(stderr, "No valid Safety Load file \"%s\"\n",
- fname_safety);
- break;
- case BS11_STATE_WAIT_MIN_CFG:
- case BS11_STATE_WAIT_MIN_CFG_2:
- bs11cfg_state = STATE_SWLOAD;
- rc = create_objects(g_bts);
- break;
- case BS11_STATE_MAINTENANCE:
- if (command) {
- if (!strcmp(command, "disconnect"))
- abis_nm_bs11_factory_logon(g_bts, 0);
- else if (!strcmp(command, "reconnect"))
- rc = abis_nm_bs11_bsc_disconnect(g_bts, 1);
- else if (!strcmp(command, "software")
- && bs11cfg_state != STATE_SWLOAD) {
- bs11cfg_state = STATE_SWLOAD;
- /* send software (FIXME: over A-bis?) */
- if (file_is_readable(fname_software))
- rc = abis_nm_bs11_load_swl(g_bts, fname_software,
- win_size, param_forced,
- swload_cbfn);
- else
- fprintf(stderr, "No valid Software file \"%s\"\n",
- fname_software);
- } else if (!strcmp(command, "delete-trx1")) {
- printf("Locing BBSIG and PA objects of TRX1\n");
- abis_nm_chg_adm_state(g_bts, NM_OC_BS11,
- BS11_OBJ_BBSIG, 0, 1,
- NM_STATE_LOCKED);
- abis_nm_chg_adm_state(g_bts, NM_OC_BS11,
- BS11_OBJ_PA, 0, 1,
- NM_STATE_LOCKED);
- sleep(1);
- printf("Deleting BBSIG and PA objects of TRX1\n");
- abis_nm_bs11_delete_object(g_bts, BS11_OBJ_BBSIG, 1);
- abis_nm_bs11_delete_object(g_bts, BS11_OBJ_PA, 1);
- sleep(1);
- abis_nm_bs11_factory_logon(g_bts, 0);
- command = NULL;
- } else if (!strcmp(command, "create-trx1")) {
- create_trx1(g_bts);
- sleep(1);
- abis_nm_bs11_factory_logon(g_bts, 0);
- command = NULL;
- } else if (!strcmp(command, "pll-e1-locked")) {
- abis_nm_bs11_set_pll_locked(g_bts, 1);
- sleep(1);
- abis_nm_bs11_factory_logon(g_bts, 0);
- command = NULL;
- } else if (!strcmp(command, "pll-standalone")) {
- abis_nm_bs11_set_pll_locked(g_bts, 0);
- sleep(1);
- abis_nm_bs11_factory_logon(g_bts, 0);
- command = NULL;
- } else if (!strcmp(command, "pll-setvalue")) {
- abis_nm_bs11_set_pll(g_bts, atoi(value));
- sleep(1);
- abis_nm_bs11_factory_logon(g_bts, 0);
- command = NULL;
- } else if (!strcmp(command, "pll-workvalue")) {
- /* To set the work value we need to login as FIELD */
- abis_nm_bs11_factory_logon(g_bts, 0);
- sleep(1);
- abis_nm_bs11_infield_logon(g_bts, 1);
- sleep(1);
- abis_nm_bs11_set_pll(g_bts, atoi(value));
- sleep(1);
- abis_nm_bs11_infield_logon(g_bts, 0);
- command = NULL;
- } else if (!strcmp(command, "oml-tei")) {
- abis_nm_bs11_conn_oml_tei(g_bts, 0, 1, 0xff, TEI_OML);
- command = NULL;
- } else if (!strcmp(command, "restart")) {
- abis_nm_bs11_restart(g_bts);
- command = NULL;
- } else if (!strcmp(command, "query")) {
- cmd_query();
- } else if (!strcmp(command, "create-bport1")) {
- abis_nm_bs11_create_bport(g_bts, 1);
- sleep(1);
- abis_nm_bs11_factory_logon(g_bts, 0);
- command = NULL;
- } else if (!strcmp(command, "delete-bport1")) {
- abis_nm_chg_adm_state(g_bts, NM_OC_BS11_BPORT, 1, 0xff, 0xff, NM_STATE_LOCKED);
- sleep(1);
- abis_nm_bs11_delete_bport(g_bts, 1);
- sleep(1);
- abis_nm_bs11_factory_logon(g_bts, 0);
- command = NULL;
- } else if (!strcmp(command, "bport0-star")) {
- abis_nm_bs11_set_bport_line_cfg(g_bts, 0, BS11_LINE_CFG_STAR);
- sleep(1);
- abis_nm_bs11_factory_logon(g_bts, 0);
- command = NULL;
- } else if (!strcmp(command, "bport0-multidrop")) {
- abis_nm_bs11_set_bport_line_cfg(g_bts, 0, BS11_LINE_CFG_MULTIDROP);
- sleep(1);
- abis_nm_bs11_factory_logon(g_bts, 0);
- command = NULL;
- } else if (!strcmp(command, "bport1-multidrop")) {
- abis_nm_bs11_set_bport_line_cfg(g_bts, 1, BS11_LINE_CFG_MULTIDROP);
- sleep(1);
- abis_nm_bs11_factory_logon(g_bts, 0);
- command = NULL;
- }
-
- }
- break;
- case BS11_STATE_NORMAL:
- if (command) {
- if (!strcmp(command, "reconnect"))
- abis_nm_bs11_factory_logon(g_bts, 0);
- else if (!strcmp(command, "disconnect"))
- abis_nm_bs11_bsc_disconnect(g_bts, 0);
- else if (!strcmp(command, "query")) {
- cmd_query();
- }
- } else if (param_disconnect) {
- param_disconnect = 0;
- abis_nm_bs11_bsc_disconnect(g_bts, 0);
- if (param_restart) {
- param_restart = 0;
- abis_nm_bs11_restart(g_bts);
- }
- }
- break;
- default:
- break;
- }
- return rc;
-}
-
-/* handle a fully-received message/packet from the RS232 port */
-static int abis_nm_bs11cfg_rcvmsg(struct msgb *rx_msg)
-{
- struct e1inp_sign_link *link = rx_msg->dst;
- struct abis_om_hdr *oh;
- struct abis_om_fom_hdr *foh;
- struct tlv_parsed tp;
- int rc = -1;
-
-#if 0
- const uint8_t too_fast[] = { 0x12, 0x80, 0x00, 0x00, 0x02, 0x02 };
-
- if (rx_msg->len < LAPD_HDR_LEN
- + sizeof(struct abis_om_fom_hdr)
- + sizeof(struct abis_om_hdr)) {
- if (!memcmp(rx_msg->data + 2, too_fast,
- sizeof(too_fast))) {
- fprintf(stderr, "BS11 tells us we're too "
- "fast, try --delay bigger than %u\n",
- delay_ms);
- return -E2BIG;
- } else
- fprintf(stderr, "unknown BS11 message\n");
- }
-#endif
-
- oh = (struct abis_om_hdr *) msgb_l2(rx_msg);
- foh = (struct abis_om_fom_hdr *) oh->data;
- switch (foh->msg_type) {
- case NM_MT_BS11_LMT_LOGON_ACK:
- printf("LMT LOGON: ACK\n\n");
- if (bs11cfg_state == STATE_NONE)
- bs11cfg_state = STATE_LOGON_ACK;
- rc = abis_nm_bs11_get_state(g_bts);
- break;
- case NM_MT_BS11_LMT_LOGOFF_ACK:
- printf("LMT LOGOFF: ACK\n");
- exit(0);
- break;
- case NM_MT_BS11_GET_STATE_ACK:
- rc = abis_nm_tlv_parse(&tp, g_bts, foh->data, oh->length-sizeof(*foh));
- print_state(&tp);
- if (TLVP_PRESENT(&tp, NM_ATT_BS11_BTS_STATE) &&
- TLVP_LEN(&tp, NM_ATT_BS11_BTS_STATE) >= 1)
- rc = handle_state_resp(*TLVP_VAL(&tp, NM_ATT_BS11_BTS_STATE));
- break;
- case NM_MT_GET_ATTR_RESP:
- printf("\n%sATTRIBUTES:\n", obj_name(foh));
- abis_nm_tlv_parse(&tp, g_bts, foh->data, oh->length-sizeof(*foh));
- rc = print_attr(&tp);
- //osmo_hexdump(foh->data, oh->length-sizeof(*foh));
- break;
- case NM_MT_BS11_SET_ATTR_ACK:
- printf("SET ATTRIBUTE ObjClass=0x%02x ObjInst=(%d,%d,%d) ACK\n",
- foh->obj_class, foh->obj_inst.bts_nr,
- foh->obj_inst.trx_nr, foh->obj_inst.ts_nr);
- rc = 0;
- break;
- case NM_MT_BS11_SET_ATTR_NACK:
- printf("SET ATTRIBUTE ObjClass=0x%02x ObjInst=(%d,%d,%d) NACK\n",
- foh->obj_class, foh->obj_inst.bts_nr,
- foh->obj_inst.trx_nr, foh->obj_inst.ts_nr);
- break;
- case NM_MT_GET_ATTR_NACK:
- printf("\n%sGET ATTR NACK\n", obj_name(foh));
- break;
- case NM_MT_BS11_CREATE_OBJ_ACK:
- printf("\n%sCREATE OBJECT ACK\n", obj_name(foh));
- break;
- case NM_MT_BS11_CREATE_OBJ_NACK:
- printf("\n%sCREATE OBJECT NACK\n", obj_name(foh));
- break;
- case NM_MT_BS11_DELETE_OBJ_ACK:
- printf("\n%sDELETE OBJECT ACK\n", obj_name(foh));
- break;
- case NM_MT_BS11_DELETE_OBJ_NACK:
- printf("\n%sDELETE OBJECT NACK\n", obj_name(foh));
- break;
- default:
- rc = abis_nm_rcvmsg(rx_msg);
- }
- if (rc < 0) {
- perror("ERROR in main loop");
- //break;
- }
- /* flush the queue of pending messages to be sent. */
- abis_nm_queue_send_next(link->trx->bts);
- if (rc == 1)
- return rc;
-
- switch (bs11cfg_state) {
- case STATE_NONE:
- abis_nm_bs11_factory_logon(g_bts, 1);
- break;
- case STATE_LOGON_ACK:
- osmo_timer_schedule(&status_timer, 5, 0);
- break;
- default:
- break;
- }
-
- return rc;
-}
-
-void status_timer_cb(void *data)
-{
- abis_nm_bs11_get_state(g_bts);
-}
-
-static void print_banner(void)
-{
- printf("bs11_config (C) 2009-2010 by Harald Welte and Dieter Spaar\n");
- printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n");
-}
-
-static void print_help(void)
-{
- printf("bs11_config [options] [command]\n");
- printf("\nSupported options:\n");
- printf("\t-h --help\t\t\tPrint this help text\n");
- printf("\t-p --port </dev/ttyXXX>\t\tSpecify serial port\n");
- printf("\t-s --software <file>\t\tSpecify Software file\n");
- printf("\t-S --safety <file>\t\tSpecify Safety Load file\n");
- printf("\t-d --delay <ms>\t\t\tSpecify delay in milliseconds\n");
- printf("\t-D --disconnect\t\t\tDisconnect BTS from BSC\n");
- printf("\t-w --win-size <num>\t\tSpecify Window Size\n");
- printf("\t-f --forced\t\t\tForce Software Load\n");
- printf("\nSupported commands:\n");
- printf("\tquery\t\t\tQuery the BS-11 about serial number and configuration\n");
- printf("\tdisconnect\t\tDisconnect A-bis link (go into administrative state)\n");
- printf("\tresconnect\t\tReconnect A-bis link (go into normal state)\n");
- printf("\trestart\t\t\tRestart the BTS\n");
- printf("\tsoftware\t\tDownload Software (only in administrative state)\n");
- printf("\tcreate-trx1\t\tCreate objects for TRX1 (Danger: Your BS-11 might overheat)\n");
- printf("\tdelete-trx1\t\tDelete objects for TRX1\n");
- printf("\tpll-e1-locked\t\tSet the PLL to be locked to E1 clock\n");
- printf("\tpll-standalone\t\tSet the PLL to be in standalone mode\n");
- printf("\tpll-setvalue <value>\tSet the PLL set value\n");
- printf("\tpll-workvalue <value>\tSet the PLL work value\n");
- printf("\toml-tei\t\t\tSet OML E1 TS and TEI\n");
- printf("\tbport0-star\t\tSet BPORT0 line config to star\n");
- printf("\tbport0-multidrop\tSet BPORT0 line config to multidrop\n");
- printf("\tbport1-multidrop\tSet BPORT1 line config to multidrop\n");
- printf("\tcreate-bport1\t\tCreate BPORT1 object\n");
- printf("\tdelete-bport1\t\tDelete BPORT1 object\n");
-}
-
-static void handle_options(int argc, char **argv)
-{
- int option_index = 0;
- print_banner();
-
- while (1) {
- int c;
- static struct option long_options[] = {
- { "help", 0, 0, 'h' },
- { "port", 1, 0, 'p' },
- { "software", 1, 0, 's' },
- { "safety", 1, 0, 'S' },
- { "delay", 1, 0, 'd' },
- { "disconnect", 0, 0, 'D' },
- { "win-size", 1, 0, 'w' },
- { "forced", 0, 0, 'f' },
- { "restart", 0, 0, 'r' },
- { "debug", 1, 0, 'b'},
- };
-
- c = getopt_long(argc, argv, "hp:s:S:td:Dw:fra:",
- long_options, &option_index);
-
- if (c == -1)
- break;
-
- switch (c) {
- case 'h':
- print_help();
- exit(0);
- case 'p':
- serial_port = optarg;
- break;
- case 'b':
- log_parse_category_mask(osmo_stderr_target, optarg);
- break;
- case 's':
- fname_software = optarg;
- break;
- case 'S':
- fname_safety = optarg;
- break;
- case 'd':
- delay_ms = atoi(optarg);
- break;
- case 'w':
- win_size = atoi(optarg);
- break;
- case 'D':
- param_disconnect = 1;
- break;
- case 'f':
- param_forced = 1;
- break;
- case 'r':
- param_disconnect = 1;
- param_restart = 1;
- break;
- default:
- break;
- }
- }
- if (optind < argc) {
- command = argv[optind];
- if (optind+1 < argc)
- value = argv[optind+1];
- }
-
-}
-
-static int num_sigint;
-
-static void signal_handler(int signal)
-{
- fprintf(stdout, "\nsignal %u received\n", signal);
-
- switch (signal) {
- case SIGINT:
- num_sigint++;
- abis_nm_bs11_factory_logon(g_bts, 0);
- if (num_sigint >= 3)
- exit(0);
- break;
- }
-}
-
-static int bs11cfg_sign_link(struct msgb *msg)
-{
- msg->dst = oml_link;
- return abis_nm_bs11cfg_rcvmsg(msg);
-}
-
-struct e1inp_line_ops bs11cfg_e1inp_line_ops = {
- .sign_link = bs11cfg_sign_link,
-};
-
-extern int bts_model_bs11_init(void);
-int main(int argc, char **argv)
-{
- struct gsm_network *gsmnet;
- struct e1inp_line *line;
-
- tall_bs11cfg_ctx = talloc_named_const(NULL, 0, "bs11-config");
- msgb_talloc_ctx_init(tall_bs11cfg_ctx, 0);
-
- osmo_init_logging(&log_info);
- handle_options(argc, argv);
- bts_model_bs11_init();
-
- gsmnet = bsc_network_init(tall_bs11cfg_ctx, 1, 1, NULL);
- if (!gsmnet) {
- fprintf(stderr, "Unable to allocate gsm network\n");
- exit(1);
- }
- g_bts = gsm_bts_alloc_register(gsmnet, GSM_BTS_TYPE_BS11,
- HARDCODED_BSIC);
-
- /* Override existing OML callback handler to set our own. */
- g_bts->model->oml_rcvmsg = abis_nm_bs11cfg_rcvmsg;
-
- libosmo_abis_init(tall_bs11cfg_ctx);
-
- /* Initialize virtual E1 line over rs232. */
- line = talloc_zero(tall_bs11cfg_ctx, struct e1inp_line);
- if (!line) {
- fprintf(stderr, "Unable to allocate memory for virtual E1 line\n");
- exit(1);
- }
- /* set the serial port. */
- bs11cfg_e1inp_line_ops.cfg.rs232.port = serial_port;
- bs11cfg_e1inp_line_ops.cfg.rs232.delay = delay_ms;
-
- line->driver = e1inp_driver_find("rs232");
- if (!line->driver) {
- fprintf(stderr, "cannot find `rs232' driver, giving up.\n");
- exit(1);
- }
- e1inp_line_bind_ops(line, &bs11cfg_e1inp_line_ops);
-
- /* configure and create signalling link for OML. */
- e1inp_ts_config_sign(&line->ts[0], line);
- g_bts->oml_link = oml_link =
- e1inp_sign_link_create(&line->ts[0], E1INP_SIGN_OML,
- g_bts->c0, TEI_OML, 0);
-
- e1inp_line_update(line);
-
- signal(SIGINT, &signal_handler);
-
- abis_nm_bs11_factory_logon(g_bts, 1);
- //abis_nm_bs11_get_serno(g_bts);
-
- osmo_timer_setup(&status_timer, status_timer_cb, NULL);
-
- while (1) {
- if (osmo_select_main(0) < 0)
- break;
- }
-
- abis_nm_bs11_factory_logon(g_bts, 0);
-
- exit(0);
-}
diff --git a/openbsc/src/utils/isdnsync.c b/openbsc/src/utils/isdnsync.c
deleted file mode 100644
index cc8ff6723..000000000
--- a/openbsc/src/utils/isdnsync.c
+++ /dev/null
@@ -1,189 +0,0 @@
-/* isdnsync.c
- *
- * Author 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 <unistd.h>
-#include <sys/ioctl.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <sys/socket.h>
-#include "mISDNif.h"
-#define MISDN_OLD_AF_COMPATIBILITY
-#define AF_COMPATIBILITY_FUNC
-#include "compat_af_isdn.h"
-
-int card = 0;
-int sock = -1;
-
-int mISDN_open(void)
-{
- int fd, ret;
- struct mISDN_devinfo devinfo;
- struct sockaddr_mISDN l2addr;
-
- fd = socket(PF_ISDN, SOCK_RAW, ISDN_P_BASE);
- if (fd < 0) {
- fprintf(stderr, "could not open socket (%s)\n", strerror(errno));
- return fd;
- }
- devinfo.id = card;
- ret = ioctl(fd, IMGETDEVINFO, &devinfo);
- if (ret < 0) {
- fprintf(stderr,"could not send IOCTL IMGETCOUNT (%s)\n", strerror(errno));
- close(fd);
- return ret;
- }
- close(fd);
- if (!(devinfo.Dprotocols & (1 << ISDN_P_TE_S0))
- && !(devinfo.Dprotocols & (1 << ISDN_P_TE_E1))) {
- fprintf(stderr,"Interface does not support TE mode (%s)\n", strerror(errno));
- return ret;
- }
- fd = socket(PF_ISDN, SOCK_DGRAM, ISDN_P_LAPD_TE);
- if (fd < 0) {
- fprintf(stderr,"could not open ISDN_P_LAPD_TE socket (%s)\n", strerror(errno));
- return fd;
- }
- l2addr.family = AF_ISDN;
- l2addr.dev = card;
- l2addr.channel = 0;
- l2addr.sapi = 0;
- l2addr.tei = 0;
- ret = bind(fd, (struct sockaddr *)&l2addr, sizeof(l2addr));
- if (ret < 0) {
- fprintf(stderr,"could not bind socket for card %d (%s)\n", card, strerror(errno));
- close(fd);
- return ret;
- }
- sock = fd;
-
- return sock;
-}
-
-
-void mISDN_handle(void)
-{
- int ret;
- fd_set rfd;
- struct timeval tv;
- struct sockaddr_mISDN addr;
- socklen_t alen;
- unsigned char buffer[2048];
- struct mISDNhead *hh = (struct mISDNhead *)buffer;
- int l1 = 0, l2 = 0, tei = 0;
-
- while(1) {
-again:
- FD_ZERO(&rfd);
- FD_SET(sock, &rfd);
- tv.tv_sec = 2;
- tv.tv_usec = 0;
- ret = select(sock+1, &rfd, NULL, NULL, &tv);
- if (ret < 0) {
- if (errno == EINTR)
- continue;
- fprintf(stderr, "%s aborted: %s\n", __FUNCTION__, strerror(errno));
- break;
- }
- if (FD_ISSET(sock, &rfd)) {
- alen = sizeof(addr);
- ret = recvfrom(sock, buffer, sizeof(buffer), 0, (struct sockaddr *) &addr, &alen);
- if (ret < 0) {
- fprintf(stderr, "%s read socket error %s\n", __FUNCTION__, strerror(errno));
- } else if (ret < MISDN_HEADER_LEN) {
- fprintf(stderr, "%s read socket shor frame\n", __FUNCTION__);
- } else {
- switch(hh->prim) {
- case MPH_ACTIVATE_IND:
- case PH_ACTIVATE_IND:
- if (!l1) {
- printf("PH_ACTIVATE\n");
- printf("*** Sync available from interface :-)\n");
- l1 = 1;
- }
- goto again;
- break;
- case MPH_DEACTIVATE_IND:
- case PH_DEACTIVATE_IND:
- if (l1) {
- printf("PH_DEACTIVATE\n");
- printf("*** Lost sync on interface :-(\n");
- l1 = 0;
- }
- goto again;
- break;
- case DL_ESTABLISH_IND:
- case DL_ESTABLISH_CNF:
- printf("DL_ESTABLISH\n");
- l2 = 1;
- goto again;
- break;
- case DL_RELEASE_IND:
- case DL_RELEASE_CNF:
- printf("DL_RELEASE\n");
- l2 = 0;
- goto again;
- break;
- case DL_INFORMATION_IND:
- printf("DL_INFORMATION (tei %d sapi %d)\n", addr.tei, addr.sapi);
- tei = 1;
- break;
- default:
-// printf("prim %x\n", hh->prim);
- goto again;
- }
- }
- }
- if (tei && !l2) {
- hh->prim = DL_ESTABLISH_REQ;
- printf("-> activating layer 2\n");
- sendto(sock, buffer, MISDN_HEADER_LEN, 0, (struct sockaddr *) &addr, alen);
- }
- }
-}
-
-int main(int argc, char *argv[])
-{
- int ret;
-
- if (argc <= 1)
- {
- printf("Usage: %s <card>\n\n", argv[0]);
- printf("Opens given card number in TE-mode PTP and tries to keep layer 2 established.\n");
- printf("This keeps layer 1 activated to retrieve a steady sync signal from network.\n");
- return(0);
- }
-
- card = atoi(argv[1]);
-
- init_af_isdn();
-
- if ((ret = mISDN_open() < 0))
- return(ret);
-
- mISDN_handle();
-
- close(sock);
-
- return 0;
-}
diff --git a/openbsc/src/utils/meas_db.c b/openbsc/src/utils/meas_db.c
deleted file mode 100644
index d81efcade..000000000
--- a/openbsc/src/utils/meas_db.c
+++ /dev/null
@@ -1,330 +0,0 @@
-/* Routines for storing measurement reports in SQLite3 database */
-
-/* (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 <stdint.h>
-#include <errno.h>
-#include <string.h>
-
-#include <sqlite3.h>
-
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/utils.h>
-#include <osmocom/gsm/gsm_utils.h>
-#include <openbsc/meas_rep.h>
-
-#include "meas_db.h"
-
-#define INS_MR "INSERT INTO meas_rep (time, imsi, name, scenario, nr, bs_power, ms_timing_offset, fpc, ms_l1_pwr, ms_l1_ta) VALUES (?,?,?,?,?,?,?,?,?,?)"
-#define INS_UD "INSERT INTO meas_rep_unidir (meas_id, rx_lev_full, rx_lev_sub, rx_qual_full, rx_qual_sub, dtx, uplink) VALUES (?,?,?,?,?,?,?)"
-#define UPD_MR "UPDATE meas_rep SET ul_unidir=?, dl_unidir=? WHERE id=?"
-
-struct meas_db_state {
- sqlite3 *db;
- sqlite3_stmt *stmt_ins_ud;
- sqlite3_stmt *stmt_ins_mr;
- sqlite3_stmt *stmt_upd_mr;
-};
-
-/* macros to check for SQLite3 result codes */
-#define _SCK_OK(db, call, exp) \
- do { \
- int rc = call; \
- if (rc != exp) { \
- fprintf(stderr,"SQL Error in line %u: %s\n", \
- __LINE__, sqlite3_errmsg(db)); \
- goto err_io; \
- } \
- } while (0)
-#define SCK_OK(db, call) _SCK_OK(db, call, SQLITE_OK)
-#define SCK_DONE(db, call) _SCK_OK(db, call, SQLITE_DONE)
-
-static int _insert_ud(struct meas_db_state *st, unsigned long meas_id, int dtx,
- int uplink, const struct gsm_meas_rep_unidir *ud)
-{
- unsigned long rowid;
-
- SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_ud, 1, meas_id));
- SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_ud, 2,
- rxlev2dbm(ud->full.rx_lev)));
- SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_ud, 3,
- rxlev2dbm(ud->sub.rx_lev)));
- SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_ud, 4, ud->full.rx_qual));
- SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_ud, 5, ud->sub.rx_qual));
- SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_ud, 6, dtx));
- SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_ud, 7, uplink));
-
- SCK_DONE(st->db, sqlite3_step(st->stmt_ins_ud));
-
- SCK_OK(st->db, sqlite3_reset(st->stmt_ins_ud));
-
- return sqlite3_last_insert_rowid(st->db);
-err_io:
- exit(1);
-}
-
-/* insert a measurement report into the database */
-int meas_db_insert(struct meas_db_state *st, const char *imsi,
- const char *name, unsigned long timestamp,
- const char *scenario,
- const struct gsm_meas_rep *mr)
-{
- int rc;
- sqlite3_int64 rowid, ul_rowid, dl_rowid;
-
- SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 1, timestamp));
-
- if (imsi)
- SCK_OK(st->db, sqlite3_bind_text(st->stmt_ins_mr, 2,
- imsi, -1, SQLITE_STATIC));
- else
- SCK_OK(st->db, sqlite3_bind_null(st->stmt_ins_mr, 2));
-
- if (name)
- SCK_OK(st->db, sqlite3_bind_text(st->stmt_ins_mr, 3,
- name, -1, SQLITE_STATIC));
- else
- SCK_OK(st->db, sqlite3_bind_null(st->stmt_ins_mr, 3));
-
- if (scenario)
- SCK_OK(st->db, sqlite3_bind_text(st->stmt_ins_mr, 4,
- scenario, -1, SQLITE_STATIC));
- else
- SCK_OK(st->db, sqlite3_bind_null(st->stmt_ins_mr, 4));
-
-
- SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 5, mr->nr));
- SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 6, mr->bs_power));
-
- if (mr->flags & MEAS_REP_F_MS_TO)
- SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 7, mr->ms_timing_offset));
- else
- SCK_OK(st->db, sqlite3_bind_null(st->stmt_ins_mr, 7));
-
- if (mr->flags & MEAS_REP_F_FPC)
- SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 8, 1));
- else
- SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 8, 0));
-
- if (mr->flags & MEAS_REP_F_MS_L1) {
- SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 9,
- mr->ms_l1.pwr));
- SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 10,
- mr->ms_l1.ta));
- }
-
- SCK_DONE(st->db, sqlite3_step(st->stmt_ins_mr));
- SCK_OK(st->db, sqlite3_reset(st->stmt_ins_mr));
-
- rowid = sqlite3_last_insert_rowid(st->db);
-
- /* insert uplink measurement */
- ul_rowid = _insert_ud(st, rowid, mr->flags & MEAS_REP_F_UL_DTX,
- 1, &mr->ul);
- SCK_OK(st->db, sqlite3_bind_int(st->stmt_upd_mr, 1, ul_rowid));
-
- /* insert downlink measurement, if present */
- if (mr->flags & MEAS_REP_F_DL_VALID) {
- dl_rowid = _insert_ud(st, rowid, mr->flags & MEAS_REP_F_DL_DTX,
- 0, &mr->dl);
- SCK_OK(st->db, sqlite3_bind_int(st->stmt_upd_mr, 2, dl_rowid));
- } else
- SCK_OK(st->db, sqlite3_bind_null(st->stmt_upd_mr, 2));
-
- /* update meas_rep with the id's of the unidirectional
- * measurements */
- SCK_OK(st->db, sqlite3_bind_int(st->stmt_upd_mr, 3, rowid));
- SCK_DONE(st->db, sqlite3_step(st->stmt_upd_mr));
- SCK_OK(st->db, sqlite3_reset(st->stmt_upd_mr));
-
- return 0;
-
-err_io:
- return -EIO;
-}
-
-int meas_db_begin(struct meas_db_state *st)
-{
- SCK_OK(st->db, sqlite3_exec(st->db, "BEGIN", NULL, NULL, NULL));
-
- return 0;
-
-err_io:
- return -EIO;
-}
-
-int meas_db_commit(struct meas_db_state *st)
-{
- SCK_OK(st->db, sqlite3_exec(st->db, "COMMIT", NULL, NULL, NULL));
-
- return 0;
-
-err_io:
- return -EIO;
-}
-
-static const char *create_stmts[] = {
- "CREATE TABLE IF NOT EXISTS meas_rep ("
- "id INTEGER PRIMARY KEY AUTOINCREMENT,"
- "time TIMESTAMP,"
- "imsi TEXT,"
- "name TEXT,"
- "scenario TEXT,"
- "nr INTEGER,"
- "bs_power INTEGER NOT NULL,"
- "ms_timing_offset INTEGER,"
- "fpc INTEGER NOT NULL DEFAULT 0,"
- "ul_unidir INTEGER REFERENCES meas_rep_unidir(id),"
- "dl_unidir INTEGER REFERENCES meas_rep_unidir(id),"
- "ms_l1_pwr INTEGER,"
- "ms_l1_ta INTEGER"
- ")",
- "CREATE TABLE IF NOT EXISTS meas_rep_unidir ("
- "id INTEGER PRIMARY KEY AUTOINCREMENT,"
- "meas_id INTEGER NOT NULL REFERENCES meas_rep(id),"
- "rx_lev_full INTEGER NOT NULL,"
- "rx_lev_sub INTEGER NOT NULL,"
- "rx_qual_full INTEGER NOT NULL,"
- "rx_qual_sub INTEGER NOT NULL,"
- "dtx BOOLEAN NOT NULL DEFAULT 0,"
- "uplink BOOLEAN NOT NULL"
- ")",
- "CREATE VIEW IF NOT EXISTS path_loss AS "
- "SELECT "
- "meas_rep.id, "
- "datetime(time,'unixepoch') AS timestamp, "
- "imsi, "
- "name, "
- "scenario, "
- "ms_timing_offset, "
- "ms_l1_ta, "
- "fpc, "
- "ms_l1_pwr, "
- "ud_ul.rx_lev_full AS ul_rx_lev_full, "
- "ms_l1_pwr-ud_ul.rx_lev_full AS ul_path_loss_full, "
- "ud_ul.rx_lev_sub ul_rx_lev_sub, "
- "ms_l1_pwr-ud_ul.rx_lev_sub AS ul_path_loss_sub, "
- "ud_ul.rx_qual_full AS ul_rx_qual_full, "
- "ud_ul.rx_qual_sub AS ul_rx_qual_sub, "
- "bs_power, "
- "ud_dl.rx_lev_full AS dl_rx_lev_full, "
- "bs_power-ud_dl.rx_lev_full AS dl_path_loss_full, "
- "ud_dl.rx_lev_sub AS dl_rx_lev_sub, "
- "bs_power-ud_dl.rx_lev_sub AS dl_path_loss_sub, "
- "ud_dl.rx_qual_full AS dl_rx_qual_full, "
- "ud_dl.rx_qual_sub AS dl_rx_qual_sub "
- "FROM "
- "meas_rep, "
- "meas_rep_unidir AS ud_dl, "
- "meas_rep_unidir AS ud_ul "
- "WHERE "
- "ud_ul.id = meas_rep.ul_unidir AND "
- "ud_dl.id = meas_rep.dl_unidir",
- "CREATE VIEW IF NOT EXISTS overview AS "
- "SELECT "
- "id,"
- "timestamp,"
- "imsi,"
- "name,"
- "scenario,"
- "ms_l1_pwr,"
- "ul_rx_lev_full,"
- "ul_path_loss_full,"
- "ul_rx_qual_full,"
- "bs_power,"
- "dl_rx_lev_full,"
- "dl_path_loss_full,"
- "dl_rx_qual_full "
- "FROM path_loss",
-};
-
-static int check_create_tbl(struct meas_db_state *st)
-{
- int i, rc;
-
- for (i = 0; i < ARRAY_SIZE(create_stmts); i++) {
- SCK_OK(st->db, sqlite3_exec(st->db, create_stmts[i],
- NULL, NULL, NULL));
- }
-
- return 0;
-err_io:
- return -EIO;
-}
-
-
-#define PREP_CHK(db, stmt, ptr) \
- do { \
- int rc; \
- rc = sqlite3_prepare_v2(db, stmt, strlen(stmt)+1, \
- ptr, NULL); \
- if (rc != SQLITE_OK) { \
- fprintf(stderr, "Error during prepare of '%s': %s\n", \
- stmt, sqlite3_errmsg(db)); \
- goto err_io; \
- } \
- } while (0)
-
-struct meas_db_state *meas_db_open(void *ctx, const char *fname)
-{
- int rc;
- struct meas_db_state *st = talloc_zero(ctx, struct meas_db_state);
-
- if (!st)
- return NULL;
-
- rc = sqlite3_open_v2(fname, &st->db,
- SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE,
- NULL);
- if (rc != SQLITE_OK) {
- fprintf(stderr, "Unable to open DB: %s\n",
- sqlite3_errmsg(st->db));
- goto err_io;
- }
-
- rc = check_create_tbl(st);
-
- PREP_CHK(st->db, INS_MR, &st->stmt_ins_mr);
- PREP_CHK(st->db, INS_UD, &st->stmt_ins_ud);
- PREP_CHK(st->db, UPD_MR, &st->stmt_upd_mr);
-
- return st;
-err_io:
- talloc_free(st);
- return NULL;
-}
-
-void meas_db_close(struct meas_db_state *st)
-{
- if (sqlite3_finalize(st->stmt_ins_mr) != SQLITE_OK)
- fprintf(stderr, "DB insert measurement report finalize error: %s\n",
- sqlite3_errmsg(st->db));
- if (sqlite3_finalize(st->stmt_ins_ud) != SQLITE_OK)
- fprintf(stderr, "DB insert unidir finalize error: %s\n",
- sqlite3_errmsg(st->db));
- if (sqlite3_finalize(st->stmt_upd_mr) != SQLITE_OK)
- fprintf(stderr, "DB update measurement report finalize error: %s\n",
- sqlite3_errmsg(st->db));
- if (sqlite3_close(st->db) != SQLITE_OK)
- fprintf(stderr, "Unable to close DB, abandoning.\n");
-
- talloc_free(st);
-
-}
diff --git a/openbsc/src/utils/meas_db.h b/openbsc/src/utils/meas_db.h
deleted file mode 100644
index 889e9022f..000000000
--- a/openbsc/src/utils/meas_db.h
+++ /dev/null
@@ -1,17 +0,0 @@
-#ifndef OPENBSC_MEAS_DB_H
-#define OPENBSC_MEAS_DB_H
-
-struct meas_db_state;
-
-struct meas_db_state *meas_db_open(void *ctx, const char *fname);
-void meas_db_close(struct meas_db_state *st);
-
-int meas_db_begin(struct meas_db_state *st);
-int meas_db_commit(struct meas_db_state *st);
-
-int meas_db_insert(struct meas_db_state *st, const char *imsi,
- const char *name, unsigned long timestamp,
- const char *scenario,
- const struct gsm_meas_rep *mr);
-
-#endif
diff --git a/openbsc/src/utils/meas_json.c b/openbsc/src/utils/meas_json.c
deleted file mode 100644
index 51eb6c74e..000000000
--- a/openbsc/src/utils/meas_json.c
+++ /dev/null
@@ -1,190 +0,0 @@
-/* Convert measurement report feed into JSON feed printed to stdout.
- * Each measurement report is printed as a separae JSON root entry.
- * All measurement reports are separated by a new line.
- */
-
-/* (C) 2015 by Alexander Chemeris <Alexander.Chemeris@fairwaves.co>
- * With parts of code adopted from different places in OpenBSC.
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public 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 <unistd.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <time.h>
-
-#include <netinet/in.h>
-
-#include <osmocom/core/socket.h>
-#include <osmocom/core/msgb.h>
-#include <osmocom/core/select.h>
-
-#include <osmocom/gsm/gsm_utils.h>
-
-#include <openbsc/gsm_data.h>
-#include <openbsc/gsm_data_shared.h>
-#include <openbsc/meas_feed.h>
-
-static void print_meas_rep_uni_json(struct gsm_meas_rep_unidir *mru)
-{
- printf("\"RXL-FULL\":%d, \"RXL-SUB\":%d, ",
- rxlev2dbm(mru->full.rx_lev),
- rxlev2dbm(mru->sub.rx_lev));
- printf("\"RXQ-FULL\":%d, \"RXQ-SUB\":%d",
- mru->full.rx_qual, mru->sub.rx_qual);
-}
-
-static void print_meas_rep_json(struct gsm_meas_rep *mr)
-{
- int i;
-
- printf("\"NR\":%d", mr->nr);
-
- if (mr->flags & MEAS_REP_F_DL_DTX)
- printf(", \"DTXd\":true");
-
- printf(", \"UL_MEAS\":{");
- print_meas_rep_uni_json(&mr->ul);
- printf("}");
- printf(", \"BS_POWER\":%d", mr->bs_power);
- if (mr->flags & MEAS_REP_F_MS_TO)
- printf(", \"MS_TO\":%d", mr->ms_timing_offset);
-
- if (mr->flags & MEAS_REP_F_MS_L1) {
- printf(", \"L1_MS_PWR\":%d", mr->ms_l1.pwr);
- printf(", \"L1_FPC\":%s",
- mr->flags & MEAS_REP_F_FPC ? "true" : "false");
- printf(", \"L1_TA\":%u", mr->ms_l1.ta);
- }
-
- if (mr->flags & MEAS_REP_F_UL_DTX)
- printf(", \"DTXu\":true");
- if (mr->flags & MEAS_REP_F_BA1)
- printf(", \"BA1\":true");
- if (mr->flags & MEAS_REP_F_DL_VALID) {
- printf(", \"DL_MEAS\":{");
- print_meas_rep_uni_json(&mr->dl);
- printf("}");
- }
-
- if (mr->num_cell == 7)
- return;
- printf(", \"NUM_NEIGH\":%u, \"NEIGH\":[", mr->num_cell);
- for (i = 0; i < mr->num_cell; i++) {
- struct gsm_meas_rep_cell *mrc = &mr->cell[i];
- if (i!=0) printf(", ");
- printf("{\"IDX\":%u, \"ARFCN\":%u, \"BSIC\":%u, \"POWER\":%d}",
- mrc->neigh_idx, mrc->arfcn, mrc->bsic, rxlev2dbm(mrc->rxlev));
- }
- printf("]");
-}
-
-static void print_chan_info_json(struct meas_feed_meas *mfm)
-{
- printf("\"lchan_type\":\"%s\", \"pchan_type\":\"%s\", "
- "\"bts_nr\":%d, \"trx_nr\":%d, \"ts_nr\":%d, \"ss_nr\":%d",
- gsm_lchant_name(mfm->lchan_type), gsm_pchan_name(mfm->pchan_type),
- mfm->bts_nr, mfm->trx_nr, mfm->ts_nr, mfm->ss_nr);
-}
-
-static void print_meas_feed_json(struct meas_feed_meas *mfm)
-{
- time_t now = time(NULL);
-
- printf("{");
- printf("\"time\":%ld, \"imsi\":\"%s\", \"name\":\"%s\", \"scenario\":\"%s\", ",
- now, mfm->imsi, mfm->name, mfm->scenario);
-
- switch (mfm->hdr.version) {
- case 1:
- printf("\"chan_info\":{");
- print_chan_info_json(mfm);
- printf("}, ");
- /* no break, fall to version 0 */
- case 0:
- printf("\"meas_rep\":{");
- print_meas_rep_json(&mfm->mr);
- printf("}");
- break;
- }
-
- printf("}\n");
-
-}
-
-static int handle_meas(struct msgb *msg)
-{
- struct meas_feed_meas *mfm = (struct meas_feed_meas *) msgb_data(msg);
-
- print_meas_feed_json(mfm);
-
- return 0;
-}
-
-static int handle_msg(struct msgb *msg)
-{
- struct meas_feed_hdr *mfh = (struct meas_feed_hdr *) msgb_data(msg);
-
- if (mfh->version != MEAS_FEED_VERSION)
- return -EINVAL;
-
- switch (mfh->msg_type) {
- case MEAS_FEED_MEAS:
- handle_meas(msg);
- break;
- default:
- break;
- }
- return 0;
-}
-
-static int udp_fd_cb(struct osmo_fd *ofd, unsigned int what)
-{
- int rc;
-
- if (what & BSC_FD_READ) {
- struct msgb *msg = msgb_alloc(1024, "UDP Rx");
-
- rc = read(ofd->fd, msgb_data(msg), msgb_tailroom(msg));
- if (rc < 0)
- return rc;
- msgb_put(msg, rc);
- handle_msg(msg);
- msgb_free(msg);
- }
-
- return 0;
-}
-
-int main(int argc, char **argv)
-{
- int rc;
- struct osmo_fd udp_ofd;
-
- udp_ofd.cb = udp_fd_cb;
- rc = osmo_sock_init_ofd(&udp_ofd, AF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, 8888, OSMO_SOCK_F_BIND);
- if (rc < 0)
- exit(1);
-
- while (1) {
- osmo_select_main(0);
- };
-
- exit(0);
-}
diff --git a/openbsc/src/utils/meas_pcap2db.c b/openbsc/src/utils/meas_pcap2db.c
deleted file mode 100644
index b874ac403..000000000
--- a/openbsc/src/utils/meas_pcap2db.c
+++ /dev/null
@@ -1,138 +0,0 @@
-/* read PCAP file with meas_feed data and write it to sqlite3 database */
-
-/* (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 <string.h>
-#include <errno.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <stdio.h>
-
-#include <netinet/in.h>
-#include <netinet/ip.h>
-#include <netinet/udp.h>
-
-#include <osmocom/core/socket.h>
-#include <osmocom/core/utils.h>
-#include <osmocom/core/select.h>
-#include <osmocom/core/talloc.h>
-
-#include <osmocom/gsm/gsm_utils.h>
-
-#include <openbsc/meas_feed.h>
-
-#include <pcap/pcap.h>
-
-#include "meas_db.h"
-
-static struct meas_db_state *db;
-
-static void handle_mfm(const struct pcap_pkthdr *h,
- const struct meas_feed_meas *mfm)
-{
- const char *scenario;
-
- if (strlen(mfm->scenario))
- scenario = mfm->scenario;
- else
- scenario = NULL;
-
- meas_db_insert(db, mfm->imsi, mfm->name, h->ts.tv_sec,
- scenario, &mfm->mr);
-}
-
-static void pcap_cb(u_char *user, const struct pcap_pkthdr *h,
- const u_char *bytes)
-{
- const char *cur = bytes;
- const struct iphdr *ip;
- const struct udphdr *udp;
- const struct meas_feed_meas *mfm;
- uint16_t udplen;
-
- if (h->caplen < 14+20+8)
- return;
-
- /* Check if there is IPv4 in the Ethernet */
- if (cur[12] != 0x08 || cur[13] != 0x00)
- return;
-
- cur += 14; /* ethernet header */
- ip = (struct iphdr *) cur;
-
- if (ip->version != 4)
- return;
- cur += ip->ihl * 4;
-
- if (ip->protocol != IPPROTO_UDP)
- return;
-
- udp = (struct udphdr *) cur;
-
- if (udp->dest != htons(8888))
- return;
-
- udplen = ntohs(udp->len);
- if (udplen != sizeof(*udp) + sizeof(*mfm))
- return;
- cur += sizeof(*udp);
-
- mfm = (const struct meas_feed_meas *) cur;
-
- handle_mfm(h, mfm);
-}
-
-int main(int argc, char **argv)
-{
- char errbuf[PCAP_ERRBUF_SIZE+1];
- char *pcap_fname, *db_fname;
- pcap_t *pc;
- int rc;
-
- if (argc < 3) {
- fprintf(stderr, "You need to specify PCAP and database file\n");
- exit(2);
- }
-
- pcap_fname = argv[1];
- db_fname = argv[2];
-
- pc = pcap_open_offline(pcap_fname, errbuf);
- if (!pc) {
- fprintf(stderr, "Cannot open %s: %s\n", pcap_fname, errbuf);
- exit(1);
- }
-
- db = meas_db_open(NULL, db_fname);
- if (!db)
- exit(0);
-
- rc = meas_db_begin(db);
- if (rc < 0) {
- fprintf(stderr, "Error during BEGIN\n");
- exit(1);
- }
-
- pcap_loop(pc, 0 , pcap_cb, NULL);
-
- meas_db_commit(db);
-
- exit(0);
-}
diff --git a/openbsc/src/utils/meas_udp2db.c b/openbsc/src/utils/meas_udp2db.c
deleted file mode 100644
index 5032d0c3e..000000000
--- a/openbsc/src/utils/meas_udp2db.c
+++ /dev/null
@@ -1,126 +0,0 @@
-/* liesten to meas_feed on UDP and write it to sqlite3 database */
-
-/* (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 <string.h>
-#include <errno.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <time.h>
-
-#include <netinet/in.h>
-
-#include <osmocom/core/socket.h>
-#include <osmocom/core/utils.h>
-#include <osmocom/core/msgb.h>
-#include <osmocom/core/select.h>
-#include <osmocom/core/talloc.h>
-
-#include <osmocom/gsm/gsm_utils.h>
-
-#include <openbsc/meas_feed.h>
-
-#include "meas_db.h"
-
-static struct osmo_fd udp_ofd;
-static struct meas_db_state *db;
-
-static int handle_msg(struct msgb *msg)
-{
- struct meas_feed_hdr *mfh = (struct meas_feed_hdr *) msgb_data(msg);
- struct meas_feed_meas *mfm = (struct meas_feed_meas *) msgb_data(msg);
- const char *scenario;
- time_t now = time(NULL);
-
- if (mfh->version != MEAS_FEED_VERSION)
- return -EINVAL;
-
- if (mfh->msg_type != MEAS_FEED_MEAS)
- return -EINVAL;
-
- if (strlen(mfm->scenario))
- scenario = mfm->scenario;
- else
- scenario = NULL;
-
- meas_db_insert(db, mfm->imsi, mfm->name, now,
- scenario, &mfm->mr);
-
- return 0;
-}
-
-static int udp_fd_cb(struct osmo_fd *ofd, unsigned int what)
-{
- int rc;
-
- if (what & BSC_FD_READ) {
- struct msgb *msg = msgb_alloc(1024, "UDP Rx");
-
- rc = read(ofd->fd, msgb_data(msg), msgb_tailroom(msg));
- if (rc < 0)
- return rc;
- msgb_put(msg, rc);
- handle_msg(msg);
- msgb_free(msg);
- }
-
- return 0;
-}
-
-int main(int argc, char **argv)
-{
- char *db_fname;
- int rc;
-
- msgb_talloc_ctx_init(NULL, 0);
-
- if (argc < 2) {
- fprintf(stderr, "You have to specify the database file name\n");
- exit(2);
- }
-
- db_fname = argv[1];
-
- udp_ofd.cb = udp_fd_cb;
- rc = osmo_sock_init_ofd(&udp_ofd, AF_INET, SOCK_DGRAM,
- IPPROTO_UDP, NULL, 8888, OSMO_SOCK_F_BIND);
- if (rc < 0) {
- fprintf(stderr, "Unable to create UDP listen socket\n");
- exit(1);
- }
-
- db = meas_db_open(NULL, db_fname);
- if (!db) {
- fprintf(stderr, "Unable to open database\n");
- exit(1);
- }
-
- /* FIXME: timer-based BEGIN/COMMIT */
-
- while (1) {
- osmo_select_main(0);
- };
-
- meas_db_close(db);
-
- exit(0);
-}
-
diff --git a/openbsc/src/utils/meas_vis.c b/openbsc/src/utils/meas_vis.c
deleted file mode 100644
index 77194ded4..000000000
--- a/openbsc/src/utils/meas_vis.c
+++ /dev/null
@@ -1,310 +0,0 @@
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <stdio.h>
-
-#include <netinet/in.h>
-
-#include <cdk/cdk.h>
-
-#include <osmocom/core/socket.h>
-#include <osmocom/core/utils.h>
-#include <osmocom/core/msgb.h>
-#include <osmocom/core/select.h>
-#include <osmocom/core/talloc.h>
-
-#include <osmocom/gsm/gsm_utils.h>
-
-#include <openbsc/meas_feed.h>
-
-struct ms_state_uni {
- CDKSLIDER *cdk;
- CDKLABEL *cdk_label;
-
- time_t last_update;
- char label[32];
- char *_lbl[1];
-};
-
-
-struct ms_state {
- struct llist_head list;
-
- char name[31+1];
- char imsi[15+1];
- struct gsm_meas_rep mr;
-
- struct ms_state_uni ul;
- struct ms_state_uni dl;
-};
-
-struct state {
- struct osmo_fd udp_ofd;
- struct llist_head ms_list;
-
- CDKSCREEN *cdkscreen;
- WINDOW *curses_win;
-
- CDKLABEL *cdk_title;
- char *title;
-
- CDKLABEL *cdk_header;
- char header[256];
-};
-
-static struct state g_st;
-
-struct ms_state *find_ms(const char *imsi)
-{
- struct ms_state *ms;
-
- llist_for_each_entry(ms, &g_st.ms_list, list) {
- if (!strcmp(ms->imsi, imsi))
- return ms;
- }
- return NULL;
-}
-
-static struct ms_state *find_alloc_ms(const char *imsi)
-{
- struct ms_state *ms;
-
- ms = find_ms(imsi);
- if (!ms) {
- ms = talloc_zero(NULL, struct ms_state);
- osmo_strlcpy(ms->imsi, imsi, sizeof(ms->imsi));
- ms->ul._lbl[0] = ms->ul.label;
- ms->dl._lbl[0] = ms->dl.label;
- llist_add_tail(&ms->list, &g_st.ms_list);
- }
-
- return ms;
-}
-
-static int handle_meas(struct msgb *msg)
-{
- struct meas_feed_meas *mfm = (struct meas_feed_meas *) msgb_data(msg);
- struct ms_state *ms = find_alloc_ms(mfm->imsi);
- time_t now = time(NULL);
-
- osmo_strlcpy(ms->name, mfm->name, sizeof(ms->name));
- memcpy(&ms->mr, &mfm->mr, sizeof(ms->mr));
- ms->ul.last_update = now;
- if (ms->mr.flags & MEAS_REP_F_DL_VALID)
- ms->dl.last_update = now;
-
- /* move to head of list */
- llist_del(&ms->list);
- llist_add(&ms->list, &g_st.ms_list);
-
- return 0;
-}
-
-static int handle_msg(struct msgb *msg)
-{
- struct meas_feed_hdr *mfh = (struct meas_feed_hdr *) msgb_data(msg);
-
- if (mfh->version != MEAS_FEED_VERSION)
- return -EINVAL;
-
- switch (mfh->msg_type) {
- case MEAS_FEED_MEAS:
- handle_meas(msg);
- break;
- default:
- break;
- }
-
- return 0;
-}
-
-static int udp_fd_cb(struct osmo_fd *ofd, unsigned int what)
-{
- int rc;
-
- if (what & BSC_FD_READ) {
- struct msgb *msg = msgb_alloc(1024, "UDP Rx");
-
- rc = read(ofd->fd, msgb_data(msg), msgb_tailroom(msg));
- if (rc < 0)
- return rc;
- msgb_put(msg, rc);
- handle_msg(msg);
- msgb_free(msg);
- }
-
- return 0;
-}
-
-
-static void destroy_dir(struct ms_state_uni *uni)
-{
- if (uni->cdk) {
- destroyCDKSlider(uni->cdk);
- uni->cdk = NULL;
- }
- if (uni->cdk_label) {
- destroyCDKLabel(uni->cdk_label);
- uni->cdk_label = NULL;
- }
-}
-
-#define DIR_UL 0
-#define DIR_DL 1
-static const char *dir_str[2] = {
- [DIR_UL] = "UL",
- [DIR_DL] = "DL",
-};
-
-static int colpair_by_qual(uint8_t rx_qual)
-{
- if (rx_qual == 0)
- return 24;
- else if (rx_qual <= 4)
- return 32;
- else
- return 16;
-}
-
-static int colpair_by_lev(int rx_lev)
-{
- if (rx_lev < -95)
- return 16;
- else if (rx_lev < -80)
- return 32;
- else
- return 24;
-}
-
-
-void write_uni(struct ms_state *ms, struct ms_state_uni *msu,
- struct gsm_rx_lev_qual *lq, int dir, int row)
-{
-
- char label[128];
- time_t now = time(NULL);
- int qual_col = colpair_by_qual(lq->rx_qual);
- int lev_col = colpair_by_lev(rxlev2dbm(lq->rx_lev));
- int color, pwr;
-
- if (dir == DIR_UL) {
- pwr = ms->mr.ms_l1.pwr;
- } else {
- pwr = ms->mr.bs_power;
- }
-
- color = A_REVERSE | COLOR_PAIR(lev_col) | ' ';
- snprintf(label, sizeof(label), "%s %s ", ms->imsi, dir_str[dir]);
- msu->cdk = newCDKSlider(g_st.cdkscreen, 0, row, NULL, label, color,
- COLS-40, rxlev2dbm(lq->rx_lev), -110, -47,
- 1, 2, FALSE, FALSE);
- //IsVisibleObj(ms->ul.cdk) = FALSE;
- snprintf(msu->label, sizeof(msu->label), "</%d>%1d<!%d> %3d %2u %2d %4u",
- qual_col, lq->rx_qual, qual_col, pwr,
- ms->mr.ms_l1.ta, ms->mr.ms_timing_offset,
- now - msu->last_update);
- msu->cdk_label = newCDKLabel(g_st.cdkscreen, RIGHT, row,
- msu->_lbl, 1, FALSE, FALSE);
-}
-
-static void update_sliders(void)
-{
- int num_vis_sliders = 0;
- struct ms_state *ms;
-#define HEADER_LINES 2
-
- /* remove all sliders */
- llist_for_each_entry(ms, &g_st.ms_list, list) {
- destroy_dir(&ms->ul);
- destroy_dir(&ms->dl);
-
- }
-
- llist_for_each_entry(ms, &g_st.ms_list, list) {
- struct gsm_rx_lev_qual *lq;
- unsigned int row = HEADER_LINES + num_vis_sliders*3;
-
- if (ms->mr.flags & MEAS_REP_F_UL_DTX)
- lq = &ms->mr.ul.sub;
- else
- lq = &ms->mr.ul.full;
- write_uni(ms, &ms->ul, lq, DIR_UL, row);
-
- if (ms->mr.flags & MEAS_REP_F_DL_DTX)
- lq = &ms->mr.dl.sub;
- else
- lq = &ms->mr.dl.full;
- write_uni(ms, &ms->dl, lq, DIR_DL, row+1);
-
- num_vis_sliders++;
- if (num_vis_sliders >= LINES/3)
- break;
- }
-
- refreshCDKScreen(g_st.cdkscreen);
-
-}
-
-const struct value_string col_strs[] = {
- { COLOR_WHITE, "white" },
- { COLOR_RED, "red" },
- { COLOR_GREEN, "green" },
- { COLOR_YELLOW, "yellow" },
- { COLOR_BLUE, "blue" },
- { COLOR_MAGENTA,"magenta" },
- { COLOR_CYAN, "cyan" },
- { COLOR_BLACK, "black" },
- { 0, NULL }
-};
-
-int main(int argc, char **argv)
-{
- int rc;
- char *header[1];
- char *title[1];
-
- msgb_talloc_ctx_init(NULL, 0);
-
- printf("sizeof(gsm_meas_rep)=%u\n", sizeof(struct gsm_meas_rep));
- printf("sizeof(meas_feed_meas)=%u\n", sizeof(struct meas_feed_meas));
-
- INIT_LLIST_HEAD(&g_st.ms_list);
- g_st.curses_win = initscr();
- g_st.cdkscreen = initCDKScreen(g_st.curses_win);
- initCDKColor();
-
- g_st.title = "OpenBSC link quality monitor";
- title[0] = g_st.title;
- g_st.cdk_title = newCDKLabel(g_st.cdkscreen, CENTER, 0, title, 1, FALSE, FALSE);
-
- snprintf(g_st.header, sizeof(g_st.header), "Q Pwr TA TO Time");
- header[0] = g_st.header;
- g_st.cdk_header = newCDKLabel(g_st.cdkscreen, RIGHT, 1, header, 1, FALSE, FALSE);
-
-#if 0
- int i;
- for (i = 0; i < 64; i++) {
- short f, b;
- pair_content(i, &f, &b);
- attron(COLOR_PAIR(i));
- printw("%u: %u (%s) ", i, f, get_value_string(col_strs, f));
- printw("%u (%s)\n\r", b, get_value_string(col_strs, b));
- }
- refresh();
- getch();
- exit(0);
-#endif
-
- g_st.udp_ofd.cb = udp_fd_cb;
- rc = osmo_sock_init_ofd(&g_st.udp_ofd, AF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, 8888, OSMO_SOCK_F_BIND);
- if (rc < 0)
- exit(1);
-
- while (1) {
- osmo_select_main(0);
- update_sliders();
- };
-
- exit(0);
-}
diff --git a/openbsc/src/utils/smpp_mirror.c b/openbsc/src/utils/smpp_mirror.c
deleted file mode 100644
index 95df5f2a6..000000000
--- a/openbsc/src/utils/smpp_mirror.c
+++ /dev/null
@@ -1,329 +0,0 @@
-#include <stdio.h>
-#include <unistd.h>
-#include <stdint.h>
-#include <errno.h>
-#include <string.h>
-
-#include <netinet/in.h>
-
-#include <smpp34.h>
-#include <smpp34_structs.h>
-#include <smpp34_params.h>
-
-#include <osmocom/core/application.h>
-#include <osmocom/core/utils.h>
-#include <osmocom/core/msgb.h>
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/select.h>
-#include <osmocom/core/socket.h>
-#include <osmocom/core/write_queue.h>
-
-#include <openbsc/debug.h>
-
-/* FIXME: merge with smpp_smsc.c */
-#define SMPP_SYS_ID_LEN 16
-enum esme_read_state {
- READ_ST_IN_LEN = 0,
- READ_ST_IN_MSG = 1,
-};
-/* FIXME: merge with smpp_smsc.c */
-
-struct esme {
- struct osmo_fd ofd;
-
- uint32_t own_seq_nr;
-
- struct osmo_wqueue wqueue;
- enum esme_read_state read_state;
- uint32_t read_len;
- uint32_t read_idx;
- struct msgb *read_msg;
-
- uint8_t smpp_version;
- char system_id[SMPP_SYS_ID_LEN+1];
- char password[SMPP_SYS_ID_LEN+1];
-};
-
-/* FIXME: merge with smpp_smsc.c */
-#define SMPP34_UNPACK(rc, type, str, data, len) \
- memset(str, 0, sizeof(*str)); \
- rc = smpp34_unpack(type, str, data, len)
-#define INIT_RESP(type, resp, req) { \
- memset((resp), 0, sizeof(*(resp))); \
- (resp)->command_length = 0; \
- (resp)->command_id = type; \
- (resp)->command_status = ESME_ROK; \
- (resp)->sequence_number = (req)->sequence_number; \
-}
-#define PACK_AND_SEND(esme, ptr) pack_and_send(esme, (ptr)->command_id, ptr)
-static inline uint32_t smpp_msgb_cmdid(struct msgb *msg)
-{
- uint8_t *tmp = msgb_data(msg) + 4;
- return ntohl(*(uint32_t *)tmp);
-}
-static uint32_t esme_inc_seq_nr(struct esme *esme)
-{
- esme->own_seq_nr++;
- if (esme->own_seq_nr > 0x7fffffff)
- esme->own_seq_nr = 1;
-
- return esme->own_seq_nr;
-}
-static int pack_and_send(struct esme *esme, uint32_t type, void *ptr)
-{
- struct msgb *msg = msgb_alloc(4096, "SMPP_Tx");
- int rc, rlen;
- if (!msg)
- return -ENOMEM;
-
- rc = smpp34_pack(type, msg->tail, msgb_tailroom(msg), &rlen, ptr);
- if (rc != 0) {
- LOGP(DSMPP, LOGL_ERROR, "[%s] Error during smpp34_pack(): %s\n",
- esme->system_id, smpp34_strerror);
- msgb_free(msg);
- return -EINVAL;
- }
- msgb_put(msg, rlen);
-
- if (osmo_wqueue_enqueue(&esme->wqueue, msg) != 0) {
- LOGP(DSMPP, LOGL_ERROR, "[%s] Write queue full. Dropping message\n",
- esme->system_id);
- msgb_free(msg);
- return -EAGAIN;
- }
- return 0;
-}
-/* FIXME: merge with smpp_smsc.c */
-
-
-static int smpp_handle_deliver(struct esme *esme, struct msgb *msg)
-{
- struct deliver_sm_t deliver;
- struct deliver_sm_resp_t deliver_r;
- struct submit_sm_t submit;
- int rc;
-
- memset(&deliver, 0, sizeof(deliver));
- SMPP34_UNPACK(rc, DELIVER_SM, &deliver, msgb_data(msg), msgb_length(msg));
- if (rc < 0)
- return rc;
-
- INIT_RESP(DELIVER_SM_RESP, &deliver_r, &deliver);
-
- PACK_AND_SEND(esme, &deliver_r);
-
- memset(&submit, 0, sizeof(submit));
- submit.command_id = SUBMIT_SM;
- submit.command_status = ESME_ROK;
- submit.sequence_number = esme_inc_seq_nr(esme);
-
- submit.dest_addr_ton = deliver.source_addr_ton;
- submit.dest_addr_npi = deliver.source_addr_npi;
- memcpy(submit.destination_addr, deliver.source_addr,
- OSMO_MIN(sizeof(submit.destination_addr),
- sizeof(deliver.source_addr)));
-
- submit.source_addr_ton = deliver.dest_addr_ton;
- submit.source_addr_npi = deliver.dest_addr_npi;
- memcpy(submit.source_addr, deliver.destination_addr,
- OSMO_MIN(sizeof(submit.source_addr),
- sizeof(deliver.destination_addr)));
-
- submit.esm_class = deliver.esm_class;
- submit.protocol_id = deliver.protocol_id;
- submit.priority_flag = deliver.priority_flag;
- memcpy(submit.schedule_delivery_time, deliver.schedule_delivery_time,
- OSMO_MIN(sizeof(submit.schedule_delivery_time),
- sizeof(deliver.schedule_delivery_time)));
- memcpy(submit.validity_period, deliver.validity_period,
- OSMO_MIN(sizeof(submit.validity_period),
- sizeof(deliver.validity_period)));
- submit.registered_delivery = deliver.registered_delivery;
- submit.replace_if_present_flag = deliver.replace_if_present_flag;
- submit.data_coding = deliver.data_coding;
- submit.sm_default_msg_id = deliver.sm_default_msg_id;
- submit.sm_length = deliver.sm_length;
- memcpy(submit.short_message, deliver.short_message,
- OSMO_MIN(sizeof(submit.short_message),
- sizeof(deliver.short_message)));
- /* FIXME: TLV? */
-
- return PACK_AND_SEND(esme, &submit);
-}
-
-static int bind_transceiver(struct esme *esme)
-{
- struct bind_transceiver_t bind;
-
- memset(&bind, 0, sizeof(bind));
- bind.command_id = BIND_TRANSCEIVER;
- bind.sequence_number = esme_inc_seq_nr(esme);
- snprintf((char *)bind.system_id, sizeof(bind.system_id), "%s", esme->system_id);
- snprintf((char *)bind.password, sizeof(bind.password), "%s", esme->password);
- snprintf((char *)bind.system_type, sizeof(bind.system_type), "mirror");
- bind.interface_version = esme->smpp_version;
-
- return PACK_AND_SEND(esme, &bind);
-}
-
-static int smpp_pdu_rx(struct esme *esme, struct msgb *msg)
-{
- uint32_t cmd_id = smpp_msgb_cmdid(msg);
- int rc;
-
- switch (cmd_id) {
- case DELIVER_SM:
- rc = smpp_handle_deliver(esme, msg);
- break;
- default:
- LOGP(DSMPP, LOGL_NOTICE, "unhandled case %d\n", cmd_id);
- rc = 0;
- break;
- }
-
- return rc;
-}
-
-/* FIXME: merge with smpp_smsc.c */
-static int esme_read_cb(struct osmo_fd *ofd)
-{
- struct esme *esme = ofd->data;
- uint32_t len;
- uint8_t *lenptr = (uint8_t *) &len;
- uint8_t *cur;
- struct msgb *msg;
- int rdlen;
- int rc;
-
- switch (esme->read_state) {
- case READ_ST_IN_LEN:
- rdlen = sizeof(uint32_t) - esme->read_idx;
- rc = read(ofd->fd, lenptr + esme->read_idx, rdlen);
- if (rc < 0) {
- LOGP(DSMPP, LOGL_ERROR, "[%s] read returned %d\n",
- esme->system_id, rc);
- } else if (rc == 0) {
- goto dead_socket;
- } else
- esme->read_idx += rc;
- if (esme->read_idx >= sizeof(uint32_t)) {
- esme->read_len = ntohl(len);
- msg = msgb_alloc(esme->read_len, "SMPP Rx");
- if (!msg)
- return -ENOMEM;
- esme->read_msg = msg;
- cur = msgb_put(msg, sizeof(uint32_t));
- memcpy(cur, lenptr, sizeof(uint32_t));
- esme->read_state = READ_ST_IN_MSG;
- esme->read_idx = sizeof(uint32_t);
- }
- break;
- case READ_ST_IN_MSG:
- msg = esme->read_msg;
- rdlen = esme->read_len - esme->read_idx;
- rc = read(ofd->fd, msg->tail, OSMO_MIN(rdlen, msgb_tailroom(msg)));
- if (rc < 0) {
- LOGP(DSMPP, LOGL_ERROR, "[%s] read returned %d\n",
- esme->system_id, rc);
- } else if (rc == 0) {
- goto dead_socket;
- } else {
- esme->read_idx += rc;
- msgb_put(msg, rc);
- }
-
- if (esme->read_idx >= esme->read_len) {
- rc = smpp_pdu_rx(esme, esme->read_msg);
- esme->read_msg = NULL;
- esme->read_idx = 0;
- esme->read_len = 0;
- esme->read_state = READ_ST_IN_LEN;
- }
- break;
- }
-
- return 0;
-dead_socket:
- msgb_free(esme->read_msg);
- osmo_fd_unregister(&esme->wqueue.bfd);
- close(esme->wqueue.bfd.fd);
- esme->wqueue.bfd.fd = -1;
- exit(2342);
-
- return 0;
-}
-
-static int esme_write_cb(struct osmo_fd *ofd, struct msgb *msg)
-{
- struct esme *esme = ofd->data;
- int rc;
-
- rc = write(ofd->fd, msgb_data(msg), msgb_length(msg));
- if (rc == 0) {
- osmo_fd_unregister(&esme->wqueue.bfd);
- close(esme->wqueue.bfd.fd);
- esme->wqueue.bfd.fd = -1;
- exit(99);
- } else if (rc < msgb_length(msg)) {
- LOGP(DSMPP, LOGL_ERROR, "[%s] Short write\n", esme->system_id);
- return 0;
- }
-
- return 0;
-}
-
-static int smpp_esme_init(struct esme *esme, const char *host, uint16_t port)
-{
- int rc;
-
- if (port == 0)
- port = 2775;
-
- esme->own_seq_nr = rand();
- esme_inc_seq_nr(esme);
- osmo_wqueue_init(&esme->wqueue, 10);
- esme->wqueue.bfd.data = esme;
- esme->wqueue.read_cb = esme_read_cb;
- esme->wqueue.write_cb = esme_write_cb;
-
- rc = osmo_sock_init_ofd(&esme->wqueue.bfd, AF_UNSPEC, SOCK_STREAM,
- IPPROTO_TCP, host, port, OSMO_SOCK_F_CONNECT);
- if (rc < 0)
- return rc;
-
- return bind_transceiver(esme);
-}
-
-
-int main(int argc, char **argv)
-{
- struct esme esme;
- char *host = "localhost";
- int port = 0;
- int rc;
-
- msgb_talloc_ctx_init(NULL, 0);
-
- memset(&esme, 0, sizeof(esme));
-
- osmo_init_logging(&log_info);
-
- snprintf((char *) esme.system_id, sizeof(esme.system_id), "mirror");
- snprintf((char *) esme.password, sizeof(esme.password), "mirror");
- esme.smpp_version = 0x34;
-
- if (argc >= 2)
- host = argv[1];
- if (argc >= 3)
- port = atoi(argv[2]);
-
- rc = smpp_esme_init(&esme, host, port);
- if (rc < 0)
- exit(1);
-
- while (1) {
- osmo_select_main(0);
- }
-
- exit(0);
-}